Всех рад снова приветствовать на нашем сайте! В нескольких из предыдущих уроков мы досконально изучили варианты работы с цифровыми пинами (все статьи можно найти в соответствующем разделе), так что самое время перейти к аналоговым входам Arduino. Таким образом, сегодняшняя статья будет целиком и полностью посвящена именно им. Начнем с теории, затем плавно перейдем к практике и к реальным примерам скетчей, в которых, в том числе, рассмотрим, как подключить потенциометр к Ардуино, так что приступаем.
Теоретическая часть.
Введение.
Для начала освежим в памяти ключевой нюанс использования цифровых пинов. Он прост и незатейлив - уровень сигнала на цифровом выводе может принимать два разных значения - высокий уровень (5 В), либо низкий (0 В). Опять же, я в рамках курса по Arduino использую Arduino Uno, микроконтроллер (ATmega328) на которой питается от 5 В. В случае другой платы и другого контроллера, например Arduino Due (SAM3X8E), которая использует 3.3 В, цифровые пины, а также все остальные модули, оперируют уже с этими значениями (а не с 5 В, как все та же Arduino Uno). Я же дальше буду все описывать для Uno ввиду ее распространенности и популярности, так что не забывайте об этих деталях, если у вас другая плата.
На этом лирическое отступление закончено, двигаемся дальше. Аналоговые входы, в отличие от цифровых, могут работать с аналоговыми же сигналами, величина которых может принимать любое значение от 0 до 5 В. Давайте разбавим текст небольшой наглядной иллюстрацией:
Для цифрового входа у нас было два варианта:
Состояние входа | Напряжение |
---|---|
Логический 0 | от -0.5 В до 1.5 В (0.3 * Vcc) |
Логическая 1 | от 3.0 В (0.6 * Vcc) до 5.5 В (Vcc + 0.5 В) |
Плюс промежуточная между этими диапазонами зона, в которой сигнал не может быть гарантированно определен. Аналоговый же вход подключен к модулю аналого-цифрового преобразователя (АЦП, он же ADC) микроконтроллера, что дарует нам возможность измерить значение сигнала. В практической части мы разберем конкретные примеры, так что все окончательно встанет на свои места, пока же продолжаем с теорией.
Опорное напряжение АЦП Arduino.
Микроконтроллер может измерить напряжение от 0 Вольт до значения опорного напряжения, для которого возможен целый ряд вариантов:
- DEFAULT - дефолтный вариант, опорное напряжение равно напряжению питания микроконтроллера
- INTERNAL - внутренний источник напряжения, 1.1 В - для ATmega168 и ATmega328P, 2.56 В - для ATmega32U4 и ATmega8 (не доступно для Arduino Mega)
- INTERNAL2V56 - внутренний источник на 2.56 В (только для Arduino Mega)
- INTERNAL1V1 - внутренний источник на 1.1 В (только для Arduino Mega)
- EXTERNAL - внешнее опорное напряжение, поданное на вход AREF (от 0 до 5 В)
По умолчанию в качестве опорного используется напряжение питания, то есть все те же 5 Вольт для Arduino Uno и большинства плат из линейки Ардуино. Далее следует ряд внутренних источников опорного напряжения, доступных для той или иной платы. Они отличаются непосредственно значением напряжения.
Тут может возникнуть логичный вопрос - зачем использовать, к примеру, источник на 1.1 В, который позволит измерить аналоговое напряжение от 0 до 1.1 В, если можно просто использовать напряжение питания и измерять от 0 до 5 В. То есть в чем смысл ограничивать себе же допустимый диапазон? Этот нюанс мы разберем чуть позже, буквально в следующем разделе.
А список вариантов завершает внешний источник опорного напряжения, который подключается к выводу AREF платы, для Arduino Uno:
Сами же аналоговые входы Arduino промаркированы буквой A, Uno на своем борту имеет 6 таких входов, соответственно, вот они все здесь (A0 - A5):
С этим разобрались, на очереди следующий пункт.
Разрядность АЦП и расчет напряжений.
Модуль АЦП в микроконтроллерах AVR является 10-ти битным (для большинства Arduino-совместимых плат, для некоторых же есть возможность использовать разрешение в 12 бит), то есть результат измерения может принимать значения от 0 до 1023. Число 1023 в двоичной системе представляет из себя следующий набор из единиц: 1111111111. Как видите, здесь их ровно 10 штук, то есть 10 битов, поэтому 1023 и является максимально возможным вариантом в данном случае. Вот некоторые из возможных результатов с АЦП и соответствующие им напряжения в Вольтах:
Измеренное значение | Напряжение |
---|---|
0 | 0 В |
205 | 1 В |
614 | 3 В |
1023 | 5 В |
Резюмируя, отметим следующий факт (здесь подразумеваем, что в качестве опорного напряжения используется напряжение питания): мы можем измерить напряжение от 0 до 5 В, при этом данному диапазону соответствуют числа от 0 до 1023, то есть 1024 разных значения. Таким образом, из полученного с АЦП 10-битного значения можно рассчитать величину напряжения на аналоговом входе в Вольтах:
U = \frac{ ADC_{res} \cdot 5 \medspace В}{1024}
Здесь ADC_{res} - результат аналого-цифрового преобразования, U - искомое напряжение в Вольтах.
Вот так все просто и незатейливо, но в то же время четко и логично 👍 И на этой мажорной ноте мы возвращаемся к вопросу о значении опорного напряжения, поднятому в предыдущем разделе. Зачем уменьшать допустимый диапазон измерений, уменьшая опорное напряжение? Смотрите, сейчас все разберем в деталях.
Итак, пусть опорное напряжение по-прежнему равно 5 В. Величина, полученная в ходе измерений, может принимать 1024 разных значения (0, 1, 2 ... 1023). А это значит, что точность измерения составит:
\frac {5 \medspace В}{1024} = 0.0049 \medspace В
Действительно, если мы получили значение 120, то оно соответствует напряжению:
U = \frac{ 120 \cdot 5 \medspace В}{1024} = 0.5859 \medspace В
Соседние же значения дают:
- значение 119 - напряжение 0.5810 В
- значение 121 - напряжение 0.5908 В
То есть мы не можем точно измерить, к примеру, значение 0.5875 В, поскольку шаг между соседними значениями у нас дискретный и величина его составляет уже рассчитанные 0.0049 В.
А теперь используем в качестве опорного напряжения внутренний источник на 1.1 В, тогда точность уже будет иной:
\frac {1.1 \medspace В}{1024} = 0.0011 \medspace В
Те же самые 1024 возможных значения теперь соответствуют диапазону от 0 до 1.1 В, что автоматически приводит к повышению точности. Вот в этом то и кроется смысл использования разных источников опорного напряжения. Если мы знаем, что нам нужно измерять сигналы из интервала (0 В; 1 В), то есть смысл использовать меньшее значение опорного, во имя повышения точности результата. Все снова логично до безобразия, так что перемещаемся к следующему разделу.
Делитель напряжения.
Здесь мы разберем потенциальную и вполне себе реальную ситуацию - пусть нам требуется измерить напряжение, которое заведомо может превышать опорное. Физически на аналоговый вход не рекомендуется, более того, воспрещается, подавать сигнал более 5.5 В (для Arduino Uno). Это просто-напросто может вывести микроконтроллер из строя.
Поэтому для того, чтобы измерить напряжение, допустим, от 0 до 12 В, можно использовать простейший делитель напряжения, представляющий из себя два резистора. Подключение производится следующим образом:
Про делитель напряжения в целом написано около миллиарда статей, поэтому дублировать не буду, а сейчас рассмотрим конкретный пример расчета. Приведу финальную формулу для напряжения в точке 1:
U_1 = \frac{U_2 \cdot R_1}{R_1 + R_2}
По условию нашей выдуманной задачи измеряемый сигнал (U_2 \medspace) может принимать максимальное значение равное 12 В. В качестве опорного напряжения - 5 В, то есть максимальное напряжение на аналоговом входе (U_1 \medspace) составляет столько же, 5 В. Кстати, технически мы можем подать сигнал от 5 до 5.5 В, что допустимо для данного контроллера. Но практической ценности в этом минимум - любое значение, превышающее опорное напряжение, даст результат равный 1023.
Возвращаемся к примеру: из всего вышесказанного следует простой факт, что 12 Вольтам в точке 2 должны соответствовать 5 Вольт в точке 1. Исходя из этих простых сведений производим расчет делителя напряжения, то есть номиналов резисторов. Ток, потребляемый аналоговым входом Ардуино очень мал, поэтому его значением пренебрегаем. В итоге получаем, что по цепи точка 2 - точка 1 - GND течет один и тот же ток:
И рассчитать его можно следующим образом (в точке GND напряжение равно 0 В):
I = \frac{U_1 - 0}{R_1} = \frac{U_2 - U_1}{R_2}
При этом мы уже знаем величины напряжений, они попросту являются входным данными в нашей задаче:
U_2 = 12 \medspace В \\ U_1 = 5 \medspace В
Подставив значения в предыдущую формулу получим соотношение:
\frac{R_2}{R_1} = \frac{U_2 - U1}{U_1} = \frac{7}{5} = 1.4
Таким образом, мы теперь знаем, каким должно быть соотношение сопротивлений резисторов. Абсолютные же величины выбираются исходя из требований конкретной задачи, мы не будем в это углубляться в этой статье. В двух словах, если разрабатывается, например, устройство, критичное к потребляемой мощности, то стоит постараться уменьшить ток через делитель и взять сопротивления побольше, поскольку по закону Ома, как вы помните, ток обратно пропорционален сопротивлению:
I = \frac{U}{R}
В нашем же случае ограничим себя "средними" величинами от 0 до 10-20 КОм. То есть сумма R_1 + R_2 пусть будет в районе этих самых 10-20 КОм. Остается подобрать величины исходя из ряда номиналов, чтобы их отношение было максимально близко к 1.4. Выбираем:
R_2 = 5.1 \medspace КОм \\ R_1 = 3.6 \medspace КОм
Тогда при напряжении в точке 2, равном 12 В, в точке 1 получим:
U_1 = \frac{U_2 \cdot R_1}{R_1 + R_2} = \frac{12 \cdot 3.6}{3.6 + 5.1} = 4.96 \medspace В
То есть мы заведомо не превысим ограничение в 5 В на аналоговом входе Ардуино, что нас полностью устраивает. Получаем итоговую принципиальную схему:
И вот теперь уже самое время продвинуться к практической части!
Практическая часть.
Функции для работы с аналоговыми входами Arduino.
До начала создания демо-скетчей необходимо ознакомиться с функциями, которыми и будем оперировать сегодня. И начинаем с функции, позволяющей выбрать источник опорного напряжения из тех вариантов, которые мы обсудили (полный список вариантов для разных плат можно найти по ссылке):
analogReference(refType);
У функции один аргумент, его значения для Arduino Uno могут быть такими:
- DEFAULT
- INTERNAL
- EXTERNAL
По умолчанию активен вариант DEFAULT
, пример кода для внутреннего источника на 1.1 В:
analogReference(INTERNAL);
Все довольно просто, осталось разобрать всего одну функцию, которая осуществляет непосредственно то, ради чего мы и собрались, а именно получение результата измерения:
analogRead(pin);
Аргументом является просто номер интересующего нас пина, причем тут есть несколько вариантов, возьмем для примера аналоговых вход A1. Тогда мы можем передать в функцию:
- непосредственно A1: analogRead(A1);
- порядковый номер аналогового пина, в данном случае это 1: analogRead(1);
- порядковый номер пина, A0 - 14, A1 - 15 и так далее по возрастанию: analogRead(15);
Возвращаемое же значение соответствует результату измерения, в чем нам и предстоит убедиться на реальных примерах, следующих далее.
Пример 1. Подключение потенциометра к Arduino.
Начинаем само собой с максимально базового примера. Подключим к аналоговому входу потенциометр, у меня под рукой есть такие экспонаты:
Как работает потенциометр в целом уже разбирали, так что имеем схему следующего вида:
По сути имеем делитель напряжения с возможностью менять соотношение сопротивлений. Реализация на макетной плате также присутствует:
То есть для подключения потенциометра необходимо использовать 3 вывода:
- 5V
- A1 (либо другой аналоговый вход)
- GND
Задачу себе поставим такую - измерить напряжение с потенциометра (которое будет от 0 до 5 В ввиду его подключения к GND и 5V) и вывести это значение в Serial Monitor, он же "Монитор порта". Получаем скетч:
// Аналоговый вход, напряжение на котором будем анализировать int analogPin = A1; // Сюда будем сохранять результат с АЦП int adcResult = 0; void setup() { Serial.begin(115200); } void loop() { // Собственно, считываем требуемое значение adcResult = analogRead(analogPin); // И выводим результат Serial.println(adcResult); // Задержка для повышения наглядности delay(100); }
Я снабдил его комментариями, тем не менее в случае возникновения вопросов, смело задавайте их любым из доступных способов:
- комментарии к статье
- форум
- наша группа
Вопросы категорически приветствуются 👍
Компилируем скетч и прошиваем, в Serial Monitor'е наблюдаем бегущие значения, которые меняются в соответствии с положением ручки потенциометра от 0 до 1023 в крайних положениях. Что в точности совпадает с той теорией, которую мы обсудили:
Здесь я добавил задержку delay(100)
для того, чтобы значения не молотили с бешеной скоростью, а давали возможность спокойно за собой понаблюдать.
Пример 2. Расчет напряжения с потенциометра на аналоговом входе Ардуино.
Модернизируем пример для того, чтобы увидеть значения в Вольтах, для этого произведем пересчет:
U = \frac{ ADC_{res} \cdot 5 \medspace В}{1024}
// Аналоговый вход, напряжение на котором будем анализировать int analogPin = A1; // Сюда будем сохранять результат с АЦП int adcResult = 0; // Напряжение в Вольтах float voltage = 0; void setup() { Serial.begin(115200); } void loop() { // Собственно, считываем требуемое значение adcResult = analogRead(analogPin); // Пересчитываем в Вольты voltage = (float)adcResult * 5 / 1024; // И выводим результат Serial.println("Voltage: " + String(voltage)); // Задержка для повышения наглядности delay(100); }
Все в точности по формуле, никаких неожиданностей, результат не заставляет себя ждать:
Аналогично можно использовать еще один инструмент Arduino IDE под названием "Serial Plotter": Tools > Serial Plotter. Открываем, крутим ручку потенциометра и получаем закономерные изменения сигнала:
Отлично! На очереди следующий пример.
Пример 3. Напряжение источника питания.
Что еще можно взять для теста из широкораспространенных материалов?... Да хоть ту же самую простейшую батарейку:
Подключим ее "+" ко входу A1, а минус посадим на землю (подключим к GND). Запускаем пример с измерением напряжения и получаем результат:
Что снова соответствует ожидаемому и не может не радовать )
Пример 4. Управление периодом мигания светодиода при помощи потенциометра.
И финишируем еще одним примером, в котором задействуем изученный нами давеча светодиод. В статье про подключение кнопки мы меняли период мигания светодиода в соответствии с состоянием кнопки, что давало нам два возможных значения - кнопка нажата / кнопка не нажата. Теперь же период мигания будем задавать в зависимости от значения напряжения на аналоговом входе Ардуино A1, к которому все так же подключен потенциометр:
На breadboard:
// Светодиод подключен к D2 int ledPin = 2; // Аналоговый вход, напряжение на котором будем анализировать int analogPin = A1; // Сюда будем сохранять результат с АЦП int adcResult = 0; // Текущий период (строго говоря полупериод) int currentPeriod = 0; // Максимальное значение периода constexpr int PERIOD_MAX = 1000; // Минимальное значение периода constexpr int PERIOD_MIN = 100; // Делитель для перерасчета из результата АЦП constexpr int ADC_MAX = 1023; void setup() { Serial.begin(115200); // D2 работает в качестве выхода pinMode(ledPin, OUTPUT); } void loop() { // Собственно, считываем требуемое значение adcResult = analogRead(analogPin); // Расчет длительности мигания currentPeriod = (float)adcResult / ADC_MAX * (PERIOD_MAX - PERIOD_MIN) + PERIOD_MIN; // Зажигаем светодиод digitalWrite(ledPin, HIGH); delay(currentPeriod); // Гасим светодиод digitalWrite(ledPin, LOW); delay(currentPeriod); }
Используем примитивные задержки с delay()
, чтобы не перегружать ничем пример. Вращая ручку потенциометра, теперь можем менять период мигания светодиода от значения PERIOD_MIN
(100) до значения PERIOD_MAX
(1000), что и требовалось в данном примере. Математика для расчета периода проста, не буду отдельно расписывать, если что - спрашивайте. По итогу крайние значения такие:
Измеренное значение | currentPeriod |
---|---|
0 | PERIOD_MIN (100) |
1023 | PERIOD_MAX (1000) |
И вот на этом на сегодня и завершаем, пункт "потенциометр Ардуино" можно отметить как выполненный, переходим к следующим разделам курса, следите за обновлениями и до новых встреч 🤝
Но как перевести этот сигнал с потенциометра на адресный светодиод, а именно ленту из 8 и что бы они зажигалиль помере вращения потенциометра?
Принято, в относительно ближайшее время добавлю статей по Arduino, помечу Ваш вопрос про ленту, чтобы не забыть 👍
Спасибо. Буду ждать.