Сколько аналоговых входов у ардуино atmega328
Перейти к содержимому

Сколько аналоговых входов у ардуино atmega328

  • автор:

Arduino.ru

Микроконтроллеры Atmega, используемые в Arduino, содержат шестиканальный аналого-цифровой преобразователь (АЦП). Разрешение преобразователя составляет 10 бит, что позволяет на выходе получать значения от 0 до 1023. Основным применением аналоговых входов большинства платформ Arduino является чтение аналоговых датчиком, но в тоже время они имеют функциональность вводов/выводов широкого применения (GPIO) (то же, что и цифровые порты ввода/вывода 0 — 13).

Таким образом, при необходимости применения дополнительных портов ввода/вывода имеется возможность сконфигурировать неиспользуемые аналоговые входы.

Цоколевка

Выводы Arduino, соответствующие аналоговым входам, имеют номера от 14 до 19. Это относится только к выводам Arduino, а не к физическим номерам выводов микроконтроллера Atmega. Аналоговые входы могут использоваться как цифровые выводы портов ввода/вывода. Например, код программы для установки вывода 0 аналогового входа на порт вывода со значением HIGH:

pinMode(14, OUTPUT);
digitalWrite(14, HIGH);

Подтягивающие резисторы

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

digitalWrite(14, HIGH); // включить резистор на выводе аналогового входа 0

пока вывод работает как порт ввода.

Подключение резистора повлияет на величину сообщаемую функцией analogRead() при использовании некоторых датчиков. Большинство пользователей использует подтягивающий резистор при применении вывода аналогового входа в его цифровом режиме.

Подробности и предостережения

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

Руководство на микроконтроллер Atmega не рекомендует производить быстрое переключение между аналоговыми входами для их чтения. Это может вызвать наложение сигналов и внести искажения в аналоговую систему. Однако после работы аналогового входа в цифровом режиме может потребоваться настроить паузу между чтением функцией analogRead() других входов.

Аналоговые входы в Arduino

Существует четыре вида микроконтроллеров Arduino, на которых используются аналоговые порты: Atmega 1280, Atmega 328 Atmega 8, Atmega 168. Их отличительной особенностью является наличие шестнадцатиканального аналогово-цифрового преобразователя с 10-битным разрешением, выдающим до 1024 значений для каждого входа.

Хотя их основное предназначение – получение сигналов от аналоговых датчиков, они могут использоваться для ввода/вывода данных широкого применения (GPIO). Таким образом, в случае необходимости можно организовать дополнительные вводы/выводы путем конфигурации неиспользуемых аналоговых портов.

Аналоговые входы в Arduino

Аналоговые входы Arduino имеют номера от 14 до 19 (не путать с физическими номерами выводов микроконтроллера). При необходимости аналоговые входы можно использовать в качестве цифровых. Для этого необходимо задать специальную команду.

Каждый аналоговый порт связан с подтягивающим резистором, имеющим тот же алгоритм действия, что и на цифровых вводах/выводах. Для его запуска используется команда digitalWrite(14, HIGH).

Применение некоторых датчиков и подключение резистора приводит к изменению величины, задающейся функцией analogRead(). Таким образом подтягивающий резистор включают только в случае использования аналогового порта в цифровом режиме.

Правила использования аналоговых контактов

При составлении программ для Arduino существуют некоторые правила. Если ранее выводы использовались как цифровые порты, то использовать команду analogRead некорректно.

Для корректной работы необходимо сконфигурировать входы как аналоговые. И наоборот, если аналоговый цифровой порт выступал со значением HIGH, его обратная установка приведёт к подключению подтягивающего резистора.

Несколько предостережений

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

Ещё одно правило: если вы используете аналоговый порт в качестве цифрового, в настройках необходимо задать функцию PinMode(), а порту присвоить номер соответствующего ему цифрового порта с 14 (для входа A0) по 19 (для входа A5).

Аналоговые пины

В прошлом уроке мы разобрали измерение и вывод цифрового сигнала, а в этом разберём аналоговый сигнал. Зачем нужно читать аналоговый сигнал? Микроконтроллер может выступать в роли вольтметра, измерять собственное напряжение питания, например от аккумулятора, может измерять ток через шунт (если вы знаете закон Ома), можно измерять сопротивление, а также работать с потенциометрами (крутильными, линейными, джойстиками), которые являются очень удобными органами управления.

В уроке про возможности микроконтроллера мы обсуждали аналоговые входы, т.е. входы, подключенные к АЦП – аналогово-цифровому преобразователю (ADC). Взглянем на распиновку популярных плат (Arduino Nano и Wemos Mini):

blank blank

