АЦП микроконтроллеров AVR на пальцах

Хоть я и не позиционирую свой сайт как «учебник», но частенько приходится отвечать на вопросы начинающих эмбеддеров. Народ пишет и в личку, и в комменты, некоторые даже звонят. Когда однотипных вопросов накапливается достаточно много, то, мне кажется,  полезнее написать статью по теме, чем отвечать каждому товарищу по-отдельности, тем более, когда они так сильно прОсют 🙂
Тема использования АЦП описана чуть ли не на каждом микроконтроллерном сайте, но, как видим, вопросы у многих остаются. Не знаю, может народ не понимает английского для прочтения и изучения даташитов, но книга А.В. Евстифеева «Микроконтроллеры AVR семейств Tiny и Mega фирмы ATMEL», по-моему, должна быть у каждого человека, занимающегося такими  (и не только такими) микроконтроллерами. Эта книга по своей сути — перевод и обобщение даташитов наиболее популярных контроллеров. ? не беда, что номенклатура последних уже существенно обогатилась — основные принципы те же.
?так, аналого-цифровой преобразователь, как следует из его названия, преобразовывает аналоговую величину (в нашем случае напряжение) в цифровой код. Управляется он в ATmega8 всего двумя регистрами. На рисунке ниже я попытался максимально просто изобразить принцип действия АЦП и его основные характеристики.
ADC_picture

Входной аналоговый сигнал через вход мультиплексора MUX, который мы выбрали разрядами MUX3-MUX0 регистра ADMUX, поступает непосредственно на вход АЦП микроконтроллера и сравнивается с установленным REFS1,REFS0 (ADMUX) опорным напряжением (входной сигнал не может быть выше опорного — для измерения величин выше опорного следует применять делители напряжения). Весь диапазон опорного напряжения разбит на 2 в степени разрешение, т.е для нашего случая 2^10=1024 градации. При запуске АЦП установкой ADSC (ADSCRA) входной аналоговый сигнал сравнивается с опорным в течении времени t преобразования, и, по истечении этого времени в пару регистров ADCH(выделен жёлтым)ADCL(выделен голубым) записывается цифровой выходной 10-ти разрядный код, показывающий относительную величину входного, по отношению к опорному. Например, если опорное напряжение равно 5 В, а на вход АЦП пришёл сигнал 2,5 В, то в регистры ADCH-ADCL запишется значение 512 — половина от 1024. Причём результат в пару ADCH-ADCL будет записан со сдвигом вправо или влево в зависимости от значения разряда ADLAR регистра ADCSRA. Абсолютную величину напряжения легко вычислить , зная опорное.
Все разряды регистра ADMUX на рисунке наглядно представлены. ?з ADSCRA не рассмотрены: 7-й разряд — ADEN — разрешение работы АЦП; 5-й — ADFR — включение режима непрерывного преобразования; 4-й — ADIF — флаг прерывания от АЦП; 3-й — ADIE —  разрешение прерывания от АЦП.
Теперь код для простейшего примера, демонстрирующего работу АЦП. Вся логика работы здесь, как и во всех моих примерах, расписана в комментариях. Код написан на классическом Си для WinAVR, но по идее должен работать и под CodeVision (только подключаемые файлы там по другому называются). МК ATmega8 работает от внутреннего генератора на частоте 1 МГц.

/*Учебный пример написаный товарищем s_black www.embed.com.ua для товарища ВМ, который ленится читать даташиты*/
#include <avr/io.h>
#include <avr/interrupt.h>
 
#define PORT_LED PORTD /*назначение порта светодиодов*/
#define DDR_LED  DDRD  /*назначение регистра направления порта светодиодов*/
#define LED_1    PD0   /*первый светодиод - индикатор 1/4 максимального напряжения*/
#define LED_2    PD1   /*второй светодиод - индикатор 2/4 максимального напряжения*/
#define LED_3    PD2   /*третий светодиод - индикатор 3/4 максимального напряжения*/
 
#define ON_LED(LED) (PORT_LED &= ~(1< <LED))  /*макрос включения светодиода*/
#define OFF_LED(LED) (PORT_LED |= (1<<LED))  /*макрос выключения светодиода*/
 
