Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: GCC: Аварийный выход из прерывания (функции)
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > MCS51, AVR, PIC, STM8, 8bit
MaxiMuz
Возникла ситуация когда при обработке прерывания нужно изменить привычный ход программы, когда содержимое сохранненых регистров и указатель стека уже не важны. Оператор goto тут не работает:
Код
ISR (xxx_vect)
{
  ...
  ...
  goto m1;
}
void main (void)
{
  ...
  ...
  while (1)
    { ...
           ...
         }
m1:
  ...
  ...
}

Что можно здесь сделать ?
Lagman
Просто из прерывания вызываете другую функцию, зачем вам в main возвращаться, если вам не важны стэк и регистры.
zombi
Не знаю как на СИ.
А на асме записываю в стек нужный мне адрес и ret
Палыч
Функции setjmp / longjmp подойдут ?
Xenia
Проще всего в программе установить семафоры с ветвлением по if или switch, а из прерывания эти семафоры переключать.
MaxiMuz
Цитата(Lagman @ May 16 2013, 18:45) *
Просто из прерывания вызываете другую функцию, зачем вам в main возвращаться, если вам не важны стэк и регистры.
не могу перегружать память сохраняемыми регистрами
в теме была описана ситуевина: http://electronix.ru/forum/index.php?showtopic=112377

Цитата(zombi @ May 16 2013, 19:09) *
Не знаю как на СИ.
А на асме записываю в стек нужный мне адрес и ret
на Асме я бы просто rjmp написал

Цитата(Палыч @ May 16 2013, 19:40) *
Функции setjmp / longjmp подойдут ?
похоже нет

Цитата(Палыч @ May 16 2013, 19:40) *
Функции setjmp / longjmp подойдут ?
похоже нет

Цитата(Xenia @ May 16 2013, 20:51) *
Проще всего в программе установить семафоры с ветвлением по if или switch, а из прерывания эти семафоры переключать.
я так и поступил , но всеже хочется разобраться с "прыжками"
_Артём_
Цитата(MaxiMuz @ May 16 2013, 21:20) *
не могу перегружать память сохраняемыми регистрами
в теме была описана ситуевина: http://electronix.ru/forum/index.php?showtopic=112377

А регистры сохраняются? Вы смотрели?
Цитата(MaxiMuz @ May 16 2013, 21:20) *
на Асме я бы просто rjmp написал

Так и напишите
Код
ISR(...)
{
asm("rjmp 0x1234"); // нужный адрес выберете сами
}

По выбранному адресу расположите вашу функцию (Си или асм)
Цитата(MaxiMuz @ May 16 2013, 21:20) *
я так и поступил , но всеже хочется разобраться с "прыжками"

Изврат это какой-то, чего с ними разбираться.
zombi
Цитата(MaxiMuz @ May 16 2013, 21:20) *
на Асме я бы просто rjmp написал

Смотря какой проц.
Палыч
Цитата(MaxiMuz @ May 16 2013, 22:20) *
похоже нет


Ну, отчего же...
Вот, почти полное повторение того, что Вы хотели
Код
    #include <setjmp.h>

    jmp_buf env;

    int main (void)
    {
        if (setjmp (env))
        {
            ... handle error ...
        }

        while (1)
        {
           ... main processing loop which calls foo() some where ...
        }
    }

    ...

    void foo (void)
    {
        ... blah, blah, blah ...

        if (err)
        {
            longjmp (env, 1);
        }
    }
MaxiMuz
Цитата(_Артём_ @ May 16 2013, 21:28) *
А регистры сохраняются? Вы смотрели?
почти все старшие регистры, независимо используются они или нет
Tiro
Цитата(MaxiMuz @ May 17 2013, 14:10) *
почти все старшие регистры, независимо используются они или нет

Метки как значения давно есть в ГСС. 4.3 Labels as Values
_Артём_
Цитата(MaxiMuz @ May 17 2013, 14:10) *
почти все старшие регистры, независимо используются они или нет

