Перейти к содержанию
    

mega2561, проблема в АЦП с ADMUX

Всем доброго времени!!! Помогите пожалуйста разобраться в проблеме... Устал, уже весь мозг сломал, всё перепробовал. Устройство: АЦП контроллера измеряет сигнал через делитель напряжения (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 мкФ, ноль эмоций :((

 

К меге подсоединено восемь семисегментных индикаторов, реализована динам. индикация через таймер с прерыванием.

 

Что скажете? Что непонятно - спрашивайте. Могу выложить фотки устройства...

Изменено пользователем AngelChik

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

После смены канала перед запуском следующего преобразования нужно подождать пока АЦП сделает 1 клок.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

После смены канала перед запуском следующего преобразования нужно подождать пока АЦП сделает 1 клок.

В курсе, читал. Доки уже наизусть... Я же написал, что делал задержки после модификации ADMUX, никакого толку нет, всё равно бред считывает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Явно имеет место недозаряд емкости внутреннего УВХ АЦП. Задержка после перключения каналов должна была бы помочь.

Точнее, можно:

1 запустить преобразование и сразу переключить канал.

2. По завершении преобразования вынуть данные - они будут с предыдущего канала, с которого переключили.

3. подождать некоторое время до окончания переходного процесса

и - п. 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Эгеее... Спасибо, ща попробую.

 

Ну вот, "перевернул"...

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

По опыту, на Меге8, для правильного конвертирования напряжения внутреннего ИОН, внутреннее сопротивление которого порядка 30 кОм, если переключаться с 0, необходимо было подождать не менее 10000 тактов (для 10-битной точности).

 

Без задержек на выходном сопротивлении источника ( порядка 10 кОм) будет безбожно врать.

Либо сопроивление источника понизить до менее 1 кОм, либо задержки.

 

З.Ы. А почему делается именно 5 выборок, а не круглое число? Сразу бы объем программы уменьшился)

 

И - во втором цикле первый результат будет из канала (1<<REFS1) | (1<<REFS0), который внесет свою коррективу в размере 20%)

 

Попробуйте сначала получить требуемый результат без фильтрации.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо ещё раз за мнение и совет. Снижу сопротивление в 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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Неплохо бы и флаг ADIF сбрасывать после чтения регистров АЦП, а то будете долго читать одно и тоже.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Точняк! Забыл, спасибо. Сброс производится записью 1 в него? правильно?

 

Типа

ADCSRA |= (1<<ADIF);

? Так?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Операцию

ADMUX = (1<<REFS1) | (1<<REFS0) + 1;

можно поставить пораньше - например, сразу после выхода из первого цикла. Операции деления с float сработают как задержка куда большая, чем на 10 тактов). А можно и вообще обналеть и поставить в первом цикле

if(i == 4) ADMUX = (1<<REFS1) | (1<<REFS0) + 1;

тогда время заряда второго канала увеличится на время преобразования, т.е. на 13*К деления тактов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

ADCSRA |= (1<<ADIF)? Так?

Так.

 

Ещё одно мелкое замечание. В цикле вы вычисляете samples2 += (ADCResult*2.56)/1024;

Оставьте только samples2 +=ADCResult, а после цикла samples2 = samples2*2.56/5120. Быстрее будет.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так.

 

Ещё одно мелкое замечание. В цикле вы вычисляете samples2 += (ADCResult*2.56)/1024;

Оставьте только samples2 +=ADCResult, а после цикла samples2 = samples2*2.56/5120. Быстрее будет.

Логично, спасибо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Там эта. На всякий случай.

Когда АЦП, то надо не только сделать вход входом (DDR), но и подтяжку на нём отключить (PORT).

... а если про подтяжку забыть, то вероятно: никто никогда и не заметит такую багу. Ну будет ноль не совсем нулём, ну будет чучуть ошибка ваще мелкая, нудаисним.

 

(не читал)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

была подобная фигня на mega128 - ref был с AVCC

все решилось когда было замерено падение напряжение на неизвестной smd 1210 индуктивности фильтра -20 -30мв

ее сопротивление было десятки ом

заменил на 1210 47uH или даже 100uH - и все заработало нормально

короче проверьте ваш фильтр avcc

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...