Глиф Учебники

Урок 7. Аналоговые входы и ШИМ — analogRead и analogWrite

Курс «Arduino» · урок 7 из 10 · 4 минуты чтения

Кнопка знает только два состояния: нажата или нет. Но многие датчики сообщают величину — сколько света, под каким углом повёрнута ручка, какая температура. Они выдают напряжение, и плата умеет его измерять. А в обратную сторону плата умеет не только включать и выключать светодиод, но и менять его яркость.

Аналоговый вход: analogRead

Выводы A0–A5 на Uno (на Nano ещё A6 и A7) подключены к АЦП — аналого-цифровому преобразователю. Он переводит напряжение от 0 до 5 В в число от 0 до 1023:

Один шаг — примерно 5 / 1024 ≈ 0,0049 В, то есть около 5 мВ. Функция analogRead(A0) возвращает это число; вызывать pinMode для аналогового входа не нужно.

На вход нельзя подавать больше 5 В и меньше 0 В (у плат на 3,3 В — больше 3,3 В): вывод и микроконтроллер могут сгореть. Большие напряжения, например от аккумулятора на 12 В, измеряют через делитель из двух резисторов.

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

Потенциометр (переменный резистор) — резистор с ручкой и тремя выводами. Крайние подключите к 5V и GND, средний — к A0. Поворачивая ручку, вы меняете напряжение на среднем выводе от 0 до 5 В. Номинал 10 кОм — самый удобный.

Arduino 5V A0 GND 10 кОм ручка крайние — 5V и GND, средний — A0 напряжение на A0 0 В 0 2,5 В ≈ 512 5 В 1023 АЦП число analogRead(A0) шаг ≈ 5 мВ
Потенциометр 10 кОм крайними выводами подключён к 5V и GND, средним — к A0; АЦП переводит 0 В, 2,5 В и 5 В на A0 в числа 0, около 512 и 1023.
Arduino
void setup() {
  Serial.begin(9600);
}

void loop() {
  int value = analogRead(A0);              // число 0–1023
  float voltage = value * (5.0 / 1023.0);  // перевести в вольты
  Serial.print(value);
  Serial.print("  ");
  Serial.print(voltage);
  Serial.println(" В");
  delay(200);
}

Откройте монитор порта и покрутите ручку — числа будут меняться, например так:

Текст
0  0.00 В
512  2.50 В
1023  5.00 В

Обратите внимание на 5.0 и 1023.0 с точкой. Если написать value * (5 / 1023), получится всегда 0: в C++ деление целых чисел отбрасывает дробную часть, и 5 / 1023 даёт 0. Число с точкой делает вычисление дробным.

Если к входу ничего не подключено, analogRead возвращает случайно меняющиеся значения — как и висящий цифровой вход. Это не поломка.

Делитель напряжения и фоторезистор

Многие простые датчики — это резистор, сопротивление которого меняется: фоторезистор (от света), терморезистор (от температуры). Плата измеряет напряжение, а не сопротивление, поэтому такой датчик ставят в делитель напряжения: 5V → фоторезистор → A0 → резистор 10 кОм → GND. Чем светлее, тем меньше сопротивление фоторезистора и тем больше число на A0. Скетч тот же, что для потенциометра.

ШИМ: analogWrite

Напряжение на цифровом выводе бывает только 0 или 5 В. Но если очень быстро включать и выключать вывод, светодиод будет казаться тусклее, а мотор — крутиться медленнее. Это ШИМ — широтно-импульсная модуляция (по-английски PWM). Важно, какую долю времени вывод включён: эта доля называется скважностью или коэффициентом заполнения.

analogWrite(вывод, значение) включает ШИМ: значение от 0 (всегда выключено) до 255 (всегда включено), 128 — включено примерно половину времени. Частота на Uno и Nano — около 490 раз в секунду, на выводах 5 и 6 — около 980.

analogWrite напряжение на выводе 0 выключен 128 тусклее 255 полная яркость 5 В 0 В ≈ 2 мс — около 490 раз в секунду вкл выкл 128 — вывод включён примерно половину времени
ШИМ на выводе: при 0 напряжение всё время 0 В, при 128 вывод примерно половину каждого периода около 2 мс включён на 5 В, при 255 включён всегда.

ШИМ есть не на всех выводах — только на отмеченных ~: на Uno и Nano это 3, 5, 6, 9, 10, 11. Подробнее — в справке Что такое ШИМ.

Плавное мигание

Подключите светодиод с резистором к выводу 9, как в уроке Электричество для начинающих:

Arduino
const int LED = 9;     // вывод с ШИМ
int brightness = 0;    // текущая яркость 0–255
int step = 5;          // на сколько менять за один шаг

void setup() {
  pinMode(LED, OUTPUT);
}

void loop() {
  analogWrite(LED, brightness);
  brightness = brightness + step;
  if (brightness <= 0 || brightness >= 255) {
    step = -step;      // на краю — повернуть в обратную сторону
  }
  delay(30);
}

Яркость растёт шагами по 5 от 0 до 255, затем убывает обратно. || значит «или»: условие выполняется, если верна хотя бы одна часть.

Ручка управляет яркостью: map

Соедините оба опыта: потенциометр на A0, светодиод на выводе 9. Остаётся перевести число 0–1023 в 0–255. Для этого есть функция map(значение, от_мин, от_макс, в_мин, в_макс) — она пересчитывает число из одного диапазона в другой:

Arduino
const int POT = A0;
const int LED = 9;

void setup() {
  pinMode(LED, OUTPUT);
}

void loop() {
  int value = analogRead(POT);                    // 0–1023
  int brightness = map(value, 0, 1023, 0, 255);   // 0–255
  analogWrite(LED, brightness);
}

map(512, 0, 1023, 0, 255) даёт 127: функция работает с целыми числами и дробную часть отбрасывает. Она не ограничивает результат: если значение выйдет за исходный диапазон, результат выйдет за новый. Чтобы удержать число в пределах, есть constrain(x, мин, макс).

A0 вывод 9 analogRead(POT) 0–1023 0 0 512 127 1023 255 map(value, 0, 1023, 0, 255) analogWrite(LED, brightness) 0–255
Функция map переводит число с потенциометра из диапазона 0–1023 в яркость 0–255 для вывода 9: 0 → 0, 512 → 127, 1023 → 255.

На глаз яркость меняется неравномерно: от 0 до 50 разница заметна сильно, от 200 до 255 — едва. Так устроено зрение, а не плата.

Коротко

Проверьте себя

1. Что вернёт analogRead(A0), если на входе 5 В (плата Uno)?
2. На каком выводе Arduino Uno работает analogWrite?
3. Что делает analogWrite(9, 128)?
4. Чему равно value * (5 / 1023) при value = 1023?