реклама на сайте
подробности

 
 
4 страниц V   1 2 3 > »   
Reply to this topicStart new topic
> GCC: Аварийный выход из прерывания (функции), чем заменить оператор goto?
MaxiMuz
сообщение May 16 2013, 14:50
Сообщение #1


Местный
***

Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658



Возникла ситуация когда при обработке прерывания нужно изменить привычный ход программы, когда содержимое сохранненых регистров и указатель стека уже не важны. Оператор goto тут не работает:
Код
ISR (xxx_vect)
{
  ...
  ...
  goto m1;
}
void main (void)
{
  ...
  ...
  while (1)
    { ...
           ...
         }
m1:
  ...
  ...
}

Что можно здесь сделать ?
Go to the top of the page
 
+Quote Post
Lagman
сообщение May 16 2013, 15:45
Сообщение #2


Знающий
****

Группа: Свой
Сообщений: 875
Регистрация: 28-10-05
Пользователь №: 10 245



Просто из прерывания вызываете другую функцию, зачем вам в main возвращаться, если вам не важны стэк и регистры.
Go to the top of the page
 
+Quote Post
zombi
сообщение May 16 2013, 16:09
Сообщение #3


Гуру
******

Группа: Свой
Сообщений: 2 076
Регистрация: 10-09-08
Пользователь №: 40 106



Не знаю как на СИ.
А на асме записываю в стек нужный мне адрес и ret
Go to the top of the page
 
+Quote Post
Палыч
сообщение May 16 2013, 16:40
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Функции setjmp / longjmp подойдут ?
Go to the top of the page
 
+Quote Post
Xenia
сообщение May 16 2013, 17:51
Сообщение #5


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



Проще всего в программе установить семафоры с ветвлением по if или switch, а из прерывания эти семафоры переключать.
Go to the top of the page
 
+Quote Post
MaxiMuz
сообщение May 16 2013, 18:20
Сообщение #6


Местный
***

Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658



Цитата(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, а из прерывания эти семафоры переключать.
я так и поступил , но всеже хочется разобраться с "прыжками"
Go to the top of the page
 
+Quote Post
_Артём_
сообщение May 16 2013, 18:28
Сообщение #7


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(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) *
я так и поступил , но всеже хочется разобраться с "прыжками"

Изврат это какой-то, чего с ними разбираться.
Go to the top of the page
 
+Quote Post
zombi
сообщение May 16 2013, 19:03
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 2 076
Регистрация: 10-09-08
Пользователь №: 40 106



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

Смотря какой проц.
Go to the top of the page
 
+Quote Post
Палыч
сообщение May 16 2013, 19:40
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Цитата(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);
        }
    }
Go to the top of the page
 
+Quote Post
MaxiMuz
сообщение May 17 2013, 11:10
Сообщение #10


Местный
***

Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658



Цитата(_Артём_ @ May 16 2013, 21:28) *
А регистры сохраняются? Вы смотрели?
почти все старшие регистры, независимо используются они или нет
Go to the top of the page
 
+Quote Post
Tiro
сообщение May 17 2013, 21:26
Сообщение #11


Знающий
****

Группа: Свой
Сообщений: 781
Регистрация: 3-10-04
Из: Санкт-Петербург
Пользователь №: 768



Цитата(MaxiMuz @ May 17 2013, 14:10) *
почти все старшие регистры, независимо используются они или нет

Метки как значения давно есть в ГСС. 4.3 Labels as Values
Go to the top of the page
 
+Quote Post
_Артём_
сообщение May 18 2013, 22:42
Сообщение #12


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(MaxiMuz @ May 17 2013, 14:10) *
почти все старшие регистры, независимо используются они или нет

Вызываете функции из прерывания? Если да, то попробуйте закомментировать все вызовы и посмотрите сколько регистров сохраняется в стек. Или попоробуцте заинлайнить все вызываемые функции.
Возможно в прерывании действительно нужны все сохраняемые регистры.
Go to the top of the page
 
+Quote Post
Xenia
сообщение May 19 2013, 00:12
Сообщение #13


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



Цитата(MaxiMuz @ May 16 2013, 22:20) *
я так и поступил , но всеже хочется разобраться с "прыжками"


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

Например, была вызвана функция, которая при входе создала кучу малу локальных переменных на стеке. Потом эта функция вызвала другую функцию, которая сделала то же самое. И т.д. А тут в тот момент, когда процессор в самой глубокой фунции код выполнял, ваше прерывание и приспичило. И куда вы теперь пойдете, когда стек полным-полнёхонек?
Go to the top of the page
 
