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

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

ОСОБЕННОСТИ

  • Поворот рукоятки без ограничения

  • Типичный энкодер имеет 28 “тиков” на оборот

  • Рукоятка является кнопкой, с модуля идёт отдельный выход

  • Большая куча теории “как это работает” с примерами прошивок (работоспособность не гарантируется) – ссылка

в магазин

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

Подключается модуль энкодера очень просто: питание на питание, логические пины CLK, DT (выводы энкодера) и SW (вывод кнопки) на пины (D или A). У круглых модулей выводы энкодера подписаны как S1 и S2, а вывод кнопки как Key. От порядка подключения выводов энкодера зависит “направление” его работы, но это можно поправить в скетче.

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

  • Отработка поворота рукоятки энкодера
  • Отработка “нажатого поворота” энкодера
  • Отработка “быстрого поворота” энкодера
  • Работа с двумя типами энкодеров
  • Отработка нажатия/клика/удержания кнопки с антидребезгом контактов

Внимание! Параметры защиты от дребезга контактов и таймаут удержания кнопки настраивается в файле .h библиотеки почти в самом начале.

Скачать библиотеку 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 - направление вращения энкодера
void setFastTimeout(int timeout);        // установка таймаута быстрого поворота
boolean isTurn();                        // возвращает true при любом повороте, сама сбрасывается в false
boolean isRight();                       // возвращает true при повороте направо, сама сбрасывается в false
boolean isLeft();                        // возвращает true при повороте налево, сама сбрасывается в false
boolean isRightH();                      // возвращает true при удержании кнопки и повороте направо, сама сбрасывается в false
boolean isLeftH();                       // возвращает true при удержании кнопки и повороте налево, сама сбрасывается в false
boolean isFastR();                       // возвращает true при быстром повороте
boolean isFastL();                       // возвращает true при быстром повороте
boolean isPress();                       // возвращает true при нажатии кнопки, сама сбрасывается в false
boolean isRelease();                     // возвращает true при отпускании кнопки, сама сбрасывается в false
boolean isClick();                       // возвращает 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);   // выводим значение при повороте
}  
}
#define CLK 2
#define DT 3
#define SW 4
#include "GyverEncoder.h"
Encoder enc1(CLK, DT, SW);
int value = 0;
void setup() {
Serial.begin(9600);
enc1.setType(TYPE1);        // тип энкодера TYPE1 одношаговый, TYPE2 двухшаговый. Если ваш энкодер работает странно, смените тип\=
enc1.setFastTimeout(40);    // таймаут на скорость isFastR. По умолч. 50
}
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.isFastR()) value += 10;    // если был быстрый поворот направо, увеличиваем на 10
if (enc1.isFastL()) value -= 10;    // если был быстрый поворот налево, уменьшаем на 10
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");  
}

Если вам важна не функциональность библиотеки, а максимальная скорость работы с энкодером и минимальное время его опроса, предлагаю следующий код. Здесь используется аппаратное прерывание (одно на один энкодер), для увеличения скорости выполнения прерывания используется функция bitRead вместо digitalRead (скорость выполнения разнится в десятки раз). Также в этом коде предусмотрен выбор типа энкодера соответствующей настройкой. Отработка энкодера идёт параллельно выполнению скетча, переменная encCounter меняет своё значение при повороте рукоятки и выводится через порт.

/*
Максимально быстрый универсальный код для обработки энкодера
Работает на перывании (используется одно)
*/
#define ENC_A 2       // пин энкодера
#define ENC_B 4       // пин энкодера
#define ENC_TYPE 1    // тип энкодера, 0 или 1
volatile int encCounter;
volatile boolean state0, lastState, turnFlag;
void setup() {
Serial.begin(9600);
attachInterrupt(0, int0, CHANGE);
}
void int0() {
state0 = bitRead(PIND, ENC_A);
if (state0 != lastState) {
#if (ENC_TYPE == 1)
turnFlag = !turnFlag;
if (turnFlag)
encCounter += (bitRead(PIND, ENC_B) != lastState) ? -1 : 1;
#else
encCounter += (bitRead(PIND, ENC_B) != lastState) ? -1 : 1;
#endif
lastState = state0;
}
}
void loop() {
Serial.println(encCounter);
delay(100);
}

Подключение энкодера к Arduino. Библиотека GyverEncoder 3.0
4.8 (96.63%) 95 vote[s]

2019-04-10T09:29:58+03:00