АЦП — Аналого-цифровой преобразователь. Из названия можно догадаться, что на вход подается аналоговый сигнал, который преобразуется в число.
Первое о чем нужно сказать — АЦП микроконтроллера умеет измерять только напряжение. Чтобы произвести измерение других физических величин, их нужно вначале преобразовать в напряжение. Сигнал всегда измеряется относительно точки называемой опорное напряжение, эта же точка является максимумом который можно измерить. В качестве источника опорного напряжения (ИОН), рекомендуется выбирать высокостабильный источник напряжения, иначе все измерения будут плясать вместе с опорным.
Одной из важнейших характеристик является разрешающая способность, которая влияет на точность измерения. Весь диапазон измерения разбивается на части. Минимум ноль, максимум напряжение ИОН. Для 8 битного АЦП это 2^8=256 значений, для 10 битного 2^10=1024 значения. Таким образом, чем выше разрядность тем точнее можно измерять сигнал.
Допустим вы измеряете сигнал от 0 до 10В. Микроконтроллер используем Atmega8, с 10 битным АЦП. Это значит что диапазон 10В будет разделен на 1024 значений. 10В/1024=0,0097В — с таким шагом мы сможем измерять напряжение. Но учтите, что микроконтроллер будет считать, величину 0.0097, 0.0098, 0.0099… одинаковыми.
Тем не менее шаг в 0,01 это достаточно неплохо. Однако, есть несколько рекомендаций, без которых эта точность не будет соблюдена, например для измерения с точностью 10бит, частота на которой работает АЦП должна быть 50-200 кГц. Первое преобразование занимает 25 циклов и 13 циклов далее. Таким образом, при частоте 200кГц мы сможем максимум выжать
200 000/13 = 15 384 измерений.
В качестве источника опорного напряжения можно использовать внутренний источник и внешний. Напряжение внутреннего источника (2,3-2,7В) не рекомендуется использовать, по причине низкой стабильности. Внешний источник подключается к ножке AVCC или Aref, в зависимости от настроек программы.
При использовании АЦП ножка AVCC должна быть подключена. Напряжение AVCC не должно отличаться от напряжения питания микроконтроллера более чем на 0,3В. Как было сказано, максимальное измеряемое напряжение равно опорному напряжению(Vref), находится оно в диапазоне 2В-AVCC. Таким образом, микроконтроллер не может измерить более 5В.
Чтобы расширить диапазон измерения, нужно измерять сигнал через делитель напряжения. Например, максимальное измеряемое напряжение 10В, опорное напряжение 5В. Чтобы расширить диапазон измерения, нужно уменьшить измеряемый сигнал в 2 раза.
Формула для расчета делителя выглядит так:
Uвых = UвхR2/(R1 + R2)
Подставим наши значения в формулу:
5 = 10*R2/(R1+R2)
(R1+R2)=2*R2
R1=R2
т.е. можно взять любые два одинаковых резистора и подключить их по схеме
Следовательно, когда мы измеряем напряжение через делитель, нужно полученное значение АЦП умножить на коэффициент=Uвых/Uвх.
Полная формула вычисления измеряемого напряжения будет выглядеть так:
U=(опорное напряжение*значение АЦП*коэффициент делителя)/число разрядов АЦП
Пример: опорное 5В, измеренное значение АЦП = 512, коэффициент делителя =2, АЦП 10разрядный.
(5*512*2)/1024=5В — реальное измеренное значение напряжения.
Некоторые программисты пишут программу так, чтобы микроконтроллер автоматически вычислял коэффициент делителя, для этого выходной сигнал измеряют образцовым прибором и заносят это значение в программу. Микроконтроллер сам соотносит истинное напряжение каждому значению АЦП, сам процесс однократный и носит название калибровки.
Перейдем к программной реализации. Создаем проект с указанными параметрами. Также подключим дисплей на порт D для отображения информации.
Измерение будет производиться в автоматическом режиме, обработка кода в прерывании, опорное напряжение подключаем к ножке AVCC. По сути нам нужно только обрабатывать получаемые данные. Измеренные данные хранятся в переменной adc_data[0]. Если нужно опрашивать несколько каналов, то выбираем какие каналы сканировать, а данные будут для ножки 0 в adc_data[0], для ножки 1 в adc_data[1] и т.д.
В основном цикле добавим строки:
result=((5.00*adc_data[0])/1024.00); //пересчитываем значение АЦП в вольты
sprintf(lcd_buffer,»U=%.2fV»,result); //помещаем во временную переменную результат
lcd_puts(lcd_buffer); //выводим на экран
Небольшое замечание, чтобы использовать числа с плавающей точкой, нужно в настройках проекта изменить (s)printf Features: int, width на float, width, precision. Если этого не сделать десятые и сотые мы не увидим.
Таким образом, мы всего лишь перевели значение АЦП в вольты и вывели на дисплей. Результат в протеусе выглядит так:
Резистором можно менять напряжение, измеряемое напряжение выведено на дисплей. При сборке на реальном железе к ножке Aref нужно подключить конденсатор на 0,1мкФ. Урок получился немного сложным, но думаю он вам понравится.
Файл протеуса и прошивка:
Update:
Измерение тока:
Скачал проект, но не пойму. Где вы указываете что 23 ножка будет АЦП? я так понимаю АЦП это где написано ADC. Получаеться после активации АЦП я не могу пользоваться ножками ADC1,2,3 как обыкновенными портами?
при работе с АЦП рекомендуют на этот порт вообще ничего больше не вешать, только измерение
Приветствую! Хочу сразу поблагодарить за сайт, всё так доступно излагаете, что после прочтения 10 уроков столько проектов в голове родилось, уже не терпится воплотить:) .Увы, как всегда есть вопросы. 1) надо сделать что бы вместо дисплея результат выводить на комп через uart, т.е в терминалке отображалось: U=2.25V. 2) как опрашивать не один, а несколько пинов порта или все сразу. Допустим имеется несколько датчиков.
1. посмотрите урок8 про юарт
2. у меги8 есть мультиплексор, одновременно измерять не получиться, только последовательно переключаясь между ножками. у цавр можно сгенерить код там вроде галочка есть automatically scan и там выбираешь ножки порта С. результат будет выдан массивом
а у меня при компиляции проекта выходят ошибки:
Error: volt.c(49): undefined symbol ‘result’
Error: volt.c(50): undefined symbol ‘sprintf’
в чем может быть проблема?
stdio.h не подключена, int result не объявлена
А не мргли бы вы объяснить подробнее абзац про «25 циклов и 13 циклов далее»? Не понятно откуда эти числа взялись.
Спасибо.
Инфа из даташита
ДОБРЫЙ ВРЕМЯ СУТОК!!!! Хочу сразу поблагодарить за сайт!!!
скачал проект 10 урока, в протеусе все работаеть ОК. но какда стал компилировать код с проекта «СИ» компилятор ругается на эти строчки
1)register static unsigned char input_index=0;
2)adc_data[input_index]=ADCW;
3)if (++input_index > (LAST_ADC_INPUT-FIRST_ADC_INPUT))
4)ADMUX=(FIRST_ADC_INPUT | (ADC_VREF_TYPE & 0xff))+input_index;
стираю слово register и все окей компилируется но в протеусе на дисплее показывает всегда U= 2FV
Достаточно убрать register, проект рабочий. Если вы переносите код в другой проект, не забывайте менять настройки проекта, об этом в статье сказано
я может что то не понимаю но настройка регистеров эта и есть настройка проекта
я скопировал код полностю 😳 ❓
Настройки проекта Project-Configure-C compiler-sprint features
И проверьте строчку
sprintf(lcd_buffer,"U=%.2fV",result);
добрый день!!!! 😳 получиться, все работает. спасибо
по проектам собрал конструкцию, где должно опрашивать ацп, и задовать число для сравнения для таймера 1 и выводить число на дисплей с порта Д. компилятор ругается
Error : undefined symbol ‘sprintf’
добрый день выходных!!!! за думал такую идею.
регулятор по мощности способом изменением фазового угла.
допустим что максимальное число АЦП преобразовать так чтобы он был равен тикам 10мсек (всего 78 тиков)
1) замеряем АЦП \\ понятно
2) ждем переход через ноль по фронту срабатывать INT0 \\ понятно
3) прерывание INT0 а) преабразовенное число присваиваем регистру сравнения \\ как
б) разнешаем прерывание по совпадению
в) обныляем TIMER1
4) ждем прерывание по совпадению
вопрос такой как выполнить пункт № 3
Не совсем понял что нужно, если нужно пихать число в регистр сравнения, значит пихайте OCRx=result. Только нужно смотреть разрядности чисел
да но число result floal !!! можно ли на прямую записать это число OCRL? может с начало как то дана его в INT преобразовать на пример в S7 есть такая команда RTD и DTR .
может тогда и нет смысла измерять во float?
вернемся уроку №10
в место float result; записа int result; и result=((78.00*adc_data[0])/1024.00); заменил result=((78*adc_data[0])/1024); и так и так на дисплее кроме U=2fV ничего нет.как быть
сорри U=0,00 V показывает
Вы определитесь чего хотите. Я так и не понял зачем что то пихать в регистр сравнения
с помощью регулятора изменяется уровень напряжение на входе АТмега8, производиться АЦП. частота в розетке 50 ГЦ значить 20 мс, из этого половина 10мс использование предделитель 1024 из тактовой чистоты 8Мгц получилося 7813 Гц за 10мс мак 78 тиков. по этому формула приходить result=((78*adc_data[0])/1024) вид, result выводим на дисплей . как толко в напряжение в сети
проходить через ноль на в ход INT0 поступает сигнал пихаем OCRL=result; и в прерывание по совпадение включаем какой ни буть выход на несколько мс чтобы открыть симистор и OCRL пихаем 78 чтобы во второй половине это повторить. симистор останиться открытом до окончания этого полупереуда и закроется. так и чем менше result тем большой угол фаза!!! HELP
может есть рациональный путь к решению???! 😕 😕 😕
было бы целесообразно вывести на лсд result=((220*adc_data[0])/1024)
для лсд я исполь портB 0,1,2,4 и портD 5,6
Почему не задетектить прохождение через ноль и отсчитать нужное время для открытия симистора? У вас напряжение в сети стабильно 50Гц. Не понимаю вообще роль АЦП в этой задаче. Весь период 20мс, хочешь 50% мощности — прошло через ноль, отсчитал 10мс, открыл симистор, прошло 10мс, закрыл симистор. Еще вы забываете, что АЦП преобразует результат не моментально, для этого требуется время, поэтому я вдвойне не понимаю причем тут АЦП
с помощью ацп я изменяю уровень мощности, конечно можно было бы использовать две кнопки «+» » -» но с ацп интересней а время реагирование не так уш критицные
Чтобы измерять уровень мощности, нужно измерять и ток и напряжение, высчитывать по формуле однофазной мощности, при этом нужно знать максимум нагрузки.
То что вы пишите понятно, но какой смысл для этого высчитывать величину АЦП
википедия Фазовое регулирование
ну мы друг друга понимать начинаем :)))))))))))
Еще раз спрошу что вы мерить то собрались? Напряжение? Ну измерите вы мгновенную величину напряжения и что это вам даст?
классическое устройства получается крутещ рукоятку например лампочка гаснет тусклей горить обратно крутиш итд
Вы не ответили на мой вопрос, АЦП измеряет 15000 раз в секунду, ну выдерните вы одно значение за период, что оно вам даст?
например 5в это 100% и 10мс тоже 100% составис пропорцию и по пропорции уменшаем или увеличеваем значения регистера сравнения
Можно сделать как вы предлагаете, выпрямляем 220, на выход кондер, получаем постоянку 310. Считаем это 100%. 1%=3В Затем кнопками +- регулируем регистр сравнения. Результирующая мощность, P=result(уже переведенный в вольты, с учетом делителя)*100/310.
Вот только вы делаете обратною связ а я думал что повешу как в примере переменный килоом иподам 5волт без обратной связи!!
Ресулт в инт е не могу вывести на лсд
А как сделать на семи сегментном индикаторе?