Как использовать аналогово-цифровой преобразователь (АЦП) в микроконтроллере AVR ATmega16


Часто во многих конструкциях, использующих микроконтроллеры AVR, используется аналогово-цифровой преобразователь (АЦП) данных микроконтроллеров. Он используется везде где необходимо преобразовать какое-нибудь аналоговое значение в цифровое. Обычно это конструкции с датчиками температуры, датчиками наклона, датчиками тока, гибкими датчиками и т.п.

Как использовать АЦП в микроконтроллере AVR ATmega16: внешний вид конструкции

На нашем сайте мы уже рассматривали использование АЦП в микроконтроллерах AVR в следующих проектах:

Но в данной статье мы более подробно рассмотрим использование АЦП в микроконтроллере AVR ATmega16. В этом проекте мы будем подсоединять ко входу АЦП микроконтроллера небольшой потенциометр и будем использовать 8 светодиодов чтобы показывать изменение напряжения на выходе АЦП в зависимости от изменения сигнала на его входе.

Что такое АЦП (аналого-цифровой преобразователь)

В электронике под АЦП (в переводе с англ. от ADC - analog-to-digital converter) понимают устройство которое конвертирует аналоговый сигнал (например, ток или напряжение) в цифровой код (двоичную форму). В реальном мире большинство сигналов являются аналоговыми, но все микроконтроллеры и микропроцессоры способны понимать только двоичные (бинарные) сигналы – 0 или 1. То есть чтобы заставить микроконтроллер понимать аналоговые сигналы необходимо конвертировать их в цифровую форму – это и делает АЦП. Существуют различные типы АЦП, каждый тип удобен для конкретных приложений. Наиболее популярные типы АЦП используют такие типы аппроксимаций как приближенная, последовательная и дельта-аппроксимацию. 

Самые дешевые АЦП – с последовательной аппроксимацией, их мы и будем рассматривать в данной статье. В данном случае для каждого фиксированного аналогового уровня последовательно формируется серия соответствующих им цифровых кодов. Внутренний счетчик используется для их сравнения с аналоговым сигналом после конверсии. Генерация цифровых кодов останавливается когда соответствующий им аналоговый уровень становится чуть-чуть больше чем аналоговый сигнал на входе АЦП. Этот цифровой код и будет представлять собой конвертированное значение аналогового сигнала.

Мы в данной статье будем использовать встроенный в микроконтроллер AVR ATmega16 аналого-цифровой преобразователь – практически все микроконтроллеры семейства AVR оснащаются встроенным АЦП. Но вместе с тем следует помнить о том, что существуют и другие типы микроконтроллеров, у которых нет собственных АЦП – в этом случае необходимо использовать внешний АЦП. Как правило, внешние АЦП сейчас выпускаются в виде одной микросхемы.

АЦП в микроконтроллере AVR ATmega16

Микроконтроллер ATmega16 имеет встроенный 10-битный 8-канальный АЦП. Разрядность 10 бит означает, что каждый входной аналоговый сигнал (для ATmega16 он должен быть в диапазоне 0-5В) представляется 1024 уровнями дискретного сигнала (2 в степени 10 = 1024), то есть дискретизируется с точностью Uвх/1024. 8-канальный означает что АЦП может быть задействован на 8 контактах микроконтроллера одновременно. Фактически весь PortA (GPIO33-GPIO40) может быть использован для операций АЦП. По умолчанию выводы PORTA являются контактами ввода/вывода общего назначения. Чтобы задействовать на них функции АЦП необходимо сконфигурировать специальные регистры, ответственные за функции аналого-цифрового преобразования в микроконтроллере. Поэтому их и называют регистрами АЦП. В данной статье мы разберем как правильно их конфигурировать.

На следующем рисунке показано расположение контактов АЦП на корпусе микроконтроллера ATmega16

Расположение контактов АЦП в микроконтроллере ATmega16

Необходимые компоненты

  1. Микроконтроллер ATmega16 (купить на AliExpress).
  2. Источник питания с напряжением 5 Вольт.
  3. Программатор AVR-ISP (купить на AliExpress), USBASP (купить на AliExpress) или другой подобный.
  4. Кварцевый генератор 16 МГц (купить на AliExpress).
  5. Конденсатор 100 нФ (2 шт.) (купить на AliExpress).
  6. Конденсатор 22 пФ (2 шт.) (купить на AliExpress).
  7. Кнопка.
  8. Соединительные провода.
  9. Макетная плата.
  10. Светодиоды (любого цвета) (купить на AliExpress).

