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.ru
Как аналоговые пины A0 A1 A2. использовать как цифровые?
- Войдите на сайт для отправки комментариев
10 ответов [Последнее сообщение]
Чт, 10/04/2014 — 10:55
Зарегистрирован: 12.12.2013
подскажите хочу перенести внешние сигналы с цифровых d2 d3 на аналоговые a0 a1 как написать в программе?
int Enable_Sensor_1 = 2; // датчик 1 int Enable_Sensor_2 = 3; // датчик 2 void setup() < digitalWrite(Enable_Sensor_1, HIGH); // деактивация датчика 1 digitalWrite(Enable_Sensor_2, HIGH); // деактивация датчика 2 >
- Войдите на сайт для отправки комментариев
Чт, 10/04/2014 — 11:10
Зарегистрирован: 29.09.2011
Вы не поверите ;), но нужно просто сказать о своем желании не форуму, а ардуине 😉
int Enable_Sensor_1 = A0; // датчик 1
Только ваш код скоре всего не будет ни на аналоговых, ни на цифровых работать. Не знаю что вы хотели сделать, но сдели вот что: «включили подтягивающий резистор на пинах Enable_Sensor_1, и Enable_Sensor_2). Вернее может быть и будет работать, но «если повезет». «Плавающие глюки» — могут появится влюбой момент.
P.S. Кстати, ответ на свой вопрос, вы могли найти в документации http://arduino.ru/Reference/DigitalWrite . Там «чуть-чуть по другому выглядит», но работать будет так-же.
P.S.S. И зачем тип int использовался?
- Войдите на сайт для отправки комментариев
Чт, 10/04/2014 — 11:24
Зарегистрирован: 12.12.2013
я конечно подозревал что все так просто, но проверю попозже сейчас нет под рукой платы.
какой тип использовать ? boolean?
брал с примеров. да даже в примере по ссылке int стоит
int ledPin = 13; // Светодиод подключенный к вход/выходу 13
void setup() |
pinMode(ledPin, OUTPUT); // устанавливает режим работы — выход |
пропущены строки ? я их не указал специально.
pinMode(Enable_Sensor_1, OUTPUT); // устанавливает режим работы — выход
pinMode(Enable_Sensor_2, OUTPUT); // устанавливает режим работы — выход
ну насчет работы на цифровых все работает, а почему то недолжен?, да правильно включил высокий на датчиках на обоих это в setup, ниже я им по очереди LOW подключаю.
- Войдите на сайт для отправки комментариев
Чт, 10/04/2014 — 11:28
inspiritus
Зарегистрирован: 17.12.2012
А еще 14 , 15, 16 , 17, 18, 19.
не так — Вы не провели инициализацию пинов, как OUTPUT, командой pinMode, потому пины работают, как входы с подтяжкой к плюсу, через резистор 20 кОм.
- Войдите на сайт для отправки комментариев
Чт, 10/04/2014 — 11:35
Зарегистрирован: 12.12.2013
да это есть, просто не стал выкладывать ибо понятно, а вот со входами аналоговыми было не понятно.
- Войдите на сайт для отправки комментариев
Чт, 10/04/2014 — 11:49
Зарегистрирован: 29.09.2011
> а почему то недолжен?
Не, ну вы смешной. Ну представте что вы позвали соседа-автоэлектрика. Помочь поменять в фарах лампочки с обычных на ксенон. Так как «через час в дорогу. а светят очень плохо». В процессе он заглядывает под капот и восклицает «куда ты ехать собрался? У тебя же вместо тросика газа какая-то ниточка привязана. Далеко ты не уедешь». На что вы удивленно(!) смотрите на него: «Почему это не уеду? Запросто уеду. Я тросик в багажник специально спрятал. Да и со старыми лампами, пробовал газ нажимать — нормально движок обороты набирает».
> да правильно включил высокий на датчиках
Срочно прекращайте ожидать на форуме телепатов. Иначе либо игронить будут, либо троллить все кому не лень. По коду который вы ПОКАЗАЛИ, вы не включали высокий. Вы включали подтягивающий резистор. И что в итоге будет на пине, высокий или низкий — зависит от сопротивления нагрузки на пине и фазы луны.
Вообщем всегда перечитывайте свое сообщение глазами человека которые не видит вашей схемы, не знает что вы делаете, не смотрит вам через плечо, не знает что вы задумали. Представте себе что вы взяли себе советчика-помошника. Который парализован и сидит за ширмой. Может только слышать вас и посмотреть на бумажку которую вы ему принисете. К тому же — со склерозом (может через пол часа забыть что вы ему говорили раньше). Вот такому «консультанту» вы и должны объяснить «что у вас происходит и что вы хотите».
> какой тип использовать ? boolean?
А почему boolean? Какие возможные значения бывают у булеан? Что вам нужно сохранить туда? Номер пина. Каким может быть номер пина? Может быть дробным? насколько большим может быть? Может быть отрицательным? Возмите бумажку, ответте на все эти вопросы. Потом идете в раздел програмирование, читаете про ВСЕ типы данных. И подбираете какой «минимально подходит» (сможет не потерять значение и при этом не расходовать лишнюю память).
Вообщем вам нужно самому научится подбирать нужный тип данных под конкретную ситуацию, а не «потому что кто-то на форуме сказал» или «в примере так». И на форуме и авторы примеров — люди. Которые могут быть ошибаться, могут быть не аккуратными (а. и так сойдет), могут быть «вредителями».
- Войдите на сайт для отправки комментариев
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(). Поэтому единственная сложность возникает лишь в ненаглядности функций.
Аналоговые пины
В прошлом уроке мы разобрали измерение и вывод цифрового сигнала, а в этом разберём аналоговый сигнал. Зачем нужно читать аналоговый сигнал? Микроконтроллер может выступать в роли вольтметра, измерять собственное напряжение питания, например от аккумулятора, может измерять ток через шунт (если вы знаете закон Ома), можно измерять сопротивление, а также работать с потенциометрами (крутильными, линейными, джойстиками), которые являются очень удобными органами управления.
В уроке про возможности микроконтроллера мы обсуждали аналоговые входы, т.е. входы, подключенные к АЦП – аналогово-цифровому преобразователю (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 – 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 кОм. Чем больше, тем более шумным будет приходить сигнал, а если брать меньше – пойдут потери тока в нагрев потенциометра, а это никому не нужно.
Опорное напряжение (для 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])