ivainc1789 0 10 октября, 2007 Опубликовано 10 октября, 2007 · Жалоба Устройство должно измерять три аналоговых сигнала: AS1, AS2, AS3. Таймер 1 переполняется каждые 32 мкс. К факту переполнения привязан ADC с общими настройками: ADCSRA = BIN(10111101); // ADC 250kHz при Fbq=8MHz, включен ADCSRB = BIN(00000110); Конечно, таймер 1 переполняется чаще, чем ADC успевает выполнить одно преобразование, но это пока представляется не важным. Обработчик прерывания по завершению преобразования выполнен так: // ADC Conversion Complete #pragma vector = ADC_vect __interrupt void ADC_COMPLITE(void) { unsigned int tmp = ADC; if(ADMUX == BIN(10010010)){AS1 = tmp;ADMUX = BIN(10000010);return;} if(ADMUX == BIN(10000010)){AS2 = tmp;ADMUX = BIN(10000011);return;} if(ADMUX == BIN(10000011)){AS3 = tmp;ADMUX = BIN(10010010);return;} } Казалось бы, все правильно: закончили одно преобразование, забрали результат, посмотрели от какого MUX оно произошло и записали в соотв глобалную переменную. Но не работает. Причина установлена, заключается в том, что ADMUX некорректно менять в данный момент в данном прерывании. Если читать только одну величину преобразования (не меняя ADMUX) ее значение получается правильным и устройство работает. В даташите по поводу работы с мультиплексором написано на стр.147-148: If Auto Triggering is used, the exact time of the triggering event can be indeterministic. Special care must be taken when updating the ADMUX Register, in order to control which conversion will be affected by the new settings. If both ADATE and ADEN is written to one, an interrupt event can occur at any time. If the ADMUX Register is changed in this period, the user cannot tell if the next conversion is based on the old or the new settings. ADMUX can be safely updated in the following ways: a. When ADATE or ADEN is cleared. b. During conversion, minimum one ADC clock cycle after the trigger event. c. After a conversion, before the Interrupt Flag used as trigger source is cleared. When updating ADMUX in one of these conditions, the new settings will affect the next ADC conversion. Использовать вариант (a) как-то не хочется, вкл/выкл ADC на такой частоте чреват переходными процессами... Хотелось бы все же менять ADMUX в прерывании по окончанию преобразования, но как это лучше сделать и можно ли понять пока не смог... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Qwertty 0 10 октября, 2007 Опубликовано 10 октября, 2007 · Жалоба .... Использовать вариант (a) как-то не хочется, вкл/выкл ADC на такой частоте чреват переходными процессами... Хотелось бы все же менять ADMUX в прерывании по окончанию преобразования, но как это лучше сделать и можно ли понять пока не смог... Может измерять каждый канал по два раза подряд, и использовать только результат второго преобразования? Насколько критична скорость оцифровки? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ivainc1789 0 10 октября, 2007 Опубликовано 10 октября, 2007 · Жалоба Может измерять каждый канал по два раза подряд, и использовать только результат второго преобразования? Насколько критична скорость оцифровки? Скорость оцифровки желательно иметь максимальную. Вопрос по сути: как правильно менять настройки ADMUX для трех AS1,AS2,AS3 при триггерировании ADC от TOV1, если период триггерного сигнала меньше, чем время преобразования ADC? Варианты (B) и (с) даташита сложно использовать для обеспеч. макс скорости оцифровки... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Qwertty 0 10 октября, 2007 Опубликовано 10 октября, 2007 · Жалоба Если ADC работает в режиме одиночного преобразоания, то в прерывании от него можно изменить ADMUX и запустить следующее преобразование- получится псевдо автоматический режим. Или по другому - ADC молотит непрерывно, в прерывании мы сохраняем результат преобразования в зависимости от ADMUX, т.е. если второй канал в ADMUX, то результат отностися к первому каналу :), третий - результат от второго. Далее ADMUX меняется. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ivainc1789 0 10 октября, 2007 Опубликовано 10 октября, 2007 · Жалоба Если ADC работает в режиме одиночного преобразоания, то в прерывании от него можно изменить ADMUX и запустить следующее преобразование- получится псевдо автоматический режим. Или по другому - ADC молотит непрерывно, в прерывании мы сохраняем результат преобразования в зависимости от ADMUX, т.е. если второй канал в ADMUX, то результат отностися к первому каналу :) , третий - результат от второго. Далее ADMUX меняется. Да, это все понятно. Я так и сделал: ADC молотит по кругу: #define AS1set BIN(10000011)// mux const for AS1 #define AS2set BIN(10010010)// mux const for AS2-AS1 #define AS3set BIN(10000010)// mux const for AS3 __C_task void main (void) { // settings for ADC ADMUX = AS2set; ADCSRA = BIN(10011101); ADCSRB = BIN(00000000); DIDR0 = BIN(11111111); DIDR1 = BIN(11110000); __enable_interrupt(); SETBIT(ADCSRA,ADSC);// first ADC conversion start while(1); } // ADC Conversion Complete #pragma vector = ADC_vect __interrupt void ADC_COMPLITE(void) { //AS2value = ADC; switch(ADMUX){ case AS1set: ADMUX = AS2set;AS1value = ADC;break; case AS2set: ADMUX = AS3set;AS2value = ADC;break; case AS3set: ADMUX = AS1set;AS3value = ADC;break; } SETBIT(ADCSRA,ADSC); } Проверил: сигналы AS1 и AS3 измеряются нормально. А вот AS2 - это дифференциальный сигнал, пропущенный через внутр. инструментальный усилитель. Вот с ним-то и возникают проблемы: 1. Если обработчик использовать как приведено выше (мультиплексор периодически переключается), то ADC = 0 для сигнала AS2. 2. Если в обработчике убрать весь оператор SWITCH и раскомментировать строку //AS2value = ADC;, т.е. мультиплексор остается ПОСТОЯННО настроенным для сигнала AS2, то данный сигнал измеряется нормально. Никак не могу понять, почему так??? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ivainc1789 0 10 октября, 2007 Опубликовано 10 октября, 2007 · Жалоба И, наконец, окончательно установлено: в ATtiny461 переключение мультиплексора ADC с single ended input на differential input приведет к последующему ошибочному преобразованию, которое необходимо пропустить. Что-то в даташите на данную тему никаких notes не обнаружено. Для моего случая обработчик должен выглядеть так: unsigned char ADMUXval=0; // ADC Conversion Complete #pragma vector = ADC_vect __interrupt void ADC_COMPLITE(void) { //AS2value = ADC; switch(ADMUXval){ case 0: break;// холостое измерение для AS2 case 1: ADMUX = AS1set;AS2value = ADC;break; case 2: ADMUX = AS3set;AS1value = ADC;break; case 3: ADMUX = AS2set;AS3value = ADC;ADMUXval=0xFF;break; } ADMUXval++; SETBIT(ADCSRA,ADSC);// начать очередное преобразование } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
singlskv 0 10 октября, 2007 Опубликовано 10 октября, 2007 · Жалоба И, наконец, окончательно установлено: ............................ Покажите наконец весь код ....:) Ваша проблема скорее всего заключается в использовании 2 прерываний для работы с ADC, в итоге Вы и имеете то что переключение каналов происходит в момент когда его нельзя переключать, Вы должны понять что преобразование НЕ начинается в момент когда Вы выставили флаг начать преобразование и НЕ начинается синхронно от таймера в случае если частота преобразования ADC не кратна частоте таймера. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ivainc1789 0 10 октября, 2007 Опубликовано 10 октября, 2007 · Жалоба Покажите наконец весь код .... :) Ваша проблема скорее всего заключается в использовании 2 прерываний для работы с ADC, в итоге Вы и имеете то что переключение каналов происходит в момент когда его нельзя переключать, Вы должны понять что преобразование НЕ начинается в момент когда Вы выставили флаг начать преобразование и НЕ начинается синхронно от таймера в случае если частота преобразования ADC не кратна частоте таймера. Весь код я привел выше. Единственное, о чем умолчал - как я определял правильно измеряется величина ADC или нет. Так как Дракона под рукой все еще пока нет использовались два метода: 1. Значение AS2value (по сути значение ADC для сигнала AS2) записывалось в EEPROM с периодом 5 сек. В некоторый момент EEPROM считывалась программатором. Если мультиплексор переключался в прерывании по оконч преобр., то считывалось из EEPROM AS2value == 0; 2. Так как программатор самопал, в истинности считывания я усомнился и для контроля инициализировал таймер1 в режим inverted fastPWM, далее значение ADC записывалось в регистр сравнения. Контроль скважности проводился на пине OC1D. При увеличении значения ADC ширина импульсов должна была уменьшаться - так и было для всех трех сигналов, если mux не переключать в прерывании по оконч. преобразования... Заметьте, что флаг ADATE в исправленном коде не установлен. То есть ADC работает в режиме единичного преобразования но с разрешенным прерыванием по оконч. преобраз. В обработчике стартует новое преобраз. Так организовано зацикливание. Согласно даташиту, если ADATE не установлен, то ADMUX можно безопасно записывать. Но даже в этом случае, если не пропускалось "битое" измерение от AS2, AS2value == 0, а на выводе OC1D присутствовал высокий уровень... Далее я пробовал менять записи в ADMUX в прерывании, т. е. измерял сигналы в разной последовательности. И опять: только дифференциальный сигнал AS2 всегда возвращал некорректное (нулевое) значение... "Двойственность" прерываний легко проверить. Для этого устанавливаем в единицу какой-либо тестовый пин в начале обработчика и сбрасываем его в ноль в конце. В моем случае на осцилле наблюдались короткие прямоуголные импульсы с периодом 62 мкс. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
artemkad 69 10 октября, 2007 Опубликовано 10 октября, 2007 (изменено) · Жалоба a. When ADATE or ADEN is cleared. b. During conversion, minimum one ADC clock cycle after the trigger event. c. After a conversion, before the Interrupt Flag used as trigger source is cleared. When updating ADMUX in one of these conditions, the new settings will affect the next ADC conversion. Использовать вариант (a) как-то не хочется,... А придется... Выключаешь ADEN по влету в прерывание и включаешь по выходу (при запуске следующего преобразования). При этом длительность каждого преобразования удваивается... Или попробуй убрать из своей программы использование SLEEP-а. На Мегах48 это мне помогло... Изменено 10 октября, 2007 пользователем ArtemKAD Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
singlskv 0 10 октября, 2007 Опубликовано 10 октября, 2007 · Жалоба Весь код я привел выше..... "Двойственность" прерываний легко проверить. Для этого устанавливаем в единицу какой-либо тестовый пин в начале обработчика и сбрасываем его в ноль в конце...... Вопрос не в "двойственности" прерываний, вопрос в том вовремя ли Вы меняете ADMUX или нет ? Приведите весь код, и настройку таймером в том числе, да, Вы вроде еще и результаты тестов в EEPROM писали ? , тоже укажите как Вы это делали. А придется... Выключаешь ADEN по влету в прерывание и включаешь по выходу (при запуске следующего преобразования). При этом длительность каждого преобразования удваивается...Так нужно ? Правда ? Что я не так делаю, что у меня и без этого преобразования на разных каналах правильно работают ? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
artemkad 69 10 октября, 2007 Опубликовано 10 октября, 2007 · Жалоба Что я не так делаю, что у меня и без этого преобразования на разных каналах правильно работают ? Наткнулся на эту особенность на мегах. Особенность - "перетекание" сигнала с другого канала при переключении ADMUX-ов. Наблюдалась только при использовании SLEEP-а. Без SLEEP-а "перетекание" не обнаружил. Решения проблемы у меня получилось два - оба с почти одинаковым результатом. Или включение/выключение АЦП (что заставляло ее принудительно запустить холостое преобразование - те самые 25 тактов) или убиранием команды SLEEP. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ivainc1789 0 10 октября, 2007 Опубликовано 10 октября, 2007 · Жалоба Привожу полный код. Оказывается, в прерывании от ADC важна последовательность измерения сигналов. Один из трех измеренных измеряется неверно. Теперь это AS3. // мультиплексор ADC #define AS1set BIN(10000011)// для сигнала AS1 #define AS2set BIN(10010010)// для сигнала AS2-AS1 #define AS3set BIN(10000010)// для сигнала AS3 /********************************************************************************/ unsigned int AS3value,AS2value,AS1value; unsigned int __eeprom A; /********************************************************************************/ __C_task void main (void) { SETUP(SWUP,OUT0);SETUP(SWBAT,OUT0);SETUP(SWPWR,OUT0); SETUP(AS1GND,OUT0);SETUP(AS2GND,OUT0);SETUP(AS4GND,OUT0);// подключить делители // общие настройки компаратора CLRBIT(ACSRA,ACIE); ACSRA = BIN(01010000); // компаратор с AIN+ = BANDGAP_REF включен ACSRB = BIN(00000100); //SETBIT(ACSRA,ACIE); // общие настройки ADC ADMUX = AS2set; // опора компаратора и ADC = 1.1 вольт ADCSRA = BIN(10011101); // ADC 250kHz при Fbq=8MHz,включен,прерывание по оконч. ADCSRB = BIN(00000000); DIDR0 = BIN(11111111);// отключить входные буферы ADC(нет вх цифровых сигналов) DIDR1 = BIN(11110000); Tmr1Ini(); __enable_interrupt(); SETBIT(ADCSRA,ADSC);// начать первое преобразование Tmr1Start; Work: { unsigned int tmp; __disable_interrupt(); tmp = AS3value;// !!!!!!!!!!!!!значение AS3 наблюдаем неверное!!!!!!!!!!!! __enable_interrupt(); A = tmp; } goto Work; } /********************************************************************************/ // Timer/Counter1 Overflow #pragma vector = TIM1_OVF_vect __interrupt void TIM1_OVF(void) { unsigned char tmp2; tmp2 = AS3value;// !!!!!!!!!!!!!значение AS3 наблюдаем неверное!!!!!!!!!!!! TC1H = ((tmp2 >> 8) & 0x0003); OCR1D = (tmp2 & 0x00FF);// загрузка величины сравнения для след. цикла } // ADC Conversion Complete #pragma vector = ADC_vect __interrupt void ADC_COMPLITE(void) { SETBIT(ADCSRA,ADSC);// начать преобразование по текущему ADMUX switch(ADMUX){// установить следующий ADMUX case AS1set: ADMUX = AS2set;AS1value = ADC;break; case AS2set: ADMUX = AS3set;AS2value = ADC;break; case AS3set: ADMUX = AS1set;AS3value = ADC;break; } } /********************************************************************************/ // ини таймера1 для stepDown и stepUp регуляторов void Tmr1Ini(void){ Tmr1Stop; CLRBIT(PRR,PRTIM1); // включить модуль таймера 1 SETBIT(PLLCSR,LSM); // PLL = 32MHz SETBIT(PLLCSR,PLLE);Delay(200*us);do{}while(!CHKBIT(PLLCSR,PLOCK));// запуск PLL SETBIT(PLLCSR,PCKE); // разрешить тактирование таймера от PLL TCCR1A = BIN(00110001); TCCR1B = BIN(01000000);do{}while(CHKBIT(TCCR1B,PSR1));// сброс прескалера TCCR1C = BIN(00111101); TCCR1D = BIN(00000000); TCCR1E = BIN(00000000); DT1 = 0x00; Reg10write(TCNT1reg,0x000); Reg10write(OCR1Areg,0x3FF); Reg10write(OCR1Breg,0x3FF); Reg10write(OCR1Creg,0x3FF); Reg10write(OCR1Dreg,0x3FF); SETBIT(TIFR,TOV1); SETBIT(TIMSK,TOIE1); } /********************************************************************************/ Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
smk 0 11 октября, 2007 Опубликовано 11 октября, 2007 · Жалоба Вопрос по сути: как правильно менять настройки ADMUX для трех AS1,AS2,AS3 при триггерировании ADC от TOV1, если период триггерного сигнала меньше, чем время преобразования ADC? Вы хотите заставить АЦП мерять чаще чем он может? Нмкак нельзя, так как напряжение на накопительной емкости используется для сравнения в течение всего преобразования. Так же желательно не использовать первый результат преобразования при переключении входа АЦП. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
singlskv 0 11 октября, 2007 Опубликовано 11 октября, 2007 · Жалоба // ADC Conversion Complete #pragma vector = ADC_vect __interrupt void ADC_COMPLITE(void) { SETBIT(ADCSRA,ADSC);// начать преобразование по текущему ADMUX switch(ADMUX){// установить следующий ADMUX case AS1set: ADMUX = AS2set;AS1value = ADC;break; case AS2set: ADMUX = AS3set;AS2value = ADC;break; case AS3set: ADMUX = AS1set;AS3value = ADC;break; } } Проблема в том что Вы сначала запускаете преобразование SETBIT(ADCSRA,ADSC); и тут же меняете номер канала ADMUX = .... SETBIT(ADCSRA,ADSC); НЕ запускает преобразование немедленно, реально новое преобразование запустится только когда начнется очередной такт ADC клока. Попробуйте так: // ADC Conversion Complete #pragma vector = ADC_vect __interrupt void ADC_COMPLITE(void) { switch(ADMUX){// установить следующий ADMUX case AS1set: ADMUX = AS2set;AS1value = ADC;break; case AS2set: ADMUX = AS3set;AS2value = ADC;break; case AS3set: ADMUX = AS1set;AS3value = ADC;break; } SETBIT(ADCSRA,ADSC); // запускаем новый канал } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ivainc1789 0 11 октября, 2007 Опубликовано 11 октября, 2007 · Жалоба реально новое преобразование запустится только когда начнется очередной такт ADC клока. Попробуйте так: Честно сказать, много приходилось заниматься AVR кристаллами, но такого капризного как 461 я никогда не встречал... Слабое место тиней 461 - это безусловно мультиплексор. Может из-за того, что сильно уж наворочен. Уже два кристалла вышли из строя при долговременной (около часа) работе при штатном режиме! Причем в одном сдох мультиплексор компаратора, а в другом - АЦП. Перенапряжения исключены, программа не перепрошивалась: просто работала ИС работала и в какой-то момент - отказ. Выражается это в том, что входы мультиплексоров вдруг по неизвестной причине садятся на землю. Причем к этим входам подключены делители от 12 вольт 56к/2к7 - достаточно безопасное подключение... При всем этом ИС нормально прошивается, читается сигнатура и т. д. Насколько пока позволило время разобраться - отказ получается частичный. У меня ревизия А. Что касается момента начала преобразования, то, конечно, я инструкцию изначально ставил и после установки мультиплексора в прерывании, однако это не спасало: дифф сигнал не измерялся, АЦП выдавал только 0... Вскоре после множества экспериментов созрела идея: так как аналоговые сигналы посупают на входы через делители а выход АЦП контролируется по ШИМу, то можно просто последовательно контролировать сигналы замыкая делители на входах через амперметр. Если именно НУЖНЫЙ сигнал измеряется, на выводе OCR1D должен установиться высокий уровень в момент замыкания делителя. Так вот, для single ended сигналов такой фокус сработал безотказно, а для дифф - стоял высокий уровень и на шаманство с делителями вообще не отзывался. При этом в прерывании инструкция начала преобразования стояла после выбора ADMUX. Короче, предполагаю, что мне попались не совсем исправные ИС, при условии, что в даташите нет опечаток в таблицах мультиплексоров... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться