ПОДКЛЮЧЕНИЕ ЭНКОДЕРА К ARDUINO

Крутильный энкодер - очень интересная штука, при вращении рукоятки в любую сторону с небольшим шагом даёт сигналы, что позволяет например уменьшать и увеличивать значение. В отличие от потенциометра не имеет ограничения по углу поворота! Также энкодер имеет кнопку, с которой можно придумать пару интересных режимов (см. ниже).

ОСОБЕННОСТИ

  • Поворот на неограниченный угол (проворот) с отработкой значений
  • Рукоятка является кнопкой, с модуля идёт отдельный выход
  • Большая куча теории “как это работает” с примерами прошивок (работоспособность не гарантируется) – ссылка
  • Пины CLK и DT подтянуты к GND (работаем как INPUT), пин SW (кнопка) висит в воздухе, поэтому работаю с ним как INPUT_PULLUP
в магазин

СХЕМЫ И БИБЛИОТЕКИ

  • Отработка поворота с антидребезгом
  • Отработка нажатия кнопки с антидребезгом

  • Отработка нажатия и удержания кнопки

  • Отработка “нажатого поворота”

  • Работа с двумя типами экнодеров

Я не нашёл в интернете нормальных библиотек для энкодера с хорошей функциональностью, поэтому написал свою, GyverEncoder.

Важный момент, о котором не пишут нигде (по крайней мере я не нашёл): существует два типа энкодеров! Визуально они почти одинаковые (отличается размер, смотрите картинку сверху), но при повороте на один “тик” первый тип даёт один импульс, а второй тип даёт два импульса, из-за чего возникают проблемы с библиотеками и примерами из интернета. Если энкодер отрабатывается не по своему типу, то вас ждёт или два срабатывания вместо одного (как будто повернули два раза), или 0.5 срабатывания (нужно повернуть на два тика, чтобы отработался один). В моей библиотеке это всё настраивается. Также заметил, что энкодеры второго типа чаще “барахлят”. Скачать библиотеку GyverEncoder можно по кнопке ниже, она входит в пак библиотек GyverLibs.

скачать на github

Основные функции и методы библиотеки

Encoder(uint8_t, uint8_t, uint8_t);
// CLK, DT, SW

Encoder(uint8_t, uint8_t, uint8_t, boolean);
// CLK, DT, SW, тип (TYPE1 / TYPE2): TYPE1 одношаговый, TYPE2 двухшаговый. Если ваш энкодер работает странно, смените тип
  
void tick();                             // опрос энкодера, нужно вызывать постоянно или в прерывании
void setType(boolean type);              // TYPE1 / TYPE2 - тип энкодера TYPE1 одношаговый, TYPE2 двухшаговый. Если ваш энкодер работает странно, смените тип
void setTickMode(boolean tickMode);      // MANUAL / AUTO - ручной или автоматический опрос энкодера функцией tick(). (по умолчанию ручной)
void setDirection(boolean direction);    // NORM / REVERSE - направление вращения энкодера
 
boolean isTurn();                        // возвращает true при любом повороте, сама сбрасывается в false
boolean isRight();                       // возвращает true при повороте направо, сама сбрасывается в false
boolean isLeft();                        // возвращает true при повороте налево, сама сбрасывается в false
boolean isRightH();                      // возвращает true при удержании кнопки и повороте направо, сама сбрасывается в false
boolean isLeftH();                       // возвращает true при удержании кнопки и повороте налево, сама сбрасывается в false
 
boolean isPress();                       // возвращает true при нажатии кнопки, сама сбрасывается в false
boolean isRelease();                     // возвращает true при отпускании кнопки, сама сбрасывается в false
boolean isHolded();                      // возвращает true при удержании кнопки, сама сбрасывается в false
boolean isHold();                        // возвращает true при удержании кнопки, НЕ СБРАСЫВАЕТСЯ
ПЕРВЫЕ ШАГИ С ARDUINO

ВНИМАНИЕ!

Если вы не знаете, куда подключить Arduino, где взять программу для прошивки, как установить драйвера, как всё настроить и как устанавливать библиотеки – читайте статейку “Первые шаги с Arduino”, ссылка справа от этого текста. Там же разобраны типичные ошибки, описаны варианты питания и есть краткий FAQ.

ПЕРВЫЕ ШАГИ С ARDUINO

ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ

#define CLK 7
#define DT 8
#define SW 9

#include "GyverEncoder.h"
Encoder enc1(CLK, DT, SW);

void setup() {
  Serial.begin(9600);
}

void loop() {
	// обязательная функция отработки. Должна постоянно опрашиваться
  enc1.tick();
  
  if (enc1.isTurn()) {     // если был совершён поворот (индикатор поворота в любую сторону)
    // ваш код
  }
  
  if (enc1.isRight()) Serial.println("Right");         // если был поворот
  if (enc1.isLeft()) Serial.println("Left");
  
  if (enc1.isRightH()) Serial.println("Right holded"); // если было удержание + поворот
  if (enc1.isLeftH()) Serial.println("Left holded");
  
  if (enc1.isPress()) Serial.println("Press");         // нажатие на кнопку (+ дебаунс)
  if (enc1.isRelease()) Serial.println("Release");     // отпускание кнопки (+ дебаунс)
  if (enc1.isHolded()) Serial.println("Holded");       // если была удержана и энк не поворачивался
  //if (enc1.isHold()) Serial.println("Hold");         // возвращает состояние кнопки
}
#define CLK 7
#define DT 8
#define SW 9

#include "GyverEncoder.h"
Encoder enc1(CLK, DT, SW);

