Аналоговые пины
В прошлом уроке мы разобрали измерение и вывод цифрового сигнала, а в этом разберём аналоговый сигнал. Зачем нужно читать аналоговый сигнал? Микроконтроллер может выступать в роли вольтметра, измерять собственное напряжение питания, например от аккумулятора, может измерять ток через шунт (если вы знаете закон Ома), можно измерять сопротивление, а также работать с потенциометрами (крутильными, линейными, джойстиками), которые являются очень удобными органами управления.
В уроке про возможности микроконтроллера мы обсуждали аналоговые входы, т.е. входы, подключенные к АЦП - аналогово-цифровому преобразователю (ADC). Взглянем на распиновку популярных плат (Arduino Nano и Wemos Mini):
Пины, на которых выведен ADC, могут измерять аналоговый сигнал. На плате Nano это пины, маркированные буквой А (A0-A7), а у 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 -
- Wemos Mini
- Просто номером А-пина: A0 -
0
- Как на плате: A0 -
A0
- Просто номером А-пина: A0 -
Пример, опрашивающий пин А0:
int value1 = analogRead(0); // считать напряжение с пина A0 int value2 = analogRead(A0); // считать напряжение с пина A0 int value3 = analogRead(14); // считать напряжение с пина A0
Хранить полученное значение разумно в переменной типа int
, потому что значение варьируется от 0 до 1023.
Потенциометры
Аналоговые пины очень часто используются при работе с потенциометрами (переменный резистор). При помощи полученного значения можно влиять на ход работы программы, менять какие-то настройки и тому подобное. У потенциометра всегда три ноги: две крайние и одна центральная. Всё вместе это представляет собой делитель напряжения, который и позволяет менять напряжение в диапазоне 0-VCC: К Arduino потенциометр подключается следующим образом: средний вывод на любой A-пин, крайние - на GND и питание. От порядка подключения GND и питания зависит направление изменения значения. Что касается сопротивления, то читай заметку по делителям напряжения ниже в этом уроке. Чаще всего для МК ставят потенциометры с сопротивлением 10 кОм, но диапазон в принципе очень широк: от 1 кОм до 100 кОм. Чем больше, тем более шумным будет приходить сигнал, а если брать меньше - пойдут потери тока в нагрев потенциометра, а это никому не нужно.
Опорное напряжение (для 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])