На моей Uno жили две серво и диммер на отрезке белой светодиодной ленты — обычный полуденный проект, ничего боевого. Диммер сидел на пине 9 — просто потому, что так было нарисовано в первой попавшейся схеме, а пины тогда я раздавал наугад. В тот день, когда я дописал строку sv.attach(9), лента замерла на одном уровне яркости: analogWrite на девятом пине умер — не потускнел, а именно замер, будто пин заклинило. Я полчаса искал «сломанный» скетч, которого не существовало: проверял резистор, перепаивал провод, перезагружал IDE, гуглил «analogWrite не работает». Виновник — Timer1: библиотека Servo забирает его целиком, а он на Uno обслуживает ШИМ пинов 9 и 10. Диммер я перенёс на 11-й, а заодно решил, что хватит относиться к библиотеке как к чёрному ящику, и полез читать исходники Servo.h: что вообще происходит на плате, когда библиотека «крутит» до двенадцати серво одновременно, а loop при этом остаётся свободным. Оказалось — ничего магического: один таймер, очередь импульсов и прерывания. Ни датчиков, ни магии — арифметика на тиках, и она целиком помещается в этом уроке. Этот урок — про то, что я там нашёл: таймер с тиками по полмикросекунды, бюджет кадра в 40000 тиков и импульс, который можно послать руками, без единой библиотеки.
Что делает Timer1, пока вы пишете write()
Разбор полёта. Timer1 — единственный 16-битный таймер у Atmega328, и библиотека настраивает его так: тактовые 16 МГц делятся на 8, и тик получается длиной 0,5 мкс. Из этих тиков библиотека лепит и кадр, и каждый импульс, поэтому вся микросекундная кухня у неё целочисленная. Кадр серво — 20 мс — это ровно 40 000 тиков. Внутри кадра библиотека по очереди раздаёт импульсы всем подключённым серво: пин поднимается, таймер отсчитывает заказанные микросекунды, пин падает, наступает очередь следующего серво. Управление идёт через прерывания таймера: каждое срабатывание стоит микросекунды процессорного времени, а между ними ваш loop работает, как будто импульсов на плате не существует. Точность здесь выше всяких похвал: тик 0,5 мкс — это в двадцать раз мельче мёртвой зоны SG90, то есть разрешение, которое серво физически не отличит. Для сравнения: квант delay() — целая миллисекунда, в две тысячи раз грубее. Именно поэтому у библиотеки такой богатый выбор пинов: сигнал серво можно повесить почти на любой цифровой вывод, таймер один на всех. В кадре они стоят в очереди по порядку подключения, и очередь длинной в дюжину — всё ещё короче 20 мс, если импульсы короткие. Как устроены таймеры этой платы и что такое прерывание по совпадению, я подробно писал в обзоре таймеров Atmega328 и в уроке про прерывание по таймеру — там же видно, почему Timer0 никто не трогает: он кормит millis() и delay(). Если после этого раздела захочется самому дёрнуть таймер — начинайте именно с тех уроков: там всё то же самое, только по частям.
| Величина | В микросекундах | В тиках Timer1 |
|---|---|---|
| один тик таймера | 0,5 мкс | 1 |
| импульс 0° (RC) | 1000 мкс | 2000 тиков |
| импульс 90° (RC) | 1500 мкс | 3000 тиков |
| импульс 180° (RC) | 2000 мкс | 4000 тиков |
| целый кадр | 20 000 мкс | 40 000 тиков |
Импульс руками: HIGH, пауза, LOW
Теперь сделаем то же самое без библиотеки. Минимальный серво-сигнал — это digitalWrite(HIGH), пауза на нужное число микросекунд и digitalWrite(LOW); дальше тишина до конца кадра. Три строки — и серво вас понимает: вы наконец заговорили на её языке длительностей, а не скважностей. Для пауз такого масштаба в языке есть delayMicroseconds(): она на два порядка точнее delay() — delay(1) означает «примерно миллисекунда с накладными», а delayMicroseconds(1500) держит линию с точностью порядка 2–3 мкс. Верхний разумный предел у неё — порядка 16000 мкс: дольше она тоже умеет, но точность и назначение уже у других средств. Каждый digitalWrite сверху добавляет свои 3–5 мкс накладных — против импульса в тысячу микросекунд это пыль, но цену знать надо: пять-шесть микросекунд шума на кадр. В моей первой версии стояла классическая ошибка, которую я потом находил в чужих скетчах десятки раз: после импульса delay(20). Кадр получался длиной 21,5 мс — импульс, пауза, накладные расходы, — и серво у края хода мелко подрагивала, хотя в середине всё выглядело прилично. Лечится арифметикой: пауза равна кадру минус импульс. После импульса в 2 мс ставлю delay(18), и период встаёт на место; после импульса в 1 мс — delay(19), но и без пересчёта для каждого кадра достаточно просто помнить бюджет. Напомню вилку: серво терпят кадры 16–25 мс, но краем хода замечают и меньшую небрежность.
// Импульс посылаем сами: HIGH, delayMicroseconds, LOWconst int PIN_SRVO = 11; void impyls(int mks) { // один импульс digitalWrite(PIN_SRVO, HIGH); delayMicroseconds(mks); // 1000-2000 мкс, точность 2-3 мкс digitalWrite(PIN_SRVO, LOW);} void setup() { pinMode(PIN_SRVO, OUTPUT); Serial.begin(9600);} void loop() { for (int k = 0; k < 30; k++) { // 30 кадров на 0 градусов: ~600 мс impyls(1000); delay(18); // кадр = импульс 1 мс + пауза 18 мс } for (int k = 0; k < 30; k++) { // и 30 кадров на 180 градусов impyls(2000); delay(18); }}Полный скетч — выше, в нём нет ни одной библиотеки: только pinMode, digitalWrite и две паузы. Функция impyls() — три строки, и в них весь протокол серво, от и до. Прежде чем заменять им Servo.h во всех проектах, честная граница: пока delayMicroseconds() держит пин, loop стоит. Одна серво — это 1–2 мс блокировки из каждых 20, терпимо; пять серво подряд — уже 10 мс, и кооперативная логика на millis() начнёт захлёбываться: кнопки начнут пропускать нажатия, Serial — ронять строки, а мигание по расписанию — отставать. Проверка дешёвая: добавьте в loop счётчик проходов в секунду и посмотрите, как он падает с каждой новой серво. У меня счётчик рухнул с тысяч до пары сотен на пяти серво — диагноз без вскрытия. Библиотека же раздаёт импульсы из прерываний и вашего времени не ест вообще. Я держу такой код для трёх случаев: понять, как серво слышат плату; выжать точность, когда под рукой нет writeMicroseconds(); и экзотика вроде синхронного строба с другим устройством, когда импульс должен совпасть с внешним событием. В девяти проектах из десяти ответ — библиотека: она сама разберётся с очередью и границами значений за вас. Если хочется освежить, что делают attach(), write() и detach(), вернитесь к уроку про азы библиотеки Servo, а про неблокирующие циклы почитайте в millis() вместо delay.
Замер на живой плате и потолок в 12 серво
Чем проверить себя на живой плате? Вторым Arduino: pulseIn() на его пине меряет длину высокого уровня — тот же приём, что у дальномера HC-SR04, только объект другой. Подаёте impyls(1472) — и в мониторе второй платы видите свои 1470–1475. Схема замера — три провода: сигнал с пина первой платы на пин второй, минусы плат соединить, таймаут я ставлю явно, чтобы молчащая линия не подвешивала замер на целую секунду. Заодно это лучший способ подсмотреть, что шлёт библиотека: подключите сигнальный провод серво к пину второй платы (серво при этом питается как обычно, от первой) и сравните write(90) с writeMicroseconds(1500) — разница в 28 мкс видна в мониторе как на ладони. Замер по одному импульсу дрожит на пару микросекунд — это нормально, берите среднее из десяти. И последнее про потолок: до 12 серво на Uno — не магия числа, а бюджет кадра, и расходуется он только импульсами. Каждый импульс занимает 1000–2000 мкс, кадр — 20 мс; когда сумма импульсов вырастает, библиотека растягивает кадр, частота обновления падает ниже 40 Гц, и серво отзываются лениво. Двенадцать — это когда импульсы короткие; повесите дюжину MG995 с полным ходом по 2000 мкс — разницу почувствуете без осциллографа: серво начнут отвечать с натугой и ленивым дожёванием.
- 1. Сколько тиков Timer1 занимает импульс 1500 мкс?
- 2. Что забирает себе Servo.h на Arduino Uno?
- 3. После импульса длиной 2 мс какую паузу поставить, чтобы кадр был 20 мс?
- 4. Что сделает writeMicroseconds(3000) в библиотеке Servo.h?
- 5. Почему двенадцать серво на Uno — потолок?
- Timer1 тикает по 0,5 мкс: кадр 20 мс = 40000 тиков, импульс 1500 мкс = 3000 тиков.
- Импульс руками: HIGH, delayMicroseconds(1000–2000), LOW, тишина до конца кадра.
- Паузу считайте как кадр минус импульс: после 2 мс импульса — delay(18).
- Servo.h отбирает ШИМ пинов 9 и 10 — распределяйте пины до attach().
- Замерить импульсы можно pulseIn() со второй платы; 12 серво — бюджет кадра, а не магия.
Частые вопросы по теме урока
- Сколько тиков Timer1 занимает импульс 1500 мкс?
- Тик при делителе 8 длится 0,5 мкс, значит 1500 / 0,5 = 3000 тиков. Целый кадр — 40000 тиков, импульс занимает меньше десятой его части. 3000
- Что забирает себе Servo.h на Arduino Uno?
- Библиотека работает через Timer1, который на Uno обслуживает analogWrite пинов 9 и 10. Мой диммер на 9-м пине умер ровно поэтому — планируйте пины до attach(). Timer1 — ШИМ уходит с пинов 9 и 10
- После импульса длиной 2 мс какую паузу поставить, чтобы кадр был 20 мс?
- Пауза равна кадру минус импульс: 20 − 2 = 18 мс. Delay(20) даст кадр 22 мс — а договорённость о кадре серво проверяет каждый раз, и краем хода это замечает. delay(18)
- Что сделает writeMicroseconds(3000) в библиотеке Servo.h?
- Верхняя граница библиотеки — 2400 мкс, лишнее отрезается. Дальше только механическая коррекция качалки, код тут бессилен. обрежет до 2400 мкс
- Почему двенадцать серво на Uno — потолок?
- Кадр 20 мс при импульсах по 1000–2000 мкс вмещает около десятка посылок; дальше библиотека растягивает кадр. Шесть серво и больше — уже считайте бюджет сами. сумма импульсов в кадре растёт и частота обновления падает