|
Опрос АЦП внутри прерывания |
|
|
|
Dec 7 2013, 06:41
|
Знающий
   
Группа: Свой
Сообщений: 754
Регистрация: 29-06-06
Из: Volgograd
Пользователь №: 18 458

|
Всем привет! Перенес код опроса АЦП (ATmega16) внутрь обработчика прерываний от таймера, АЦП напрочь перестал работать, оба байта стабильно 00, 00. Есть какие-то подводные камни при запуске однократного преобразования АЦП в теле обработчика прерывания? (Сам факт вызова процедуры обработчика прерывания вижу на осциллографе.) Этим кодом пользовался много раз: Код //АЦП доступен, прерывания разрешены //ADCSRA: 7-ADEEN 6-ADSC 5-ADATE 4-ADIF 3-ADIE 2-ADPS2 1-ADPS2 0-ADPS0 // 1 0 0 0 0 1 1 1 ADCSRA = 0x87; ... ADCSRA |= 0x40; //Начать одиночное преобразование АЦП while( (ADCSRA & 0x40)==0x40 ); //ждем завершения преобразования ADSC (13-14 тактов) c1 = ADCL; //Читаем результат преобразования сначала младший c2 = ADCH; //затем старший (иначе не работает!) VADC = c1 + c2*256;
|
|
|
|
|
Dec 7 2013, 09:12
|
Знающий
   
Группа: Свой
Сообщений: 754
Регистрация: 29-06-06
Из: Volgograd
Пользователь №: 18 458

|
Цитата(Abell @ Dec 7 2013, 11:03)  А Вы не обрабатывайте АЦП в прерывании, Вы обрабатывайте его в главном цикле, так-то оно правильней будет... за 13-14 тактов многое может произойти, да так, что и выйти корректно из прерывания может не получиться, или выйти не туда  Ну так оно и было, все работало. Мне нужно обеспечить гарантированное время в петле регулирования. Все что связано с регулированием (измерение и вычисление управляющего воздействия) засунул в прерывание от таймера, а интерфейсную часть (опрос клавиатуры и вывод на LCD) оставил в основном цикле. И... неожиданно обломался. Уже пару дней перечитываю даташит - ну не вижу я ни одной причины по которой нельзя опросить АЦП внутри обработчика прерывания! Времена на стадии отладки сделал огромные (21 mS), при входе в прерывание выставляю на пин "1" на осциллографе вижу, что прерывание случилось, при выходе из прерывания сбрасываю пин в "0", то есть, все работает, но не измеряет. Опора как была так и есть. В общем, фигня какая-то.  Клавиатура работает, LCD тоже, то есть структура программы не рушится, если случаются вложенные прерывания, они отрабатывают корректно.
|
|
|
|
|
Dec 7 2013, 10:02
|
Знающий
   
Группа: Свой
Сообщений: 754
Регистрация: 29-06-06
Из: Volgograd
Пользователь №: 18 458

|
Цитата(Abell @ Dec 7 2013, 12:41)  Ну пусть дальше и работает  А, случайно, прерывания АЦП запретили? Регистр состояния не меняется в процессе обработки? Может, проще в основном цикле запускать преобразование, а по прерыванию АЦП брать результат и синхронизировать с таймером? Да, сам АЦП прерывания не генерит, ADIE=0. Так все красиво в программном коде разложилось, не хочется перелопачивать. Попробую посмотреть на ассемблере, что там компилятор сотворил. Цитата я сталкивался с неправильной обработкой АЦП в подпрограмме обработки прерывания от таймера А у Вас тоже 00 00 было или просто точность измерений упала?
|
|
|
|
|
Dec 7 2013, 17:15
|

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

