Страница 1 из 1

Глюк программы в прерывании

Добавлено: Сб мар 08, 2014 16:48:26
ИС-пытатель
Доброго времени, уважаемые форумчане!

Обращаюсь к Вам с просьбой помощи, ибо сам я уже бьюсь над проблемой неделю и вывихнул себе весь мозг.

Итак, суть. Имеется отладочная плата STK600 + к ней микроконтроллер 2560.

Пишется элементарная программа (пробовал ее писать в AVR-studio 5.1, 6.0; CVavr; IAR; ImageCraft) везде работает практически одинаково, но везде не так, как мне хочется. Причем в Proteus моделируется точно так же, как на железе (поэтому грешу все-таки на свои кривые руки, а не на кристалл).

Суть программы: периодически запускается таймер. В обработке прерывания таймера запускается АЦП. По прерыванию АЦП запускается его обработчик, в котором реализуется следующий алгоритм: если переменная меньше определенного числа, то инкремент переменной. Если условие не выполняется, то в порт A (подключен шлейфом к светодиодам отладочной платы) отправляется значение 0xF0 (половина светодиодов платы должна зажечься). Итак, сама программа в упрощенном варианте(для AVR студио 5.1):

// Standard Input/Output functions
#include <stdio.h>
#include <avr\interrupt.h>

#define porog 0
#define ADC_VREF_TYPE 0x00
#define ER_F 0 // результат готов к обработке
#define UO_F 1 // передано по UART


// Declare your global variables here
unsigned int array_of_freq[1000]={0xAA};
volatile signed int adc_data;
volatile unsigned int period, freq=0, diff=0;
volatile signed int diskr=0, counter=0;
signed int preview_result=0;

// Timer 0 output compare A interrupt service routine
ISR(TIMER0_COMPA_vect)
{
asm("wdr");
ADCSRA |= (1<<ADSC);
}

// ADC interrupt service routine
ISR(ADC_vect)
{
// Read the AD conversion result
adc_data=ADCW;
adc_data -=512;
#if porog
if ((adc_data <= porog)&&(adc_data >= (-(porog)))) adc_data = 0;
#endif
GPIOR0 |= (1<<ER_F);

if (diskr<7813) diskr++;
else PORTA = ~0xf0;

}


int main(void)
{
// Инициализация (Вырезана, чтобы не мозолить глаза. см полную версию)
while (1)
{
}
}

Комментарии и куски неиспользуемого кода специально оставил как есть.
Маленькие пояснения. светодиоды по прошествии цикла инкремента переменной diskr не зажигаются. есть подозрения, что цикл инкремента выполняется всего 2 раза (вывод сделан на основании того факта, что если в начале переменной diskr присвоить значение 7812 (т.е. на 1 меньше критического), то светодиоды загораются). Изначально при просмотре дизассемблированного кода программы было обнаружено, что не был сгенерирован код сброса сторожевого таймера. Была введена команда отключения стор. таймера. Это не помогло. Повсеместная вставка команды сброса сторожевого таймера тоже не помогает. Отключение оптимизации и перебор разлиных настроек оптимизации также не помогает. Самое интересное, если вынести код инкремента переменной в основной цикл main, то все прекрасно работает. Опять повторюсь, смутило то, что Proteus полностью повторяет действия контроллера.

Помогите решить загадку! Я даже не прочь, если меня потыкают мордой в моё собственное дерьмо, только покажите, где ошибка?

Полный код программы (для вставки в студию и разбора):

Re: Глюк программы в прерывании

Добавлено: Вс мар 09, 2014 17:18:45
ИС-пытатель
Господа, всем спасибо! Еще 2 дня мучений и разгадка найдена. Не могу сказать, у всех это контроллеров 2560 или только мой браковый...
В общем, смысл в том, что наличие любого обращения к стеку или к его регистрам (in, out, push, pop, sts и т.д.) в обработчике прерывания таймера почему-то напрочь блочили команды сложения, инкремента, декремента и вычитания (др. не проверял) в обработчике прерывания от АЦП (и только в нем. в основном цикле все работало на ура). Если быть точнее эти команды один раз выполнялись, и на этом все заканчивалось. К тому же по мере моего мучения кристалла начал проявляться еще один баг. Чтобы работала команда сложения (опять-таки остальное проверять не стал) стало необходимо добавлять после нее команду nop. А далее стало необходимо 2 таких команды (видимо, совсем замучал я его).
Считаю, тему закрытой. Мне попался плохой кристал. Уффф.. Руки, слава богу, прямые и растут из плеч! :))) :music:

Re: Глюк программы в прерывании

