Урок 8.
Аналого-Цифровой преобразователь — устройство, преобразующее входной аналоговый сигнал в цифровой код. Под аналоговой величиной подразумевается ток, напряжение, сопротивление, емкость, частота, и так далее. На выходе АЦП мы получаем цифровое представление входной величины. Именно благодаря АЦП микроконтроллер может оперировать аналоговыми сигналами.
Однако АЦП микроконтроллера может измерять только напряжение. Поэтому любую другую величину, например ток, перед измерением необходимо преобразовать в напряжение.
Преобразование входного сигнала в численное представление происходит относительно опорного напряжения. Опорное напряжение(Vref) — эталонное напряжение, относительно которого происходит преобразование входной величины. При использовании АЦП микроконтроллера AtMega8, опорное напряжение необходимо подавать на соответствующую ножку контроллера.
Измерение напряжения производится в диапазоне от 0 до Vref.Весь этот диапазон делится в соответствии с разрядностью. То есть если разрядность АЦП — 10 Бит, то диапазон делится на 1023, если 8 бит, то на 255 и так далее. Предполагая, что Vref = 5 Вольтам, при разрядности 10 Бит мы получим шаг измерения 5/1023 = 0.0049 Вольт, а при разрядности 8 бит, шаг измерения станет 5/255 = 0.02 Вольт. То есть, чем меньше разрядность — тем ниже точность преобразования.
Предположим, что мы подаем на вход 10 битного АЦП, c Vref =5 Вольтам, напряжение 3 вольта. На выходе АЦП мы получим численное представление входного напряжения. Легко подсчитать, что шаг измерения равен 0.0049 Вольт, значит, на выходе АЦП мы получим . То есть, для получения значения входного напряжения, необходимо шаг измерения умножить на выходное значение АЦП.
Давайте рассмотрим регистры, отвечающие за конфигурацию АЦП микроконтроллера AtMega8. АЦП в AtMega8 всего один, однако, имеет 8 входных каналов. За выбор входного канала, а так же настройку опорного напряжения отвечает регистр ADMUX.
ADMUX:
- Биты REFS0-REFS1 отвечают за выбор опорного напряжения.
- Биты MUX0-MUX3 отвечают за выбор входного канала.
- Бит ADLAR. Определяет порядок записи результатов преобразования в регистры ADCL и ADCH.
АЦП микроконтроллера AtMega8, может работать как в режиме однократного преобразования, так и в режиме непрерывного преобразования (Следующее преобразование начинается сразу после предыдущего). За настройку режима преобразования, прерываний, а так же тактирования АЦП, отвечает регистр ADCSRA.
ADCSRA:
- Бит ADEN включает и выключает АЦП микроконтроллера.
ADEN = 1 — АЦП включен.
ADEN = 0 — АЦП выключен.
- При записи 1 в бит ADSC, в режиме однократного преобразования запускается преобразование.
- Бит ADFR, отвечает за выбор режима преобразования.
ADFR = 1 — Непрерывное преобразование.
ADFR = 0 — Однократное преобразование.
- Бит ADIF, флаг окончания преобразования, становится равен 1 при окончании преобразования
- Бит ADIE, разрешает прерывание АЦП.
ADIE = 1 — Прерывание разрешено.
ADIE = 0 — Прерывание запрещено.
- Биты ADPS0-ADPS2 отвечают за выбор предделителя между частотой тактирования микроконтроллера и АЦП.
Результат преобразования помещается в пару регистров ADCH и ADCL, в виде заданным значением бита ADLAR.
В качестве примера соберем простой вольтметр с применением LCD WH1602, и встроенного АЦП микроконтроллера AtMega8.
Соберем в Proteus следующую схему:
Код прошивки на C:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
#include <avr/io.h> #include <avr/interrupt.h> #include <stdio.h> #define F_CPU 16000000UL #include <util/delay.h> #define E_Bit 1 #define E_Port PORTB #define E_DDR DDRB #define RS_Bit 0 #define RS_Port PORTB #define RS_DDR DDRB #define Data_Port PORTD #define Data_DDR DDRD void Pulse_E(void) //Переключить линию E { E_Port |= (1<<E_Bit); _delay_us(60); E_Port &=~(1<<E_Bit); _delay_us(60); } void Send_Byte (char Data)// Отправить байт данных LCD { RS_Port |=(1<<RS_Bit); Data_Port = Data; Pulse_E(); } void Send_Cmd (char Cmd)// Отправить команду LCD { RS_Port &=~(1<<RS_Bit); Data_Port = Cmd; Pulse_E(); } void Send_Str (char* str,char length)// Отправить строку LCD, длины length { for (int i = ;i<length;i++) { Send_Byte(*str); str++; _delay_us(60); } } void Init_LCD (void)//Инициализация LCD { Data_DDR = 0xFF; E_DDR|=(1<<E_Bit); RS_DDR |=(1<<RS_Bit); Send_Cmd(0b00110000); _delay_us(60); Send_Cmd(0b00000001); _delay_ms(2); Send_Cmd(0b00001100); _delay_us(60); } ISR (ADC_vect) //Прерывание окончания преобразования АЦП { float Voltage = ADCW*0.00489;//Результат преобразования хранится в ADCW = ADCH:ADCL char buffer [10];// Строка для отправки LCD sprintf(buffer,"U=%.2f V",Voltage);// Преобразуем значение напряжения к строке Send_Cmd(0b00000001);// Подаем команду очистки экрана _delay_ms(2);// Пауза для ожидания завершения исполнения команды Send_Str(buffer,10); // Отправляем строку _delay_ms(100); //Пауза ADCSRA |=(1<<ADSC); //Запускаем следующее преобразование } void Init_ADC(void) { ADMUX |=(1<<REFS0);//AVCC — источник опорного напряжения ADCSRA |=(1<<ADEN)|(1<<ADIE)|(1<<ADPS0)|(1<<ADPS1)|(1<<ADPS2);// Включаем АЦП, разрешаем прерывание, настраиваем тактирование sei ();// Глобально разрешаем прерывания } int main(void) { Init_ADC(); Init_LCD(); ADCSRA |=(1<<ADSC);// Т.к. мы работаем в режиме однократного преобразования — запускаем цикл преобразования. while(1) { } } |
В результате получаем:
Если у вас ещё остались вопросы на тему использования АЦП микроконтроллера, мы будем рады ответить на них в комментариях!
До новых уроков!
Любое копирование, воспроизведение, цитирование материала, или его частей разрешено только с письменного согласия администрации MKPROG.RU. Незаконное копирование, цитирование, воспроизведение преследуется по закону!
void Send_Str (char* str,char length)// Отправить строку LCD, длины length
{
for (int i = ;i<length;i++)
{
Send_Byte(*str);
str++;
_delay_us(60);
}
}
при компиляции в AVRStudio 6.2 ругается что i ничему не равно
for (int i = ;i
unsigned int counter;
for ( counter = 0; counter < length; counter++)
{
Send_Byte (*str);
str++;
delay_ms (1);
}
я на это поменял и все ок
Здравствуйте. А если надо измерить напряжение 12-15 вольт, то как это реализовать при помощи atmega8?
Ведь если правильно понял, то можно измерять до 5 вольт.
Здравствуйте, Павел!
Я бы использовал простейший делитель напряжения на двух резисторах)
Спасибо Вам, DamiKK.
Подаем мы 15 вольт. Делитель делит и на микроконтроллер подается допустим 5 вольт. А как снятое значение с ножки увеличивается до 15? Да, я читал Ваши объяснения в уроке про биты и шаги измерения. Но так и не смог пристроить объяснения к своему вопросу. Хотя получается что надо снятое значение умножить на 3. Объясните, пожалуйста. Желательно с куском кода, обрабатывающем эту операцию)
На делитель напряжения подается 12-15в,с него снимается пропорциональное входному напряжение.
Допустим при номиналах 1кОм,470Ом, если на входе 12вольт, то на выходе примерно 3.85в.При подаче 15в, на выходе будет почти 5 вольт. А это уже напряжение которое можно подать на АЦП.
Думаю, все понятно?
Спасибо. Понятно. Но я ведь спросил о том, как эти самые 3.58в. внутри кристалла увеличиваются до 12в. для дальнейшего вывода на экран. Какой участок кода за это отвечает?
Просто умножайте коэффицент делителя напряжения на значение полученное АЦП.Если я Вас правильно понял.
Понятно. Спасибо. У меня еще вопрос. При симуляции в протеусе вместо U=2.50V показывает U=?V. Не могу понять в чем дело.
Он вообще любое напряжение не показывает
Ошибки в схеме или свойствах компонентов
Схему проверил. А вот по свойствам не знаю. Атмга — 8 МГц. Кристалл так же. Питание везде 5v. LCD не трогал. Переменный — LOG и 10 кОм. Единственное различие которое нашел, так это XTAL1 и XTAL2 у меня синие при симуляции, а у Вас красные.
Скриншот пожалуйста
Какая=то проблема в функции «sprintf(buffer,»U=%.2f V»,Voltage);»
Выводит на дисплей «U=? V» при любом раскладе
Здравствуйте,Сергей!
Необходимо настроить среду разработки. Как это сделать, смотрите в гугле по запросу «Atmel Studio sprintf float»
Добрый день! А если я вместо ATMEGA8 возьму ATMEGA16 в корпусе DIP-40, что нужно будет переписывать в коде?
Здравствуйте!
Сверьте регистры микроконтроллеров, и расположение выводов. Тогда все станет понятно)
Если нет, пишите.
for (int i = 0;i<length;i++)
в этом цикле выдает ошибку,в чем может быть проблема?
В AVR studio
Error 2 ‘for’ loop initial declarations are only allowed in C99 mode
Вот такая вот ошибка.
Или это можно как то включить?
Объявите переменную i до начала исполняемого кода прошивки.
То есть в самом начале функции или программы.
Добрый день, помогите пожалуйста воплотить такое же, но в алгоритм билдере) имеется пласа с атмега8 и дисплейчик
Здравствуйте! можно ли как-то, незначительно изменяя данную схему и программу, реализовать вольтметр для сетевого напряжения?
Здравствуйте, изменения будут внушительные, однако все возможно.
Аналоговые порты могут работать в режиме оратного ЦА преобразования?, т.е. я хочу получать на ножках изменяемое напряжение (0…5В) по моему запросу, скажем от энкодера управлять напряженим на одном из аналоговых портов?
Спасибо!
Здравствуйте!
Для полноценного ЦА преобразования необходим модуль ЦАП контроллера. Такого в составе AtMega8 нет.
Смотрите в сторону ШИМ (PWM, Широтно-Импульсная Модуляция)