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