АЦП позволяет считывать аналоговый сигнал и переводить его в цифровой, благодаря этому можно аналоговым сигналом, например от потенциометра, управлять работой микроконтроллера.
Алгоритм работы АЦП:
- Выбор канала для считывания сигнала;
- Запуск преобразования (считывания сигнала);
- Ожидание пока преобразование закончится;
- Запись считанного значения в переменную.
ATmega328 имеет 6 каналов для считывания аналогового сигнала ADC5 (28) - ADC0 (23). А также еще два вывода: на AREF (21) подается опорное напряжение, а на AVCC (20) питание для аналоговой части. Если нужны точные измерения, то к AVCC можно подвести отдельное питание без помех, если точность не критична, то можно подключить к питанию микроконтроллера.
Регистры АЦП
Настройка и управление АЦП осуществляется с помощью регистров:
- ADMUX - регистр настройки мультиплексора;
- ADCSRA - управляющий и статусный регистр;
- ADCSRB - управляющий регистр;
- ADCH и ADCL - регистры в которых записывается значение АЦП.
Регистр ADMUX
Включает в себя следующее
Номер бита | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Название | REFS1 | REFS0 | ADLAR | - | MUX3 | MUX2 | MUX1 | MUX0 |
REFS1, REFS0 - выбор опорного напряжения
REFS1 | REFS0 | Описание |
0 | 0 | Внешний источник питания на AREF (21), внутренний отключен |
0 | 1 | Внешний источник питания на AVCC (20), с внешним конденсатором на AREF (21) |
1 | 0 | - |
1 | 1 | Внутренний источник опорного напряжения 1,1 В с внешним конденсатором на AREF (21) |
ADLAR - коррекция представления в регистре данных. Так как АЦП имеет разрядность 10 бит, то входящее 10-битное значение разбивается на два регистра ADCH и ADCL и в зависимости от значения ADLAR сдвигается. Если в ADLAR записать 1, 8 бит будет в ADCH и 2 в ADCL, если 0 - то 8 бит будет в ADCL и 2 в ADCH. Если не нужна высока точность (больше 8 бит), то можно сделать ADLAR = 1 и взять старшие 8 бит из регистра ADCH.
ADLAR = 1
ADCH | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 |
ADCL | 1 | 0 | - | - | - | - | - | - |
ADLAR = 0
ADCH | - | - | - | - | - | - | 9 | 8 |
ADCL | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
MUX3, MUX2, MUX1, MUX0 - выбор аналогового канала. Значение этих битов определяет, какие аналоговые входы подключены к АЦП
MUX3 | MUX2 | MUX1 | MUX0 | Описание |
0 | 0 | 0 | 0 | ADC0 |
0 | 0 | 0 | 1 | ADC1 |
0 | 0 | 1 | 0 | ADC2 |
0 | 0 | 1 | 1 | ADC3 |
0 | 1 | 0 | 0 | ADC4 |
0 | 1 | 0 | 1 | ADC5 |
1 | 0 | 0 | 0 | Temperature sensor |
Регистр ADCSRA
Включает в себя следующее
Номер бита | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Название | ADEN | ADSC | ADATE | ADIF | ADIE | ADPS2 | ADPS1 | ADPS0 |
ADEN - включение/выключение АЦП.
ADSC - запуск преобразования АЦП. В режиме одиночного преобразования нужно записать единицу в этот бит, чтобы начать преобразование. В режиме свободного запуска нужно записать единицу в этот бит, чтобы начать первое преобразование.
ADATE - включение автоматического запуска АЦП. Когда этот бит записывается в единицу, включается автоматическая работа АЦП, то есть значения будут считываться постоянно.
ADIF - флаг прерывания АЦП. Этот бит устанавливается, когда преобразование АЦП завершается и регистры данных обновляются.
ADIE - активация прерывания АЦП.
ADPS2, ADPS1, ADPS0 - выбор предделителя АЦП
ADPS2 | ADPS1 | ADPS0 | Описание |
0 | 0 | 0 | Предделитель 2 |
0 | 0 | 1 | Предделитель 2 |
0 | 1 | 0 | Предделитель 4 |
0 | 1 | 1 | Предделитель 8 |
1 | 0 | 0 | Предделитель 16 |
1 | 0 | 1 | Предделитель 32 |
1 | 1 | 0 | Предделитель 64 |
1 | 1 | 1 | Предделитель 128 |
Регистр ADCSRB
Включает в себя следующее
Номер бита | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Название | - | ACME | - | - | - | ADTS2 | ADTS1 | ADTS0 |
ADTS2, ADTS1, ADTS0 - источник автоматического запуска преобразования. Действует, когда в ADATE записана единица.
ADTS2 | ADTS1 | ADTS0 | Описание |
0 | 0 | 0 | Режим свободного запуска |
0 | 0 | 1 | Совпадение по аналоговому компаратору |
0 | 1 | 0 | Внешнее прерывание 0 |
0 | 1 | 1 | При совпадении с регистром сравнения A таймера 0 |
1 | 0 | 0 | При переполнении таймера 0 |
1 | 0 | 1 | При совпадении с регистром сравнения B таймера 1 |
1 | 1 | 0 | При переполнении таймера 1 |
1 | 1 | 1 | При событии захвата таймера 1 |
Примеры
Минимальный код для работы АЦП. Опорное напряжение с AREF, аналоговый сигнал считывается с ADC0.
#define F_CPU 16000000UL //частота работы микроконтроллера
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
void adc_init(){
ADMUX = 0;
ADCSRA = 0;
ADCSRB = 0;
ADMUX|= (1 << ADLAR); //значение в ADCH
ADCSRA|=(1 << ADEN); //включение АЦП
}
int main(void){
adc_init();
while (1){
ADCSRA |= (1 << ADSC); //запуск преобразования
while(ADCSRA & (1 << ADSC)); //проверка, закончилось ли аналого-цифровое преобразование
value_adc = ADCH; //считывание значения в переменную
}
}
Считывание аналогового сигнала с нескольких каналов и с усреднением значений
#define F_CPU 16000000UL //частота работы микроконтроллера
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
uint8_t adc_in_0 = 0;
uint8_t adc_average_0 = 0;
uint8_t adc_out_0 = 0;
uint8_t adc_in_1 = 0;
uint8_t adc_average_1 = 0;
uint8_t adc_out_1 = 0;
uint8_t adc_in_2 = 0;
uint8_t adc_average_2 = 0;
uint8_t adc_out_2 = 0;
float coef =0.5;
void adc_init(){
ADMUX = 0;
ADCSRA = 0;
ADCSRB = 0;
ADMUX|= (1 << ADLAR); //значение в ADCH
ADCSRA|=(1 << ADEN); //включение АЦП
}
int main(void){
adc_init();
while (1){
//выбор канала ADC0
ADMUX &= ~(1 << MUX0);
ADMUX &= ~(1 << MUX1);
ADMUX &= ~(1 << MUX2);
ADMUX &= ~(1 << MUX3);
ADCSRA |= (1 << ADSC); //запуск преобразования
while(ADCSRA & (1 << ADSC)); //проверка, закончилось ли аналого-цифровое преобразование
adc_in_0 = ADCH;
adc_average_0 += (adc_in_0 - adc_average_0) * coef;
adc_out_0 = adc_average_0;
//выбор канала ADC1
ADMUX |= (1 << MUX0);
ADMUX &= ~(1 << MUX1);
ADMUX &= ~(1 << MUX2);
ADMUX &= ~(1 << MUX3);
ADCSRA |= (1 << ADSC); //запуск преобразования
while((ADCSRA & (1 << ADSC))); //проверка, закончилось ли аналого-цифровое преобразование
adc_in_1 = ADCH;
adc_average_1 += (adc_in_1 - adc_average_1) * coef;
adc_out_1 = adc_average_1;
//выбор канала ADC2
ADMUX &= ~(1 << MUX0);
ADMUX |= (1 << MUX1);
ADMUX &= ~(1 << MUX2);
ADMUX &= ~(1 << MUX3);
ADCSRA |= (1 << ADSC); //запуск преобразования
while(ADCSRA & (1 << ADSC)); //проверка, закончилось ли аналого-цифровое преобразование
adc_in_2 = ADCH;
adc_average_2 += (adc_in_2 - adc_average_2) * coef;
adc_out_2 = adc_average_2;
}
}
Управление скважностью ШИМ сигнала с помощью потенциометра
#define F_CPU 16000000UL //частота работы микроконтроллера
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
int ctrl = 0;
uint8_t adc_in_0 = 0;
uint8_t adc_average_0 = 0;
uint8_t adc_out_0 = 0;
uint8_t adc_in_1 = 0;
uint8_t adc_average_1 = 0;
uint8_t adc_out_1 = 0;
uint8_t adc_in_2 = 0;
uint8_t adc_average_2 = 0;
uint8_t adc_out_2 = 0;
float coef =0.5;
void adc_init(){
ADMUX = 0;
ADCSRA = 0;
ADCSRB = 0;
ADMUX|= (1 << ADLAR); //значение в ADCH
ADCSRA|=(1 << ADEN); //включение АЦП
}
void timer0_init() {
TCCR0A = 0;
TCCR0B = 0;
TCNT0 = 0;
// Не инверсный режим работы OC0A и OC0B
TCCR0A |= (1 << COM0A1);
TCCR0A |= (1 << COM0B1);
TCCR0A |= (1 << WGM00) | (1 << WGM01); //Режим 3: Быстрый ШИМ, скважность регулируется OCR0A и OCR0B
TCCR0B |= (1 << CS01) | (1 << CS00); //64 - 976 ГЦ
OCR0A = 0;
OCR0B = 0;
}
void timer2_init() {
TCCR2A = 0;
TCCR2B = 0;
TCNT2 = 0;
// Не инверсный режим работы OC0B
TCCR2A |= (1 << COM2B1);
TCCR2A |= (1 << WGM20) | (1 << WGM21); //Режим 3: Быстрый ШИМ, скважность регулируется OCR2A и OCR2B
TCCR2B |= (1 << CS22); //64 - 976 ГЦ
OCR2B = 0;
}
int main(void)
{
DDRD |= (1 << PORTD3);
DDRD |= (1 << PORTD5);
DDRD |= (1 << PORTD6);
cli();
adc_init();
timer0_init();
timer2_init();
sei();
while (1){
ctrl++;
if (ctrl > 10){
ctrl = 0;
ADMUX &= ~(1 << MUX0);
ADMUX &= ~(1 << MUX1);
ADMUX &= ~(1 << MUX2);
ADMUX &= ~(1 << MUX3);
ADCSRA |= (1 << ADSC); //запуск преобразования
while(ADCSRA & (1 << ADSC)); //проверка, закончилось ли аналого-цифровое преобразование
potenciometr_in_0 = ADCH;
potenciometr_average_0 += (potenciometr_in_0 - potenciometr_average_0) * coef;
if (potenciometr_average_0 < 10 ){
potenciometr_out_0 = 0;
DDRD &= ~(1 << PORTD6);
}
else{
DDRD |= (1 << PORTD6);
potenciometr_out_0 = potenciometr_average_0;
}
OCR0A = potenciometr_average_0;
ADMUX |= (1 << MUX0);
ADMUX &= ~(1 << MUX1);
ADMUX &= ~(1 << MUX2);
ADMUX &= ~(1 << MUX3);
ADCSRA |= (1 << ADSC); //запуск преобразования
while((ADCSRA & (1 << ADSC))); //проверка, закончилось ли аналого-цифровое преобразование
potenciometr_in_1 = ADCH;
potenciometr_average_1 += (potenciometr_in_1 - potenciometr_average_1) * coef;
if (potenciometr_average_1 < 10 ){
potenciometr_out_1 = 0;
DDRD &= ~(1 << PORTD5);
}
else{
DDRD |= (1 << PORTD5);
potenciometr_out_1 = potenciometr_average_1;
}
OCR0B = potenciometr_out_1;
ADMUX &= ~(1 << MUX0);
ADMUX |= (1 << MUX1);
ADMUX &= ~(1 << MUX2);
ADMUX &= ~(1 << MUX3);
ADCSRA |= (1 << ADSC); //запуск преобразования
while(ADCSRA & (1 << ADSC)); //проверка, закончилось ли аналого-цифровое преобразование
potenciometr_in_2 = ADCH;
potenciometr_average_2 += (potenciometr_in_2 - potenciometr_average_2) * coef;
if (potenciometr_average_2 < 10 ){
potenciometr_out_2 = 0;
DDRD &= ~(1 << PORTD3);
}
else{
DDRD |= (1 << PORTD3);
potenciometr_out_2 = potenciometr_average_2;
}
OCR2B = potenciometr_average_2;
}
}
}