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

Atmega16 - значения с АЦП "пляшут"


freebits

Рекомендуемые сообщения

Добрый день.

Реализовал получение и обработку аналогового сигнала как в данной статье -> ссылка. Т.е. используется внутреннее опорное напряжение 2,56 Вольта, прием через прерывания, а на дисплее выводится числовое значение напряжения, которое присутствует на входе АЦП - нога ADC6.

В обработчике прерывания считываются значения из регистров ADCL и ADCH, из которых формируется значение переменной adc_value. В теле программы данное значение АЦП преобразуется в значение напряжения, посредством деления adc_value на 400. Затем полученный результат выводится на дисплей.

Проблема в том, что выводимое значение не стабильно и скачет в диапазоне +/- 300 мВ. Т.е. если к аналоговому входу приложено напряжение 1,4 вольта, то на дисплее значения будут хаотично меняться в диапазоне от 1,1 Вольт до 1,7 вольт, т.е. весьма ощутимый разброс в сотни милливольт. При этом если смотреть сигнал на входе осциллографом, то по факту нет такой картины - максимальный разброс (Vpp) составляет несколько десятков милливольт, но никак не сотен. Даже если этот вход посадить на землю, все равно на дисплее будут хаотичные значения доходящие до 0.4 вольта. Откуда он берет такие цифры на понятно.

Подскажите, в чем может быть проблема и как получить стабильные показания, хотя бы до сотен милливольт?

unsigned int adc_value;

char high_adc=0, low_adc=0;

ISR(ADC_vect) //обработчик прерывания ADC_vect
{
	low_adc = ADCL;
	high_adc = ADCH; //Верхняя часть регистра ADC должна быть считана последней иначе не продолжится преобразование
	adc_value = high_adc * 256 + low_adc; //значение АЦП
}

void ADC_Init(void) //инициализация АЦП
{
	ADCSRA |= (1<<ADEN) // Разрешение использования АЦП
		|(1<<ADSC)	//Запуск преобразования
		|(1<<ADATE)	//Непрерывный режим работы АЦП
		|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)//Делитель 128 = 64 кГц
		|(1<<ADIE);	//Разрешение прерывания от АЦП

	ADMUX |= 0b11000110; //Внутренний Источник ОН 2,56В вход ADC6
}

void main(void)
{
	float n = 0;
	while(1)
	{		

		n = (float) adc_value / 400; // преобразование значения АЦП в напряжение

		/*
			Отправка на дисплей
		*/

		_delay_ms(2);
		
	}
}

 

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

Попробуйте частоту семплирования уменьшить. Я плохо знаком с АВРами, но сдаётся мне, что 64 КГц - это весьма немаленькая частота для подобных камушков.

А так, посоветую сделать усреднение за n-ное количество семплов.

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

32 минуты назад, freebits сказал:

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

Короткие помехи, которые вы не видите осциллографом (на питании, в измеряемом источнике), отсутствие конденсатора на Vref, плохое AVcc.  

Убрать помеху,  применить усреднение. Батарейку попробуйте мерять, другой источник питания, Li, например. Попробуйте свой код в Протеусе. Если дело в коде, вылезет и там. 

Частота 64кГц - нормальная. Upd.  Заодно проверьте, не требует ли использованный  вами пример деления CLK на 8 (фьюз CLKDIV)? 

Изменено пользователем _abk_
Ссылка на комментарий
Поделиться на другие сайты

Сравнительное тестирование аккумуляторов EVE Energy и Samsung типоразмера 18650

Инженеры КОМПЭЛ провели сравнительное тестирование аккумуляторов EVE и Samsung популярного для бытовых и индустриальных применений типоразмера 18650. 

Для теста были выбраны аккумуляторы литий-никельмарганцевой системы: по два образца одного наименования каждого производителя – и протестированы на двух значениях тока разряда: 0,5 А и 2,5 А. Испытания проводились в нормальных условиях на электронной нагрузке EBD-USB от ZKEtech, а зарядка осуществлялась от лабораторного источника питания в режиме CC+CV в соответствии с рекомендациями в даташите на определенную модель. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

