Страница 1 из 1
Ошибки с доступом к переменным из прерываний
Добавлено: Вт авг 19, 2014 08:09:45
miXOnIN
Здравствуйте, обыскал весь интернет - почти нет инфы по данной теме, по этому и решил целую тему завести.
Проблема следующая (на сколько я понял):
Есть главный цикл работы программы:
Код: Выделить всё
unsigned long volatile int my_var= 0;
int main( void )
{
...............
while (1) {
printr(my_var*60); // Берём глобальную переменную, работаем с ней, и выводим на LED
}
}
И есть прерывание по таймеру (8 МГц внутренний резонатор, прерывание обычное, считает до 255 и вылетает, делитель на 8 )
Код: Выделить всё
ISR(TIMER0_OVF_vect) // Timer0
{
my_var++; // Считаем +1
}
В итоге, такая конструкция работает, НО время от времени вылетают ошибки (в протеус - почти сразу, а на железе - через час-два)

а этот арес ошибки первый - не открывается

На сколько я понимаю - проблема в том что в данный момент начало выполнятся printr(my_var*60);, и в этот момент вылезло прерывание, и начало считать "занятую" переменную...
Как от этого избавиться?
Из того что нашёл - это сделать переменную с типом
volatile, но она и так с ним - вылетает(
думал сделать cli(); ... sei(); но не получится, учитывая что с данной переменной вопервых - работа идёт в 5-7 местах, а во вторых - нужно ОБЯЗАТЕЛЬНО считать её, не пропустив ни разу ни одного такта (атомарные часы, если будет выключатся через cli() - пропустит такты - собьются часы)
У кого какие идеи? или вообще проблема не в этом?
и да, почему RAM так по странному распределена, не много в начале, и не много в конце, остальное пустует... какая логика её заполнения?
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Вт авг 19, 2014 09:46:15
ARV
1. Немного в начале - это статические переменные, в конце - это стек и локальные в нем
2. Единственное, что вам может помочь, это атомарный доступ к переменной. Как-то так:
Код: Выделить всё
#include <util/atomic.h>
unsigned long get_my_var(void){
ATOMIC_BLOCK(ATOMIC_RESTORE_STATE){
return my_var;
}
}
И везде получать значение вашей переменной только через эту функцию.
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Вт авг 19, 2014 10:33:29
miXOnIN
Функция не получилась (матюкалось на разные типы аргументов хоть убей), но не суть, нашёл статейку по ATOMIC_BLOCK в AVR, почитай, и использовал условие
Код: Выделить всё
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
... работа с переменными
}
и всё заработало! УРА!)
НО, есть вопрос. По факту, как я понял, ДО выполнения кода в блоке - эта штука запрещает прерываения, а после выполнения - разрешает их, так?
Если так - тогда вопрос - во время выполнения кода внутри - может 2-3 раза сработать Таймер0, и получается что потеряется инфа?
т.е. на 2-3 такта в "часах" посчитает меньше? И тем самым каждый раз будет отставать по чучуть?
Или где-то записывается что "прерывание было", и компенсируется запуском этих событий после внеочереди?
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Вт авг 19, 2014 10:58:33
Engineer_Keen
miXOnIN писал(а):во время выполнения кода внутри - может 2-3 раза сработать Таймер0, и получается что потеряется инфа?
т.е. на 2-3 такта в "часах" посчитает меньше? И тем самым каждый раз будет отставать по чучуть?
Чтобы запретить или разрешить прерывание нужен 1 такт, для чтения переменной нужно 2 такта на байт, если за это время таймер успевает сработать несколько раз, то в алгоритме явно что-то не то

Re: Ошибки с доступом к переменным из прерываний
Добавлено: Вт авг 19, 2014 11:30:04
miXOnIN
И......... после 5 минут работы - опять посыпалось........

