← До всіх проєктів
Дисплеї Середній

Енкодер KY-040 і індикатор TM1637: лічильник на перериваннях

Поворотний енкодер — «нескінченна» ручка для меню й налаштувань. Розбираємо квадратурний сигнал, ловимо кроки перериванням, щоб не пропустити жодного, і виводимо значення на чотирицифровий індикатор TM1637.

Складність
Середній
Час
40 min
Категорія
Дисплеї
Оновлено

Про проєкт

Потенціометр має упори й аналоговий вихід, а енкодер крутиться нескінченно і видає цифрові імпульси: кожне клацання — один крок. Ще в ньому є кнопка під ручкою. Ідеально для меню, гучності чи уставок термостата. Лічимо кроки на перериванні й показуємо число на індикаторі TM1637.

Що знадобиться

  • ESP32 DevKit
  • Енкодер-модуль KY-040
  • 4-розрядний 7-сегментний індикатор TM1637
  • Бібліотека TM1637Display (avishorp)

Схема

Модуль Вивід ESP32
KY-040 CLK GPIO25
DT GPIO26
SW GPIO27
+ / GND 3V3 / GND
TM1637 CLK GPIO22
DIO GPIO23
VCC / GND 3V3 / GND

Модуль KY-040 уже має підтягувальні резистори 10 кОм на CLK і DT, тож досить INPUT. На кнопці SW підтяжки на модулі немає, тому для неї INPUT_PULLUP.

Квадратурний сигнал

Усередині енкодера два контакти, зсунуті на чверть кроку, — канали A (CLK) і B (DT):

за годинниковою:   CLK ‾‾|__|‾‾      DT ‾|__|‾‾‾
проти годинникової: CLK ‾|__|‾‾‾      DT ‾‾|__|‾‾

У момент фронту CLK дивимося на DT: якщо рівні різні, крок в один бік, якщо однакові, в інший. Звідси порівняння digitalRead(DT) != digitalRead(CLK) у коді.

Навіщо переривання

Якщо опитувати енкодер у loop(), а там є delay() або повільний вивід на дисплей, швидкі повороти загубляться. Переривання викликає функцію onClk() у момент фронту, що б не робила програма.

Правила для обробника:

  • позначка IRAM_ATTR, щоб код лежав у швидкій пам'яті;
  • спільна змінна — volatile, інакше компілятор може «закешувати» її в loop();
  • ніяких Serial.print() і delay() усередині: лише мінімальна робота.

Код

// Енкодер KY-040 (переривання) + індикатор TM1637
#include <TM1637Display.h>

TM1637Display display(22, 23);   // CLK, DIO
const int ENC_CLK = 25, ENC_DT = 26, ENC_SW = 27;
volatile int value = 0;

void IRAM_ATTR onClk() {
  // на кожному фронті CLK рівень DT показує напрямок
  if (digitalRead(ENC_DT) != digitalRead(ENC_CLK)) value--;
  else value++;
}

void setup() {
  pinMode(ENC_CLK, INPUT);
  pinMode(ENC_DT, INPUT);
  pinMode(ENC_SW, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(ENC_CLK), onClk, RISING);
  display.setBrightness(5);
}

void loop() {
  if (digitalRead(ENC_SW) == LOW) value = 0;   // натискання ручки — скидання
  display.showNumberDec(value);
  delay(20);
}

Корисно знати

  • Брязкіт. Дешеві енкодери деренчать, і лічильник може «стрибнути» на 2 замість 1. Допоможе конденсатор 10–100 нФ із CLK і DT на землю або бібліотека зі скінченним автоматом (ESP32Encoder використовує апаратний лічильник PCNT).
  • Кроки й клацання. У більшості KY-040 на одне клацання припадає один повний цикл квадратури. Деякі дають два імпульси на клацання: тоді ділимо на 2.
  • TM1637 — не I2C, хоч теж має два дроти. Його протокол схожий, але без адрес, тож кожен індикатор потребує своєї пари виводів.