|
|
  |
mega2561, проблема в АЦП с ADMUX, проблема в АЦП со считыванием нескольких каналов |
|
|
|
May 11 2011, 09:28
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 26-04-07
Пользователь №: 27 336

|
Всем доброго времени!!! Помогите пожалуйста разобраться в проблеме... Устал, уже весь мозг сломал, всё перепробовал. Устройство: АЦП контроллера измеряет сигнал через делитель напряжения (47/10 кОм), уровень сигнала после делителя - 2 вольта (к примеру). Таких три входа - ADC0-2. Проблема заключается в том, что если считывать только один канал, то всё измеряется на ура, всё (почти) точно по вольтметру. НО! Если в процессе измерения добавить второй канал (или любой другой), то отконвертированное напряжение становится "не правильным", например вместо 12,6 вольт начинает показывать 9.5. Тем не менее, считанное значение остаётся пропорциональным сигналу. Вот код: CODE void ADC_task() { if(!bMeasure) return; __disable_interrupt(); - это я добавил только для пробы, влияет ли таймер динамической индикации на работу АЦП... samples1 = 0; ADCSRA &= ~(1<<ADSC); ADMUX = (1<<REFS1) | (1<<REFS0) + 0;
for(U8 i=0;i<5;i++) { ADCSRA |= (1<<ADSC); while(ADCSRA & (1<<ADIF)); U8 low = ADCL; U16 ADCResult = low + (ADCH<<8); samples1 += (ADCResult*2.56)/1024; } samples1 = samples1/5; fU = samples1*5.7; - компенсация делителя ADCSRA &= ~(1<<ADSC); - это тоже уже от нечего делать... ADMUX++; если закомментировать эту строчку, то напряжение fU - в норме, совпадает с реальным, а если не комм. - то меньше реального на некоторое значение samples2 = 0; for(U8 i=0;i<5;i++) { ADCSRA |= (1<<ADSC); while(ADCSRA & (1<<ADIF)); U8 low = ADCL; U16 ADCResult = low + (ADCH<<8); samples2 += (ADCResult*2.56)/1024; } samples2 = samples2/5; bMeasure = false; __enable_interrupt(); - аналогично disble'у... } После смены каналов (ADMUX) вставлял задержки __delay_cycles(), самые разные, даже до 1 секунды... Пробовал клокить АЦП по таймеру... От чипа к чипу не меняется ничего (таких 10 плат). Ничего не помогает... У меня осталось только одно подозрение. Как Вы считаете, может делитель 47/10 кОм имеет слишком большое сопротивление для нормальной работы АЦП? Дело в том что сейчас по факту, на втором канале (ADC1) 0 вольт (делитель пустой), ни к чему не подсоединён ("пирожок ни с чем"  ))... Вот я и подумал, переключение коммутатора с 2 вольт на 0 и обратно, поэтому надо вставить задержки после модификации ADMUX... Хрена. Не помогло никак. Есть интересное наблюдение: если просмотреть самое первое считанное значение fU, то оно будет нормальным в любом случае, так как сразу после запуска проги на контроллере, первым измеряется именно ADC0, но после него идёт переключение канала ADMUX++ (раньше задавал жёстко, без считывания). Отсюда вывод: как только происходит хотя бы одно переключение, то следующие считывания канала ADC0 становятся испорченными... ПОЧЕМУ??? Снимал кондёр с риференс (был 1 нФ), пробовал увеличивать до 1 мкФ, ноль эмоций  ( К меге подсоединено восемь семисегментных индикаторов, реализована динам. индикация через таймер с прерыванием. Что скажете? Что непонятно - спрашивайте. Могу выложить фотки устройства...
Сообщение отредактировал AngelChik - May 11 2011, 09:37
|
|
|
|
|
May 11 2011, 09:50
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 26-04-07
Пользователь №: 27 336

|
Цитата(Alex_1811 @ May 11 2011, 13:46)  После смены канала перед запуском следующего преобразования нужно подождать пока АЦП сделает 1 клок. В курсе, читал. Доки уже наизусть... Я же написал, что делал задержки после модификации ADMUX, никакого толку нет, всё равно бред считывает.
|
|
|
|
|
May 11 2011, 10:12
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 26-04-07
Пользователь №: 27 336

|
Эгеее... Спасибо, ща попробую. Ну вот, "перевернул"... Код void ADC_task_init() { ADCSRA |= (1<<ADEN); DIDR0 = 0xFF; ADMUX = (1<<REFS1) | (1<<REFS0) + 1; ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); ADCSRB = 0x00; }
void ADC_task() { if(!bMeasure) return; samples2 = 0; for(U8 i=0;i<5;i++) { ADCSRA |= (1<<ADSC); ADMUX = (1<<REFS1) | (1<<REFS0) + 0; while(ADCSRA & (1<<ADIF)); U8 low = ADCL; U16 ADCResult = low + (ADCH<<8); samples2 += (ADCResult*2.56)/1024; } samples2 = samples2/5;
__delay_cycles(16000); // - задержка в 1 мС... Хренли толку?!?
samples1 = 0; for(U8 i=0;i<5;i++) { ADCSRA |= (1<<ADSC); ADMUX = (1<<REFS1) | (1<<REFS0) + 1; while(ADCSRA & (1<<ADIF)); U8 low = ADCL; U16 ADCResult = low + (ADCH<<8); samples1 += (ADCResult*2.56)/1024; } samples1 = samples1/5; fU = samples1*5.7; bMeasure = false; } Получилась вот такая дрянь. Протестил. 12 вс 9,5 вольт... Бошкой об стенку бы побился, да не поможет, ибо стены из гипсокартона... А что на счёт входного сопротивления? Не многовато-ли?... Предделитель - 256, частота - 16 МГц, задержка - 1 мс....
Сообщение отредактировал AngelChik - May 11 2011, 10:15
|
|
|
|
|
May 11 2011, 10:15
|
Местный
  