Вызываете функции из прерывания? Если да, то попробуйте закомментировать все вызовы и посмотрите сколько регистров сохраняется в стек. Или попоробуцте заинлайнить все вызываемые функции.
Возможно в прерывании действительно нужны все сохраняемые регистры.
Xenia
Цитата(MaxiMuz @ May 16 2013, 22:20) *
я так и поступил , но всеже хочется разобраться с "прыжками"


А прыгать нельзя! sm.gif Мало ли в каком состоянии стеки находятся в том месте, откуда сигануло в прерывание? Вы, положим по метке перейдете, а стек кто чистить будет?

Например, была вызвана функция, которая при входе создала кучу малу локальных переменных на стеке. Потом эта функция вызвала другую функцию, которая сделала то же самое. И т.д. А тут в тот момент, когда процессор в самой глубокой фунции код выполнял, ваше прерывание и приспичило. И куда вы теперь пойдете, когда стек полным-полнёхонек?
ARV
Цитата(MaxiMuz @ May 16 2013, 18:50) *
Возникла ситуация когда при обработке прерывания нужно изменить привычный ход программы, когда содержимое сохранненых регистров и указатель стека уже не важны. Что можно здесь сделать ?
по моему скромному мнению тут можно сделать только одно: разобраться с алгоритмом программы и переделать его так, чтобы этой ситуации не возникало. все, кто пытается помочь топикстартеру в решении его надуманной проблемы, оказывают ему медвежью услугу: проблем он поимеет больше, чем удобств, о чем уважаемая Xenia уже намекала.

с моей точки зрения ситуация, когда "регистры и состояние стека уже не важны" должна приводить к аппаратному сбросу контроллера, и никак иначе, тем более из прерывания.
MaxiMuz
Цитата(Tiro @ May 18 2013, 00:26) *
Метки как значения давно есть в ГСС. 4.3 Labels as Values

Помоему то что нужно!
Но всеже както странно , первый раз вижу оператор '&&' в таком применении. Это какаято особенность GCC стандарта ?

Цитата(Xenia @ May 19 2013, 03:12) *
А прыгать нельзя! sm.gif Мало ли в каком состоянии стеки находятся в том месте, откуда сигануло в прерывание? Вы, положим по метке перейдете, а стек кто чистить будет?

Xenia такой сценарий sm.gif Если уж выполненно определенное условие , то стек и состояние др.регистров неважно

Цитата(ARV @ May 19 2013, 09:49) *
с моей точки зрения ситуация, когда "регистры и состояние стека уже не важны" должна приводить к аппаратному сбросу контроллера, и никак иначе, тем более из прерывания.
Точно! программа должна выполнить определенные действия и завершиться.
Lagman
Цитата(MaxiMuz @ May 19 2013, 20:27) *
Точно! программа должна выполнить определенные действия и завершиться.

Если брать микроконтроллеры то там редко когда программа завершается, передавать управление некому, запуск только через включение питания (ресет).
Xenia
Цитата(Lagman @ May 19 2013, 22:26) *
Если брать микроконтроллеры то там редко когда программа завершается, передавать управление некому, запуск только через включение питания (ресет).


Lagman подал хорошую идею. Разовью.
Ветвление надо произвести только один раз - в самом начале main(), еще до вхождения в бесконечный цикл.
Что вроде того:
Код
char semafor = false;
main()
{
   if( semafor)
   {
      // определенные экстренные действия
   }
   for(;;)
   {
       // бесконечный цикл
   }
}

В прерывании же при обнаружении экстренного случая взводится светофор (semafor = true) и программа запускается с нулевого адреса (setjmp 0).

P.S. Кажись с пронеслась я со своей идеей, т.к. при инициализации semafor тоже почистится. Впрочем, в качестве семафора можно взять какой-нибудь аппаратный регистр.
_Артём_
Цитата(MaxiMuz @ May 19 2013, 19:27) *
Точно! программа должна выполнить определенные действия и завершиться.

Если нужно завершить программу, то почему бы не сделать возврат из прерывания не в точку вызова, а туда где лежит фнкция завершающая программу?
Код
volatile unsigned char fatal_error;
void my_exit()
{
    cli();
    while (fatal_error) {
        PORTC^=1;
        asm("nop");
        asm("nop");
        asm("nop");
        asm("nop");
    }
    
}
ISR(SPM_RDY_vect)
{
    SPMCR&=~(1<<SPMIE);
    unsigned char error_detected=0;
    // какие-то дайствия
    //
    if (error_detected) {
        unsigned short sp = SP;
        unsigned short return_address=(unsigned short)my_exit;
        unsigned char *ptr=(unsigned char *)sp;
        fatal_error=1;
        ptr++;
        *ptr++=return_address>>8;
        *ptr++=return_address&255;
        asm("ret");
    }
}
Tiro
Цитата(MaxiMuz @ May 19 2013, 19:27) *
Помоему то что нужно!
Но всеже както странно , первый раз вижу оператор '&&' в таком применении. Это какаято особенность GCC стандарта ?

Да, это GNU расширение стандарта "С". В Вашем случае я бы использовал для восстановления микроконтроллера функцию с атрибутами static inline, а потом пересбросил бы микроконтроллер.
Xenia
Цитата(_Артём_ @ May 20 2013, 00:02) *
Если нужно завершить программу, то почему бы не сделать возврат из прерывания не в точку вызова, а туда где лежит функция завершающая программу?


Тогда отчего бы не написать завершающую функцию прямо в прерывании? sm.gif
_Артём_
Цитата(Xenia @ May 20 2013, 02:31) *
Тогда отчего бы не написать завершающую функцию прямо в прерывании? sm.gif

Можно и написать, но наверняка тогда прерыванию потребуется больше регистров для завершения(количество регистров зависит отсложности действий при завершении), которое выполнится один раз. То есть прерывание всегда будет сохранять-востанавливать много регистров.
А если сделать по-моему варианту, то может быть прерыванию регистров меньше понадобится(но вариант с jmp лучше чем ret). Но работа программы завершится и возобновлена может быть только после сброса.
Палыч
Цитата(Xenia @ May 19 2013, 23:04) *
В прерывании же при обнаружении экстренного случая взводится светофор (semafor = true) и программа запускается с нулевого адреса

Если достаточно "мягкого" рестарта, то семафор заменяет пара функций setjmp / longjmp.
MaxiMuz
Цитата(_Артём_ @ May 19 2013, 23:02) *
Если нужно завершить программу, то почему бы не сделать возврат из прерывания не в точку вызова, а туда где лежит фнкция завершающая программу?

Вообщето я так и хочу сделать, только не в функцию , а в участок main
Сергей Борщ
QUOTE (MaxiMuz @ May 20 2013, 15:40) *
а в участок main
А вы уверены, что этот участок адекватно отреагирует на мусор в стеке?
_Артём_
Цитата(MaxiMuz @ May 20 2013, 15:40) *
только не в функцию , а в участок main

Мне почему-то кажется что код идущий после метки будет выкинут оптимизатором. Или нет?
Вы с какими настройками компилируете?
Xenia
Цитата(Сергей Борщ @ May 20 2013, 17:21) *
А вы уверены, что этот участок адекватно отреагирует на мусор в стеке?


Если это лебединая песнь, то легко можно установить стек(и) на начало перед прыжком.
Сергей Борщ
QUOTE (Xenia @ May 20 2013, 16:44) *
легко можно установить стек(и) на начало перед прыжком.
установить-то можно. Вот только main() может выделить на стеке место под переменные лебединой песни и даже, возможно, разместить там заранее какие-то значения. И при установке указателя стека на начало получим обращение к несуществующей памяти, а при переходе с кучей хлама на стеке будем обрабатывать мусор. Поэтому все и пишут, что решение ущербное по условию..
ARV
если очень хочется, то можно вот так:
Код
void  __attribute__((noreturn)) my_exit(void){
    PORTB = 0xFF;
    while(1);
}

