Ночник у кровати должен светиться на треть яркости: будить нельзя, но и в темноте спотыкаться об кота не хочется. Я тогда ещё не знал про ШИМ и пошёл наивным путём — подбирать яркость резисторами. 220 Ом — ярко, 1 кОм — тускло, 470 Ом — почти то, 560 Ом — вот оно! Каждый уровень яркости стоил мне новой пересборки макетки, а для плавного перехода понадобился бы потенциометр прямо в силовой цепи. Вечер ушёл на то, что потом заняло одну строку: analogWrite(9, 80).
Проблема в архитектуре: digitalWrite умеет только два состояния — 5 В или 0 В. Промежуточного напряжения на цифровом пине не существует, пин — это транзисторный ключ, а не кран с водой. Но регулировать яркость и скорость всё-таки можно, и делает это ШИМ — широтно-импульсная модуляция.
В этом уроке разберём, как analogWrite выдаёт «половину вольта», не выдавая половины вольта; какие пины Uno для этого годятся (подсказка: на шелкографии они помечены тильдой); что такое duty cycle 0-255 и почему светодиод не мерцает, хотя его физически включают и выключают тысячи раз в секунду. Соберём и диммер на потенциометре — крутишь ручку, свет едет.
Два состояния и выход из положения
Почему нельзя «просто выдать 2,5 В»? Внутри каждого пина стоят два транзистора: верхний подключает ногу к 5 В, нижний — к земле, и работают они строго попеременно. Промежуточного положения у ключа нет — как нет «половины нажатой» кнопки. Линейный делитель из резисторов яркость светодиода меняет, но для мотора бесполезен, а каждый новый уровень яркости требует новой детали. Тупик, в котором я просидел тот вечер, — типичная цена попытки получить аналоговое поведение из цифровой ноги.
ШИМ выкручивается временем. Пин микросекундами щёлкает между 5 В и 0 В: держит 5 В половину периода — среднее значение получается 2,5 В. Держит 90% времени — среднее 4,5 В. Усредняет нагрузку сама физика: светодиод «размазывает» вспышки зрением, мотор — инерцией ротора, нагреватель — тепловой массой. Напряжение всё время скачет, но по действию результат неотличим от настоящего промежуточного.
Ключевой термин — duty cycle, коэффициент заполнения: доля времени, когда пин в HIGH. Arduino кодирует её числом от 0 до 255 — это 8-битное разрешение, 256 ступеней. analogWrite(9, 127) держит HIGH примерно половину времени, в среднем выходят 2,5 В. analogWrite(9, 0) прижимает пин к земле постоянно — честный LOW, analogWrite(9, 255) — честный HIGH. Слово «аналог» в названии функции лукавит: на выходе не аналоговое напряжение, а быстрый цифровой прямоугольник.
Диммер на потенциометре: map() и constrain()
Потенциометр — ручка, которая делит 5 В на долю и отдаёт её на аналоговый вход. analogRead возвращает от 0 до 1023: это 10-битный АЦП, 1024 ступени. А analogWrite принимает 0-255 — 8 бит. Диапазоны разные, нужен пересчёт, и для него есть готовая функция map(значение, fromMin, fromMax, toMin, toMax). Строка map(analogRead(A0), 0, 1023, 0, 255) превращает поворот ручки в duty cycle.
Два нюанса, на которых обжигаются почти все. Первый: map не проверяет границы — если АЦП выдаст шум за пределами диапазона, duty уедет за 255, поэтому после map я почти всегда ставлю constrain(duty, 0, 255). Второй: глаз нелинеен. duty 128 выглядит не «в полнакала», а почти как полный свет, потому что восприятие яркости логарифмическое. Лечится квадратом значения: duty = duty * duty / 255 растягивает тёмную зону хода ручки. Для своего ночника я просто ограничил максимум значением 180 — глаза довольны, и потребление падает примерно вдвое по сравнению с полным накалом.
Схема диммера стандартная: средняя нога потенциометра идёт в A0, крайние — в 5V и GND, светодиод через резистор 220 Ом — в пин 9. Крутим ручку — яркость едет плавно, без щелчков и ступенек. В симуляторе ручка уже подключена к A0, светодиод — к пину 9.
Скважность, частота и яркость светодиода — крутите ползунки и смотрите на осциллограмму.
Подгружаем интерактив…
ШИМ не только для светодиодов: моторы и серво
Обязательно держите в голове пределы пина: одна ножка Uno отдаёт до 40 мА, а постоянная работа выше 20 мА не рекомендуется вообще. Маломощный светодиод в это вписывается легко, а вот коллекторный мотор даже крошечного вентилятора ест 100-500 мА — подключать его напрямую к пину значит сжечь порт. Правильная схема: ШИМ подаётся на затвор MOSFET-транзистора, а мотор питается от своего источника через этот ключ; параллельно мотору ставится обратный диод, гасящий всплески самоиндукции. analogWrite при этом остаётся той же самой — меняется только силовая часть после пина.
Второй частый кандидат на путаницу — сервоприводы. У серво своя разновидность ШИМ: не быстрый прямоугольник на 490 Гц с произвольным заполнением, а импульсы длительностью 1-2 мс, повторяющиеся 50 раз в секунду. analogWrite такой сигнал не выдаёт — для серво существует библиотека Servo, и она, между прочим, занимает Timer1, так что после Servo.attach() точность ШИМ на пинах 9 и 10 падает. Устройство таймеров из следующего урока поможет понять, почему эти конфликты вообще возникают.
И последнее наблюдение из практики: для лент и мощных светодиодов логика та же, что для мотора — пин задаёт заполнение, а ток гоняет транзистор. Я один раз повесил метровый отрезок ленты на пин напрямую через старый транзистор без резистора затвора — и неделю не мог понять, почему яркость дёргается при каждом прикосновении к макетке: помехи через ёмкость тела заходили в незащищённый затвор. Резистор 220 Ом в разрыв затвора стоил копейки и закрыл вопрос навсегда.
Эксперименты для закрепления
- Перетащите светодиод на пин 7 — без тильды. Запустите и проверьте предсказание из врезки: analogWrite(7, 100) даст не тусклый свет, а просто выключенный светодиод (100 меньше 128).
- Добавьте нелинейность после map: duty = duty * duty / 255. Оцените, стала ли ручка понятнее в тёмной зоне, где раньше яркость «прыгала».
- Свяжите с прошлым уроком: в скетче «дыхания» замените delay(30) на неблокирующий таймер millis() с шагом 20 мс. Теперь дыхание не блокирует ничего — можно повесить рядом кнопку смены цвета.
- Соберите «комфортный диммер»: map(raw, 0, 1023, 15, 180). Светодиод никогда не гаснет в ноль и не слепит на максимуме. Именно такую формулу я ставлю в ночники.
- analogWrite работает только на пинах с тильдой: 3, 5, 6, 9, 10, 11
- Duty cycle 0-255 — 8 бит; значение 127 держит HIGH около 50% времени, среднее 2,5 В
- Частота ШИМ: примерно 490 Гц на пинах 3, 9, 10, 11 и 980 Гц на 5 и 6 (Timer0)
- ШИМ — не аналоговое напряжение, а быстрые скачки 0/5 В с нужной долей HIGH
- Связка диммера: map из 0-1023 в 0-255, затем constrain, затем analogWrite
- Глаз нелинеен: квадрат значения или ограничение максимума делают регулировку приятной
- 1. На каких пинах Arduino Uno доступен ШИМ через analogWrite()?
- 2. Что физически делает analogWrite(9, 127)?
- 3. Почему на пинах 5 и 6 частота ШИМ примерно 980 Гц, а на остальных 490 Гц?
- 4. Что вернёт map(512, 0, 1023, 0, 255)?
- 5. Что произойдёт при analogWrite(7, 100) на пине без ШИМ?
Теперь у вас есть и приём, и понимание, что «аналоговый» выход Arduino — договорённость: чип честно переключает 0 и 5 В, а плавность рождают частоты 490-980 Гц, незаметные глазу. Тем же механизмом управляются сервоприводы, яркость светодиодных лент и скорость моторов — так что этот урок будет вспоминаться часто.
В следующем уроке поднимемся на самый верх надёжности: посмотрим, почему прошивки зависают даже без ошибок в логике, познакомимся со сторожевым таймером, который перезагружает зависший чип за восемь секунд максимум, и посчитаем, во сколько микроампер обойдётся сон на батарейках. После него ваши устройства перестанут требовать поездок с кнопкой Reset.
Частые вопросы по теме урока
- На каких пинах Arduino Uno доступен ШИМ через analogWrite()?
- ШИМ-пины Uno помечены тильдой: 3, 5, 6, 9, 10, 11. На остальных вызов analogWrite вырождается в digitalWrite. 3, 5, 6, 9, 10, 11
- Что физически делает analogWrite(9, 127)?
- На выходе прямоугольный сигнал 0/5 В с заполнением около 50%. Среднее значение 2,5 В получается за счёт усреднения нагрузкой — глазом, инерцией мотора. Держит пин в HIGH около 50% времени на частоте примерно 490 Гц
- Почему на пинах 5 и 6 частота ШИМ примерно 980 Гц, а на остальных 490 Гц?
- Пины 5 и 6 завязаны на Timer0 — тот же таймер считает millis(). Остальные ШИМ-пины сидят на Timer1 и Timer2 и дают примерно 490 Гц. Они работают на Timer0, который тикает вдвое чаще
- Что вернёт map(512, 0, 1023, 0, 255)?
- map считает в целых числах: 512 * 255 / 1023 = 127,66, дробь отбрасывается, остаётся 127. Всегда помните про целочисленное деление внутри map. 127
- Что произойдёт при analogWrite(7, 100) на пине без ШИМ?
- На пине без тильды analogWrite превращается в digitalWrite: значение ниже 128 — LOW, от 128 — HIGH. Никакой яркости, только вкл/выкл. Пин просто прижмётся к LOW: значения до 127 дают выключенное состояние