ISR (ADC_vect)//прерывание по завершению преобразования АЦП
{
    unsigned int voltage_ADC ;//переменная результата напряжения
 
	voltage_ADC = ADCW;//считываем значение АЦ преобразования
	//блок операторов ниже реализует "светящийся столбик" в зависимости от измеренной величины напряжения
	     if (voltage_ADC >= 768) {ON_LED (LED_3);ON_LED (LED_2);ON_LED(LED_1);}//если U > или = 3/4 максимального - подсвечиваем светодиоды 3,2,1
	else if (voltage_ADC >= 512) {OFF_LED(LED_3);ON_LED (LED_2);ON_LED(LED_1);}//если U > или = 2/4 и < 3/4 максимального - подсвечиваем светодиоды 2,1
	else if (voltage_ADC >= 256) {OFF_LED(LED_3);OFF_LED(LED_2);ON_LED(LED_1);}//если U > или = 1/4 и < 2/4 максимального - подсвечиваем светодиод 1
	else {OFF_LED(LED_3);OFF_LED(LED_2);OFF_LED(LED_1);}//если U < 1/4 максимального - ничего не подсвечиваем
	ADCSR |= (1<<ADSC);//запускаем очередное преобразование 
}
 
int main (void)                           
{
    DDR_LED |= (1<<LED_3) | (1<<LED_2) | (1<<LED_1);//разряды порта светодиодов - выходы
	PORT_LED |= (1<<LED_3) | (1<<LED_2) | (1<<LED_1);//изначально погашены
 
	//          7     6     5     4     3     2     1     0
	//ADMUX = REFS1 REFS0 ADLAR   –    MUX3  MUX2  MUX1  MUX0
	ADMUX |= (1<<REFS0);//в качестве опорного - напряжение питания, результат выравнивается вправо, 0-й канал АЦП (PC0)
 
	//           7    6     5     4     3     2     1     0
	//ADCSRA = ADEN ADSC  ADFR  ADIF  ADIE  ADPS2 ADPS1 ADPS0
	ADCSRA |= (1<<ADEN) | (1<<ADSC) | (1<<ADIE) | (1<<ADPS1) | (1<<ADPS0);//разрешаем преобразование, запускаем его, разрешаем прерывание от АЦП
	                                                                         //тактовую частоту делим на 8 - получаем частоту преобразования 125 кГц 
	sei();//общее разрешение прерываний
	for(;;);//бесконечный цикл в ожидании прерывания по завершению преобразования АЦП
}
 
 
}

Для демонстрации работы этого кода я собрал макет по такой схеме:
ADC ATmega8Элементы с левой стороны микроконтроллера IC1 ATmega8 — это стандартная даташитовская обвязка для АЦП. В частности L1, C1 — это LC-фильтр для «аналогового» питания.  Резистор R1 подтягивает вывод сброса микроконтроллера к питанию. Конденсатором С2 вывод опорного напряжения AREF рекомендуют коротить на корпус, если не используется внешнее опорное.
Теперь рассмотрим элементы с правой стороны. ?сточник измеряемого изменяемого напряжения реализован на потенциометре R2. Резистор R3 добавлен, чтобы не «посадить» питание на корпус при самом нижнем положении ползунка. ?ндикация напряжения по градациям реализована на светодиодах LED1-3 с токоограничительными резисторами R4-6. Питание — Li-ION батарея 3,7 В от мобильного телефона. В качестве отдельных светодиодов используются сегменты «a» трёхразрядного семисегментного индикатора с общим анодом.
На видео ниже демонстрация работы макета. В исходном положении ползунок R2 в верхнем по схеме положении, т.е. подключён к питанию в результате чего светятся все три светодиода. Напряжение на ползунке индицируется мультиметром DT830B.

При «движении» ползунка R2 вниз к «корпусу» (резистор используется многооборотный) мы видим погасание сначала правого (при напряжении 2,82 В), затем среднего ( при напряжении 1,88 В), и, наконец, левого (при напряжении 0,94 В). Если посчитать в столбик, то эмпирическим путём мы подтвердим практические результаты: 1 = 3,77; 3/4 = 2,8275; 2/4 = 1,885; 1/4 = 0,9425.

 

Запись опубликована в рубрике Разное с метками , , , , . Добавьте в закладки постоянную ссылку.

27 комментариев на «АЦП микроконтроллеров AVR на пальцах»

Добавить комментарий

Ваш адрес email не будет опубликован.

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.