20 минут назад, Alex сказал:

А так, посоветую сделать усреднение за n-ное количество семплов.

Ок, спасибо, попробую внедрить усреднение.

13 минуты назад, _abk_ сказал:

Короткие помехи, которые вы не видите осциллографом (на питании, в измеряемом источнике), отсутствие конденсатора на Vref, плохое AVcc.  

Убрать помеху,  применить усреднение. Батарейку попробуйте мерять, другой источник питания, Li, например. Попробуйте свой код в Протеусе. Если дело в коде, вылезет и там. 

Частота 64кГц - нормальная.

Спасибо, попробую поставить конденсатор 0,1 мкФ на Vref.

Вот с проверкой кода в Протеусе именно для данного случая есть загвоздка - в Протеусе отсутствует тот цифровой индикатор, который я использую - 10-пиновый трехразрядный led-индикатор. У него индикация реализована не как у стандартного нидикатора поразрядно, а посегментно, т.е. в единицу времени можно зажечь только 1 сегмент, и соответственно отрисовка цифр идет совершенно по другому. Т.е. я не смогу никак увидеть какие именно показания измеряет АЦП.

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

Новый аккумулятор EVE серии PLM для GSM-трекеров, работающих в жёстких условиях (до -40°С)

Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре. 

Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств. Подробнее параметры и результаты тестов новой серии PLM по ссылке.

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

Усреднение надо вводить в последнюю очередь, когда будет устранен такой разброс. 

0,1 мкФ на Vref обязан быть при использовании внутренней "опоры". Не забудьте про AVCC.

Для проверки в Протеусе не нужен индикатор: сделайте вывод в терминал (через UART).

 

 

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

Литиевые батарейки и аккумуляторы от мирового лидера  EVE в Компэл

Компания Компэл, официальный дистрибьютор EVE Energy, бренда №1 по производству химических источников тока (ХИТ) в мире, предлагает продукцию EVE как со склада, так и под заказ. Компания EVE широко известна в странах Европы, Америки и Юго-Восточной Азии уже более 20 лет. Недавно EVE была объявлена поставщиком новых аккумуляторных элементов круглого формата для электрических моделей «нового класса» компании BMW.

Продукция EVE предназначена для самого широкого спектра применений – от бытового до промышленного. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

37 минут назад, freebits сказал:

10-пиновый трехразрядный led-индикатор.... в единицу времени можно зажечь только 1 сегмент

Очень интересно. Что это за индикатор? Наименование его?

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

27 минут назад, _abk_ сказал:

Усреднение надо вводить в последнюю очередь, когда будет устранен такой разброс. 

0,1 мкФ на Vref обязан быть при использовании внутренней "опоры". Не забудьте про AVCC.

Для проверки в Протеусе не нужен индикатор: сделайте вывод в терминал (через UART).

 

 

Ок, понял, спасибо. AVCC у меня подключено к выводу VCC, а VCC запитывается от стабилизированного источника питания (БП от принтера). Проверю осциллографом форму напряжения, но думаю что там напряжение должно быть без скачков. Про  UART погляжу сейчас.

10 минут назад, _abk_ сказал:

Очень интересно. Что это за индикатор? Наименование его?

Наименование отсутствует. Он от китайского цифрового вольтметра, заказывал через алиэекспресс, т.к. они уже были с внешней оправкой и цена не сильно большая.

Электрическую схему зарисовал, даже пытался выводить что-то в протеусе.

LED.thumb.png.72ecd979dc1d97a606446bd1a3fb79f3.png

Таблицу истинности тоже зарисовал. Когда заказывал, думал что будут стандартные, но пришло то что пришло

1.png.6604f09a90d9627021e3f6100f5244ad.png

 

 

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

26 минут назад, freebits сказал:

AVCC у меня подключено к выводу VCC

Не пойдет :)  