Добавлено: Пн мар 10, 2014 02:31:45
ANALOG
Когда ваша программа уходит в прерывание, адрес последней команды, которую она выполнила заносится в стек, чтобы потом МК мог вернуться из прерывания. Таким образом, при входе в прерывание в верхушке стека находится адрес возврата, по которому МК прыгнет при выполнении reti. Если вы в прерывании что-то кладете в стек (push), то в верхушке уже не адрес возврата, а непонятно что. Когда программа скокнет на этот адрес, начнется полный хаос и анархия :twisted: , т.к. не известно куда она при этом попадет (хорошо если хоть в блок с командами, а не с данными). Аналогично если вы берете из стека (pop).
Поэтому, в прерывания (да и вообще везде) команды push/pop всегда должны идти в паре.
А команды in, out и sts вообще стек не трогают.
Хотя, в вашем коде я вообще не нашел, где вы стек трогаете :roll:

Re: Глюк программы в прерывании

Добавлено: Пн мар 10, 2014 08:24:47
pyzhman
ИС-пытатель писал(а):обращения к стеку (in, out,... sts...
эти команды не трогают стек. А CodeVision вообще не использует команды pop и push.

Re: Глюк программы в прерывании

Добавлено: Пн мар 10, 2014 09:50:08
Engineer_Keen
Sts указатель стека не трогает, а вот прописать что-то в сам стек она может запросто. Например если использовать ее после call сразу в виде sts (ramend),r16. Но так накосячить можно только либо специально, либо в сложном коде со множеством вложенных подпрограмм и ОЗУ заполненным под завязку...

Re: Глюк программы в прерывании

Добавлено: Пн мар 10, 2014 15:52:53
ИС-пытатель
Господа! Всем еще раз спасибо за участие! По поводу последних комментариев. Я давно не школьник и я не выкладывал десятки вариантов, которые я пробовал, ломая кристалл. Я выложил основной вариант, который должен был работать. Говоря про команды я говорил про дизассемблированный код. Команды Push и Pop генерирует компилятор при входе в прерывание и выходе из него. Естественно, они идут парами. Пробовал закомментировать те или иные команды, чтобы определиться, на чем кристалл умирает. при помощи команд in и out можно загрузить в регистр указатель стека, модифицировать его и загрузить обратно. А при помощи команды sts вообще можно записать в любой адрес (в том числе и в регистр, в том числе и в регистры spl-sph). Еще раз всем спасибо!

Re: Глюк программы в прерывании

Добавлено: Пн мар 10, 2014 16:51:42
DX168B
//\\//\\

Re: Глюк программы в прерывании

Добавлено: Пн мар 10, 2014 17:11:18
ИС-пытатель
Это ты выпендриться что ли решил? )) Напрасно! )) Лучше б помог чем. )

Re: Глюк программы в прерывании

Добавлено: Пн мар 10, 2014 17:19:40
DX168B

Код: Выделить всё

unsigned int array_of_freq[1000]={0xAA};
Сколько ОЗУ у твоего камня? Поместится-ли? :)))
Далее, с какими интервалами срабатывает таймер? Можеть они загорятся, но только намного позже?
Может фьюз CKDIV8 забыли снять? Или камень не настроен на нужную частоту.

Re: Глюк программы в прерывании

Добавлено: Пн мар 10, 2014 18:29:13
ИС-пытатель
Да я уже нашел ответ на все свои вопросы. см выше. 2 дня с ассемблером махался) спасибо. ОЗУ у 2560 8к. Поместяться. ) фьюзы пофиг, потому что предделитель устанавливался программно в инициализации. Там же отключение сторожевого таймера. таймер срабатывает раз в 128 мкс. Правильность установки таймера отслеживал по изменяющейся ноге контроллера осциллографом. АЦП настроено на одиночные срабатывания и частота АЦП подобрана так, чтобы выдавало результат раньше следующего щелчка таймера ( по которому запуск АЦП идет) . А загораться... загораются, если обработку результатов из прерывания в мейн вынести. а мне очень хотелось все в прерывании сделать. Там весь цикл измерений около секунды.

В том-то и прикол, что все в поряде было и перепроверено по 100 раз. Там реально камень уже был задроченный и начал глючить. Я только его раз 200 перешил, пока косяк искал. и раз 20, пока прогу писал. А его еще до меня ни один год юзали

Re: Глюк программы в прерывании

Добавлено: Пн мар 10, 2014 18:37:55
DX168B
Хм. Странно. Наверно повредили его когда-то статикой или чем-то еще.
У меня как-то с STM32F407 была хрень. Не правильная тактовая частота ядра получалась.
Убился головой об отладчик и даташит и так причину и не нашел, но все настраивалось правильно.
В один день глюк сам пропал. Какая-то бяка была с PLL.

Re: Глюк программы в прерывании

Добавлено: Пн мар 10, 2014 19:26:40
ИС-пытатель
Вполне может быть.. Но я склонен больше почему-то к варианту, что заюзан он без меры. мож какой транзистор внутри сдох.. или парочка... а статика она обычно по площади бьет. выгорел бы блок или несколько. а то маленький такой БАЖок... неприметный...