+Quote Post
ARV
сообщение May 19 2013, 06:49
Сообщение #14


Профессионал
*****

Группа: Свой
Сообщений: 1 143
Регистрация: 30-09-08
Из: Новочеркасск
Пользователь №: 40 581



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

с моей точки зрения ситуация, когда "регистры и состояние стека уже не важны" должна приводить к аппаратному сбросу контроллера, и никак иначе, тем более из прерывания.


--------------------
Я бы взял частями... но мне надо сразу.
Go to the top of the page
 
+Quote Post
MaxiMuz
сообщение May 19 2013, 16:27
Сообщение #15


Местный
***

Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658



Цитата(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) *
с моей точки зрения ситуация, когда "регистры и состояние стека уже не важны" должна приводить к аппаратному сбросу контроллера, и никак иначе, тем более из прерывания.
Точно! программа должна выполнить определенные действия и завершиться.

Сообщение отредактировал MaxiMuz - May 19 2013, 16:27
Go to the top of the page
 
+Quote Post
Lagman
сообщение May 19 2013, 18:26
Сообщение #16


Знающий
****

Группа: Свой
Сообщений: 875
Регистрация: 28-10-05
Пользователь №: 10 245



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

Если брать микроконтроллеры то там редко когда программа завершается, передавать управление некому, запуск только через включение питания (ресет).
Go to the top of the page
 
+Quote Post
Xenia
сообщение May 19 2013, 19:04
Сообщение #17


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



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


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

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

P.S. Кажись с пронеслась я со своей идеей, т.к. при инициализации semafor тоже почистится. Впрочем, в качестве семафора можно взять какой-нибудь аппаратный регистр.
Go to the top of the page
 
+Quote Post
_Артём_
сообщение May 19 2013, 20:02
Сообщение #18


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(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");
    }
}
Go to the top of the page
 
+Quote Post
Tiro
сообщение May 19 2013, 21:32
Сообщение #19


Знающий
****

Группа: Свой
Сообщений: 781
Регистрация: 3-10-04
Из: Санкт-Петербург
Пользователь №: 768



Цитата(MaxiMuz @ May 19 2013, 19:27) *
Помоему то что нужно!
Но всеже както странно , первый раз вижу оператор '&&' в таком применении. Это какаято особенность GCC стандарта ?

Да, это GNU расширение стандарта "С". В Вашем случае я бы использовал для восстановления микроконтроллера функцию с атрибутами static inline, а потом пересбросил бы микроконтроллер.
Go to the top of the page
 
+Quote Post
Xenia
сообщение May 19 2013, 23:31
Сообщение #20


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



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


Тогда отчего бы не написать завершающую функцию прямо в прерывании? sm.gif
Go to the top of the page
 
+Quote Post
_Артём_
сообщение May 20 2013, 00:00
Сообщение #21


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



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

Можно и написать, но наверняка тогда прерыванию потребуется больше регистров для завершения(количество регистров зависит отсложности действий при завершении), которое выполнится один раз. То есть прерывание всегда будет сохранять-востанавливать много регистров.
А если сделать по-моему варианту, то может быть прерыванию регистров меньше понадобится(но вариант с jmp лучше чем ret). Но работа программы завершится и возобновлена может быть только после сброса.
Go to the top of the page
 
+Quote Post
Палыч
сообщение May 20 2013, 04:53
Сообщение #22


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Цитата(Xenia @ May 19 2013, 23:04) *
В прерывании же при обнаружении экстренного случая взводится светофор (semafor = true) и программа запускается с нулевого адреса

Если достаточно "мягкого" рестарта, то семафор заменяет пара функций setjmp / longjmp.
Go to the top of the page
 
+Quote Post
MaxiMuz
сообщение May 20 2013, 12:40
Сообщение #23


Местный
***

Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658



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

Вообщето я так и хочу сделать, только не в функцию , а в участок main
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение May 20 2013, 13:21
Сообщение #24


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



QUOTE (MaxiMuz @ May 20 2013, 15:40) *
а в участок main
А вы уверены, что этот участок адекватно отреагирует на мусор в стеке?


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
_Артём_
сообщение May 20 2013, 13:31
Сообщение #25


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(MaxiMuz @ May 20 2013, 15:40) *
только не в функцию , а в участок main

Мне почему-то кажется что код идущий после метки будет выкинут оптимизатором. Или нет?
Вы с какими настройками компилируете?
Go to the top of the page
 