Группа: Свой
Сообщений: 234
Регистрация: 30-03-07
Из: Одесса
Пользователь №: 26 621

|
В принципе п. 2 можно опустить - не дожидаться конца преобразования, а вынимать данные непосредственно перед запуском следующего преобразования, которое можно запуускать по таймеру. По опыту, на Меге8, для правильного конвертирования напряжения внутреннего ИОН, внутреннее сопротивление которого порядка 30 кОм, если переключаться с 0, необходимо было подождать не менее 10000 тактов (для 10-битной точности).
Без задержек на выходном сопротивлении источника ( порядка 10 кОм) будет безбожно врать. Либо сопроивление источника понизить до менее 1 кОм, либо задержки.
З.Ы. А почему делается именно 5 выборок, а не круглое число? Сразу бы объем программы уменьшился)
И - во втором цикле первый результат будет из канала (1<<REFS1) | (1<<REFS0), который внесет свою коррективу в размере 20%)
Попробуйте сначала получить требуемый результат без фильтрации.
|
|
|
|
|
May 11 2011, 11:11
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 26-04-07
Пользователь №: 27 336

|
Спасибо ещё раз за мнение и совет. Снижу сопротивление в 10 раз, отпишуся... Факир был пьян, фокус не удался  (( Заменил на делитель 4,7/1,0 кОм, не помогло нисколечко...Куда копать дальше?... Забыл признаться в гадости - у меня питание AVCC через фильтр 10 Ом 10 мкФ. Это ведь никак не могло повлиять? Есть одна подвижка, подтверждающая теорию о недозаряде. Ускорил клоки АЦП со 128 до 16... Теперь напряжение показывает не 9,5 против реальных 12, а 4.7  )) Питание - 5.03, на ИОНе - 2.563... Вроде всё как и должно быть. 5 выборок - это пока произвольно, здесь дело в принципе запустить для начала. Цитата И - во втором цикле первый результат будет из канала (1<<REFS1) | (1<<REFS0), который внесет свою коррективу в размере 20%) Не совсем понял, а что за корректива??? Ну насчёт переворота понятно, поэтому я и поменял местами переменные samples1 и samples2. АААААА... До меня дошло, о чём Вы  ))) Первое считывание второго цикла - это результат из последнего считывания первого цикла, а вношу в усреднение другой переменной  )) Ща исправим. Теперь код у нас вот такой: Код |= (1<<ADSC); ADMUX = (1<<REFS1) | (1<<REFS0) + 0; while(ADCSRA & (1<<ADIF)); U8 low = ADCL; U16 ADCResult = low + (ADCH<<8); samples2 = (ADCResult*2.56)/1024; __delay_cycles(32000); // - 2 мс ADCSRA |= (1<<ADSC); ADMUX = (1<<REFS1) | (1<<REFS0) + 1; while(ADCSRA & (1<<ADIF)); U8 low = ADCL; U16 ADCResult = low + (ADCH<<8); samples1 = (ADCResult*2.56)/1024; fU = samples1*5.7; УРААААА!!!!!!!! Я пошёл искать бетонную стену... Вот это чё такое?!?!!? Код while(ADCSRA & (1<<ADIF)); А должно быть Код while(!(ADCSRA & (1<<ADIF))); Ждать-то надо, пока нет флага!!! ИТОГ: Код void ADC_task() { if(!bMeasure) return; samples1 = 0; ADMUX = (1<<REFS1) | (1<<REFS0) + 0; __delay_cycles(10); for(U8 i=0;i<5;i++) { ADCSRA |= (1<<ADSC); while(!(ADCSRA & (1<<ADIF))); U8 low = ADCL; U16 ADCResult = low + (ADCH<<8); samples1 += (ADCResult*2.56)/1024; } samples1 = samples1/5; fU = samples1*5.7; samples2 = 0; ADMUX = (1<<REFS1) | (1<<REFS0) + 1; __delay_cycles(10); for(U8 i=0;i<5;i++) { ADCSRA |= (1<<ADSC); while(!(ADCSRA & (1<<ADIF))); U8 low = ADCL; U16 ADCResult = low + (ADCH<<8); samples2 += (ADCResult*2.56)/1024; } samples2 = samples2/5; bMeasure = false; }
Сообщение отредактировал AngelChik - May 11 2011, 11:05
|
|
|
|
|
May 11 2011, 11:19
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 26-04-07
Пользователь №: 27 336

