Энкодер и Arduino: схема подключения KY-040, скетч и борьба с дребезгом
Энкодер выглядит как обычная ручка громкости, но работает иначе. Потенциометр говорит «я повёрнут на треть», а энкодер — «меня только что крутнули на один щелчок вправо». У него нет ни начала, ни конца: крутите сколько угодно в любую сторону. Именно поэтому его ставят в меню приборов, в 3D-принтеры и в самодельные пульты.
Внутри два контакта, замкнутых через прорези диска. Они срабатывают со сдвигом, и по тому, какой сработал первым, программа понимает направление вращения. Эти два сигнала называются CLK и DT, а третий вывод SW — обычная кнопка: на ручку можно нажать.
Схема подключения
Распиновка
| Провод | Куда и зачем |
|---|---|
| CLK | Первый фазовый выход. Лучше на пин с прерыванием — 2 или 3. |
| DT | Второй фазовый выход, по нему определяется направление. |
| SW | Кнопка нажатия на ручку. Замыкается на землю. |
| + (VCC) | Питание 5 В. |
| GND | Земля. |
Характеристики
- Тип
- инкрементальный, механический
- Шагов на оборот
- 20 щелчков
- Питание
- 3,3 или 5 В
- Кнопка
- есть, нажатие на ось
- Подтягивающие резисторы
- на плате модуля уже стоят
Дребезг — главная проблема энкодера
Контакты внутри механические, и в момент переключения они не замыкаются один раз, а несколько раз подряд стучат за пару миллисекунд. Микроконтроллер успевает посчитать этот стук как десяток поворотов. Внешне это выглядит так: крутите вправо на один щелчок, а счётчик прыгает на пять или дёргается туда-сюда.
Лечится двумя способами. Программно — игнорировать новые срабатывания в течение двух-трёх миллисекунд после предыдущего, как в скетче ниже. Аппаратно — повесить конденсаторы на сто нанофарад между каждым фазовым выводом и землёй; тогда стук сглаживается ещё до того, как доходит до платы. Надёжнее всего сделать и то, и другое.
Библиотека
не нужна— Энкодер читается обычными digitalRead и прерыванием. Библиотеки вроде Encoder или NewEncoder пригодятся, когда энкодеров несколько или нужен счёт на высокой скорости.
Готовый скетч
const int PIN_CLK = 2;
const int PIN_DT = 3;
const int PIN_SW = 4;
volatile int counter = 0;
volatile unsigned long lastTurn = 0;
void onTurn() {
// отсекаем дребезг: щелчки чаще 3 мс — это стук контактов, а не рука
unsigned long now = millis();
if (now - lastTurn < 3) return;
lastTurn = now;
// если второй сигнал отличается от первого — крутят вправо
if (digitalRead(PIN_DT) != digitalRead(PIN_CLK)) {
counter++;
} else {
counter--;
}
}
void setup() {
Serial.begin(9600);
pinMode(PIN_CLK, INPUT);
pinMode(PIN_DT, INPUT);
pinMode(PIN_SW, INPUT_PULLUP); // кнопка замыкает на землю
attachInterrupt(digitalPinToInterrupt(PIN_CLK), onTurn, FALLING);
}
void loop() {
static int shown = 0;
if (counter != shown) {
shown = counter;
Serial.println(shown);
}
if (digitalRead(PIN_SW) == LOW) {
Serial.println("Нажата ручка");
counter = 0;
delay(300); // чтобы одно нажатие не считалось десятью
}
}Скетч собирается arduino-cli под Arduino UNO
Слово volatile перед переменными обязательно: их меняет прерывание, и без этой пометки компилятор решит, что значение не меняется, и подставит старое из регистра.
Частые ошибки
Счётчик прыгает на несколько шагов за щелчок
Дребезг контактов. Добавьте отсечку по времени, как в скетче, и конденсаторы на 100 нФ между CLK и землёй, DT и землёй.
Направление определяется случайно
Перепутаны местами CLK и DT либо энкодер подключён к пинам без прерываний и часть импульсов теряется. Проверьте, что фазовый вывод сидит на пине 2 или 3.
Кнопка срабатывает сама по себе
Забыт режим INPUT_PULLUP. Без внутренней подтяжки вывод болтается в воздухе и ловит наводки.
Значение не меняется, хотя прерывание работает
Переменная объявлена без volatile. Компилятор оптимизировал доступ к ней и не видит изменений, сделанных внутри прерывания.
Энкодер путают с потенциометром
У энкодера нет крайних положений и нет «текущего значения» — он сообщает только приращение. Если нужен именно уровень от нуля до максимума, берите потенциометр.
Что понадобится
- Энкодер KY-040
- Arduino UNO
- Провода «папа-мама»
- Два конденсатора 100 нФ — по желанию, против дребезга