ISR(TIMER1_OVF_vect){
    if(PINB & 1){
        PORTB = 0;
    } else {
        // проблема - обрабатываем лебединую песню!!!
        void *ptr = my_exit;
        goto *ptr;
    }
}
при желании my_exit() можно вызывать и в main, не нарушая общей структуры программы. хотя с моей точки зрения это кривой костыль, правильное по моему мнению решение я озвучивал ранее.
Xenia
Цитата(Сергей Борщ @ May 20 2013, 21:03) *
установить-то можно. Вот только main() может выделить на стеке место под переменные лебединой песни и даже, возможно, разместить там заранее какие-то значения. И при установке указателя стека на начало получим обращение к несуществующей памяти, а при переходе с кучей хлама на стеке будем обрабатывать мусор. Поэтому все и пишут, что решение ущербное по условию..


Нормальные люди в main() локальных переменных не заводят! sm.gif Что проку от их локальности, если main() никогда не завершается? Переменные, используемые в main(), лучше объявлять глобальными - результат тот же самый, зато стек не расходуется впустую. А если не хотите, чтобы эти переменные из других модулей были видны, то объявите их static.
_Артём_
Цитата(Xenia @ May 20 2013, 21:41) *
Нормальные люди в main() локальных переменных не заводят! sm.gif

Смишно.

Цитата(Xenia @ May 20 2013, 21:41) *
Переменные, используемые в main(), лучше объявлять глобальными - результат тот же самый, зато стек не расходуется впустую.

Нет результат не тот же самый - расходуется память под глобальные переменные, причём не меньше. В случае использования локальных переменных компилятор может использовать одни и те же адреса в ОЗУ для разных переменных, а для глобальных так не получится.

Цитата(Xenia @ May 20 2013, 21:41) *
А если не хотите, чтобы эти переменные из других модулей были видны, то объявите их static.

Зачем? Лучше их сделать локальными, тогда точно не будут видны.

Цитата(ARV @ May 20 2013, 20:12) *
ISR(TIMER1_OVF_vect){
if(PINB & 1){
PORTB = 0;
} else {
// проблема - обрабатываем лебединую песню!!!
void *ptr = my_exit;
goto *ptr;
}
}[/code]

goto заменилось на JMP my_exit без ненужного сохранения регистров. Надо же...
Xenia
Цитата(_Артём_ @ May 20 2013, 22:56) *
В случае использования локальных переменных компилятор может использовать одни и те же адреса в ОЗУ для разных переменных, а для глобальных так не получится.


Это только в том случае, если они освобождаются вместе с окончанием той функции/блока, в котором были заведены. Но main() свои локальные переменные (те, что заводятся в ее начале) не освободит никогда - на то у нее и бесконечный цикл внутри сидит.

В данном случае разговор зашел о том, что инциализация/сброс стека приведет к пропаже локальных переменных внутри main(). Так я и выразилась в том духе, что заводить внутри main() локальные переменные смысла не имеет. Т.е. речь шла не об экономии памяти, как вы это неверно поняли, а об устойчивости переменных к сбросу стека.

P.S. Не стоит обольщаться тем, что войдя джампом из прерывания во внутрь какой-то функции (в том числе и main), вы обнаружите там локальные переменные на тех же местах. Адреса локальных переменных отмеряются относительно текущего указателя стека, который при запрыгивании со стороны никак не может быть правильным (т.к. функция прерывания тоже пользуется стеком). Поэтому как ни крути, но валидными в этом случае могут быть только глобальные переменные, но не локальные. Причем независимо от того, сбрасывали вы стек или нет.
Tiro
Цитата(Xenia @ May 20 2013, 22:05) *
Адреса локальных переменных отмеряются относительно текущего указателя стека, который при запрыгивании со стороны никак не может быть правильным (т.к. функция прерывания тоже пользуется стеком). Поэтому как ни крути, но валидными в этом случае могут быть только глобальные переменные, но не локальные. Причем независимо от того, сбрасывали вы стек или нет.

Тут поддержу Ксению, но какое это имеет отношение к вопросу топикстартера? Он же сказал, что состояние программы и стека ему не известно и не важно. Возможно, стек переполнился и залез на глобальные переменные. Надежнее не использовать вообще никакие заранее определенные переменные. Используйте константы или заведите новые переменные, порушив глобальные, переставьте указатель стека на начало и вперед! Только вперед и только хардкор biggrin.gif
Сергей Борщ
_Артем_ ответил по всем пунктам.
Касаемо последних двух сообщений: компилятор не может знать вашей логики, заставить его "не использовать локальные переменные" нельзя. Решение с goto из одной функции в другую ущербное каким бы хаком оно бы ни было реализовано. longjump() еще куда ни шло - он поддержан компилятором.
Было же грамотное решение - функция с атрибутом noreturn. И вызывать ее как функцию, без всяких указателей и т.д. Компилятору этого noreturn по идее должно быть достаточно, чтобы не сохранять лишние регистры в каждом прерывании.

