Продолжим изучать аналого-цифровой преобразователь микроконтроллеров AVR на примере цифрового вольтметра постоянного напряжения, с пределами измерения от 0 до 25V. Измеряемое напряжение будет отображаться на четырехразрядном семисегментном индикаторе с общим анодом. В этом примере применим динамическую индикацию о которой подробней рассказано на одном из предыдущих занятий, кусок кода возьмем от туда же. Микроконтроллер Atmega8 тактируется от внутреннего генератора частотой 8MHz.
Входом АЦП является вывод PC0(ADCO), установлен по умолчанию в регистре ADMUX. Так как вольтметр у нас должен измерять напряжение от 0 до 25V, а 25V для порта контроллера это очень много, в таких случаях используют делитель напряжения, на схеме это резисторы R1 и R2. Например, если напряжение на входе будет меняться от 0 до 25V, то на выходе оно будет меняться от 0 до 5V. Резистор R3 - подстроечный, служит для подстройки отображаемого на дисплее уровня напряжения, желательно многооборотный. Источником опорного напряжения будет вывод питания AVCC с внешним конденсатором на выводе AREF, режим активируется установкой бита REFS0 в регистре ADMUX. Для снижения влияния шумов на результат преобразования вывод AVCC необходимо связать с цифровым питанием VCC через LC-цепь.
Рассчитаем максимальное входное напряжение делителя, чтобы случайно не подать на вход большее напряжение и не повредить микроконтроллер.
Применим такую формулу, где: R1 = 100k, R2 = 25k, Uin = 5V,
Umax = Uin*(R1+R2)/R2
Umax = 5*125k/25k = 25V
Из этого мы знаем, что больше 25V на вход делителя напряжения подавать нельзя.
Результат преобразования в Вольтах вычисляется по формуле:
U = ADC*Uref*K/1024
где ADC - результат преобразования;
Uref опорное напряжение(V);
K - коэффициент делителя напряжения.
1024 - Разрядность АЦП 10 бит
Коэффициент делителя напряжения напряжения вычисляется по формуле:
K = (R1+R2)/R2
K = (100k + 25k)/25k = 5
В нашем примере максимальная разрешающая способность будет 10 разрядов, поэтому частота на входе схемы последовательного приближения должна быть в диапазоне 50…200 кГц. Установим ее впределах 125kHz, включим предделитель на 64 путем установки битов ADPS2 и ADPS1 в регистре ADCSRA.
В программе используем прерывание по окончании преобразования АЦП, для этого включим непрерывный режим работы АЦП путем установки бита ADFR и активируем прерывание битом ADIE в этом же регистре ADCSRA
В обработчике прерывания от АЦП 10-ти битный результат преобразования(ADC) после каждого измерения помещаем в буфер, а уже в основном цикле значение буфера делим на количество измерений для усреднения показаний. Далее, применив формулу, вычисляем результат преобразования, переводим результат вычисления в милливольты, путем умножения на 100, и выводим его на дисплей. Полный текст программы ниже.
// Использование АЦП. Цифровой вольтметр 0-25V #include <avr/io.h> #include <avr/interrupt.h> // Массив комбинаций сегментов unsigned char SEGMENTE[] = { 0x3F, // 0 0x06, // 1 0x5B, // 2 0x4F, // 3 0x66, // 4 0x6D, // 5 0x7D, // 6 0x07, // 7 0x7F, // 8 0x6F // 9 }; volatile unsigned char seg_counter, adc_counter; volatile unsigned int display = 0; volatile unsigned long adc_buffer; // Прерывание по переполнению T2, динамическая индикация ISR (TIMER2_OVF_vect) { PORTD = 0xFF; // Гасим все сегменты PORTB = (1 << seg_counter); // Последовательно зажигаем общие аноды switch(seg_counter) { case 0: PORTD = ~(SEGMENTE[display % 10000/1000]); // Выводим первый разряд break; case 1: PORTD = ~((SEGMENTE[display % 1000/100])|0x80); // добавляем десятичную точку break; case 2: PORTD = ~(SEGMENTE[display % 100/10]); // Выводим третий разряд break; case 3: PORTD = ~(SEGMENTE[display % 10]); // Выводим четвертый разряд break; } if (seg_counter++ > 2) seg_counter = 0; } // Прерывание по окончанию преобразования АЦП ISR(ADC_vect) { adc_buffer += ADC; // Накапливаем в буфер значения АЦП adc_counter++; // Увеличиваем счетчик измерений } // Главная функция int main (void) { // Настройка портов ввода/вывода DDRB = 0xFF; // Выходы на общие аноды PORTB = 0x00; // Ноль на выходе DDRD = 0xFF; // Выходы на сегменты PORTD = 0x00; // Ноль на выходе // Настройка Т2 TIMSK |= (1 << TOIE2); // Разрешение прерывания по таймеру2 TCCR2 |= (1 << CS21); // Предделитель на 8 // Настройка АЦП ADMUX |= (1 << REFS0); // ИОН - AVCC с внешним конденсатором на выводе AREF, вход ADC0 ADCSRA |= (1 << ADEN) // Разрешение АЦП |(1 << ADSC) // Запуск преобразования |(1 << ADFR) // Непрерывный режим работы АЦП |(1 << ADPS2)|(1 << ADPS1) // Предделитель на 64 (частота АЦП 125kHz) |(1 << ADIE); // Разрешение прерывания от АЦП sei(); // Глобально разрешаем прерывания // Главный цикл while(1) { if(adc_counter == 250) // Если количество измерений равно 250 { display = ((adc_buffer*5*5*100)/1024)/adc_counter; // Вычисляем среднее значение АЦП adc_counter = 0; // Обнуляем счетчик измерений adc_buffer = 0; // Обнуляем буфер АЦП } } }
При программировании установить такие Fuse - биты:
Обсуждение статьи на форуме.
Архив для статьи "АЦП микроконтроллеров AVR. Делаем цифровой вольтметр 0 - 25V" | |
Описание: Файлы проекта для AVR Studio | |
Размер файла: 16.51 KB Количество загрузок: 8 718 | Скачать |
Комментарии
Я немного напутал)
Вывод который идёт от R2 забыл на землю кинуть ))
Теперь всё отлично работает)
Большое спасибо за статью!
Порт С не надо предварительно определять, т.к. мы уже настроили параметры ацп в коде
Я могу довериться этому микроконтроллер у, если он будет установлен в щите, общая нагрузка которого ~400 кВт, с огромными контакторами и частотниками. И измерять напряжение на расстоянии 30м обычным ШВВп проводом. Не заглючит мега??
Может и не заглючит, но при измерении будут наводки и падение напряжения
накопленного в буфере
adc_buffer*5*5*100/1024
ведь значение буфера adc_buffer += ADC
за 250 прерываний уже известно
display = ((adc_buffer*5*5*100)/1024)/adc_counter;
adc_buffer - Значение АЦП накопленное на данный момент
5 - опорное напряжение
5 - коэффициент делителя напряжения
100 - переводим в милливольты, чтобы удобно вывести на дисплей
1024 - разрядность АЦП, 10 бит
adc_counter - счетчик измерений, т.е. 250
индикаторе.
Да, правильно