|
Цитата(AndreyVN @ Dec 7 2013, 13:12)  Мне нужно обеспечить гарантированное время в петле регулирования. Все что связано с регулированием (измерение и вычисление управляющего воздействия) засунул в прерывание от таймера, а интерфейсную часть (опрос клавиатуры и вывод на LCD) оставил в основном цикле. И... неожиданно обломался. Уже пару дней перечитываю даташит - ну не вижу я ни одной причины по которой нельзя опросить АЦП внутри обработчика прерывания! А вы старт АЦП делайте из прерывания, а ожидание и прием данных в основном цикле. Тогда и синхронизация будет отличная и ждать долго не придется (т.к. пока выйдет из прерывания и в основном цикле до опроса дело дойдет, глядишь, преобразование само закончится без дополнительного ожидания). Для этого лишь надо кроме старта еще самодельный флаг выставить, что с АЦП данные надо востребовать, а в основном цикле его проверяете, и если стоит, то ждете готовности и опрашиваете. P.S. Сама делала так: таймер запускала на удвоенную частоту опроса, в четных перерываниях от таймера делала старт АЦП, а в нечетных его опрашивала без ожидания.
|
|
|
|
|
Dec 7 2013, 18:59
|
Знающий
   
Группа: Свой
Сообщений: 754
Регистрация: 29-06-06
Из: Volgograd
Пользователь №: 18 458