Кто может объяснить что за процедуры выполняются до атомик-блока
Код: Выделить всё
ISR(TIMER0_OVF_vect) // Timer0, главный счётчик времени
{
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
my_var++;
код
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Вт авг 19, 2014 15:08:39
Engineer_Keen
miXOnIN писал(а):Кто может объяснить что за процедуры выполняются до атомик-блока
Если про второй ряд картинок, то там сохранение регистров в стек, и похоже перед этим в какой-то момент его указатель что-то попортило из-за чего стек снесло, и он начал пытаться писать за пределы памяти.
Возможно код целиком проявит какой-либо капитальный косяк...
Кстати, отключать прерывания внутри прерывания бессмысленно, т.к. они на время прерывания отключаются автоматом и включаются при выходе из него (за исключением сильно замороченных случаев с приоритетными прерываниями, когда их принудительно включают внутри).
PS: на первой картинке как раз идет запись числа в указатель стека напрямую (OUT $3E,R29 | OUT $3F,R28), вот из-за такой манеры обращения со стеком я не люблю сишные компиляторы, концов не найдешь потом...
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Вт авг 19, 2014 15:42:52
ARV
miXOnIN писал(а):Функция не получилась (матюкалось на разные типы аргументов хоть убей), но не суть
где вы увидели аргументы в предложенной мною функции:
ARV писал(а):Код: Выделить всё
#include <util/atomic.h>
unsigned long get_my_var(void){
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
return my_var;
}
}
???
miXOnIN писал(а):как я понял, ДО выполнения кода в блоке - эта штука запрещает прерываения, а после выполнения - разрешает их, так?
нет, при использовании ATOMIC_RESTORESTATE макрос ATOMIC_BLOCK запомнит состояние флага разрешения прерываний и запретит прерывания в начале, а в конце - восстановит прежнее состояние. т.е. если прерывания были ЗАПРЕЩЕНЫ, после отработки блока они ОСТАНУТСЯ ЗАПРЕЩЕНЫ. если были РАЗРЕШЕНЫ - будут так же РАЗРЕШЕНЫ, хотя внутри блока всегда будут запрещены.
судя по тому, что в обработчик прерываний вы втулили ATOMIC_BLOCK, вы совершенно не поняли, в чем его смысл.
1. ваша long-переменная ИЗМЕНЯЕТСЯ ТОЛЬКО В ПРЕРЫВАНИИ
2. во всех прочих местах проги она ТОЛЬКО ИСПОЛЬЗУЕТСЯ, т.е. берется только ее значение.
3. функция, которая мной рекомендована, должна испольтзоваться везде, где требуется в п.2 вместопрямого обращения к переменной.
4. если вдруг все-таки требуется менять значение этой переменной не только в прерывании, для этого необходимо так же использовать функцию типа такой:
Код: Выделить всё
void set_my_var(unsigned long value){
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
my_var = value;
}
}
при соблюдении этих правил косяков с неатомарным доступом не будет.
кстати, внутри ISR никаких ATOMIC_BLOCK быть не должно.
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Вт авг 19, 2014 16:04:28
miXOnIN
ARV писал(а):судя по тому, что в обработчик прерываний вы втулили ATOMIC_BLOCK, вы совершенно не поняли, в чем его смысл.
я втулил его и там где читаю и там где пишу в переменную (в прерывании всунул ибо точно никто так и не написал что из прерывания меньшего приоритета она не уходит в прерывание высшего приоритета)
Насчёт функции то тупонул был, импорт сделал ниже чем саму функцию
ARV писал(а):нет, при использовании ATOMIC_RESTORESTATE макрос ATOMIC_BLOCK запомнит состояние флага разрешения прерываний и запретит прерывания в начале, а в конце - восстановит прежнее состояние.
я это и написал, вы просто смысл не поняли....
Например - у меня начинается зона
в ней уже начинает выполнятся код - и тут прерывание, которое
ДОЛЖНО сработать, но оно видит что выполняется этот блок, и НЕ ВЫПОЛНЯЕТСЯ? и после окончания блока это никак не компенсируется??
Проще скажу - у меня Timer0 прерывание, оно считает время (милисикунды), каждую 1ms вылазит прерывание и делает time_ms++;
от чего часы и пляшут. Так вот о чём я, если будет выполнятся АТОМИК_БЛОК, то прерывание не сработает, и время будет отставать каждый раз?
на сколько я понял то так и будет, но этого никак нельзя допустить
дальше... у меня ~10 переменных глобальных которые изменяются и их нужно считывать, так вот, зачем для каждой писать такую функцию чтения, если можно прям в коде писать
Код: Выделить всё
int tmp;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
tmp=my_var1;
}
работаем с тмп...
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
tmp=my_var2;
}
и т.д.
, вместо того что-бы прописывать 10 функций, в чём разница??
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Вт авг 19, 2014 17:15:57
uk8amk
Вам нужно разобраться как работают прерывания.
Активация периферии:
1.Разрешение прерывания таймера в регистре маскировки TIMSK
2.Пуск таймера
3.Глобальное разрешение всех прерываний флагом I ( sei ).
Когда происходит какое-то событие, способное вызвать прерывание, допустим переполнение счетчика TIM0_OVF:
1.В регистре флагов TIFR устанавливается бит TOV0. Даже если прерывание отключено, показывает что событие было. Он сбрасывается вручную либо при выполнении соответствующей подпрограммы прерывания.
2.Если TOIE0(TIMSK) и I(SREG) установлены, то происходит прерывание. Если прерывание сейчас запрещено, то оно будет запущено когда будет разрешено.
3.Если время выполнения критической секции кода с запрещенным прерыванием больше времени переполнения таймера и в сумме произойдет 2 запроса на прерывание от одного источника, то выполнится только один запрос. Это очень плохая техника запрещения прерываний на длительное время.
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Вт авг 19, 2014 18:14:09
miXOnIN
Открыл адрес ошибки - не в протеусе, а нашёл в Atmel Studio в дизасемблере его, как по мне - более вероятный вариант что именно там падает, а протеус не то даёт

