Эксперимент 34. Подключение энкодера

В этом эксперименте мы научимся подключать инкрементальный энкодер и получать с него данные. В комплект конструктора входит модуль энкодера — небольшая печатная плата с энкодером и разъемом для удобного подключения к макетной плате. Модуль также включает встроенные подтягивающие резисторы на выводах A и B и кнопку, которая срабатывает при нажатии на вал.

Подписи выводов модуля:

  • GND — общий контакт
  • + — Питание
  • SW — выход кнопки
  • DT — Сигнал А
  • CLK — Сигнал B

Соберем устройство, которое отображает на экране число, увеличивающееся при вращении энкодера по часовой стрелке и уменьшающееся при вращении против часовой стрелки.

Схема эксперимента

Рисунок 1. Электрическая принципиальная схема эксперимента

Обрати внимание, что резисторы R1 и R2 уже припаяны на плате модуля энкодера и нам не нужно устанавливать на макетной плате. Зачем же нужны резисторы R3, R4 и конденсаторы C1, C2? Дело в том, что контакты энкодера, как и любые механические контакты, подвержены неприятному эффекту — дребезгу контактов. На самом деле при нажатии на кнопку и отпускании кнопки замыкание и размыкание контактов не происходит мгновенно. После замыкания происходят многократные неконтролируемые замыкания и размыкания контактов за счет упругости материалов и деталей контактной системы — некоторое время контакты отскакивают друг от друга при соударениях, размыкая и замыкая электрическую цепь. Это и называется дребезгом контактов. Этому явлению подвержены и контакты энкодера.

С дребезгом нужно бороться программным или электрическим способом. В нашей схеме резистор с конденсатором являются фильтром для коротких импульсов, возникающих при дребезге контактов. Благодаря этому фильтру данный эффект можно в значительной мере устранить.

Рисунок 2. Монтажная схема эксперимента

Программный код эксперимента

Exp34.ino
  1. #include <LCDI2C_Multilingual.h>
  2.  
  3. #define ENC_A 13
  4. #define ENC_B 12
  5. #define DEFAULT_I2C_ADDR 0x3F // Или 0x27 в зависимости от твоей платы IoT
  6.  
  7. LCDI2C_Generic lcd(DEFAULT_I2C_ADDR, 16, 2);
  8.  
  9. bool value_a = 0;
  10. bool value_a_old = 0;
  11. bool value_b = 0;
  12. int count = 0;
  13.  
  14. void printLcd(int number) {
  15. lcd.clear();
  16. lcd.print(number);
  17. }
  18.  
  19. void setup() {
  20. Serial.begin(9600);
  21. lcd.init();
  22. lcd.setBacklight(0);
  23. pinMode(ENC_A, INPUT);
  24. pinMode(ENC_B, INPUT);
  25. printLcd(count);
  26. }
  27.  
  28. void loop() {
  29. value_a = digitalRead(ENC_A);
  30. value_b = digitalRead(ENC_B);
  31.  
  32. if (value_a != value_a_old) {
  33. if ((value_a and value_b) or (not value_a and not value_b)) {
  34. count++;
  35. Serial.println("+");
  36. printLcd(count);
  37. }
  38. else if ((not value_a and value_b) or (value_a and not value_b)) {
  39. count--;
  40. Serial.println("-");
  41. printLcd(count);
  42. }
  43. }
  44. value_a_old = value_a;
  45. }

Настраиваем выводы для работы с энкодером:

  1. pinMode(ENC_A, INPUT);
  2. pinMode(ENC_B, INPUT);

Переменная old_value_a будет хранить предыдущее состояния сигнала A, в переменной count будем считать количество сигналов от энкодера.

Чтобы лучше понять алгоритм работы программы еще раз посмотрим на график сигналов энкодера:

В основном цикле программы получаем текущие состояния линий А и B. Если состояние линии А изменилось, то проверяем условие вращения против часовой стрелки:

  1. if ((value_a and value_b) or (not value_a and not value_b))

Если уровни сигналов А и B оба стали высокими (состояние 2) или оба стали низкими (состояние 0), то увеличиваем значение count на 1, печатаем в терминал символ + и обновляем информацию на дисплее.

Если условие выше не подтвердилось, то проверяем второй вариант: вращение по часовой стрелке:

  1. else if ((not value_a and value_b) or (value_a and not value_b)):

Если уровень сигнала А стал низким, а сигнала B высоким (состояние 3) или уровень сигнала А стал высоким, а B низким (состояние 1), то уменьшаем значение count на 1, печатаем символ - в терминал и обновляем информацию на дисплее.

Если ни одно из этих условий не выполнено, то игнорируем сигналы. Перед завершением итерации записываем текущее состояние линии А как старое, для использования в следующей итерации.

  1. value_a_old = value_a;

Дополнительное задание

  • Попробуй убрать конденсаторы из схемы и покрутить энкодер. Программа будет работать не так, как хотелось бы. Необходимость конденсаторов станет очевидной.