int value = 0;

void setup() {
  Serial.begin(9600);
  enc1.setType(TYPE2);    // тип энкодера TYPE1 одношаговый, TYPE2 двухшаговый. Если ваш энкодер работает странно, смените тип
}

void loop() {
	// обязательная функция отработки. Должна постоянно опрашиваться
  enc1.tick();
  
  if (enc1.isRight()) value++;      // если был поворот направо, увеличиваем на 1
  if (enc1.isLeft()) value--;	    // если был поворот налево, уменьшаем на 1
  
  if (enc1.isRightH()) value += 5; 	// если было удержание + поворот направо, увеличиваем на 5
  if (enc1.isLeftH()) value -= 5;	// если было удержание + поворот налево, уменьшаем на 5  

  if (enc1.isTurn()) {       // если был совершён поворот (индикатор поворота в любую сторону)
    Serial.println(value);   // выводим значение при повороте
  }  
}
// два энкодера

#include "GyverEncoder.h"
Encoder enc1(4, 3, 2);
Encoder enc2(7, 6, 5);

void setup() {
  Serial.begin(9600);
}

void loop() {
	// обязательная функция отработки. Должна постоянно опрашиваться
  enc1.tick();
  enc2.tick();
  
  if (enc1.isLeft()) Serial.println("enc 1 left");
  if (enc1.isRight()) Serial.println("enc 1 right");
  if (enc2.isLeft()) Serial.println("enc 2 left");
  if (enc2.isRight()) Serial.println("enc 2 right");
}
/*
	В последнее время китайцы стали делать одинаковые модули (ку 40)
	с разными типами энкодеров - полный период и полпериода.
	Если ваш энкодер ведёт себя странно (один тик считает за два поворота),
	то смените тип энкодера
*/

#define CLK 4
#define DT 3
#define SW 2

#include "GyverEncoder.h"
Encoder enc1(CLK, DT, SW);

void setup() {
  Serial.begin(9600);
  enc1.setType(TYPE2);    // тип энкодера TYPE1 одношаговый, TYPE2 двухшаговый. Если ваш энкодер работает странно, смените тип
}

void loop() {
	// обязательная функция отработки. Должна постоянно опрашиваться
  enc1.tick();
  
  if (enc1.isRight()) Serial.println("Right");         // если был поворот
  if (enc1.isLeft()) Serial.println("Left");
  if (enc1.isRightH()) Serial.println("Right holded"); // если было удержание + поворот
  if (enc1.isLeftH()) Serial.println("Left holded");
}
#define CLK 6
#define DT 5
#define SW 4

#include "GyverEncoder.h"
Encoder enc1(CLK, DT, SW);

void setup() {
  Serial.begin(9600);
  enc1.setTickMode(AUTO);
}

void loop() {
  // enc1.tick();  // не нужна, в этом режиме (AUTO) она входит в каждую функцию!
  
  if (enc1.isTurn()) {     // если был совершён поворот (индикатор поворота в любую сторону)
    // ваш код
  }
  
  if (enc1.isRight()) Serial.println("Right");         // если был поворот
  if (enc1.isLeft()) Serial.println("Left");
  
  if (enc1.isRightH()) Serial.println("Right holded"); // если было удержание + поворот
  if (enc1.isLeftH()) Serial.println("Left holded");
  
  if (enc1.isPress()) Serial.println("Press");         // нажатие на кнопку (+ дебаунс)
  if (enc1.isRelease()) Serial.println("Release");     // отпускание кнопки (+ дебаунс)
  if (enc1.isHolded()) Serial.println("Holded");       // если была удержана и энк не поворачивался
  //if (enc1.isHold()) Serial.println("Hold");         // возвращает состояние кнопки
}
/*
   Пример работы с энкодером с прерыванием. Максимальная чёткость работы
   в любом быдлокоде!
*/

#define CLK 2
#define DT 3
#define SW 4

#include "GyverEncoder.h"
Encoder enc1(CLK, DT, SW);

void setup() {
  Serial.begin(9600);
  attachInterrupt(0, isr, CHANGE);    // прерывание на 2 пине! CLK у энка
}

void isr() {
  enc1.tick();  // отработка в прерывании
}

void loop() {
  enc1.tick();  // отработка
  if (enc1.isRight()) Serial.println("Right");         // если был поворот
  if (enc1.isLeft()) Serial.println("Left");

  if (enc1.isRightH()) Serial.println("Right holded"); // если было удержание + поворот
  if (enc1.isLeftH()) Serial.println("Left holded");
}
/*
 * Отработка по прерыванию таймера
 */

#define CLK 7
#define DT 8
#define SW 9

#include "GyverEncoder.h"
#include "TimerOne.h"
Encoder enc1(CLK, DT, SW);

void setup() {
  Serial.begin(9600);
  enc1.setType(TYPE2);    // тип энкодера TYPE1 одношаговый, TYPE2 двухшаговый. Если ваш энкодер работает странно, смените тип

  Timer1.initialize(1000);            // установка таймера на каждые 1000 микросекунд (= 1 мс)
  Timer1.attachInterrupt(timerIsr);   // запуск таймера
}

void timerIsr() {   // прерывание таймера
  enc1.tick();     // отработка теперь находится здесь
}

void loop() {
  if (enc1.isRight()) Serial.println("Right");         // если был поворот
  if (enc1.isLeft()) Serial.println("Left");
  if (enc1.isRightH()) Serial.println("Right holded"); // если было удержание + поворот
  if (enc1.isLeftH()) Serial.println("Left holded");  
}

2018-09-25T13:41:36+00:00