|
Цитата(Xenia @ Dec 7 2013, 20:15)  А вы старт АЦП делайте из прерывания, а ожидание и прием данных в основном цикле. Тогда и синхронизация будет отличная и ждать долго не придется (т.к. пока выйдет из прерывания и в основном цикле до опроса дело дойдет, глядишь, преобразование само закончится без дополнительного ожидания). Идея понятная. Нечто подобное я делал, нашел в в своих-же разработках: Запуск АЦП по переполнению таймера Код interrupt [TIM2_OVF] void timer2_ovf_isr(void) { ADCSRA |= 0x40; //Начать одиночное преобразование АЦП } Ловим результат в обработчике прерываний от АЦП (естественно, прерывания от АЦП разрешены ADIE=1) Код interrupt[ADC_INT] void ADC(void) { unsigned char c1,c2; c1 = ADCL; //Читаем результат преобразования сначала младший c2 = ADCH; //затем старший (иначе не работает!) VADC = c1 + c2*256; } Сейчас работать не хочет, видимо, причина где-то в другом месте.
|
|
|
|
|
Dec 8 2013, 00:31
|

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

|
Цитата(AndreyVN @ Dec 7 2013, 08:41)  оба байта стабильно 00, 00. А где вы их проверяете? В этом же прерывании или в основном цикле? Если в основном цикле, то как объявлена переменная VADC? Цитата(AndreyVN @ Dec 7 2013, 08:41)  Код while( (ADCSRA & 0x40)==0x40 ); //ждем завершения преобразования ADSC (13-14 тактов) Вы только не забывайте, что это 13-14 тактов частоты АЦП, в вашем случае это 13...14 * 128 = 1664 или 1792 тактов ядра. Вы хорошо подумали, прежде чем засунуть такое ожидание в обработчик прерывания? Цитата(AndreyVN @ Dec 7 2013, 08:41)  Код c1 = ADCL; //Читаем результат преобразования сначала младший c2 = ADCH; //затем старший (иначе не работает!) VADC = c1 + c2*256; Ой. Делайте VADC = ADC; и не морочьте себе голову. Любой компилятор для AVR знает, в каком порядке надо читать двухбайтные регистры. Цитата(AndreyVN @ Dec 7 2013, 21:36)  А Иде смотреть-то? В своем листинге. Внимательно смотреть, что сгенерил компилятор и думать - где вы могли неправильно объяснить ему свою задумку. Чудес не бывает.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Dec 8 2013, 06:22
|
Знающий
   
Группа: Свой
Сообщений: 754
Регистрация: 29-06-06
Из: Volgograd
Пользователь №: 18 458

|
Цитата А где вы их проверяете? В этом же прерывании или в основном цикле? Если в основном цикле, то как объявлена переменная VADC? Я контролирую не VADC, а непосредственно с1, с2, чтобы свести к минимуму возможные причины ошибок в т.ч. присвоения, преобразования типов и т.п.. VADC тоже выводится на экран после мат. обработки, но это сейчас не интересно. Вывод на LCD и в основном цикле пробовал и прямо в обработчике пробовал. Переменные тоже пробовал объявлять глобальными и локальными, ситуация не меняется. Цитата Вы только не забывайте, что это 13-14 тактов частоты АЦП, в вашем случае это 13...14 * 128 = 1664 или 1792 тактов ядра. Вы хорошо подумали, прежде чем засунуть такое ожидание в обработчик прерывания? Общего быстродействия хватает, тем более, что сейчас ловлю готовность АЦП в прерывании (см. выше). Цитата Ой. Делайте VADC = ADC; и не морочьте себе голову. Любой компилятор для AVR знает, в каком порядке надо читать двухбайтные регистры. Когда код заработает так и сделаю, а сейчас, предпочитаю видеть каждый байт как он есть. Цитата В своем листинге. Внимательно смотреть, что сгенерил компилятор и думать - где вы могли неправильно объяснить ему свою задумку. Чудес не бывает. Ой, Спасибо! Однако, Вы не сказали, удавалось лично Вам опрашивать АЦП в обработчике прерываний таймера?
|
|
|
|
|
Dec 8 2013, 08:57
|

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

|
Цитата(AndreyVN @ Dec 8 2013, 08:22)  Вывод на LCD и в основном цикле пробовал и прямо в обработчике пробовал. Переменные тоже пробовал объявлять глобальными и локальными, ситуация не меняется. Покажите объявление этих переменных. Хорошо, без намеков: используете ли вы в объявлении ключевое слово volatile? Цитата(AndreyVN @ Dec 8 2013, 08:22)  Ой, Спасибо! Однако, Вы не сказали, удавалось лично Вам опрашивать АЦП в обработчике прерываний таймера? Мне удается опрашивать его где угодно. Еще раз повторю: чудес не бывает. В документации опрашивать АЦП в прерывании таймера не запрещено.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Dec 8 2013, 09:17
|
Знающий
   
Группа: Свой
Сообщений: 754
Регистрация: 29-06-06
Из: Volgograd
Пользователь №: 18 458

|
Цитата(Сергей Борщ @ Dec 8 2013, 11:57)  Покажите объявление этих переменных. Хорошо, без намеков: используете ли вы в объявлении ключевое слово volatile? Мне удается опрашивать его где угодно. Еще раз повторю: чудес не бывает. В документации опрашивать АЦП в прерывании таймера не запрещено. Нет, volatile не использую. Раньше пользовался, когда менял C'шные переменные из кода ассемблерных вставок, но так и не знаю, зачем это надо компилятору. Собственно, проблема решилась, заработала конструкция: Код interrupt [TIM2_OVF] void timer2_ovf_isr(void) { ADCSRA |= 0x40; //Начать одиночное преобразование АЦП PORTA.4 = ~PINA.4; //Для осциллографа }
interrupt[ADC_INT] void ADC(void) { unsigned char c1,c2; #asm("sei"); //Разрешить вложенные прерывания (управляющие H-мостом) PORTA.4 = ~PINA.4; //Для осциллографа c1 = ADCL; //Читаем результат преобразования сначала младший c2 = ADCH; //затем старший (иначе не работает!) V2 = (c1 + c2*256)*4.97*Ku/1024; PORTA.4 = ~PINA.4; //Для осциллографа } Отключил 90% кода не связанного с обслуживанием АЦП, измерение ожило, затем стал возвращать процедуру за процедурой, специально проверил, работоспособность прерываний которые случаются во время interrupt[ADC_INT] (это разрешено командой SEI) - все работает, переменные c1, c2 хранятся в регистрах, но успешно сохраняются и восстанавливаются через стек. К сожалению, так и не понял, что было. Сейчас вернул почти весь код на место, все работает, возвращаться к обработке АЦП "внутри таймера" уже нет смысла.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|