AVCC надо подключить к выводу VCC через LC фильтр (0,1 мкФ, 10...47 мкГн). А лучше не к VCC, а к точке подачи VCC на плату отдельным проводком, чтобы исключить влияние неправильной разводки.  Через LC фильтр, конечно. Фильтр - непосредственно у вывода.

Про индикатор понятно.

Моделька-то работает?

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

51 минуту назад, _abk_ сказал:

AVCC надо подключить к выводу VCC через LC фильтр (0,1 мкФ, 10...47 мкГн). А лучше не к VCC, а к точке подачи VCC на плату отдельным проводком, чтобы исключить влияние неправильной разводки.  Через LC фильтр, конечно. Фильтр - непосредственно у вывода.

Ммм, про данные тонкости я был вообще не вкурсе. Спасибо!  Тогда вначале переделаю аналоговое питание и добавлю фильтры!

 

51 минуту назад, _abk_ сказал:

Моделька-то работает?

Вы имеете в виду схему в протеусе выше? Если да, то да - она работает, но только на замедленной скорости можно что-то увидеть.  На той скорости, на которой сейчас работает отрисовка реального дисплея, в протеусе совершенно другой эффект, и лишь видно как иногда, почти в хаотичном порядке зажигаются светодиоды. Я использовал эту модель для отладки отрисовки.

Изменено пользователем freebits
Ссылка на комментарий
Поделиться на другие сайты

6 часов назад, freebits сказал:

n = (float) adc_value / 400; // преобразование значения АЦП в напряжение

Я бы сделал эту операцию атомарной, т.к. она выполняется не за один такт и в процесс деления может вклинится прерывание, которое изменит переменную adc_value (тут можно посмотреть ассемблерный листинг):

#asm("cli");

n = (float) adc_value / 400;

#asm("sei");

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

14 часа назад, proekt07 сказал:

Питание для АЦП.

 

Снимок.JPG

Спасибо! Оказывается про это есть в даташите. Надо даташит внимательнее изучить.

 

16 часов назад, kotwlf сказал:

Я бы сделал эту операцию атомарной, т.к. она выполняется не за один такт и в процесс деления может вклинится прерывание, которое изменит переменную adc_value (тут можно посмотреть ассемблерный листинг):

#asm("cli");

n = (float) adc_value / 400;

#asm("sei");

Ясно, спасибо! Действительно в программе еще обрабатывается  прерывание переполнения таймера , поэтому запросто оно может вклиниться, учту и этот момент.

Как появится время, реализую все переделки, потом отпишусь о результатах.

Изменено пользователем freebits
Ссылка на комментарий
Поделиться на другие сайты

переменную adc_value может изменить только прерывание от АЦП. и никакое другое прерывание эту переменную изменить не может.

это же сколько должен длится процесс деления, чтобы опоздать к следующему прерыванию от АЦП?

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

Мудрость приходит вместе с импотенцией...

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

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

В 1/22/2019 в 06:02, freebits сказал:

ISR(ADC_vect) //обработчик прерывания ADC_vect { 
	low_adc = ADCL; 
	high_adc = ADCH; //Верхняя часть регистра ADC должна быть считана последней иначе не продолжится преобразование 
	adc_value = high_adc * 256 + low_adc; //значение АЦП 
}

 

Почему сразу не написать adc_value = ADC; - пользуйтесь тем, что вам уже предоставляет компилятор, а именно доступ к двум 8-битным регистрам АЦП (кстати, и счетчикам таймеров тоже) как к одной 16-битной переменной.

Поскольку АЦП всегда дает шум в младшем бите результата, да и всякие уже описаные проблемы с землями и т.п. "помехами" имеют место быть, всегда следует фильтровать результаты замеров. Минимально - усреднять по нескольким замерам, лучше - делать фильтр "скользящее среднее", совсем хорошо - полноценный фильтр третьего порядка или выше.

Лично я чаще всего применяю фильтрацию скользящим средним, как самым простым и понятным вариантом:

// глубина буфера усреднения. чем больше, тем сильнее фильтрация и медленнее работа
// очень хорошо, если число кратно степени двойки (т.е. 4, 8, 16, 32, 64 и т.д.)
#define AVERAGE_DEPTH	16
  
