analogWrite() и ШИМ на Arduino: скважность, частота и плавная яркость
Светодиод на пине 9 светится вполсилы, хотя в скетче всего одна строка — analogWrite(9, 128). Секрет в том, что плата не выдаёт 2,5 вольта: у Uno нет настоящего аналогового выхода, и пин быстрыми рывками прыгает между 0 и 5 В примерно 490 раз в секунду. Глаз усредняет эту дробь в ровную «половину» яркости, мотор — в половину оборотов, а осциллограф нарисовал бы прямоугольник, где верх и низ занимают поровну времени.
Разберём, как число 0-255 превращается в коэффициент заполнения, почему частота ШИМ зависит от пина (490 Гц против 980), какие библиотеки отбивают у ШИМ таймеры — и соберём плавный фейдер без единого delay(). Потрогать скважность руками можно в симуляторе ещё до того, как плата приедет с почты.
Что на самом деле делает analogWrite()
Аргумент analogWrite() живёт в 8 битах: 256 ступеней от 0 до 255, где 0 — это 0 % периода с высоким уровнем, а 255 — все 100 %. Это не проценты и не вольты, а счёт таймера: значение записывается в регистр сравнения, и таймер сам держит пин высоким свою долю периода. Среднее напряжение считается умножением: 5 В × 0,5 ≈ 2,5 В, при 64 ≈ 1,25 В, при 192 ≈ 3,75 В. Светодиоду, мотору и глазу хватает этой средней величины — они слишком медленные, чтобы разглядеть мелькание. Полная схема с резистором и живой яркостью — в уроке про analogWrite() и яркость.
Живое следствие восьми бит: соседние значения глаз почти не различает — 127 и 128 дают одинаковую яркость, а вот 0 и 1 разнятся заметно, потому что при 1 светодиод вспыхивает коротким мигом 490 раз в секунду. Верхняя четверть диапазона визуально «плотнее» нижней, поэтому плавность хода растягивайте на весь 0-255, а не на полдиапазона. И следите за пределом: аргумент в 8 бит, 256 действует как 0, а 1023 — как 255, поэтому analogWrite(pin, 1023) яркость не поднимет выше максимума. Честное аналоговое напряжение без мелькания выдают только платы с ЦАП — Due и часть ESP32.
Частота ШИМ по пинам: 490 Гц, 980 Гц и чужие таймеры
| Пины Uno | Частота ШИМ | Таймер | Кто отбирает |
|---|---|---|---|
| 3, 9, 10, 11 | ≈490 Гц | Timer1 и Timer2 | tone() занимает Timer2 — ШИМ пропадает на 3 и 11 |
| 5, 6 | ≈980 Гц | Timer0 — тот же, что считает millis() | Режим не менять: собьются millis(), delay() и таймауты |
| 9, 10 | ≈490 Гц | Timer1 | Servo.h занимает Timer1 — ШИМ на этих пинах отключается |
Частоты разбежались из-за настройки таймеров. Timer0 работает в режиме fast PWM с делителем 64: 16 МГц / 64 / 256 ≈ 976,5 Гц, в справке это округляют до 980. Timer1 и Timer2 настроены в phase-correct PWM: 16 МГц / 64 / 510 ≈ 490 Гц. Для глаза и мотора разница незаметна, но пищалке или фильтру — уже существенна; частоту поднимают до 31 кГц записью в регистры TCCR, в уроке про ШИМ 31 кГц это разобрано по битам конфигурации. Вторая ловушка — делить таймеры с библиотеками: tone() и Servo.h перехватывают Timer2 и Timer1 целиком, и ШИМ на занятых пинах молчит, пока библиотека работает. Ниже — живая лаборатория ШИМ: подвигай ручку скважности и посмотри осциллограмму и среднее напряжение.
Скважность, частота и яркость светодиода — крутите ползунки и смотрите на осциллограмму.
Подгружаем интерактив…
Плавный фейдер на millis(): яркость без delay()
const int ledPin = 9; // ШИМ-пин (волнистая ~ у цифры)int brightness = 0; // 0-255int step = 5; // шаг за один тикunsigned long prevMillis = 0;const unsigned long tick = 30; // тик каждые 30 мс void setup() { pinMode(ledPin, OUTPUT);} void loop() { unsigned long currentMillis = millis(); if (currentMillis - prevMillis >= tick) { prevMillis = currentMillis; brightness += step; if (brightness >= 255 || brightness <= 0) { step = -step; // у края — разворот } brightness = constrain(brightness, 0, 255); analogWrite(ledPin, brightness); } // между тиками loop() свободен: кнопка, датчик, Serial — без задержек}Шаг 5 каждые 30 мс — это 51 тик на подъём от 0 до 255, полный цикл «вверх-вниз» занимает около трёх секунд; цифры легко перекроить под своё настроение. Разворот держится на проверке краёв и смене знака step: без неё яркость застрянет у потолка. Главное — внутри loop() ни одной задержки: фейдер можно приостановить кнопкой, подстраивать под датчик света или завести в отдельный канал планировщика. Варианты на один и несколько каналов собраны в уроке про плавный свет на ШИМ, а базовый разбор analogWrite() и ШИМ раскладывает по регистрам, что делает каждая строка.
- ШИМ-пины помечены на плате волнистой линией: у Uno и Nano их шесть — 3, 5, 6, 9, 10, 11, у Mega — пятнадцать
- Диммер и плавная подсветка: светодиод через токоограничивающий резистор 220-330 Ом прямо на пин
- RGB-светодиод с общим катодом: три ШИМ-канала смешивают 256 × 256 × 256 ≈ 16,7 млн оттенков
- Скорость мотора: ШИМ на вход ENA драйвера (L298N, DRV8833), питание мотора — от своего источника
- Подсветка-«дыхание» на ленте 12 В: полевой транзистор коммутирует ленту, затвор получает ШИМ с пина