AngelChik 0 11 мая, 2011 Опубликовано 11 мая, 2011 (изменено) · Жалоба Всем доброго времени!!! Помогите пожалуйста разобраться в проблеме... Устал, уже весь мозг сломал, всё перепробовал. Устройство: АЦП контроллера измеряет сигнал через делитель напряжения (47/10 кОм), уровень сигнала после делителя - 2 вольта (к примеру). Таких три входа - ADC0-2. Проблема заключается в том, что если считывать только один канал, то всё измеряется на ура, всё (почти) точно по вольтметру. НО! Если в процессе измерения добавить второй канал (или любой другой), то отконвертированное напряжение становится "не правильным", например вместо 12,6 вольт начинает показывать 9.5. Тем не менее, считанное значение остаётся пропорциональным сигналу. Вот код: 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 мкФ, ноль эмоций :(( К меге подсоединено восемь семисегментных индикаторов, реализована динам. индикация через таймер с прерыванием. Что скажете? Что непонятно - спрашивайте. Могу выложить фотки устройства... Изменено 11 мая, 2011 пользователем AngelChik Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Alexey_1811 0 11 мая, 2011 Опубликовано 11 мая, 2011 · Жалоба После смены канала перед запуском следующего преобразования нужно подождать пока АЦП сделает 1 клок. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AngelChik 0 11 мая, 2011 Опубликовано 11 мая, 2011 · Жалоба После смены канала перед запуском следующего преобразования нужно подождать пока АЦП сделает 1 клок. В курсе, читал. Доки уже наизусть... Я же написал, что делал задержки после модификации ADMUX, никакого толку нет, всё равно бред считывает. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
a_electronic 0 11 мая, 2011 Опубликовано 11 мая, 2011 · Жалоба Явно имеет место недозаряд емкости внутреннего УВХ АЦП. Задержка после перключения каналов должна была бы помочь. Точнее, можно: 1 запустить преобразование и сразу переключить канал. 2. По завершении преобразования вынуть данные - они будут с предыдущего канала, с которого переключили. 3. подождать некоторое время до окончания переходного процесса и - п. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AngelChik 0 11 мая, 2011 Опубликовано 11 мая, 2011 (изменено) · Жалоба Эгеее... Спасибо, ща попробую. Ну вот, "перевернул"... 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 мс.... Изменено 11 мая, 2011 пользователем AngelChik Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
a_electronic 0 11 мая, 2011 Опубликовано 11 мая, 2011 · Жалоба В принципе п. 2 можно опустить - не дожидаться конца преобразования, а вынимать данные непосредственно перед запуском следующего преобразования, которое можно запуускать по таймеру. По опыту, на Меге8, для правильного конвертирования напряжения внутреннего ИОН, внутреннее сопротивление которого порядка 30 кОм, если переключаться с 0, необходимо было подождать не менее 10000 тактов (для 10-битной точности). Без задержек на выходном сопротивлении источника ( порядка 10 кОм) будет безбожно врать. Либо сопроивление источника понизить до менее 1 кОм, либо задержки. З.Ы. А почему делается именно 5 выборок, а не круглое число? Сразу бы объем программы уменьшился) И - во втором цикле первый результат будет из канала (1<<REFS1) | (1<<REFS0), который внесет свою коррективу в размере 20%) Попробуйте сначала получить требуемый результат без фильтрации. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AngelChik 0 11 мая, 2011 Опубликовано 11 мая, 2011 (изменено) · Жалоба Спасибо ещё раз за мнение и совет. Снижу сопротивление в 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; } Изменено 11 мая, 2011 пользователем AngelChik Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
=GM= 0 11 мая, 2011 Опубликовано 11 мая, 2011 · Жалоба Неплохо бы и флаг ADIF сбрасывать после чтения регистров АЦП, а то будете долго читать одно и тоже. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AngelChik 0 11 мая, 2011 Опубликовано 11 мая, 2011 · Жалоба Точняк! Забыл, спасибо. Сброс производится записью 1 в него? правильно? Типа ADCSRA |= (1<<ADIF); ? Так? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
a_electronic 0 11 мая, 2011 Опубликовано 11 мая, 2011 · Жалоба Операцию ADMUX = (1<<REFS1) | (1<<REFS0) + 1; можно поставить пораньше - например, сразу после выхода из первого цикла. Операции деления с float сработают как задержка куда большая, чем на 10 тактов). А можно и вообще обналеть и поставить в первом цикле if(i == 4) ADMUX = (1<<REFS1) | (1<<REFS0) + 1; тогда время заряда второго канала увеличится на время преобразования, т.е. на 13*К деления тактов. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
=GM= 0 11 мая, 2011 Опубликовано 11 мая, 2011 · Жалоба ADCSRA |= (1<<ADIF)? Так? Так. Ещё одно мелкое замечание. В цикле вы вычисляете samples2 += (ADCResult*2.56)/1024; Оставьте только samples2 +=ADCResult, а после цикла samples2 = samples2*2.56/5120. Быстрее будет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AngelChik 0 11 мая, 2011 Опубликовано 11 мая, 2011 · Жалоба Так. Ещё одно мелкое замечание. В цикле вы вычисляете samples2 += (ADCResult*2.56)/1024; Оставьте только samples2 +=ADCResult, а после цикла samples2 = samples2*2.56/5120. Быстрее будет. Логично, спасибо. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_noise 0 12 мая, 2011 Опубликовано 12 мая, 2011 · Жалоба Там эта. На всякий случай. Когда АЦП, то надо не только сделать вход входом (DDR), но и подтяжку на нём отключить (PORT). ... а если про подтяжку забыть, то вероятно: никто никогда и не заметит такую багу. Ну будет ноль не совсем нулём, ну будет чучуть ошибка ваще мелкая, нудаисним. (не читал) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
GYUR22 0 12 мая, 2011 Опубликовано 12 мая, 2011 · Жалоба была подобная фигня на mega128 - ref был с AVCC все решилось когда было замерено падение напряжение на неизвестной smd 1210 индуктивности фильтра -20 -30мв ее сопротивление было десятки ом заменил на 1210 47uH или даже 100uH - и все заработало нормально короче проверьте ваш фильтр avcc Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться