Продолжим изучать аналого-цифровой преобразователь микроконтроллеров 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 841 | Скачать |




Комментарии
Я немного напутал)
Вывод который идёт от 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
индикаторе.
Да, правильно