Урок 22
Часть 3
Изучаем АЦП
Продолжаем изучать АЦП. В прошлой части нашего занятия написали код полностью всех функций и даже посмотрели и оценили работу АЦП на практике.
Только происходило это ручным способом и мы раз в 500 милисекунд постоянно вручную вызывали процесс преобразования, ждали результат, что заметно тратит ресурсы нашего контроллера.
Для того. чтобы этого избежать, существует другой режим работы модуля АЦП в контроллерах AVR — это режим прерываний. Также есть ещё режим не ручного вызова преобразования, а циклического.
Вот такой режим мы сегодня и попробуем реализовать с помощью нашего кода.
Проект мы создадим новый с именем MyADCISRLCD, а код в главный модуль весь скопируем с предыдущего проекта, также все файлы включим те же. Это делается для того, чтобы у нас был отдельный проект для прерываний и отдельный для ручного режима.
Удалим полностью функцию запуска конвертирования из файла adc.c, а также из хедера её прототип за её дальнейшей ненадобностью ибо данный процесс у нас будет автоматизирован.
Немного перенастроим регистры в функции инициализации
void ADC_Init(void)
{
ADCSRA |= (1<<ADEN) // Разрешение использования АЦП
|(1<<ADSC)//Запуск преобразования
|(1<<ADFR)//Непрерывный режим работы АЦП
|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)//Делитель 128 = 64 кГц
|(1<<ADIE);//Разрешение прерывания от АЦП
ADMUX |= (1<<REFS1)|(1<<REFS0); //Внутренний Источник ОН 2,56в, вход ADC0
}
Вообщем, тут, я думаю также всё понятно, так как в 1 части нашего занятия мы все регистры и их биты изучили. Также не забываем что весь код, кроме последней строки — это одна единая большая строка кода.
Над данной функцией добавим функцию-обработчик прерывания от АЦП
//—————————————-
ISR(ADC_vect)
{
}
//—————————————-
Для этого у контроллера существует вот такой вектор
Здесь мы видим, что данное прерывание вызывается в момент окончания аналого-цифрового преобразования.
Также в файле adc.c нам потребуются две глобальне переменные для временного хранения старшей и младшей части регистровой пары adc
#include «adc.h»
//—————————————-
char high_adc=0,low_adc=0;
Заполним данные переменные значениями из регистра в обработчике прерывания
ISR(ADC_vect)
{
low_adc = ADCL;
high_adc = ADCH;//Верхняя часть регистра ADC должна быть считана последней, иначе не продолжится преобразование
Здесь есть хитрость. Читать нужно сначала младшую часть, так как следующее преобразование автоматически начинается именно в случае такого порядка считывания регистров.
Из функции main() удалим объявление переменной adc_value, но инициализацию оставим
float n;
adc_value=0;
и запишем её в main.h, так как данная переменная у нас теперь будет глобальная и она должна быть видна в других модулях
#include <stdlib.h>
unsigned int adc_value;
Сегодня мы с вами впервые попытаемся использовать глобальные переменные, объявленные в одном модуле проекта, в других модуля.
Тут физика такая. Если мы просто попытаемся в другом модуле обратится к такой переменной, то она всё равно будет не видна. Её необходимо в модуль подключить, для этого существует специальный оператор extern. Вот с помощью него мы и подключим нашу переменную в файл adc.h
#include «adc.h»
//—————————————-
extern unsigned int adc_value;
//—————————————-
char high_adc=0,low_adc=0;
Теперь данная переменная будет видна везде и изменения в ней тоже.
Занесём в неё считанные значения из двух переменных в обработчике прерывания от АЦП
high_adc = ADCH;//Верхняя часть регистра ADC должна быть считана последней, иначе не продолжится преобразование
adc_value=high_adc*256+low_adc;
Можно здесь было также использовать и битовые сдвиги, но мы пока поступим просто.
В функции main() удалим вызов преобразоания, так как прерывание нам вызывать не придется, оно происходит автоматически, а можно и закомментировать
while(1)
{
//adc_value = ADC_convert(); //Вызовем преобразование
Также нужно не забыть нам включить ещё прерывания глобальные после инициализации АЦП
ADC_Init(); //Инициализируем АЦП
sei();
clearlcd(); //Очистим дисплей
Соберём код, прошьём контроллер и попробуем работу нашего кода
Вот такой вот упрощённый механизм использования АЦП по прерываниям.
Предыдущая часть Программирование МК AVR Следующий урок
Программатор и дисплей можно приобрести здесь:
Программатор USBASP USBISP с адаптером USBASP USBISP 3.3 с адаптером
Смотреть ВИДЕОУРОК (нажмите на картинку)
Здравствуйте.
Спасибо за материалы и огромное спасибо за их подачу. Учусь многому из цикла статей и видео.
П.С.:Опечатка?
/*
Тут физика такая. Если мы просто попытаемся в другом модуле обратится к такой переменной, то она всё равно будет не видна. Её необходимо в модуль подключить, для этого существует специальный оператор extern. Вот с помощью него мы и подключим нашу переменную в файл adc.h
*/
Изменить в конце adc.h на adc.c ?
Возможно ответ запоздал. Но только недавно нашел этот сайт.
Можно прописать extern и в adc.c, но глобальные переменные обычно прописываются в хедер файлах.
Только открыл в протеусе, показует тока до 0200 0.5 ((( что не так? Атмель 7
Прочитал коммент из 2й части подал питание на AVCC всё заработало
Как настроить сразу два АЦП ? Мне нужно сделать два вольтметра на одной атмеге.
Спасибо за статью. Изложено все доступно и понятно.
Здравствуйте, спасибо за статью! Расчёт adc_value был не корректный, пока не поменял тип high_adc, b low_adc на int.
Добрый день, спасибо за статью. Изучаю не так давно AVR, возник вопрос как можно реализовать измерение сопротивления средствами AVR и возможно доп. компанентами?
Если есть можете показать несложную схему с примером на atmega8 или натолкнуть на нужную статью, за ранее спасибо!
Здравствуйте!
Эта статья, мне помогла реализовать мой проект. За что Вам огромное СПАСИБО!!!
Не мог бы кто-нибудь из знающих объяснить, как сменить канал АЦП, на многих форумах пишут что через прерывание проще, но я никак не могу понять. Если можно, кусочек этого кода. Заранее спасибо