АЦП — Аналого-цифровой преобразователь. Из названия можно догадаться, что на вход подается аналоговый сигнал, который преобразуется в число.
Первое о чем нужно сказать — АЦП микроконтроллера умеет измерять только напряжение. Чтобы произвести измерение других физических величин, их нужно вначале преобразовать в напряжение. Сигнал всегда измеряется относительно точки называемой опорное напряжение, эта же точка является максимумом который можно измерить. В качестве источника опорного напряжения (ИОН), рекомендуется выбирать высокостабильный источник напряжения, иначе все измерения будут плясать вместе с опорным.
Одной из важнейших характеристик является разрешающая способность, которая влияет на точность измерения. Весь диапазон измерения разбивается на части. Минимум ноль, максимум напряжение ИОН. Для 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:
Измерение тока:
спасибо admin
Добрый день, спасибо за сайт!
У меня такой вопрос, как я понял, с помощью одного АЦП можно мерять больше чем одно напрняжение? и выводить значения на разные семисегментные индикаторы (с индикаторами понятно не связан АЦП). У меня задача снимать два напряжения и выводить их значения на два индикатора, какие отличия будут в программе тогда?
АЦП у микроконтроллера действительно один, но он может последовательно измерять на ногах ADC0, ADC1, ADC2… Ваша задача задать в генераторе кода автоматическое сканирование портов, и забирать результат например adc_data[0] и adc_data[1]
Хорошо, это я понял. А скорость измерения от этого сильно пострадает? ведь АЦП требуется что-то около 14 тактов для обработки сигнала и перевода его в код. Не будет ли тормозить от этого измерения на индикаторах? или для человека это будет совсем незаметно?
Для человека это точно не заметно
Спасибо большое за ответ! 😀
Как сделать амперметр из вольтметра? Куда шунт, где мерять напряжение?
Добавил в конце проект с измерением тока, думаю все понятно
Как настроить под attiny13? Там в CodeVisionAVR Evaluation совсем другие настройки визарда, напишите пример, если не сложно, хотя бы на получение значение в вольтах в константу. И желательно с использованием в качестве опорки внутренний стабилизатор в 2.76. 🙄
Получается, если нужно измерить сопротивление, то через RC контур все делать и время заряда конденсатора высчитывать? А если нужно измерять индуктивность, то через LC контур аналогично все высчитывать?
В программе есть переменная char lcd_buffer[31]. Для новичка скажите, массив [31] что означает и почему именно 31?
Сопротивление можно измерить зная падение напряжения и ток. С индуктивностью в принципе можно так поступить
по идее 32, если прикинуть что дисплей 2 строки по 16 символов, то чтобы целиком вывести 2 строки на него, понадобится 32 символа
У меня все время показывает 5 V…
Не могу найти как исправить.
#include
// Alphanumeric LCD Module functions
#asm
.equ __lcd_port=0x18 ;PORTB
#endasm
#include
#include
#include
// Declare your global variables here
char string[10];
void main(void)
{
// Declare your local variables here
int data;
float V;
ACSR=0x80;
SFIOR=0x00;
ADMUX=0;
ADCSR=0x85;
// LCD module initialization
lcd_init(16);
lcd_putsf(«Work with ADC»);
while (1)
{
delay_ms(20);
ADCSR |= 0x40;
data = ADCW;
V = (float) data*0.0048828;
sprintf(string, «Data: %1.2f», V);
lcd_gotoxy(0,1);
lcd_puts(string);
};
}
Посмотрите исходник, он отличается от того что написано у Вас.
Подскажите пожалуйста, как вывести на экран LCD только одну переменную result, без «сборки» в строку.
уберите то что в кавычках до %
Здравствуйте! решил собрать омметр на основе вашей программы. Для точности измерений стал измерять напряжение U1 делителя напряжения. U1 подключил к ADC0, а U2 к ADC1.
При отладке на протеусе показания на ADC0 инвертируются, а на ADC1 работает как положено. Подскажите пожалуйста это в протеусе глюк или с программой что то не так?
исходники протеуса и codevision avr отправил вам на почту
Спасибо большое за этот урок!!! Только я вот читал, читал да не понял как изменить границы измерения? Например, что бы измерять напряжение от 0 до 100 В.
Пересчитать делитель. Под делитель подогнать коэффициент
(5*512*2)/1024=5В
Как я понял коэф. делителя в даном случае — 2. Делитель получается 1024?
Что имеется в виду. То что отобразить на LCD дисплее — я изменить могу. Меня интересует как изменить то, что бы реальное напряжение было не 5, а больше.
Разберитесь с тем что такое делитель.
Здравствуйте, ни как не могу сделать, чтобы эта схема измеряла еще и переменное напряжение, помогите сложно такое реализовать? 😥
переменное напряжение нужно выпрямлять
Нужно реализовать 5 вольтметров, никак не могу это сделать, помогите
Разобрался, помощь не нужна=)
Почему идет деление на 1024 а не 1023 как тут описано https://www.youtube.com/watch?v=XZOuA39qAoY ?
на 1023 правильно
Почему идет деление на 1024? Ведь при 10 битах, можно закодировать 2047 значений, или я что то неправильно понял? объясните пожалуйста.
вопрос снимаю, уже разобрался
добрый день.спасибо за ваш прекрасный сайт.Помогите разобраться пожалуйста.спомощью вашего урока настроил АЦП.полученными результатами из ацп управляю шимом(стабилизатор напряжения).написал следующую строчку.работает.но!!!!шим меняет значения по кругу.как сделать чтобы он остановился при минимальном значении шимато есть при 0.и при мваксимальном значении 0xFF.
if(result4)
{
OCR0=OCR0-1;
}
Заранее спасибо вам
добрый день.спасибо за ваш прекрасный сайт.Помогите разобраться пожалуйста.спомощью вашего урока настроил АЦП.полученными результатами из ацп управляю шимом(стабилизатор напряжения).написал следующую строчку.
if(result4)
{
OCR0=OCR0-1;
}
работает.но!!!!шим меняет значения по кругу.как сделать чтобы он остановился при минимальном значении шима,то есть при 0.и при максимальном значении 0xFF.
и ещё вопрос можно ли как то его вообще отключить
, а потом если надо включить?
Заранее спасибо вам
for ( i =OCR0;result4 ; i++ ) {
OCR0=i;
if ( OCR0 == 0x00 ) break;
вот так вот будет правильно?
if(OCR0 > 1)
{
OCR0 = OCR - 1;
}
спасибо за ответ.А как написать ,чтобы шим увеличивался если результаты ацп ниже определённого,но когда доходил до максимального значения останавливался на нём пока результаты ацп не вернуться к нормальному значению,и так же в нижнюю сторону?
если быть точнее то как сделать чтобы когда скважность шима доходила до максимального значения после не сбрасывалась в ноль и дальше не увеличивалась бы??
if(OCR0 < 0xFF) { OCR0 = OCR0 + 1; }
А если использовать в Atmega8_32PIN ADC6 или ADC7, у меня что-то не получается выбрать в настройках CodeVision больше ADC5. Подскажите как это сделать?