QUOTE (Xenia @ May 20 2013, 22:05) *
Но main() свои локальные переменные (те, что заводятся в ее начале) не освободит никогда - на то у нее и бесконечный цикл внутри сидит.
Во-первых не надо считать компиялтор настолько тупым. Во-вторых и внутри этого бесконечного цикла имеют право быть несколько блоков со своими локальными переменными.
QUOTE (Xenia @ May 20 2013, 22:05) *
Так я и выразилась в том духе, что заводить внутри main() локальные переменные смысла не имеет. Т.е. речь шла не об экономии памяти, как вы это неверно поняли, а об устойчивости переменных к сбросу стека.
То есть еще одна распорка чтобы конструкция перевязанная предыдущей расчалкой не навернулась.
QUOTE (Xenia @ May 20 2013, 22:05) *
P.S. Не стоит обольщаться тем, что войдя джампом из прерывания во внутрь какой-то функции (в том числе и main), вы обнаружите там локальные переменные на тех же местах. Адреса локальных переменных отмеряются относительно текущего указателя стека, который при запрыгивании со стороны никак не может быть правильным (т.к. функция прерывания тоже пользуется стеком).
Именно поэтому setjump() сохраняет состояние указателя стека. И после longjump() все локальные переменные находятся на своих местах совершенно нетронутые.
Палыч
Совершенно непонятно: почему ТС не хочет использовать setjmp / longjmp ? ИМХО, это - хорошее решение той задачи, что стоит перед ТС, абсолютно стандартными средствами. Даже пример, взятый мной из GCC и приведенный в сообщении #9, почти один-в один совпадает с тем, что желает ТС и привел в первом сообщении. Жалко места под буфер jmp_buf ?
Решение же проблемы почему-то "крутится" вокруг goto... Зачем этот "гемморой" ?
_Артём_
Цитата(Палыч @ May 21 2013, 11:00) *
Совершенно непонятно: почему ТС не хочет использовать setjmp / longjmp ?

ТС не хочет чтобы в прерывании куча регистров сохранялась-восстанавливалась: ссылка
ARV
Цитата(_Артём_ @ May 20 2013, 23:03) *
goto заменилось на JMP my_exit без ненужного сохранения регистров. Надо же...
так ведь это как бы и требовалось?

Цитата(Сергей Борщ)
Было же грамотное решение - функция с атрибутом noreturn. И вызывать ее как функцию, без всяких указателей и т.д. Компилятору этого noreturn по идее должно быть достаточно, чтобы не сохранять лишние регистры в каждом прерывании.
такая функция скорее всего проинлайнится внутрь обработчика, и соответственно не даст никакого выигрыша по сравнению с нормальным указанием выполняемых операторов в этом обработчике.


у меня была идея поместить эту функцию в секцию .fini1 и попадать туда стандартной функцией exit или abort, но компилятор оказался слишком глупым и тупо сохранял в стеке все регистры в обработчике прерываний...

_Артём_
Цитата(ARV @ May 21 2013, 21:08) *
так ведь это как бы и требовалось?

Да, это оно самое и есть, чего хотел ТС.

Цитата(ARV @ May 21 2013, 21:08) *
такая функция скорее всего проинлайнится внутрь обработчика, и соответственно не даст никакого выигрыша по сравнению с нормальным указанием выполняемых операторов в этом обработчике.

Не, не проинлайнится. noreturn роли не играет.
Видимо регистров мало сохраняется из-за того что goto используется.


Цитата(ARV @ May 21 2013, 21:08) *
у меня была идея поместить эту функцию в секцию .fini1 и попадать туда стандартной функцией exit или abort, но компилятор оказался слишком глупым и тупо сохранял в стеке все регистры в обработчике прерываний...

А если так:
Код
typedef void (*exit_handler_t) (); //
ISR(SPM_RDY_vect)
{
    if (fatal_error) {
        exit_handler_t exit_handler=my_exit;
        goto *exit_handler;
    }
}

то лишних сохранений не будет и функцию можно расположить по заданному адресу.
rudy_b
Что-то непонятно. Что значит "программа должна завершиться", если в ней есть main с бесконечным циклом? И что будет если из main дать return?

Если нужно заблокировать работу программы находясь в прерывании - запретите все прерывания и зациклитесь. Из этого можно можно выйти только по reset.
_Артём_
Цитата(rudy_b @ May 21 2013, 22:32) *
И что будет если из main дать return?

Перейдёт на какую-нибудь функцию exit (или что там предусматривается на такой случай).
Как-то так оно выглядит:
Код
  CLI         Global Interrupt Disable
  RJMP PC-0x0000        Relative jump

Но можно и своим чем-то заменить.

Цитата(rudy_b @ May 21 2013, 22:32) *
Если нужно заблокировать работу программы находясь в прерывании - запретите все прерывания и зациклитесь.

Возможно таких мест не одно. И там предполагается не только зацикливание, а ещё какие-то спасающие ситуацию действия.


MaxiMuz
Цитата(_Артём_ @ May 20 2013, 16:31) *
Мне почему-то кажется что код идущий после метки будет выкинут оптимизатором. Или нет?
Вы с какими настройками компилируете?

-Os
нет, код после метки отстается, возможно что там используются volatile-переменные

Цитата(Xenia @ May 19 2013, 22:04) *
Ветвление надо произвести только один раз - в самом начале main(), еще до вхождения в бесконечный цикл.

Идея с семафором, ветвлением вначале main и оригинальная, но все же я не очень доверяю сохранности портов после сброса ..

Цитата
Функции setjmp / longjmp подойдут ?

этот вариант не подходит т.к. по сути это тот же самый вызов функции с вытекающими последствиями

Цитата(ARV @ May 20 2013, 20:12) *
если очень хочется, то можно вот так:
Код
void  __attribute__((noreturn)) my_exit(void){
     PORTB = 0xFF;
     while(1);
}

ISR(TIMER1_OVF_vect){
     if(PINB & 1){
         PORTB = 0;
     } else {
         // проблема - обрабатываем лебединую песню!!!
         void *ptr = my_exit;
         goto *ptr;
     }
}
при желании my_exit() можно вызывать и в main, не нарушая общей структуры программы. хотя с моей точки зрения это кривой костыль, правильное по моему мнению решение я озвучивал ранее.

Помоему самое оптимальное решение !
т.к. даже через снятие адреса метки:
Код
ptr=&&m1;
....
goto *ptr;
компилятор заменяет на ijmp ptr что задействует еще пару регистров, которые сохраняются в стек
Только неясно почему опять кривой костыль ?
_Артём_
Цитата(MaxiMuz @ May 21 2013, 23:17) *
возможно что там используются volatile-переменные

Может быть.

Цитата(MaxiMuz @ May 21 2013, 23:17) *
но все же я не очень доверяю сохранности портов после сброса ..

О каких портах речь? Входы-выходы? После сброа они будут сброшены в дефолтное состояние.

Цитата(MaxiMuz @ May 21 2013, 23:17) *
Помоему самое оптимальное решение !

Есть, кстати ещё вариант: в прерывании, в котором обнаружена аварийная ситуация генерить вызов другого прерывания в котором и выполнять нужные действия (уход в sleep кажется?). Программное прерывание то есть.
Код
ISR(TIMER1_COMPA_vect)
{
     if (fatal_error) {
          // вызов программного прерывания
          SPMCR=1<<SPMIE;
     }
}

ISR(SPM_RDY_vect)
{

     SPMCR&=~(1<<SPMIE);
     // действия по аварии
    while (1);
}