// функция получает результат очередного замера и возвращает отфильтрованное значение
uint16_t get_average(uint16_t val){
  static uint16_t buf[AVERAGE_DEPTH];
  static uint8_t cur;
  uint16_t sum = 0;
  
  buf[cur++] = val;
  if(cur >= AVERAGE_DEPTH) cur = 0;
  
  for(uint8_t i=0; i < AVERAGE_DEPTH; i++) sum += buf[i];
  
  return sum / AVERAGE_DEPTH;
}

В вашем случае вы должны выдавать на дисплей не adc_value, а get_average(adc_value).

4 часа назад, freebits сказал:

переполнения таймера , поэтому запросто оно может вклиниться

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

Только что, Starichok сказал:

это же сколько должен длится процесс деления, чтобы опоздать к следующему прерыванию от АЦП?

А вывод на дисплей? Тут не время деления важно, а независимость изменения переменной и её считывания.

Поскольку модификация переменной делается в прерывании, т.е. асинхронно по отношению к главному циклу, даже небольшая рассинхронизация рано или поздно приведет к тому, что прерывание возникнет в аккурат "между" обработкой двух байт этой переменной :) Во всяком случае, теоретически такое возможно.

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

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

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

2 часа назад, ARV сказал:

А вывод на дисплей?

а прочитать мой пост до конца ты не сумел?

3 часа назад, Starichok сказал:

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

 

Мудрость приходит вместе с импотенцией...

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

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

4 часа назад, Starichok сказал:

это же сколько должен длится процесс деления, чтобы опоздать к следующему прерыванию от АЦП?

У него непрерывный режим работы АЦП, так что запросто.

К тому же не приведен ассемблерный листинг операции деления - кто знает как там его компилятор делает? И параметры оптимизации кода тоже неизвестны.

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

В 1/22/2019 в 10:02, freebits сказал:

выводимое значение не стабильно и скачет в диапазоне +/- 300 мВ. Т.е. если к аналоговому входу приложено напряжение 1,4 вольта, то на дисплее значения будут хаотично меняться в диапазоне от 1,1 Вольт до 1,7 вольт, т.е. весьма ощутимый разброс в сотни милливольт.

Такое поведение говорит скорее об аппаратных проблемах, чем о программных. Если бы портились байты из-за прерываний и пр., получили бы разброс на всю шкалу. Ну смотрите:  1)AVCC никак не фильтровано и прямо

В 1/22/2019 в 11:44, freebits сказал:

подключено к выводу VCC, а VCC запитывается от стабилизированного источника питания (БП от принтера).

 2) на Vref нет положенного при внутренней опоре конденсатора, 3) есть подозрение, что  неправильная разводка вкупе с динамической индикацией усугубляет дело, 4) что это за источник 1,4 В пытается  измерять ТС, на котором

В 1/22/2019 в 10:02, freebits сказал:

При этом если смотреть сигнал на входе осциллографом, то по факту нет такой картины - максимальный разброс (Vpp) составляет несколько десятков милливольт, но никак не сотен.

Если там такой разброс имеется, то чего еще ждать? Не факт, что еще и "иголок" там нет. Но это еще не все.

Об аппаратных проблемах говорит и то, что

В 1/22/2019 в 10:02, freebits сказал:

Даже если этот вход посадить на землю, все равно на дисплее будут хаотичные значения доходящие до 0.4 вольта. Откуда он берет такие цифры на понятно.

По-моему, так все понятно. Да еще и "посадить на землю" надо грамотно. 

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

3 часа назад, kotwlf сказал:

У него непрерывный режим работы АЦП, так что запросто.

К тому же не приведен ассемблерный листинг операции деления - кто знает как там его компилятор делает? И параметры оптимизации кода тоже неизвестны.

ну, если задан непрерывный режим, то точно не успеет поделить к следующему прерыванию.

и никакая оптимизация не спасет, так как код на Си работает в несколько раз медленнее кода, написанного на ассемблере.

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

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