+Quote Post
Xenia
сообщение May 20 2013, 13:44
Сообщение #26


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



Цитата(Сергей Борщ @ May 20 2013, 17:21) *
А вы уверены, что этот участок адекватно отреагирует на мусор в стеке?


Если это лебединая песнь, то легко можно установить стек(и) на начало перед прыжком.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение May 20 2013, 17:03
Сообщение #27


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



QUOTE (Xenia @ May 20 2013, 16:44) *
легко можно установить стек(и) на начало перед прыжком.
установить-то можно. Вот только main() может выделить на стеке место под переменные лебединой песни и даже, возможно, разместить там заранее какие-то значения. И при установке указателя стека на начало получим обращение к несуществующей памяти, а при переходе с кучей хлама на стеке будем обрабатывать мусор. Поэтому все и пишут, что решение ущербное по условию..


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
ARV
сообщение May 20 2013, 17:12
Сообщение #28


Профессионал
*****

Группа: Свой
Сообщений: 1 143
Регистрация: 30-09-08
Из: Новочеркасск
Пользователь №: 40 581



если очень хочется, то можно вот так:
Код
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, не нарушая общей структуры программы. хотя с моей точки зрения это кривой костыль, правильное по моему мнению решение я озвучивал ранее.


--------------------
Я бы взял частями... но мне надо сразу.
Go to the top of the page
 
+Quote Post
Xenia
сообщение May 20 2013, 18:41
Сообщение #29


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



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


Нормальные люди в main() локальных переменных не заводят! sm.gif Что проку от их локальности, если main() никогда не завершается? Переменные, используемые в main(), лучше объявлять глобальными - результат тот же самый, зато стек не расходуется впустую. А если не хотите, чтобы эти переменные из других модулей были видны, то объявите их static.
Go to the top of the page
 
+Quote Post
_Артём_
сообщение May 20 2013, 19:03
Сообщение #30


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(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 без ненужного сохранения регистров. Надо же...
Go to the top of the page
 
+Quote Post
Xenia
сообщение May 20 2013, 19:05
Сообщение #31


Гуру
******

Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237



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


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

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

P.S. Не стоит обольщаться тем, что войдя джампом из прерывания во внутрь какой-то функции (в том числе и main), вы обнаружите там локальные переменные на тех же местах. Адреса локальных переменных отмеряются относительно текущего указателя стека, который при запрыгивании со стороны никак не может быть правильным (т.к. функция прерывания тоже пользуется стеком). Поэтому как ни крути, но валидными в этом случае могут быть только глобальные переменные, но не локальные. Причем независимо от того, сбрасывали вы стек или нет.
Go to the top of the page
 
+Quote Post
Tiro
сообщение May 20 2013, 22:31
Сообщение #32


Знающий
****

Группа: Свой
Сообщений: 781
Регистрация: 3-10-04
Из: Санкт-Петербург
Пользователь №: 768



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

Тут поддержу Ксению, но какое это имеет отношение к вопросу топикстартера? Он же сказал, что состояние программы и стека ему не известно и не важно. Возможно, стек переполнился и залез на глобальные переменные. Надежнее не использовать вообще никакие заранее определенные переменные. Используйте константы или заведите новые переменные, порушив глобальные, переставьте указатель стека на начало и вперед! Только вперед и только хардкор biggrin.gif
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение May 21 2013, 07:45
Сообщение #33


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



_Артем_ ответил по всем пунктам.
Касаемо последних двух сообщений: компилятор не может знать вашей логики, заставить его "не использовать локальные переменные" нельзя. Решение с 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() все локальные переменные находятся на своих местах совершенно нетронутые.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
Палыч
сообщение May 21 2013, 08:00
Сообщение #34


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Совершенно непонятно: почему ТС не хочет использовать setjmp / longjmp ? ИМХО, это - хорошее решение той задачи, что стоит перед ТС, абсолютно стандартными средствами. Даже пример, взятый мной из GCC и приведенный в сообщении #9, почти один-в один совпадает с тем, что желает ТС и привел в первом сообщении. Жалко места под буфер jmp_buf ?
Решение же проблемы почему-то "крутится" вокруг goto... Зачем этот "гемморой" ?
Go to the top of the page
 
+Quote Post
_Артём_
сообщение May 21 2013, 12:51
Сообщение #35


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(Палыч @ May 21 2013, 11:00) *
Совершенно непонятно: почему ТС не хочет использовать setjmp / longjmp ?

ТС не хочет чтобы в прерывании куча регистров сохранялась-восстанавливалась: ссылка
Go to the top of the page
 
+Quote Post
ARV
сообщение May 21 2013, 18:08
Сообщение #36


Профессионал
*****

Группа: Свой
Сообщений: 1 143
Регистрация: 30-09-08
Из: Новочеркасск
Пользователь №: 40 581



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

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


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



--------------------
Я бы взял частями... но мне надо сразу.
Go to the top of the page
 
+Quote Post
_Артём_
сообщение May 21 2013, 19:20
Сообщение #37


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(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;
    }
}