Пины, на которых выведен ADC, могут измерять аналоговый сигнал. На плате Nano это пины, маркированные буквой А (A0A7), а у esp8266 такой пин всего один – A0.

Чтение сигнала

“Аналоговые” пины могут принимать напряжение от 0V (GND) до опорного напряжения и преобразовывать его в цифровое значение, просто в какие-то условные единицы. АЦП на AVR и esp8266 имеет разрядность в 10 бит, т.е. мы получаем измеренное напряжение в виде числа от 0 до 1023 .

Функция, которая оцифровывает напряжение, называется analogRead(pin) . Она принимает в качестве аргумента номер аналогового пина и возвращает оцифрованное напряжение. Сам пин должен быть сконфигурирован как INPUT (вход). Нумерация:

  • Arduino Nano:
    • Просто номером А-пина: A0 – 0
    • Как на плате: A0 – A0
    • Порядковым номером GPIO: А0 – 14 , A1 – 15 .. А7 – 21
    • Просто номером А-пина: A0 – 0
    • Как на плате: A0 – A0

    Пример, опрашивающий пин А0:

    int value1 = analogRead(0); // считать напряжение с пина A0 int value2 = analogRead(A0); // считать напряжение с пина A0 int value3 = analogRead(14); // считать напряжение с пина A0

    Хранить полученное значение разумно в переменной типа int , потому что значение варьируется от 0 до 1023.

    Нельзя подавать на аналоговый пин напряжение выше напряжения питания МК. Через ограничивающий резистор (~10k) – можно, но всё равно не рекомендуется этого допускать.

    Потенциометры

    Аналоговые пины очень часто используются при работе с потенциометрами (переменный резистор). При помощи полученного значения можно влиять на ход работы программы, менять какие-то настройки и тому подобное. У потенциометра всегда три ноги: две крайние и одна центральная. Всё вместе это представляет собой делитель напряжения, который и позволяет менять напряжение в диапазоне 0-VCC: К Arduino потенциометр подключается следующим образом: средний вывод на любой A-пин, крайние – на GND и питание. От порядка подключения GND и питания зависит направление изменения значения. Что касается сопротивления, то читай заметку по делителям напряжения ниже в этом уроке. Чаще всего для МК ставят потенциометры с сопротивлением 10 кОм, но диапазон в принципе очень широк: от 1 кОм до 100 кОм. Чем больше, тем более шумным будет приходить сигнал, а если брать меньше – пойдут потери тока в нагрев потенциометра, а это никому не нужно. blank

    Опорное напряжение (для AVR Arduino)

    Опорное напряжение играет главную роль в измерении аналогового сигнала, потому что именно от него зависит максимальное измеряемое напряжение и вообще возможность и точность перевода полученного значения 0-1023 в Вольты. Изучим функцию analogReference(mode) , где mode:

    • DEFAULT : опорное напряжение равно напряжению питания МК. Активно по умолчанию
    • INTERNAL : встроенный источник опорного на 1.1V (для ATmega168 или ATmega328P) и 2.56V (на ATmega8)
    • INTERNAL1V1 : встроенный источник опорного на 1.1V (только для Arduino Mega)
    • INTERNAL2V56 : встроенный источник опорного на 2.56V (только для Arduino Mega)
    • EXTERNAL : опорным будет считаться напряжение, поданное на пин AREF

    После изменения источника опорного напряжения (вызова analogReference() ) первые несколько измерений могут быть нестабильными. Значение 1023 функции analogRead() будет соответствовать выбранному опорному напряжению или напряжению выше его.

    В режиме DEFAULT мы можем оцифровать напряжение от 0 до напряжения питания VCC. Если напряжение питания 4.5 Вольта, и мы подаём 4.5 Вольт – получим оцифрованное значение 1023. Если подаём 5 Вольт – опять же получим 1023, т.к. выше опорного. Это правило работает и дальше, главное не превышать 5.5 Вольт. Как измерять более высокое напряжение, читайте ниже.

    Что касается точности: при питании от 5V и режиме DEFAULT мы получим точность измерения напряжения (5 / 1024) ~4.9 милливольт. Поставив INTERNAL мы можем измерять напряжение от 0V до 1.1V с точностью (1.1 / 1024) ~0.98 милливольт. Весьма неплохо, особенно если баловаться с делителем напряжения.

    Что касается внешнего источника опорного напряжения: нельзя подавать напряжение меньше 0V (отрицательное) или выше 5.5V в качестве внешнего опорного в пин AREF. Также при подключении внешнего опорного напряжения нужно вызвать analogReference(EXTERNAL) до первого вызова функции analogRead() (начиная с запуска программы), иначе можно повредить микроконтроллер!

    Чтобы “на лету” переключаться между внутренними и внешним опорными, можно подключить его на AREF через резистор на ~5 кОм. Вход AREF имеет собственное сопротивление в 32 кОм, поэтому реальное опорное будет вычисляться по формуле REF = V * 32 / (R + 32), где R – сопротивление резистора (кОм), через которое подключено опорное напряжение V (Вольт). Например для 2.5V получим 2.5 * 32 / (32 + 5) = ~2.2V реальное опорное.

    Измерение напряжения

    0-5 Вольт

    Простой пример, как измерить напряжение на аналоговом пине и перевести его в Вольты. Плата питается от 5V.

    float voltage = (float)(analogRead(0) * 5.0) / 1024;

    Таким образом переменная voltage получает значение в Вольтах, от 0 до 5. Чуть позже мы поговорим о более точных измерениях при помощи некоторых хаков. Почему мы делим на 1024, а не на 1023 , ведь максимальное значение измерения с АЦП составляет 1023? Ответ можно найти в даташите:
    АЦП при преобразовании отнимает один бит, т.е. 5.0 Вольт он в принципе может измерить только как 4.995, что и получится по формуле выше: 1023 * 5 / 1024 == 4.995.. . Таким образом делить нужно на 1024.

    Сильно больше 5 Вольт

    Для измерения постоянного напряжения больше 5 Вольт нужно использовать делитель напряжения на резисторах (Википедия). Схема подключения, при которой плата питается от 12V в пин Vin и может измерять напряжение источника (например, аккумулятора):
    Код для перевода значения с analogRead() в Вольты с учётом делителя напряжения:

    // GND -- [ R2 ] -- A0 -- [ R1 ] -- VIN #define VREF 5.1 // точное напряжение на пине 5V (в данном случае зависит от стабилизатора на плате Arduino) #define DIV_R1 10000 // точное значение 10 кОм резистора #define DIV_R2 4700 // точное значение 4.7 кОм резистора void setup() < float voltage = (float)analogRead(0) * VREF * ((DIV_R1 + DIV_R2) / DIV_R2) / 1024; >void loop() <>

    Как выбрать/рассчитать делитель напряжения?

    • Согласно даташиту на ATmega, сумма R1 + R2 не рекомендуется больше 10 кОм для достижения наибольшей точности измерения. В то же время через делитель на 10 кОм будет течь ощутимый ток, что критично для автономных устройств (читай ниже). Если девайс работает от сети или от аккумулятора, но МК не используется в режиме сна – ставим делитель 10 кОм и не задумываемся. Также рекомендуется поставить конденсатор между GND и аналоговым пином для уменьшения помех.
    • Если девайс работает от аккумулятора и микроконтроллер “спит”: пусть аккумулятор 12V, тогда через 10 кОм делитель пойдёт ток 1.2 мА. Сам микроконтроллер в режиме сна потребляет ~1 мкА, что в тысячу раз меньше! На самом деле можно взять делитель с гораздо бОльшим суммарным сопротивлением (но не больше 20 МОм, внутреннего сопротивления самого АЦП), но обязательно поставить конденсатор на ~0.1 мкФ между аналоговым пином и GND (вот здесь проводили эксперимент). Таким образом например при при R1+R2 = 10 МОм (не забыть про конденсатор) ток через делитель будет 1.2 мкА, что уже гораздо лучше!
    • Коэффициент делителя (не тот, который в Википедии) равен (R1 + R2) / R2 . Коэффициент должен быть таким, чтобы при делении на него измеряемого напряжения не получилось больше напряжения питания МК. У меня в примере (10 + 4.7) / 4.7 ~ 3.13 . Я хочу измерять литиевый аккумулятор с максимальным напряжением 12.8 Вольт. 12.8 / 3.13 ~ 4 Вольта – отлично. Например для измерения 36 Вольт я бы взял делитель с плечами 100к и 10к.
    • Можно воспользоваться онлайн-калькулятором.

    Сильно меньше 5 Вольт

    Для более точных измерений маленького напряжения можно подключить пин AREF к источнику низкого опорного напряжения (об этом было выше), чтобы “сузить” диапазон работы АЦП. Источник может быть как внешний, так и внутренний, например изменив опорное на внутреннее 1.1V ( analogReference(INTERNAL) ) можно измерять напряжение от 0 до 1.1 Вольта с точностью 1.1/1024 ~ 1.01 мВ.

    Видео

    Полезные страницы

    • Набор GyverKIT – большой стартовый набор Arduino моей разработки, продаётся в России
    • Каталог ссылок на дешёвые Ардуины, датчики, модули и прочие железки с AliExpress у проверенных продавцов
    • Подборка библиотек для Arduino, самых интересных и полезных, официальных и не очень
    • Полная документация по языку Ардуино, все встроенные функции и макросы, все доступные типы данных
    • Сборник полезных алгоритмов для написания скетчей: структура кода, таймеры, фильтры, парсинг данных
    • Видео уроки по программированию Arduino с канала “Заметки Ардуинщика” – одни из самых подробных в рунете
    • Поддержать автора за работу над уроками
    • Обратная связь – сообщить об ошибке в уроке или предложить дополнение по тексту ([email protected])

    Green Oak Studio

    Кому-то может не хватить тринадцати цифровых входов Arduino, и для таких людей сегодня я расскажу, как превратить шесть аналоговых входов в цифровые. Нам опять потребуется немного низкоуровневой магии, но пусть вас это не пугает.

    Пока АЦП не работает, доступны другие функции ног процессора, занятых под преобразование.

    Смотрим на картинку и видим, что аналоговые входы 0. 5 находятся на ногах 23. 28. Однако основная функция этих ног — PC0. 5.

    PC означает PORTC, или по-русски «Порт Цэ». Порт — это устройство ввода-вывода, где каждый бит может вводиться или выводиться отдельно, а может и в составе байта. У процессора ATmega328 три семибитных порта: B, C и D. Каждый пин порта управляется тремя битами в трех регистрах: DDxn, PORTxn, PINxn. Нам сейчас не интересны порты B и D, поэтому поглядим на регистры порта C.

    DDRC — регистр направления данных порта C

    7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit
    DDRC DDC6 DDC5 DDC4 DDC3 DDC2 DDC 1 DDC 0

    Каждый бит в этом регистре отвечает за направление данных на соответствующем пине:
    Значение по умолчанию — 0.
    PORTC — регистр данных порта C

    7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit
    PORTC PORTC6 PORTC5 PORTC4 PORTC3 PORTC2 PORTC1 PORTC0

    Если пин сконфигурирован как вход, то при записи 1 в соответствующий бит этого регистра активируется подтягивающий резистор. При записи 0 подтягивающий резистор отключается.

    Если пин сконфигурирован как выход, то при записи 1 в соответствующий бит этого регистра пин подтягивается к питанию. При записи 0 пин притягивается к земле.

    Значение по умолчанию — 0.

    PINC — Адреса входов порта C

    7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit
    PINC PINC6 PINC 5 PINC 4 PINC 3 PINC 2 PINC 1 PINC 0

    Каждый бит этого регистра содержит значение соответствующего пина. Прочитать значение можно независимо от значения DDxn.

    Значение по умолчанию — не определено.
    Blink, 1 способ

    Напишем всем известную программу Blink для аналогового входа (гы-гы). Подключим светодиодик к аналоговому входу 0:

    Для начала назначим бывший аналоговый вход A0 как цифровой выход. Для этого запишем единицу в бит DDC0 регистра DDRC:

    void setup () <
    DDRC = (1 >

    Чтобы включать и выключать светодиод, будем записывать то 1, то 0 в бит PORTC0 регистра PORTC:

    void loop () <
    PORTC = PORTC | (1 delay (1000);
    PORTC = PORTC ^ (1 delay (1000);
    >

    Blink, способ 2

    Есть более читерский способ, который сокращает программу еще на 2 строчки. В гайде ATmega328 пишут, что запись единицы в PINxn переключает значение PORTxn, независимо от значения DDRxn. Ну то есть если там был 0, то становится 1, а если был 1, то становится 0. За сим накалякаем следующий код:

    void setup () <
    DDRC = (1 >

    void loop () <
    PINC = PINC | (1 delay (1000);
    >

    Вот и весь код. Работает прямо так же, как на предыдущем видео. Можно было бы еще избавиться от строчки с DDRC=(1 <Более продвинутая установка

    Чтобы было весело, займем все 6 аналоговых входов. На 0-4 повесим светодиоды, а на 5 — кнопку:

    Теперь назначим пины PC0. 4 как выходы, а PC5 как вход. Помня, что выход — это 1 в DDCn, а вход — это 0, можно написать строчку, которая все сделает, так:

    DDRC = B00011111;

    Для того, чтобы сделать что-то по нажатию кнопки, надо сдетектировать это самое нажатие. Ждем единицу на PINC5, и по ней запускаем бегущие огни — записываем по очереди единицы в PORTCn, а потом записываем нули точно так же. Полный код:

    void setup () <

    Программа в основном цикле содержит сколько же строк, сколько было бы при использовании стандартных цифровых выходов и конструкций digitalWrite(). Поэтому единственная сложность возникает лишь в ненаглядности функций.

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

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