Мудрость приходит вместе с импотенцией...

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

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

Давайте внесу ясность, чтобы предметно говорить и не гадать на кофейной гуще, что есть по факту  и для чего все.

В общем, я конструирую паяльную станцию. На данный момент все подключено в точности по схеме ниже (только что зарисовал) и все работает прекрасно, кроме, конечно же, регистрации температуры. Даже интерфейсная оболочка уже написана (выбор одного из 5-ти термопрофилей: два готовых (бессвинцовый и свинцовосодержащий припой) и три ручных). На данном этапе произвожу наладку регистрации температуры. Имеется датчик температуры  до 500°С - позистор, у которого сопротивление меняется от 0 до 60 Ом (при комнатной температуре 10 Ом). Предусилительный каскад отлажен и испытан при помощи на 100-омного переменного резистора вместо датчика температуры. На выходе каскада, как и задумано, напряжение меняется от 0 до 2,6 Вольт при изменении сопротивления от 0 до 60 Ом. Есть калибровка через подстроечный резистор. Осталось теперь оцифровать значение на аналоговом входе для дальнейшей обработки.  Порт С пока не задействован, т.к. на данном контроллере находится в неисправном состоянии. После замены микросхемы, порт С планируется задействовать на управление 5-ью секциями нагревателей (каждая секция - это два 1 кВт-ных нагревателя, подключенных последовательно; итого 10 нагревателей общей мощностью 2,5 кВт). Секции можно отключать/включать в зависимости от заданных настроек. Плюс также порт С будет задействован на индикацию 3-х светодиодов. Итого свободным будет только 1 пин PA7 (DCA7), который возможно тоже будет задействован в качестве обратной связи с драйверов нагревателей. 

5c492adb18d92_.thumb.png.a634b64297bf7f28d8cf40fb37f4789c.png

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

12 часа назад, Starichok сказал:

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

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

Скорее всего так и сделаю - буду производить измерение по таймеру.

13 часа назад, _abk_ сказал:

По-моему, так все понятно. Да еще и "посадить на землю" надо грамотно. 

По поводу "посадки на землю" я погорячился)) я имел в виду, что на выходе предусилительного каскада присутствовал 0, это конечно не одно и тоже. Но осциллографом отчетливо было видно, что там нет шума амплитудой 0,4 Вольта.

В выходные реализую переделки по питанию, и фильтры. До выходных переделаю постоянное измерение на измерение по таймеру.

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

В 23.01.2019 в 13:27, ARV сказал:

Лично я чаще всего применяю фильтрацию скользящим средним, как самым простым и понятным вариантом:


// глубина буфера усреднения. чем больше, тем сильнее фильтрация и медленнее работа
// очень хорошо, если число кратно степени двойки (т.е. 4, 8, 16, 32, 64 и т.д.)
#define AVERAGE_DEPTH	16
  
// функция получает результат очередного замера и возвращает отфильтрованное значение
uint16_t get_average(uint16_t val){
  static uint16_t buf[AVERAGE_DEPTH];
  static uint8_t cur;
  uint16_t sum = 0;
  
  buf[cur++] = val;
  if(cur >= AVERAGE_DEPTH) cur = 0;
  
  for(uint8_t i=0; i < AVERAGE_DEPTH; i++) sum += buf[i];
  
  return sum / AVERAGE_DEPTH;
}

В вашем случае вы должны выдавать на дисплей не adc_value, а get_average(adc_value).

Спасибо за код! Подскажите здесь при каждом вызове функции массив buf будет переинициализироваться или как раз конструкция static защищает от этого?

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

Только что, freebits сказал:

Подскажите здесь при каждом вызове функции массив buf будет переинициализироваться или как раз конструкция static защищает от этого?

Все static-переменные инициализируются один раз, потом сохраняют свое состояние.

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

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

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

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

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Ответить в этой теме...

×   Вставлено с форматированием.   Восстановить форматирование

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

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

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

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

Загрузка...
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...