Всех рад снова приветствовать на нашем сайте! В нескольких из предыдущих уроков мы досконально изучили варианты работы с цифровыми пинами (все статьи можно найти в соответствующем разделе), так что самое время перейти к аналоговым входам 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, помечу Ваш вопрос про ленту, чтобы не забыть 👍
Спасибо. Буду ждать.