На запуск програмного прерывания нужно всего пара команд, поэтому дополнительных регистров не потребуется. Только сработает оно через десятков-другой тактов, но программа при этом порушена не будет.
MaxiMuz
Цитата(_Артём_ @ May 21 2013, 23:54) *
О каких портах речь? Входы-выходы? После сброа они будут сброшены в дефолтное состояние.

тот вариант о котором говорила Xenia
#17
с вызовом прерывани из прерывания я тоже думал , как вариант , но всеже rjmp проще всего
demiurg_spb
Всё это (аварийный выход) стоит завернуть в какой-нибудь макрос с внятным именем типа abort();
Кстати, вариант с программным прерыванием - ИМХО самый оптимальный - вызов одна инструкция sbi, ну и обработчик со всеми нужными атрибутами...
Палыч
Цитата(demiurg_spb @ May 22 2013, 10:52) *
вызов одна инструкция sbi...

В AVR программное прерывание "сгенерить" не так-то и просто. В АVR, обычно, запись единицы в флаг прерывания приводит к её сбросу.
demiurg_spb
Цитата(Палыч @ May 22 2013, 11:01) *
В AVR программное прерывание "сгенерить" не так-то и просто. В АVR, обычно, запись единицы в флаг прерывания приводит к её сбросу.
На худой конец можно задействовать и внешнее прерывание + дёрнуть уровень на ноге sbi или cbi или просто его разрешить на нужной ноге с нужным уровнем.
Всё это уже лирика...
Цитата
• Bit 7 – SPMIE: SPM Interrupt Enable
When the SPMIE bit is written to one, and the I-bit in the Status Register is set (one), the SPM
ready interrupt will be enabled. The SPM ready Interrupt will be executed as long as the SPMEN
bit in the SPMCR Register is cleared.
Ведь не зря же его для нужд rtos частенько используют...
rudy_b
Цитата(_Артём_ @ May 21 2013, 22:47) *
Перейдёт на какую-нибудь функцию exit (или что там предусматривается на такой случай).
Как-то так оно выглядит:
Код
  CLI         Global Interrupt Disable
  RJMP PC-0x0000        Relative jump

Но можно и своим чем-то заменить.

Так это и получится - запрет прерываний и зацикливание. И его можно сделать в любом месте, не обязательно выходить в main.

Цитата
Возможно таких мест не одно. И там предполагается не только зацикливание, а ещё какие-то спасающие ситуацию действия.

Так в каждом из этих мест сделать то же самое, предварительно совершив "спасательные" действия.

А чтобы после reseta понять что именно случилось - можно записать что-то в память или во флешь. А в начале программы (от reset) поставить анализ этого со стиранием в исходное состояние.
MaxiMuz
Цитата(rudy_b @ May 22 2013, 11:28) *
А чтобы после reseta понять что именно случилось - можно записать что-то в память или во флешь. А в начале программы (от reset) поставить анализ этого со стиранием в исходное состояние.

Как раз Xenia это и предлагала:
Цитата
char semafor = false;
main()
{
if( semafor)
{
// определенные экстренные действия
}
for(;;)
{
// бесконечный цикл
}
}
В прерывании же при обнаружении экстренного случая взводится светофор (semafor = true) и программа запускается с нулевого адреса (setjmp 0).

P.S. Кажись с пронеслась я со своей идеей, т.к. при инициализации semafor тоже почистится. Впрочем, в качестве семафора можно взять какой-нибудь аппаратный регистр.


Цитата(Сергей Борщ @ May 21 2013, 10:45) *
Было же грамотное решение - функция с атрибутом noreturn. И вызывать ее как функцию, без всяких указателей и т.д. Компилятору этого noreturn по идее должно быть достаточно, чтобы не сохранять лишние регистры в каждом прерывании.

Тогда в этом случае я должен буду сам позаботится о сохранении используемых регистров, а как я догадаюсь, какие регистры вздумает задействовать компилятор ?

Цитата(Сергей Борщ @ May 21 2013, 10:45) *
Решение с goto из одной функции в другую ущербное каким бы хаком оно бы ни было реализовано. longjump() еще куда ни шло - он поддержан компилятором.
В чем именно ущербность goto ?
_Pasha
Пора голосовать, хто за setjmp/longjmp ? biggrin.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.