то лишних сохранений не будет и функцию можно расположить по заданному адресу.
Go to the top of the page
 
+Quote Post
rudy_b
сообщение May 21 2013, 19:32
Сообщение #38


Знающий
****

Группа: Свой
Сообщений: 888
Регистрация: 25-09-08
Из: Питер
Пользователь №: 40 458



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

Если нужно заблокировать работу программы находясь в прерывании - запретите все прерывания и зациклитесь. Из этого можно можно выйти только по reset.
Go to the top of the page
 
+Quote Post
_Артём_
сообщение May 21 2013, 19:47
Сообщение #39


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(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) *
Если нужно заблокировать работу программы находясь в прерывании - запретите все прерывания и зациклитесь.

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


Go to the top of the page
 
+Quote Post
MaxiMuz
сообщение May 21 2013, 20:17
Сообщение #40


Местный
***

Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658



Цитата(_Артём_ @ 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 что задействует еще пару регистров, которые сохраняются в стек
Только неясно почему опять кривой костыль ?
Go to the top of the page
 
+Quote Post
_Артём_
сообщение May 21 2013, 20:54
Сообщение #41


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(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);
}

На запуск програмного прерывания нужно всего пара команд, поэтому дополнительных регистров не потребуется. Только сработает оно через десятков-другой тактов, но программа при этом порушена не будет.
Go to the top of the page
 
+Quote Post
MaxiMuz
сообщение May 21 2013, 21:01
Сообщение #42


Местный
***

Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658



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

тот вариант о котором говорила Xenia
#17
с вызовом прерывани из прерывания я тоже думал , как вариант , но всеже rjmp проще всего
Go to the top of the page
 
+Quote Post
demiurg_spb
сообщение May 22 2013, 06:52
Сообщение #43


неотягощённый злом
******

Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643



Всё это (аварийный выход) стоит завернуть в какой-нибудь макрос с внятным именем типа abort();
Кстати, вариант с программным прерыванием - ИМХО самый оптимальный - вызов одна инструкция sbi, ну и обработчик со всеми нужными атрибутами...


--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
Go to the top of the page
 
+Quote Post
Палыч
сообщение May 22 2013, 07:01
Сообщение #44


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Цитата(demiurg_spb @ May 22 2013, 10:52) *
вызов одна инструкция sbi...

В AVR программное прерывание "сгенерить" не так-то и просто. В АVR, обычно, запись единицы в флаг прерывания приводит к её сбросу.
Go to the top of the page
 
+Quote Post
demiurg_spb
сообщение May 22 2013, 07:05
Сообщение #45


неотягощённый злом
******

Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643



Цитата(Палыч @ 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 частенько используют...


--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
Go to the top of the page
 
+Quote Post
rudy_b
сообщение May 22 2013, 08:28
Сообщение #46


Знающий
****

Группа: Свой
Сообщений: 888
Регистрация: 25-09-08
Из: Питер
Пользователь №: 40 458



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

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

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

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

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

А чтобы после reseta понять что именно случилось - можно записать что-то в память или во флешь. А в начале программы (от reset) поставить анализ этого со стиранием в исходное состояние.
Go to the top of the page
 
+Quote Post
MaxiMuz
сообщение May 25 2013, 06:49
Сообщение #47


Местный
***

Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658



Цитата(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 ?

Сообщение отредактировал MaxiMuz - May 25 2013, 08:07
Go to the top of the page
 
+Quote Post
_Pasha
сообщение May 25 2013, 10:16
Сообщение #48


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Пора голосовать, хто за setjmp/longjmp ? biggrin.gif
Go to the top of the page
 
+Quote Post

4 страниц V   1 2 3 > » 
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 21st July 2025 - 19:19
Рейтинг@Mail.ru


Страница сгенерированна за 0.01848 секунд с 7
ELECTRONIX ©2004-2016