Работа схемы

Схема устройства приведена на следующем рисунке.

Схема устройства для проверки АЦП в микроконтроллере ATmega16

Внешний вид макетной платы с собранной на ней схемой устройства будет выглядеть следующим образом.

Собранное устройство на макетной плате

Установка регистров АЦП в микроконтроллере ATmega16

Регистр ADMUX (регистр выбора и мультиплексирования канала АЦП) - предназначен для выбора канала АЦП и опорного напряжения (reference voltage). Структура данного регистра представлена на следующем рисунке.

Структура регистра ADMUX в микроконтроллере AVR ATmega16

Биты 0-4 используются для выбора канала.

MUX4 MUX3 MUX2 MUX1 MUX0

ADC Channel Selected

0 0 0 0 0 ADC0
0 0 0 0 1 ADC1
0 0 0 1 0 ADC2
0 0 0 1 1 ADC3
0 0 1 0 0 ADC4
0 0 1 0 1 ADC5
0 0 1 1 0 ADC6
0 0 1 1 1 ADC7

Бит 5 используется для коррекции результата преобразования вправо или влево.

ADLAR Description
0 Right adjust the result
1 Left adjust the result

Биты 6-7 используются для выбора опорного напряжения АЦП.

REFS1 REFS0 Voltage Reference Selection
0 0 AREF, Internal Vref turned off
0 1 AREF, Internal Vref turned off
1 0 Reserved
1 1 Internal 2.56 Voltage Reference with external capacitor at AREF Pin

Теперь попробуем правильно сконфигурировать данный регистр в программе.

Исходный код программы на языке С (Си) с пояснениями

Полный текст программы приведен ниже. В этом разделе статьи объяснено значение некоторых элементов программы.

Для начала определим функцию для чтения конвертированного в результате АЦП значения. В качестве аргумента в данной функции будет выступать номер канала, на котором необходимо будет производить АЦП.

Поскольку у нас всего 8 каналов АЦП, то номер канала может принимать значение от 0 до 7.

Записав число ‘40’ (в двоичном виде ‘01000000’) в регистр ADMUX мы выберем канал ADC0 для аналого-цифрового преобразования.

На этом шаге мы начнем процесс аналого-цифрового преобразования, записав "1" в бит ADSC регистра ADCSRA. После этого необходимо подождать до тех пор пока бит ADIF не сигнализирует нам о том что процесс преобразования завершен. Мы закончим процесс АЦП записав ‘1’ в бит ADIF регистра ADCSRA. Когда преобразование закончено, возвратим значение АЦП.

Выберем опорное напряжение АЦП установив бит REFS0. После этого разрешим АЦП и установим коэффициент деления предделителя равным 128.

Теперь сохраним значение АЦП и передадим его на PORTC. В результате этого 8 светодиодов, подключенных к PORTC, покажут получившееся в результате аналого-цифрового преобразования значение в 8-битном формате. В рассматриваемом примере мы показываем изменение напряжения от 0 до 5В, которое регулируется на входе АЦП микроконтроллера с помощью потенциометра.

Как использовать АЦП в микроконтроллере AVR ATmega16: внешний вид конструкции

Величину подаваемого напряжения можно контролировать с помощью вольтметра. В представленном примере мы используем для этого цифровой мультиметр.

Полный текст программы

Видео, демонстрирующее работу схемы

(2 голосов, оценка: 5,00 из 5)
Загрузка...
13 891 просмотров

Комментарии

Как использовать аналогово-цифровой преобразователь (АЦП) в микроконтроллере AVR ATmega16 — 27 комментариев

  1. Написал код для атмеги 128. Дисплей нокиа 5510. но не понимаю куда воткнуть цикл для перебора каналов, помогите пожалуйста.
    вот часть кода, прошу подсказать как правильно перебор каналов сделать?
    int readAdc(int channel)
    {
    int LowBitValue;
    ADMUX = (1<<REFS0) | channel;
    ADCSRA |= (1<<ADSC);
    while((ADCSRA &(1<<ADIF))==0);
    //while (ADCSRA & (1 << ADSC));
    ADCSRA |= (1<<ADIF);
    LowBitValue = ADCL;
    return((ADCH<<8) | LowBitValue);
    }

    int main(void)
    {
    adc_start();
    displey_start();
    sei();

    while (1)
    {
    Lcd_clear();
    Lcd_printf(0,0,FONT_1X,(((double)readAdc(0)*5)/1023)*4,2); //Вывод с нулевого канала
    Lcd_printf(0,1,FONT_1X,(((double)readAdc(1)*5)/1023)*4,2); // вывод с первого канала
    Lcd_printf(0,2,FONT_1X,(((double)readAdc(2)*5)/1023)*4,2); // вывод со второго канала
    Lcd_update();
    }
    }

    • Перед Lcd_clear(). А в Lcd_printf подставляйте переменную-итератор цикла чтобы произвести вывод с итерируемого канала

      • Я использую дисплей от нокиа 5510. И хочу что бы все семь каналов отображались в разных строках.
        Можете в моем коде дописать часть? я просто только осваиваю МК и плохо понимаю принципы(

      • Все получилось) но есть другой вопрос) как показать в милливольтах значения?)

  2. Попробовал использовать фрагмент приведённой программы для последовательного опроса двух каналов АЦП. Возникли проблемы - неправильно определяется факт завершения преобразования (строки 8 и 9). Как оказалось, такая конструкция применима для режима постоянного преобразования АЦП (Free Running Mode). В режиме запуска одиночного преобразования, что имеет место в нашем случае, флаг прерывания ADIF не используется, и для определения факта завершения преобразования нужно анализировать состояние бита ADSC регистра ADCSRA. Данный бит остается в высоком состоянии в процессе преобразования и сбрасывается по завершении преобразования. У меня программа нормально заработала, когда я заменил текст строки 8 на:
    while (ADCSRA & (1 << ADSC)); //Ожидание завершения преобразования АЦП
    а строку 9 удалил.

    • Ну вы молодец, разобрались в этом уже лучше чем я. Просто после написания более двухсот статей по Ардуино начинаешь уже немного забывать некоторые тонкости работы с AVR

      • Здравствуйте, while (ADCSRA & (1 << ADSC)); эта конструкция не позволяет пройти программе прежде выполнения условия (завершения конвертации АЦП), то есть, вернуть значение функии return;? Верно?

  3. Спасибо! Очень полезный материал. Только я так понимаю, что в строке 6 значение chnl нужно добавить к значению ADMUX=0x40, иначе всегда будет опрашиваться только канал 0:
    ADMUX = (1 << REFS0) | chnl;

    • Да, именно так. Только нужно установить то значение chnl, которое вам нужно. Просто в нашей схеме мы считываем значение только с канала 0, поэтому chnl и не используем

      • Нет, у вас входной параметр chnl функции ADC_read нигде не используется. Поэтому его и нужно использовать, добавив к значению ADMUX.

        • Это тоже верно, входной параметр chnl функции ADC_read сейчас не используется. Возможно, я что то хотел с этим сделать, но потом забыл. Ваше предложение конструктивное, не спорю. Но почему то я так не сделал в свое время

    • Нет, конечно. Но в строке "ADMUX = 0x40;" число 40 не в десятичном виде, а шестнадцатеричном, при его переводе в двоичный код как раз и получается 01000000

  4. Программу Вы написали, а как ею прошить контроллер, что бы посмотреть эффект.

  5. Никак не могу понять этот ADIF. При преобразовании ADIF устанавливается в 1 и чтобы сбросить его надо тоже записать 1 ?? Так чтоли ? Как-то странно это, по идеи сбрасывать 0 же нужно если в нём уже 1

    • Алексей, вот что написано про этот бит в сети:
      Bit 4 -ADIF: ADC Interrupt Flag - Флаг прерывания ADC
      Данный бит устанавливается в состояние 1 по завершению преобразования и обновления регистров данных. Прерывание по завершению преобразования ADC выполняется если в состояние 1 установлены бит ADIE и I-бит регистра SREG. Бит ADIF сбрасывается аппаратно при выполнении подпрограммы обработки соответствующего вектора прерывания. Кроме того, бит ADIF может быть очищен записью во флаг логической 1.
      Насколько я понимаю, ошибки в программе нет.

  6. В коде ошибка:
    PORTC = 0xFF; //конфигурируем PORTC на выход поскольку к нему подключены светодиоды

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *