Ночник-«дыхание» в детскую я собирал с упорством, достойным лучшего применения: вручную включал и выключал пин командами digitalWrite и delayMicroseconds — короткие импульсы должны были усредниться в полумрак. Светодиод действительно горел тускло, но при каждом проходе loop() заметно подрагивал, Serial молчал, потому что весь скетч стоял в паузах, а кнопка в соседней ветке кода перестала отзываться вовсе. Два вечера я лечил это заменой резисторов, пока наконец не признал: плавную яркость кто-то придумал до меня, и придумал аппаратно.
Придумали, и называется это ШИМ — широтно-импульсная модуляция. В этом уроке разберём, что такое duty cycle и почему 490 Гц делают мигание невидимым для глаза, где на Uno живут ШИМ-пины (подсказка: они помечены волнистой чертой ~ на шелкографии), как analogWrite(9, 0..255) меняет яркость одной строкой и почему число внутри — не напряжение и не проценты. Соберём то самое «дыхание» светодиода, только без самодеятельности, и заглянем в два тупика, в которых я оставил нервы: analogWrite на пине без черты и проценты вместо диапазона 0–255.
Что такое ШИМ: импульсы вместо половинки напряжения
Интуиция шепчет: чтобы светодиод светил вполсилы, надо подать на пин два с половиной вольта. Arduino так не умеет: у цифрового пина ровно два состояния, 5 В или 0 В, промежутка между ними нет. ШИМ решает задачу хитрее — пин очень быстро переключается между полными пятью вольтами и нулём. Если половину периода пин держится наверху, а половину — внизу, светодиод горит лишь половину каждого цикла, и глаз видит ровно половину яркости. Это и есть duty cycle, коэффициент заполнения: доля времени, которую сигнал проводит в единице.
Считается он от полного диапазона: duty 50% — светодиод горит половину времени, 10% — каждую десятую долю, 90% — почти всё время. Теперь про скорость. На пинах 3, 9, 10 и 11 Uno переключает ШИМ-сигнал с частотой около 490 Гц — четыреста девяносто раз в секунду; на пинах 5 и 6 частота вдвое выше, около 980 Гц. Глаз перестаёт замечать мигание уже в районе 50–60 Гц, так что на 490 Гц зрение усредняет импульсы в ровное, спокойное свечение без всякого дрожания. Мультиметр на постоянном напряжении покажет то же усреднение: при duty 51 из 255 он увидит примерно 1,0 В, хотя между импульсами пин честно выдаёт полные 5 В.
Вот почему analogWrite не «делает напряжение» — он делает импульсы. Путаница здесь дорого стоит: новичок ждёт от пина «2,5 В для моторчика» или считает, что светодиод при половинном duty питается половиной вольта. Потребление действительно падает примерно пропорционально duty, но физика другая: кристалл каждый миг либо горит в полную силу, либо выключен, просто выключен он так часто, что глаз за этим не поспевает. Как только картинка «быстрое мигание, которое глаз усредняет» уложилась в голову, половина вопросов к ШИМ исчезает сама собой.
Проверить импульсы можно без осциллографа. Наведите камеру смартфона на светодиод под ШИМ: из-за покадровой съёмки по линейке света поползут тёмные и светлые полосы — камера видит мигание, которое глаз пропускает. А если снизить частоту в сотню раз и подавать импульсы вручную через digitalWrite и delay(500), мигание увидит и ребёнок: та же самая механика, только без усреднения. Разница между «мигает» и «светит вполсилы» — исключительно частота переключения, и весь аппаратный ШИМ существует ради того, чтобы делать это переключение за процессор и не давать ему ни единого такта.
Где живут ШИМ-пины: волнистая черта и диапазон 0–255
На плате Uno аппаратный ШИМ умеют шесть пинов — 3, 5, 6, 9, 10 и 11. Возле их номеров на шелкографии нанесена волнистая черта ~, и это единственная подсказка прямо на корпусе платы: производители не зря её рисуют, стоит один раз рассмотреть свою Uno глазами. Пины привязаны к трём аппаратным таймерам внутри ATmega328P, поэтому «перенести» ШИМ на соседний пин силой воли не выйдет: у пина 8 таймера под ШИМ нет, и analogWrite там ведёт себя как обычный digitalWrite.
Сам вызов выглядит так: analogWrite(9, 128) — номер пина и число от 0 до 255. Ноль — пин всё время внизу, светодиод погашен. Двести пятьдесят пять — всё время наверху, полная яркость. Середина, 127–128, — примерно половина яркости. Диапазон принципиален: не проценты и не вольты, а именно 0–255, потому что внутри это восьмибитное число — ровно столько значений умещается в один байт. Отсюда классическая ошибка «я хочу 50% яркости»: analogWrite(9, 50) — а на деле вы попросили меньше пятой части, 50 из 255. Светодиод загорается тускло, а вы обшариваете макетку в поисках плохого контакта, которого нет.
pinMode для ШИМ-пина настраивать не обязательно — analogWrite сам переведёт пин в режим выхода. Я всё равно пишу pinMode в setup(): схему видно целиком, а привычка выручает, когда позже часть кода переезжает на digitalWrite. Мнение с оговоркой: на старте я взял себе правило — любому пину, где нужна плавность, заранее назначать место только из шестёрки 3, 5, 6, 9, 10, 11, и проверять черту на плате. Планируете больше трёх ШИМ-каналов одновременно, например RGB плюс диммер, — считайте пины на бумажке до сборки: их ровно шесть, и потом придётся что-то вычёркивать.
Дыхание светодиода: цикл for по 0..255
Собираем ночник по-нормальному. Схема минимальная: светодиод длинной ножкой к пину 9, резистор 220 Ом в разрыв, короткая ножка к GND — та же схема из первого урока, только пин теперь ШИМ-ный. Логика укладывается в два цикла: первый гоняет яркость от 0 до 255 с шагом 5, на каждом шаге analogWrite(9, b) и delay(5); второй — от 255 до 0, обратно. Полный вдох-выдох занимает около двух с половиной секунд: 52 шага вверх, столько же вниз, по 5 мс на каждый.
Два параметра крутят весь характер анимации. Шаг: при 5 яркость меняется непрерывно и глазу кажется плавной; при 25 вы получите заметные ступеньки, как на индикаторе старой микроволновки — семь-восемь скачков вместо ровного нарастания. Пауза: delay(5) даёт спокойное «дыхание», delay(1) — почти мгновенную вспышку, delay(50) — растянутую, ленивую. Оба параметра лежат в const наверху скетча: правятся одним числом, логику не трогают — качественная привычка для любых будущих анимаций.
И честная оговорка про цену. Цикл с delay() блокирует loop() на всё время вдоха: для ночника это неважно, а для кнопки в том же скетче — уже проблема, знакомая вам по уроку про дребезг. Неблокирующая версия «дыхания» на millis() и плавные переходы по таймерам ждут в продвинутом разборе ШИМ — там же диммер на потенциометре и разговор про то, что глаз чувствует яркость нелинейно. Сегодня же наша цель — механика analogWrite, и с ней блокирующий цикл справляется безупречно.
const int LED_PIN = 9; void setup() { pinMode(LED_PIN, OUTPUT); Serial.begin(9600); Serial.println("Полумрак своими руками: 2 мс горим, 2 мс темны");} void loop() { digitalWrite(LED_PIN, HIGH); delayMicroseconds(2000); digitalWrite(LED_PIN, LOW); delayMicroseconds(2000); // импульсы честно усредняются в половину яркости, // но весь loop() занят только ими: Serial и кнопки молчат}Скважность, частота и яркость светодиода — крутите ползунки и смотрите на осциллограмму.
Подгружаем интерактив…
- Остановите анимацию и выведите фиксированный duty: закомментируйте оба цикла в loop() и вызовите analogWrite(LED_PIN, 64). Это четверть яркости — сравните глазами 64, 128 и 191, три вызова подряд с delay(2000) между ними.
- Урежьте шаг до 1 и паузу до 1 мс: дыхание станет почти мгновенной вспышкой. Найдите значения, при которых анимация ещё читается глазом как плавная, — это и есть ваш комфортный диапазон.
- Напечатайте duty в Serial: допишите Serial.println(b) внутрь первого цикла и убедитесь, что число пробегает все значения подряд, а светодиод меняется непрерывно. Потом уберите печать — она тормозит цикл.
- Замените пин 9 на 13 и запустите. В симуляторе дыхание сохранится — движок не моделирует отсутствие таймеров. Запомните честную поправку: на реальной Uno analogWrite(13, x) работает как digitalWrite, то есть половина времени в полную силу, а не вполсилы.
- Соберите «пульс»: после достижения пика вставьте delay(300) перед обратным циклом. Сердцебиение отличается от дыхания именно паузой на пике — один параметр, а характер анимации другой.
- ШИМ — не промежуточное напряжение, а быстрые импульсы между полными 5 В и 0 В; глаз усредняет их в плавную яркость.
- Duty cycle — доля времени в единице: 128 из 255 ≈ 50%; диапазон analogWrite всегда 0–255, не проценты.
- На Uno ШИМ есть на пинах 3, 5, 6, 9, 10, 11 (черта ~ на шелкографии); частота ≈ 490 Гц, на пинах 5 и 6 ≈ 980 Гц.
- analogWrite(пин, 0..255) сам настраивает пин выходом; на пине без черты превращается в digitalWrite.
- Дыхание светодиода — два цикла for по 0..255 и обратно с шагом и паузой; оба параметра держите в const.
- 1. Что делает analogWrite(9, 128) на Arduino Uno?
- 2. На каких пинах Arduino Uno есть аппаратный ШИМ?
- 3. Нужна четверть яркости. Какое число передать в analogWrite?
- 4. Светодиод горит в полную силу при любом значении analogWrite, кроме нуля. Пин — 8. В чём причина?
Теперь плавная яркость для вас — одна строка: analogWrite с числом от 0 до 255 на любом пине с чертой. Из этого на одном светодиоде собираются ночники, индикаторы заряда и «дыхание»; вместе с потенциометром из урока про analogRead получится диммер с ручкой. Дальше по курсу каналов станет три: возьмём RGB-светодиод и смешаем из красного, зелёного и синего любой цвет — та же analogWrite, только в три руки. А если захочется неблокирующих анимаций, диммера и разговора про нелинейность глаза — открывайте продвинутый разбор ШИМ, он подхватит ровно с того места, где мы остановились.
Частые вопросы по теме урока
- Что делает analogWrite(9, 128) на Arduino Uno?
- ШИМ не меняет напряжение импульсов — они всегда полные 5 В и 0 В. Меняется доля времени в единице (duty cycle), а глаз и мультиметр усредняют её в половину яркости. Пин 9 будет 490 раз в секунду переключаться между 5 В и 0 В, проводя в единице примерно половину периода
- На каких пинах Arduino Uno есть аппаратный ШИМ?
- ШИМ-пины привязаны к трём аппаратным таймерам ATmega328P: на Uno это 3, 5, 6, 9, 10 и 11. Черта ~ на шелкографии — их метка. 3, 5, 6, 9, 10, 11 — они помечены волнистой чертой
- Нужна четверть яркости. Какое число передать в analogWrite?
- 64 из 255 ≈ 25% duty. 1023 — потолок analogRead, у analogWrite максимум 255; 25 — меньше десятой части диапазона. 64
- Светодиод горит в полную силу при любом значении analogWrite, кроме нуля. Пин — 8. В чём причина?
- У пина 8 нет аппаратного таймера для ШИМ. analogWrite без таймера деградирует до digitalWrite: значение больше 127 — горит, меньше — погашен. У пина 8 нет ШИМ: analogWrite на нём работает как digitalWrite