9 марта 2013 г.

AVR работа с АЦП

АЦП или Аналого Цифровое Преобразование. Как ни странно, преобразует аналоговое значение напряжения в цифровое, с которым удобнее работать микроконтроллеру. Он это делает на подобие компаратора, сравнивая напряжение на выходе с некоторым опорным напряжением.

От разрешения АЦП зависит его точность. к примеру если опорное напряжение 5 Вольт, АЦП 10 битный. то на каждый бит приходиться     5 / ( 2^10 - 1 )      = 0.00489 или 5 мВ. Точнее замерить нельзя.

За настройку АЦП отвечает регистр ADSCRA 
7 ADEN включение АЦП
1 ON  
0 OFF

6 ADSC отвечает за запуск АЦП, если настроено однократное преобразование, то по выполнении ставиться 0, нужно вручную ставить 1 чтобы снова запустить АЦП.

5 ADFR выбор режима работы АЦП
0 – режим однократного преобразования
1 – режим непрерывного преобразования

4 ADIF флаг прерывания, выставляется 1 при завершении преобразования.

3 ADIE разрешение прерывания
0 – прерывание запрещено
1 – прерывание разрешено

2 1 0 ADPS выбор частоты работы АЦП
CK = тактовая частота микроконтроллера

000         СК/2
001         СК/2
010         СК/4
011         СК/8
100         СК/16
101         СК/32
110         СК/64
111         СК/128


За выбор опорного напряжения и выбора выводов АЦП отвечает ADMUX
7-6 REFS выбор опорного напряжения, максимальное напряжение которое может измерить АЦП. Изменения происходят после завершения текущего преобразования.
00    AREF
01    AVcc, с внешним конденсатором на AREF
10    Зарезервировано, не используется.
11    Внутренний 2.56В  источник, с внешним конденсатором на AREF

5 ADLAR определяет как значение АЦП запишется в регистры ADCL и ADCH. Т.к АЦП 10 битный 1 регистра мало. Значение обоих регистров хранится в ADCW.



4-0 MUX# определяет вывод МК откуда будет считываться значение АЦП. (ATmega8)
0000      ADC0
0001      ADC1
0010      ADC2
0011      ADC3
0100      ADC4
0101      ADC5
0110      ADC6
0111      ADC7


Готовый проект в протеусе

#include <avr io.h>
#include <avr interrupt.h>

//прерывание
ISR(ADC_vect)
{    
 unsigned int ADCdata, voltage_0, voltage, voltage_2, voltage_3;
 ADCdata = ADCW; // В ADCW хранится напряжение в двоичном коде
 voltage_0 = ADCdata * 48875 / 10000; // 5 вольт / 1023 = 4.8875
 voltage = voltage_0 % 10000 / 1000;  
 voltage_2 = voltage_0 % 1000 / 100;
 voltage_3 = voltage_0 % 100 / 10;


 PORTC = voltage;
 PORTD = voltage_2;
 PORTB = voltage_3;

 ADCSRA = ADCSRA | 0x40;// Регистр для начала нового преобразования 0b01000000
}

int main (void)
{
 DDRB = 0xFF;
 DDRD = 0xFF;
 DDRC = 0xFF;
 ADMUX = 0x00; // PA0
 ADCSRA = 0b11001110; 

 PORTB = 0x00;
 PORTD = 0x00;
 PORTC = 0x00;
 
 sei();
 while(1); 
}
Как выглядит схема в Proteus




Подаем напряжение на PA0. Затем преобразуем его в цифровое, и выдаем нужные значения в порты в двоичном коде. Целые в Port B,  десятые в Port C и сотые в Port D.
То есть у нас 2,35 Вольта.

Теперь нужно преобразовать значение из ADCW ( здесь храниться значение напряжения ) в информацию с которой легко работать..
АЦП (в моём случае) сравнивает значение аналогового входа с напряжением питания.
АЦП 10 битный, значит когда на входе 5 Вольт, в ADCW 2^10-1 = 1023.

Теперь нужно воспользоваться формулой
Вольты = ADCW * Напряжение с которым сравниваем / разрешение
В нашем случае

Вольты = ADCW * 5 / 1023
Я где-то прочитал что лучше не писать дроби а записывать таким образом:
5 / 1023 = 0.0048876 = 48876 / 10000000

Но мне нужны десятые и сотые доли вольта, поэтому я умножаю 5 на 1000 чтобы были не дроби а целые числа.

5000 / 1023 = 4.8875 = 48875 / 10000

Еще советуют ADCW засунуть в unsigned int
В конце выглядит так

unsigned int ADCdata
ADCdata = ADCW
voltage_0 = ADCW * 48875 / 10000

Значит у нас уже в переменной хранится  четырёхзначное число обозначающее вольты к примеру 3524 что равно 3,524 вольта.
нам остается из него выделять то что нужно нам.

voltage = voltage_0 % 10000 / 1000
voltage_0 наше чертырёхзначное число.

берём из него остаток при делении на 10000, у нас получилось бы 3524
дальше делим это на 1000, т.к это int у нас будет целое число то есть 3.
В итоге voltage = 3
Выводим 3 в двоичном коде на нужный нам порт.
Проделываем то же самое с десятичной и сотой частью.

Информация:
samou4ka.net
avrlab.com
chipenable.ru
radioparty.ru
my-avr.at.ua
myrobot.ru
avr-tutorials.com
Скан книги "Шпак Ю.А. - Программирование на языке C для AVR и PIC" С хорошим примером работы АЦП.
1 2

1 комментарий :

  1. Можно так записать?

    //прерывание
    ISR(ADC_vect)
    {
    unsigned int voltage_0, voltage, voltage_2, voltage_3;
    voltage_0 = ADCW * 48875 / 10000;
    voltage = voltage_0 % 10000 / 1000;
    voltage_2 = voltage_0 % 1000 / 100;
    voltage_3 = voltage_0 % 100 / 10;


    PORTC = voltage;
    PORTD = voltage_2;
    PORTB = voltage_3;

    ADCSRA = ADCSRA | 0x40;
    }

    т.е. сделать вычисление сразу voltage_0 = ADCW * 48875 / 10000;
    а не
    ADCdata = ADCW;
    voltage_0 = ADCW * 48875 / 10000;

    ОтветитьУдалить