Продолжим изучать аналого-цифровой преобразователь микроконтроллеров AVR на примере цифрового вольтметра постоянного напряжения, с пределами измерения от 0 до 25V. Измеряемое напряжение будет отображаться на трехразрядном семисегментном индикаторе с общим анодом. В этом примере применим динамическую индикацию о которой подробней рассказано на одном из предыдущих занятий, кусок исходного кода возьмем от туда же. Микроконтроллер Atmega8 тактируется от внутреннего генератора частотой 8MHz.
Далее займемся настройкой АЦП. В этот раз попробуем использовать внутренний источник опорного напряжения 2,56V, т.к. выход Aref микроконтроллера соединен с ИОН, для обеспечения стабильности ИОН подключаем к выводу Aref конденсатор. Резистор R3 - подстроечный, он служит для регулировки номинального уровня напряжения, желательно многооборотный.
Входом АЦП является линия PC0(ADCO), т.к. вольтметр у нас должен измерять напряжение до 25V, а 25V для порта контроллера это очень много, в таких случаях используют делитель напряжения. Например, если напряжение на входе будет меняться от 0 до 25V, то на выходе оно будет меняться от 0 до 5V.
Рассчитаем максимальное напряжение Uemax подаваемое на вход АЦП по формуле:
Uemax = 1023*Uref/1024
Uemax = 1023*2.56/1024 = 2,5575V
Рассчитаем максимальное входное напряжение делителя, исходя из параметров: R1=100k, R2=10k, Uemax=2,5575, применим такую формулу:
Uemax = Uin*R2/R1+R2
,где Uin максимальное напряжение подаваемое на вход делителя.
Uin = 2,5575*110k/10k = 28,1325V
Из этого мы знаем, что больше 28,1325V на вход вольтметра подавать нельзя. Также надо знать какой результат будет сохраняться в регистре ADC при изменении напряжения на входе АЦП. Результат преобразования вычисляется по формуле:
ADC = 1024*Uemax/Uref
Например при максимальном напряжении на входе 2,5575V результат преобразования будет таким:
ADC = 1024*2,5575/2,56 = 1023
При напряжении на входе 2V результат будет таким:
ADC = 1024*2/2.56 = 800
Чтобы на индикаторе получить реальные цифры в Вольтах результат преобразования надо умножить на коэффициент равный отношению максимального напряжения(4 разряда) подаваемого на вход делителя к максимальному результату преобразования АЦП.
k = 2813/1023 = 2,75
В программе обработчика прерываний от АЦП результат преобразования перемножаем на этот коэффициент и получаем величину напряжения подаваемого на вход делителя, т.к для операции умножения на дробное число микроконтроллеру потребуется много памяти, существует способ представить число 2,75 по другому, например: (ADC*11)/4. Настраиваем регистры АЦП и Таймера2, глобально разрешаем прерывания, так же в коде вычисляем средний показатель результата преобразования и выводим данные на индикатор. Полный текст программы ниже.
// Использование АЦП. Цифровой вольтметр
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
//------------------0-----1-----2-----3-----4-----5-----6-----7-----8------9----dp
char SEGMENTE[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x80};
volatile unsigned char segcounter = 0;
volatile int display = 0;
// Прерывание по переполнению T2, динамическая индикация
ISR(TIMER2_OVF_vect)
{
PORTD = 0xFF;
PORTB = (1 << segcounter);
switch (segcounter)
{
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;
}
if ((segcounter++) > 2) segcounter = 0;
}
volatile unsigned long value;
volatile unsigned int adc_counter;
// Прерывание по окончанию преобразования АЦП
ISR (ADC_vect)
{
value = value + (ADC*11/4);
adc_counter++;
}
// Главная функция
int main (void)
{
DDRD = 0xFF;
DDRB |= (1 << PB0)|(1 << PB1)|(1 << PB2)|(1 << PB3);
PORTD = 0x00;
PORTB = 0x00;
DDRC = 0x00;
TIMSK |= (1 << TOIE2); // разрешение прерывания по таймеру2
TCCR2 |= (1 << CS21); //предделитель на 8
ADCSRA |= (1 << ADEN) // разрешение АЦП
|(1 << ADSC) // запуск преобразования
|(1 << ADFR) // непрерывный режим работы АЦП
|(1 << ADPS2)|(1 << ADPS1)|(0 << ADPS0) // предделитель на 64 (частота АЦП 125kHz)
|(1 << ADIE); // разрешение прерывания
ADMUX |= (1 << REFS1)|(1 << REFS0) // внутренний ИОН 2,56V
|(0 << MUX3)|(0 << MUX2)|(0 << MUX1)|(0 << MUX0); // вход ADC0
_delay_ms(50);
sei(); //глобально разрешаем прерывания
while(1)
{
if (adc_counter > 300) // вычисляем среднее значение АЦП
{
display = value/adc_counter;
adc_counter = 0;
value = 0;
}
_delay_ms(50);
}
}
Исходный код для индикатора с общим катодом
// Использование АЦП. Цифровой вольтметр
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
//------------------0-----1-----2-----3-----4-----5-----6-----7-----8------9----dp
char SEGMENTE[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x80};
volatile unsigned char segcounter = 0;
volatile int display = 0;
// Прерывание по переполнению T2, динамическая индикация
ISR(TIMER2_OVF_vect)
{
PORTD = 0x00;
PORTB = ~(1 << segcounter);
switch (segcounter)
{
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;
}
if ((segcounter++) > 2) segcounter = 0;
}
volatile unsigned long value;
volatile unsigned int adc_counter;
// Прерывание по окончанию преобразования АЦП
ISR (ADC_vect)
{
value = value + (ADC*11/4);
adc_counter++;
}
// Главная функция
int main (void)
{
DDRD = 0xFF;
DDRB |= (1 << PB0)|(1 << PB1)|(1 << PB2)|(1 << PB3);
PORTD = 0x00;
PORTB = 0x00;
DDRC = 0x00;
TIMSK |= (1 << TOIE2); // разрешение прерывания по таймеру2
TCCR2 |= (1 << CS21); //предделитель на 8
ADCSRA |= (1 << ADEN) // разрешение АЦП
|(1 << ADSC) // запуск преобразования
|(1 << ADFR) // непрерывный режим работы АЦП
|(1 << ADPS2)|(1 << ADPS1)|(0 << ADPS0) // предделитель на 64 (частота АЦП 125kHz)
|(1 << ADIE); // разрешение прерывания
ADMUX |= (1 << REFS1)|(1 << REFS0) // внутренний ИОН 2,56V
|(0 << MUX3)|(0 << MUX2)|(0 << MUX1)|(0 << MUX0); // вход ADC0
_delay_ms(50);
sei(); //глобально разрешаем прерывания
while(1)
{
if (adc_counter > 300) // вычисляем среднее значение АЦП
{
display = value/adc_counter;
adc_counter = 0;
value = 0;
}
_delay_ms(50);
}
}
|