и всё-же падает...(
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Вт авг 19, 2014 18:38:59
uk8amk
Вам следует разбить программу на части и тестировать их по отдельности, чтобы определить, какая именно вызывает сбой.
То что протеус вам показывает, что идет запись вне допустимого диапазона - это скорее всего следствие а не причина ошибки. Причиной может быть к примеру разрушение стека, что проявится через 3-4 вызова подпрограммы или когда окажется что возвращаться из прерывания некуда.
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Вт авг 19, 2014 19:30:04
Аlex
А потом окажется, что в протеусе выбран не тот проц, под который компилилась программа

Re: Ошибки с доступом к переменным из прерываний
Добавлено: Вт авг 19, 2014 19:41:22
ARV
miXOnIN писал(а):дальше... у меня ~10 переменных глобальных которые изменяются и их нужно считывать
ой, что-то в консерватории не так, по-моему...
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Ср авг 20, 2014 09:54:00
B@R5uk
Жесть какая-то! Атомарный доступ...

Вот на ассемблере это всё с пол пинка делается.
И вообще. Концептуально неправильный подход к выводу переменной в порты. Переменная изменяется редко и только в одном месте — в прерывании. Вот и надо сразу в прерывании эту переменную выводить как только она изменилась.
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Ср авг 20, 2014 12:18:34
ARV
B@R5uk писал(а):Жесть какая-то! Атомарный доступ...

Вот на ассемблере это всё с пол пинка делается.
на ассемблере это делается ровно так же, только записывается по-другому и требует от программиста большего внимания.
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Ср авг 20, 2014 17:37:04
eess9
Аlex писал(а):А потом окажется, что в протеусе выбран не тот проц, под который компилилась программа

Присоединяюсь. Вы точно уверены что везде используется один и тот же контроллер? В стандартные файлы изменений не вносили (типа iom8.h для вашего контроллера, секция адресов)? Файлы линкера стандартные или свои пристегиваете?
Такое ощущение, что именно компоновщик адресацию секций сделал совершенно левой при сборке.
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Ср авг 20, 2014 18:15:27
miXOnIN
eess9 писал(а):Вы точно уверены что везде используется один и тот же контроллер?
Программу делаю под ATmega8a (шью которую)
в среде разработки Atmega8 (тупонул, уже выбрал 8A)
а в протеусе ATmega8_32PIN называется (единственная мега 8я)
тч я думаю что в полне реально может быть и такое
тогда вопрос по другому звучит - где нарыть 8а на протеус??
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Ср авг 20, 2014 18:26:48
Engineer_Keen
miXOnIN писал(а):где нарыть 8а на протеус??
Не нужно искать мегу8а, это не причина ошибки, размер памяти у них одинаковый.
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Ср авг 20, 2014 19:28:56
ARV
Вы обратили внимание, что на скринах протеус пишет "доступ к не существующему адресу", а команда при этом lds с внешне нормальным адресом? Сдаётся мне, дело в том, что протеус не понимает, что адресация от атмела и avr-gcc отличаются в 2 раза... Сравните адреса в дизассемблере протеуса и в листинге компилятора...
Может, искать причину надо не там совсем, где показывает прот... Ощущение, что портится стек и получается ret на несуществующий адрес.
Это так, предположение... Было у меня похожее: из-за переполнения стека прот вылетал с абсолютно бессмысленными сообщениями и адресами... Нашёл внимательным отслеживанием значения указателя стека при пошаговом прогоне
Re: Ошибки с доступом к переменным из прерываний
Добавлено: Ср авг 20, 2014 23:46:52
miXOnIN
ARV писал(а):Сравните адреса в дизассемблере протеуса и в листинге компилятора...
Заметил что поиск по адресам в Atmel Studio в два раза изменяется
Например, мне нужно перейти по адресу 0х036A, я ввожу 0x0000036A, а оно переходит на 0x000001B5, а если 036A*2=0x000006D4, перехожу и попадаю на нужный мне 0x0000036A
Адреса кстати не поспадают, протеус ссылается на ISR как на скринах ниже... а тот же адрес в Атмел Студии ссылается на другое место, на скрине выше в посте я это и показал, что адрес тот-же но ссылается уже не на ISR а на другое место...
Вот пост:
http://radiokot.ru/forum/viewtopic.php? ... 4#p2104344
Кстати. Забавно то что протеус каждый пару минут вылетает и рестартует МК из-за ошибки, а в жизни собранный - по 2-3 часа работает и норм
***********************************
Сейчас вообще проблема блин, Атмега8а это хорошо, но кончилась память, решил заменить более мошьным Atmega32a
Портировал за пол часа программу, в протеусе - работает, а вот в жизни нифига, не могу понять в чём проблема блин(
ошибки кстати остались и в новой проге на новой МК, всё так-же(
Фух спустя час ели нашёл чего мега 32а не работала, 4 пина не пахали, оказалось это пины ЖТАГа, и он по дефолту в фьюзах был включён, и ничего с портами работать не могла те которые ЖТАГные
пиииииииииииииипееееееееееееец.............
походу умер мой программатор, не видит МКшки, после того как +12 на плату попало на миг....... а по переди 5 выходных дней почты, это просто пиииииииииииииииииипппппппппеееееееееееееццццццццццццццццццц