|
Точняк! Забыл, спасибо. Сброс производится записью 1 в него? правильно? Типа Код ADCSRA |= (1<<ADIF); ? Так?
|
|
|
|
|
May 11 2011, 11:49
|

Ambidexter
    
Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282

|
Цитата(AngelChik @ May 11 2011, 10:19)  ADCSRA |= (1<<ADIF)? Так? Так. Ещё одно мелкое замечание. В цикле вы вычисляете samples2 += (ADCResult*2.56)/1024; Оставьте только samples2 +=ADCResult, а после цикла samples2 = samples2*2.56/5120. Быстрее будет.
--------------------
Делай сразу хорошо, плохо само получится
|
|
|
|
|
May 11 2011, 11:56
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 26-04-07
Пользователь №: 27 336

|
Цитата(=GM= @ May 11 2011, 15:49)  Так.
Ещё одно мелкое замечание. В цикле вы вычисляете samples2 += (ADCResult*2.56)/1024; Оставьте только samples2 +=ADCResult, а после цикла samples2 = samples2*2.56/5120. Быстрее будет. Логично, спасибо.
|
|
|
|
|
May 12 2011, 09:02
|
Участник

Группа: Участник
Сообщений: 58
Регистрация: 16-02-09
Пользователь №: 44 931

|
была подобная фигня на mega128 - ref был с AVCC все решилось когда было замерено падение напряжение на неизвестной smd 1210 индуктивности фильтра -20 -30мв ее сопротивление было десятки ом заменил на 1210 47uH или даже 100uH - и все заработало нормально короче проверьте ваш фильтр avcc
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|