Руль для самодельной аркады я собрал из энкодера EC11 за один вечер: ручка, два контакта фаз A и B, кнопка под большим пальцем. Скетч был наивный, по привычке из урока про кнопку и digitalRead: опрашиваю обе фазы в loop(), сравниваю с прошлым значением, delay(30), чтобы дребезг не мешал. На медленном кручении всё выглядело прекрасно. А на быстром — а руль хочется крутить именно быстро — счётчик отставал от щелчков примерно вдвое, а иногда делал шаг в обратную сторону. За пять минут гонки набегало плюс-минус двадцать фантомных шагов, и камера уезжала туда, откуда её только что выкрутили.
Проблема была не в энкодере, а в моём способе его слушать. За одним щелчком EC11 стоит полный цикл из четырёх переключений двух контактов: фазы A и B меняются по очереди, с шагом в четверть периода. При щелчке в десять миллисекунд фронт приходит каждые две с половиной — а мой loop() открывал глаза раз в тридцать. Между его взглядами проходило до двенадцати фронтов, и все они исчезали без следа: digitalRead видит не события, а кадры состояний. Дребезг контактов на каждом переключении добавлял мусорных фронтов ещё столько же.
Лечение двухслойное, и ему посвящён урок. Первое — слушать не в loop(), а в прерываниях: у Uno внешние прерывания есть на пинах 2 и 3, и это ровно то место, куда втыкаются фазы энкодера; механику attachInterrupt() и правила обработчиков мы уже разбирали в уроке про прерывания. Второе — не просто считать фронты, а декодировать их таблицей переходов: она восстанавливает направление вращения и заодно глотает часть мусора от дребезга. В конце — симулятор, в котором ручку энкодера заменяют две кнопки: вы сами проиграете последовательность фронтов и убедитесь, что счётчик не теряет ни одного шага.
Квадратура: два сигнала со сдвигом на четверть
Инкрементальный энкодер — это не потенциометр с бесконечным ходом, а пара выключателей, которые размыкает и замыкает зубчатый диск. У типичного EC11 на оборот тридцать щелчков. Пока ручка стоит, обе фазы через подтяжки сидят на 5 В. При вращении диск размыкает контакты по очереди: сначала A, через четверть цикла B, потом A возвращается, потом B. Получается последовательность состояний 11-01-00-10 (записываю как A B) и снова 11 — код Грея, в котором за один шаг меняется ровно один бит. Порядок, в котором меняются биты, и есть направление: по часовой — цепочка 3-1-0-2-3, против — та же цепочка задом наперёд.
Отсюда три практических следствия. Первое: на один щелчок приходится четыре фронта — четыре изменения уровня на двух линиях. Считать щелчками фронты нельзя, их надо сперва декодировать, этим займёмся в следующей части. Второе: фазам нужны подтяжки — хватает INPUT_PULLUP, с ним у меня и живёт; на длинных проводах от панели до платы я ставил внешние 10 кОм, потому что от наводок внутренние подтяжки спасают хуже. Третье: кнопка под ручкой — отдельный контакт на землю, она живёт по обычным кнопочным правилам того же урока про кнопку и такого же антидребезга.
Почему направление надо честно вытаскивать из сигнала, а не угадывать: энкодеры попадают в проекты, где счёт — это деньги. Дозатор насоса, положение шторы, выбранная частота генератора. Ошибка направления на один шаг портит всё: сумма сходится, а едет мимо. Поэтому никаких упрощений вида считаем только фронты A — соберём полный декодер, который различает обе фазы.
Шестнадцать пар, восемь законных
Идея декодера — не реагировать на отдельные фронты, а сравнивать состояния. Кодируем пару фаз в два бита и запоминаем прошлое состояние. На каждом фронте собираем из старого и нового кода четырёхбитное число и заглядываем в таблицу законных переходов. Для шага по часовой законны четыре пары: 3-1, 1-0, 0-2 и 2-3 — это последовательность 11-01-00-10, записанная парами. Для шага против — те же четыре в обратную сторону: 3-2, 2-0, 0-1, 1-3. Остальные восемь пар из шестнадцати — мусор: скачок сразу на два бита, которого при честном вращении физически не бывает.
Что дают эти восемь мусорных пар? Страховка от пропусков. Если ISR пропустил фронт — скажем, пока обслуживалось другое прерывание, — декодер увидит не 3-1, а сразу 3-0. Честная таблица такую пару молча проглотит: лучше не заметить один шаг, чем насчитать два в неизвестную сторону. А вот дребезг таблица глотает лишь частично: дрожащий контакт выдаёт цепочки вида 3-1-3-1, половина которых выглядит как честные шаги туда-обратно. Поэтому на болтливых экземплярах я ставлю конденсаторы 100 нФ от каждой фазы на землю; программную сторону дребезга мы уже разбирали в уроке про антидребезг, там же и про окно в миллисекундах.
В коде вся таблица — две строки if с составными условиями через ИЛИ: даже лестница из урока про условия разворачивать её не заставляет. Главное — место жительства декодера. Он должен жить в обработчике прерывания и трогать только volatile-переменные: Serial.print внутри ISR на реальной плате подвешивает скетч, это правило из урока про прерывания. Кнопку SW при этом опрашивайте из loop() с millis-гейтом, как учил урок про millis вместо delay, — ей прерывание не нужно.
// Тупик: считаем любые смены состояния. Медленно — похоже на правду,// быстро — шаги пропадают, дребезг считается наравне со щелчками.int kodByl = 3; // обе фазы подтянуты к 5 Вint shagi = 0; void setup() { pinMode(2, INPUT_PULLUP); pinMode(3, INPUT_PULLUP); Serial.begin(9600);} void loop() { int kod = (digitalRead(2) << 1) | digitalRead(3); if (kod != kodByl) { shagi = shagi + 1; // это не шаги: мусор и дребезг тоже тут kodByl = kod; Serial.print("kod="); Serial.println(kod); } delay(30); // вот эта пауза и съедает фронты}Ручка из двух кнопок: симулятор
Движок симулятора не крутит ручек и не рисует вращающийся энкодер, поэтому в макетке ниже ручку замещают две кнопки: D2 — фаза A, D3 — фаза B. Кнопки дают ровно то, что даёт механика EC11, — переключения уровня 5 В и 0 В, а порядок переключений проигрываете вы. Светодиод показывает последнюю сторону вращения: горит — последний шаг был по часовой, погашен — против. В мониторе печатается shag= — суммарный декодированный счёт, рядом a= и b= — текущие уровни фаз; те же ступеньки в виде графика построит плоттер из урока про Serial.
Оговорка про железо: скетч ниже на настоящей плате работает без переделок — те же пины 2 и 3, тот же attachInterrupt с режимом CHANGE, та же таблица. Разница только в том, кто дёргает фронты: в симуляторе — ваши пальцы по кнопкам, на плате — зубчатый диск под ручкой. Максимальная скорость, с которой вы успеете щёлкать кнопками, — это как раз та полоса фронтов, которую у реального EC11 обязан ловить ISR.
- Прокрутите энкодер по часовой на один щелчок: нажмите D2, затем D3, отпустите D2, отпустите D3. shag= должен прирасти ровно на 4, светодиод — загореться. Посчитайте в мониторе, что за каждым приращением стоит смена ровно одного бита в a=/b=.
- Теперь против часовой: D3, D2, отпустить D3, отпустить D2. shag= вернётся к нулю, светодиод погаснет. Одна и та же таблица обслуживает оба направления — для неё важно не куда крутите, а в каком порядке меняются биты.
- Сорвите последовательность: зажмите обе кнопки почти одновременно или отпустите одну слишком рано. Найдите в мониторе момент, где shag= не изменился, — нелегальная пара проглочена молча. Сравните это поведение со счётчиком фронтов из тупик-скетча выше.
- Поменяйте в izFaz местами digitalRead(2) и digitalRead(3) и запустите заново. Направление перевернётся: по часовой станет против часовой. Это и есть цена перепутанных на макетке CLK и DT — счётчик не ломается, но едет мимо.
- Один щелчок EC11 — четыре фронта на двух фазах: счёт фронтов вместо декодирования завышает счёт в четыре раза и не знает направления
- Фазы A и B сдвинуты на четверть периода: направление определяется порядком смены битов, последовательность состояний 11-01-00-10 — код Грея, за шаг меняется один бит
- Таблица переходов: пара старое-новое в четырёх битах даёт 16 вариантов; восемь законных пар дают шаги, восемь мусорных (пропуски) глотаются молча
- Фазы — только на пины 2 и 3: внешние прерывания Uno живут на INT0 и INT1; подтяжки — INPUT_PULLUP или внешние 10 кОм на длинных проводах
- ISR — только арифметика и volatile; кнопку SW опрашивает loop() с millis-гейтом; болтливые контакты лечатся конденсаторами 100 нФ на землю
- 1. Сколько фронтов приходится на один щелчок EC11 при полном цикле?
- 2. Что видит loop() с delay(30), когда ручку крутят быстро?
- 3. Декодер встретил пару 3-0: прошлое состояние 11, новое 00. Что делает таблица переходов?
- 4. Почему фазы энкодера вешают именно на пины 2 и 3?
Частые вопросы по теме урока
- Сколько фронтов приходится на один щелчок EC11 при полном цикле?
- Полный цикл квадратуры — четыре состояния 11-01-00-10, и между соседними по одному фронту: два на фазе A, два на фазе B. Отсюда и счёт: четыре фронта — один щелчок. четыре
- Что видит loop() с delay(30), когда ручку крутят быстро?
- digitalRead возвращает кадр состояния на момент вызова. За 30 мс паузы проходят до двенадцати фронтов, и loop видит лишь финальный кадр — счёт занижен, направление потеряно. каждый фронт без потерь, но с опозданием
- Декодер встретил пару 3-0: прошлое состояние 11, новое 00. Что делает таблица переходов?
- Законные пары меняют один бит. Переход через два бита означает пропущенный фронт, и честный декодер не выдумывает шаг там, где не видел последовательности. засчитывает один шаг против часовой
- Почему фазы энкодера вешают именно на пины 2 и 3?
- ATmega328P выводит внешние прерывания на INT0 и INT1 — пины 2 и 3. Остальные пины прерываний по изменению без PCINT не умеют, а энкодеру нужен отклик на каждый фронт. только на них на Uno живут внешние прерывания INT0 и INT1