add lib
This commit is contained in:
parent
eb1a420979
commit
59a740b947
21
libraries/GyverEncoder/LICENSE
Normal file
21
libraries/GyverEncoder/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Alex
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
149
libraries/GyverEncoder/README.md
Normal file
149
libraries/GyverEncoder/README.md
Normal file
@ -0,0 +1,149 @@
|
|||||||
|

|
||||||
|

|
||||||
|
# GyverEncoder
|
||||||
|
Библиотека для расширенной работы с энкодером
|
||||||
|
**ВНИМАНИЕ, БИБЛИОТЕКА УСТАРЕЛА! ИСПОЛЬЗУЙ БИБЛИОТЕКУ [EncButton](https://github.com/GyverLibs/EncButton)**
|
||||||
|
- Отработка поворота энкодера
|
||||||
|
- Отработка "нажатого поворота"
|
||||||
|
- Отработка "быстрого поворота"
|
||||||
|
- Несколько алгоритмов опроса энкодера
|
||||||
|
- Выбор подтяжки подключения энкодера
|
||||||
|
- Работа с двумя типами экнодеров
|
||||||
|
- Работа с внешним энкодером (через расширитель пинов и т.п.)
|
||||||
|
- Отработка нажатия/удержания кнопки с антидребезгом
|
||||||
|
|
||||||
|
### Совместимость
|
||||||
|
Совместима со всеми Arduino платформами (используются Arduino-функции)
|
||||||
|
|
||||||
|
### Документация
|
||||||
|
К библиотеке есть [расширенная документация](https://alexgyver.ru/encoder/)
|
||||||
|
|
||||||
|
## Содержание
|
||||||
|
- [Установка](#install)
|
||||||
|
- [Инициализация](#init)
|
||||||
|
- [Использование](#usage)
|
||||||
|
- [Пример](#example)
|
||||||
|
- [Версии](#versions)
|
||||||
|
- [Баги и обратная связь](#feedback)
|
||||||
|
|
||||||
|
<a id="install"></a>
|
||||||
|
## Установка
|
||||||
|
- Библиотеку можно найти по названию **GyverEncoder** и установить через менеджер библиотек в:
|
||||||
|
- Arduino IDE
|
||||||
|
- Arduino IDE v2
|
||||||
|
- PlatformIO
|
||||||
|
- [Скачать библиотеку](https://github.com/GyverLibs/GyverEncoder/archive/refs/heads/main.zip) .zip архивом для ручной установки:
|
||||||
|
- Распаковать и положить в *C:\Program Files (x86)\Arduino\libraries* (Windows x64)
|
||||||
|
- Распаковать и положить в *C:\Program Files\Arduino\libraries* (Windows x32)
|
||||||
|
- Распаковать и положить в *Документы/Arduino/libraries/*
|
||||||
|
- (Arduino IDE) автоматическая установка из .zip: *Скетч/Подключить библиотеку/Добавить .ZIP библиотеку…* и указать скачанный архив
|
||||||
|
- Читай более подробную инструкцию по установке библиотек [здесь](https://alexgyver.ru/arduino-first/#%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA)
|
||||||
|
|
||||||
|
<a id="init"></a>
|
||||||
|
## Инициализация
|
||||||
|
```cpp
|
||||||
|
Encoder enc; // не привязан к пину
|
||||||
|
Encoder enc(пин CLK, пин DT); // энкодер без кнопки (ускоренный опрос)
|
||||||
|
Encoder enc(пин CLK, пин DT, пин SW); // энкодер с кнопкой
|
||||||
|
Encoder enc(пин CLK, пин DT, пин SW, тип); // энкодер с кнопкой и указанием типа
|
||||||
|
Encoder enc(пин CLK, пин DT, ENC_NO_BUTTON, тип); // энкодер без кнопкой и с указанием типа
|
||||||
|
```
|
||||||
|
|
||||||
|
<a id="usage"></a>
|
||||||
|
## Использование
|
||||||
|
```cpp
|
||||||
|
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); // установка таймаута быстрого поворота
|
||||||
|
void setPinMode(bool mode); // тип подключения энкодера, подтяжка HIGH_PULL (внутренняя) или LOW_PULL (внешняя на GND)
|
||||||
|
void setBtnPinMode(bool mode); // тип подключения кнопки, подтяжка HIGH_PULL (внутренняя) или LOW_PULL (внешняя на GND)
|
||||||
|
|
||||||
|
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 при удержании кнопки, НЕ СБРАСЫВАЕТСЯ
|
||||||
|
boolean isSingle(); // возвращает true при одиночном клике (после таймаута), сама сбрасывается в false
|
||||||
|
boolean isDouble(); // возвращает true при двойном клике, сама сбрасывается в false
|
||||||
|
void resetStates(); // сбрасывает все is-флаги
|
||||||
|
```
|
||||||
|
|
||||||
|
<a id="example"></a>
|
||||||
|
## Пример
|
||||||
|
Остальные примеры смотри в **examples**!
|
||||||
|
```cpp
|
||||||
|
#define CLK 2
|
||||||
|
#define DT 3
|
||||||
|
#define SW 4
|
||||||
|
|
||||||
|
#include "GyverEncoder.h"
|
||||||
|
Encoder enc1(CLK, DT, SW); // для работы c кнопкой
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
enc1.setType(TYPE2);
|
||||||
|
}
|
||||||
|
|
||||||
|
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"); // то же самое, что isClick
|
||||||
|
|
||||||
|
if (enc1.isClick()) Serial.println("Click"); // одиночный клик
|
||||||
|
if (enc1.isSingle()) Serial.println("Single"); // одиночный клик (с таймаутом для двойного)
|
||||||
|
if (enc1.isDouble()) Serial.println("Double"); // двойной клик
|
||||||
|
|
||||||
|
|
||||||
|
if (enc1.isHolded()) Serial.println("Holded"); // если была удержана и энк не поворачивался
|
||||||
|
//if (enc1.isHold()) Serial.println("Hold"); // возвращает состояние кнопки
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<a id="versions"></a>
|
||||||
|
## Версии
|
||||||
|
- v3.6 от 16.09.2019 - Возвращены дефайны настроек
|
||||||
|
- v4.0 от 13.11.2019
|
||||||
|
- Оптимизирован код
|
||||||
|
- Исправлены баги
|
||||||
|
- Добавлены другие алгоритмы опроса
|
||||||
|
- Добавлена возможность полностью убрать кнопку (экономия памяти)
|
||||||
|
- Добавлена возможность подключения внешнего энкодера
|
||||||
|
- Добавлена настройка подтяжки пинов
|
||||||
|
- v4.1: Исправлено изменение подтяжек
|
||||||
|
- v4.2
|
||||||
|
- Добавлена поддержка TYPE1 для алгоритма PRECISE_ALGORITHM
|
||||||
|
- Добавлена отработка двойного клика: isSingle / isDouble
|
||||||
|
- v4.3: Исправлено ложное isSingle
|
||||||
|
- v4.4: Добавлен метод resetStates, сбрасывает все is-флаги и счётчики
|
||||||
|
- v4.5: Улучшен алгоритм BINARY_ALGORITHM (спасибо Ярославу Курусу)
|
||||||
|
- v4.6: BINARY_ALGORITHM пофикшен для TYPE1, добавлена isReleaseHold
|
||||||
|
- v4.7: Исправлен случайный нажатый поворот в BINARY_ALGORITHM
|
||||||
|
- v4.8: увеличена производительность для AVR Arduino
|
||||||
|
- v4.9: быстрый поворот отключен если кнопка удерживается
|
||||||
|
|
||||||
|
<a id="feedback"></a>
|
||||||
|
## Баги и обратная связь
|
||||||
|
При нахождении багов создавайте **Issue**, а лучше сразу пишите на почту [alex@alexgyver.ru](mailto:alex@alexgyver.ru)
|
||||||
|
Библиотека открыта для доработки и ваших **Pull Request**'ов!
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
// пример с прерываниями pinChangeInterrupt (прерывания на любом пине)
|
||||||
|
// только для ATmega328 (UNO, Nano, Pro Mini)
|
||||||
|
|
||||||
|
#define SW 0
|
||||||
|
#define DT 2
|
||||||
|
#define CLK 3
|
||||||
|
|
||||||
|
#include "GyverEncoder.h"
|
||||||
|
Encoder enc1(CLK, DT, SW);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
// настроить PCINT
|
||||||
|
attachPCINT(CLK);
|
||||||
|
attachPCINT(DT);
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
// функция для настройки PCINT для ATmega328 (UNO, Nano, Pro Mini)
|
||||||
|
uint8_t attachPCINT(uint8_t pin) {
|
||||||
|
if (pin < 8) { // D0-D7 // PCINT2
|
||||||
|
PCICR |= (1 << PCIE2);
|
||||||
|
PCMSK2 |= (1 << pin);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
else if (pin > 13) { //A0-A5 // PCINT1
|
||||||
|
PCICR |= (1 << PCIE1);
|
||||||
|
PCMSK1 |= (1 << pin - 14);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else { // D8-D13 // PCINT0
|
||||||
|
PCICR |= (1 << PCIE0);
|
||||||
|
PCMSK0 |= (1 << pin - 8);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Векторы PCINT, нужно кинуть сюда тики
|
||||||
|
// не обязательно в каждый вектор, достаточно в тот, который задействован
|
||||||
|
// пины 0-7: PCINT2
|
||||||
|
// пины 8-13: PCINT0
|
||||||
|
// пины A0-A5: PCINT1
|
||||||
|
ISR(PCINT0_vect) {
|
||||||
|
//enc1.tick();
|
||||||
|
}
|
||||||
|
ISR(PCINT1_vect) {
|
||||||
|
//enc1.tick();
|
||||||
|
}
|
||||||
|
ISR(PCINT2_vect) {
|
||||||
|
enc1.tick();
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
В последнее время китайцы стали делать одинаковые модули (ку 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");
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
#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"); // возвращает состояние кнопки
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
#define CLK 2
|
||||||
|
#define DT 3
|
||||||
|
#define SW 4
|
||||||
|
|
||||||
|
#include "GyverEncoder.h"
|
||||||
|
//Encoder enc1(CLK, DT); // для работы без кнопки
|
||||||
|
Encoder enc1(CLK, DT, SW); // для работы c кнопкой
|
||||||
|
//Encoder enc1(CLK, DT, SW, TYPE2); // для работы c кнопкой и сразу выбираем тип
|
||||||
|
//Encoder enc1(CLK, DT, ENC_NO_BUTTON, TYPE2); // для работы без кнопки и сразу выбираем тип
|
||||||
|
|
||||||
|
// Варианты инициализации:
|
||||||
|
// Encoder enc; // не привязан к пину
|
||||||
|
// Encoder enc(пин CLK, пин DT); // энкодер без кнопки (ускоренный опрос)
|
||||||
|
// Encoder enc(пин CLK, пин DT, пин SW); // энкодер с кнопкой
|
||||||
|
// Encoder enc(пин CLK, пин DT, пин SW, тип); // энкодер с кнопкой и указанием типа
|
||||||
|
// Encoder enc(пин CLK, пин DT, ENC_NO_BUTTON, тип); // энкодер без кнопкой и с указанием типа
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
enc1.setType(TYPE2);
|
||||||
|
}
|
||||||
|
|
||||||
|
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"); // то же самое, что isClick
|
||||||
|
|
||||||
|
if (enc1.isClick()) Serial.println("Click"); // одиночный клик
|
||||||
|
if (enc1.isSingle()) Serial.println("Single"); // одиночный клик (с таймаутом для двойного)
|
||||||
|
if (enc1.isDouble()) Serial.println("Double"); // двойной клик
|
||||||
|
|
||||||
|
|
||||||
|
if (enc1.isHolded()) Serial.println("Holded"); // если была удержана и энк не поворачивался
|
||||||
|
//if (enc1.isHold()) Serial.println("Hold"); // возвращает состояние кнопки
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
#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); // выводим значение при повороте
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
Пример работы с энкодером с прерыванием. Максимальная чёткость работы
|
||||||
|
в любом быдлокоде!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CLK 2
|
||||||
|
#define DT 3
|
||||||
|
#define SW 4
|
||||||
|
|
||||||
|
#include "GyverEncoder.h"
|
||||||
|
Encoder enc1(CLK, DT, SW);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
attachInterrupt(0, isrCLK, CHANGE); // прерывание на 2 пине! CLK у энка
|
||||||
|
attachInterrupt(1, isrDT, CHANGE); // прерывание на 3 пине! DT у энка
|
||||||
|
}
|
||||||
|
|
||||||
|
void isrCLK() {
|
||||||
|
enc1.tick(); // отработка в прерывании
|
||||||
|
}
|
||||||
|
void isrDT() {
|
||||||
|
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");
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
// подключаем "внешний" энкодер, для работы с расширителями пинов например
|
||||||
|
|
||||||
|
#define SW 0
|
||||||
|
#define DT 2
|
||||||
|
#define CLK 3
|
||||||
|
|
||||||
|
#include "GyverEncoder.h"
|
||||||
|
Encoder enc1;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
enc1.setType(TYPE2);
|
||||||
|
pinMode(SW, INPUT_PULLUP);
|
||||||
|
pinMode(CLK, INPUT);
|
||||||
|
pinMode(DT, INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// подаём значения напрямую в tick()
|
||||||
|
// можно подавать лог. величины с любых расширителей пинов!!!
|
||||||
|
// Здесь в качестве примера digitalRead
|
||||||
|
enc1.tick(digitalRead(CLK), digitalRead(DT), !digitalRead(SW));
|
||||||
|
|
||||||
|
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.isClick()) Serial.println("Click"); // отпускание кнопки (+ дебаунс)
|
||||||
|
//if (enc1.isRelease()) Serial.println("Release"); // то же самое, что isClick
|
||||||
|
|
||||||
|
if (enc1.isHolded()) Serial.println("Holded"); // если была удержана и энк не поворачивался
|
||||||
|
//if (enc1.isHold()) Serial.println("Hold"); // возвращает состояние кнопки
|
||||||
|
}
|
||||||
31
libraries/GyverEncoder/examples/timer_isr/timer_isr.ino
Normal file
31
libraries/GyverEncoder/examples/timer_isr/timer_isr.ino
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Отработка по прерыванию таймера
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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");
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
// два энкодера
|
||||||
|
|
||||||
|
#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");
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
#define CLK 7
|
||||||
|
#define DT 8
|
||||||
|
#define SW 9
|
||||||
|
|
||||||
|
#include "GyverEncoder.h"
|
||||||
|
//Encoder enc1(CLK, DT); // для работы без кнопки
|
||||||
|
Encoder enc1(CLK, DT, SW); // для работы c кнопкой
|
||||||
|
//Encoder enc1(CLK, DT, SW, TYPE2); // для работы c кнопкой и сразу выбираем тип
|
||||||
|
//Encoder enc1(CLK, DT, ENC_NO_BUTTON, TYPE2); // для работы без кнопки и сразу выбираем тип
|
||||||
|
|
||||||
|
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); // выводим значение при повороте
|
||||||
|
}
|
||||||
|
}
|
||||||
52
libraries/GyverEncoder/keywords.txt
Normal file
52
libraries/GyverEncoder/keywords.txt
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#######################################
|
||||||
|
# Syntax Coloring Map For GyverEncoder
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Datatypes (KEYWORD1)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
Encoder KEYWORD1
|
||||||
|
GyverEncoder KEYWORD1
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Methods and Functions (KEYWORD2)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
tick KEYWORD2
|
||||||
|
setType KEYWORD2
|
||||||
|
setDirection KEYWORD2
|
||||||
|
setFastTimeout KEYWORD2
|
||||||
|
setPinMode KEYWORD2
|
||||||
|
setBtnPinMode KEYWORD2
|
||||||
|
isTurn KEYWORD2
|
||||||
|
isRight KEYWORD2
|
||||||
|
isLeft KEYWORD2
|
||||||
|
isRightH KEYWORD2
|
||||||
|
isLeftH KEYWORD2
|
||||||
|
isFastR KEYWORD2
|
||||||
|
isFastL KEYWORD2
|
||||||
|
|
||||||
|
isPress KEYWORD2
|
||||||
|
isRelease KEYWORD2
|
||||||
|
isReleaseHold KEYWORD2
|
||||||
|
isClick KEYWORD2
|
||||||
|
isSingle KEYWORD2
|
||||||
|
isDouble KEYWORD2
|
||||||
|
isHolded KEYWORD2
|
||||||
|
isHold KEYWORD2
|
||||||
|
resetStates KEYWORD2
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Constants (LITERAL1)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
TYPE1 LITERAL1
|
||||||
|
TYPE2 LITERAL1
|
||||||
|
MANUAL LITERAL1
|
||||||
|
AUTO LITERAL1
|
||||||
|
NORM LITERAL1
|
||||||
|
REVERSE LITERAL1
|
||||||
|
HIGH_PULL LITERAL1
|
||||||
|
LOW_PULL LITERAL1
|
||||||
|
ENC_NO_BUTTON LITERAL1
|
||||||
9
libraries/GyverEncoder/library.properties
Normal file
9
libraries/GyverEncoder/library.properties
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
name=GyverEncoder
|
||||||
|
version=4.9
|
||||||
|
author=AlexGyver <alex@alexgyver.ru>
|
||||||
|
maintainer=AlexGyver <alex@alexgyver.ru>
|
||||||
|
sentence=Advanced encoder control library
|
||||||
|
paragraph=Advanced encoder control library
|
||||||
|
category=Sensors
|
||||||
|
url=https://github.com/GyverLibs/GyverEncoder
|
||||||
|
architectures=*
|
||||||
352
libraries/GyverEncoder/src/GyverEncoder.cpp
Normal file
352
libraries/GyverEncoder/src/GyverEncoder.cpp
Normal file
@ -0,0 +1,352 @@
|
|||||||
|
#include "GyverEncoder.h"
|
||||||
|
|
||||||
|
#if defined(PRECISE_ALGORITHM)
|
||||||
|
const int8_t KNOBDIR[] = {
|
||||||
|
0, -1, 1, 0,
|
||||||
|
1, 0, 0, -1,
|
||||||
|
-1, 0, 0, 1,
|
||||||
|
0, 1, -1, 0,
|
||||||
|
};
|
||||||
|
int8_t encPos = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ================= CONSTRUCTOR =================
|
||||||
|
Encoder::Encoder() {
|
||||||
|
flags.use_button = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Encoder::Encoder(uint8_t clk, uint8_t dt, int8_t sw, bool type) {
|
||||||
|
_CLK = clk;
|
||||||
|
_DT = dt;
|
||||||
|
if (sw != -1) {
|
||||||
|
_SW = sw;
|
||||||
|
flags.use_button = true;
|
||||||
|
} else {
|
||||||
|
flags.use_button = false;
|
||||||
|
}
|
||||||
|
flags.enc_type = type;
|
||||||
|
|
||||||
|
#if defined(__AVR__)
|
||||||
|
_pin_reg_CLK = portInputRegister(digitalPinToPort(_CLK));
|
||||||
|
_bit_mask_CLK = digitalPinToBitMask(_CLK);
|
||||||
|
_pin_reg_DT = portInputRegister(digitalPinToPort(_DT));
|
||||||
|
_bit_mask_DT = digitalPinToBitMask(_DT);
|
||||||
|
if (sw != -1) {
|
||||||
|
_pin_reg_SW = portInputRegister(digitalPinToPort(_SW));
|
||||||
|
_bit_mask_SW = digitalPinToBitMask(_SW);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pinMode(_CLK, (DEFAULT_ENC_PULL ? INPUT : INPUT_PULLUP));
|
||||||
|
pinMode(_DT, (DEFAULT_ENC_PULL ? INPUT : INPUT_PULLUP));
|
||||||
|
if (flags.use_button) pinMode(_SW, (DEFAULT_BTN_PULL ? INPUT : INPUT_PULLUP));
|
||||||
|
flags.invBtn = (DEFAULT_BTN_PULL == HIGH_PULL) ? true : false;
|
||||||
|
|
||||||
|
#if defined(FAST_ALGORITHM)
|
||||||
|
prevState = _readCLK();
|
||||||
|
#else
|
||||||
|
prevState = _readCLK() | (_readDT() << 1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================= SET =================
|
||||||
|
void Encoder::setDirection(bool direction) {
|
||||||
|
if (direction) {
|
||||||
|
uint8_t buf = _CLK;
|
||||||
|
_CLK = _DT;
|
||||||
|
_DT = buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void Encoder::setPinMode(bool mode) {
|
||||||
|
pinMode(_CLK, (mode) ? INPUT : INPUT_PULLUP);
|
||||||
|
pinMode(_DT, (mode) ? INPUT : INPUT_PULLUP);
|
||||||
|
}
|
||||||
|
void Encoder::setBtnPinMode(bool mode) {
|
||||||
|
pinMode(_SW, (mode) ? INPUT : INPUT_PULLUP);
|
||||||
|
flags.invBtn = (mode) ? 0 : 1;
|
||||||
|
}
|
||||||
|
void Encoder::setType(bool type) {
|
||||||
|
flags.enc_type = type;
|
||||||
|
}
|
||||||
|
void Encoder::setTickMode(bool tickMode) {
|
||||||
|
flags.enc_tick_mode = tickMode;
|
||||||
|
}
|
||||||
|
void Encoder::setFastTimeout(uint16_t timeout) {
|
||||||
|
_fast_timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================= IS =================
|
||||||
|
// повороты
|
||||||
|
boolean Encoder::isTurn() {
|
||||||
|
if (flags.enc_tick_mode) Encoder::tick();
|
||||||
|
if (flags.isTurn_f) {
|
||||||
|
flags.isTurn_f = false;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
boolean Encoder::isRight() {
|
||||||
|
if (flags.enc_tick_mode) Encoder::tick();
|
||||||
|
if (encState == 2) {
|
||||||
|
encState = 0;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
boolean Encoder::isLeft() {
|
||||||
|
if (flags.enc_tick_mode) Encoder::tick();
|
||||||
|
if (encState == 1) {
|
||||||
|
encState = 0;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
boolean Encoder::isRightH() {
|
||||||
|
if (flags.enc_tick_mode) Encoder::tick();
|
||||||
|
if (encState == 4) {
|
||||||
|
encState = 0;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
boolean Encoder::isLeftH() {
|
||||||
|
if (flags.enc_tick_mode) Encoder::tick();
|
||||||
|
if (encState == 3) {
|
||||||
|
encState = 0;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
boolean Encoder::isFastR() {
|
||||||
|
if (flags.enc_tick_mode) Encoder::tick();
|
||||||
|
if (flags.isFastR_f) {
|
||||||
|
flags.isFastR_f = false;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
boolean Encoder::isFastL() {
|
||||||
|
if (flags.enc_tick_mode) Encoder::tick();
|
||||||
|
if (flags.isFastL_f) {
|
||||||
|
flags.isFastL_f = false;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// кнопка
|
||||||
|
boolean Encoder::isPress() {
|
||||||
|
if (flags.enc_tick_mode) Encoder::tick();
|
||||||
|
if (flags.isPress_f) {
|
||||||
|
flags.isPress_f = false;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
boolean Encoder::isRelease() {
|
||||||
|
if (flags.enc_tick_mode) Encoder::tick();
|
||||||
|
if (flags.isRelease_f) {
|
||||||
|
flags.isRelease_f = false;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
boolean Encoder::isReleaseHold() {
|
||||||
|
if (flags.enc_tick_mode) Encoder::tick();
|
||||||
|
if (flags.isReleaseHold_f) {
|
||||||
|
flags.isReleaseHold_f = false;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
boolean Encoder::isClick() {
|
||||||
|
if (flags.enc_tick_mode) Encoder::tick();
|
||||||
|
if (flags.isRelease_f) {
|
||||||
|
flags.isRelease_f = false;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
boolean Encoder::isHolded() {
|
||||||
|
if (flags.enc_tick_mode) Encoder::tick();
|
||||||
|
if (flags.hold_flag && flags.isHolded_f) {
|
||||||
|
flags.isHolded_f = false;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
boolean Encoder::isSingle() {
|
||||||
|
if (flags.enc_tick_mode) Encoder::tick();
|
||||||
|
if (flags.isSingle_f) {
|
||||||
|
flags.isSingle_f = false;
|
||||||
|
flags.isDouble_f = false;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
boolean Encoder::isDouble() {
|
||||||
|
if (flags.enc_tick_mode) Encoder::tick();
|
||||||
|
if (flags.isDouble_f) {
|
||||||
|
flags.isDouble_f = false;
|
||||||
|
flags.isSingle_f = false;
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
boolean Encoder::isHold() {
|
||||||
|
if (flags.enc_tick_mode) Encoder::tick();
|
||||||
|
return (SW_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Encoder::resetStates() {
|
||||||
|
encState = 0;
|
||||||
|
flags.isTurn_f = false;
|
||||||
|
flags.isFastR_f = false;
|
||||||
|
flags.isFastL_f = false;
|
||||||
|
flags.isPress_f = false;
|
||||||
|
flags.isRelease_f = false;
|
||||||
|
flags.isReleaseHold_f = false;
|
||||||
|
flags.isHolded_f = false;
|
||||||
|
flags.isSingle_f = false;
|
||||||
|
flags.isDouble_f = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================= TICK =================
|
||||||
|
void Encoder::tick(bool clk, bool dt, bool sw) {
|
||||||
|
extTick = true;
|
||||||
|
flags.extCLK = clk;
|
||||||
|
flags.extDT = dt;
|
||||||
|
flags.extSW = sw;
|
||||||
|
Encoder::tick();
|
||||||
|
extTick = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Encoder::tick() {
|
||||||
|
uint32_t thisMls = millis();
|
||||||
|
uint32_t debounceDelta = thisMls - debounce_timer;
|
||||||
|
|
||||||
|
#ifdef ENC_WITH_BUTTON
|
||||||
|
if (flags.use_button) {
|
||||||
|
if (!extTick) SW_state = _readSW() ^ flags.invBtn; // читаем состояние кнопки SW
|
||||||
|
else SW_state = flags.extSW;
|
||||||
|
|
||||||
|
if (SW_state && !flags.butt_flag && (debounceDelta > ENC_DEBOUNCE_BUTTON)) {
|
||||||
|
flags.butt_flag = true;
|
||||||
|
flags.turn_flag = false;
|
||||||
|
debounce_timer = thisMls;
|
||||||
|
debounceDelta = 0;
|
||||||
|
flags.isPress_f = true;
|
||||||
|
flags.isHolded_f = true;
|
||||||
|
flags.doubleAllow = true;
|
||||||
|
}
|
||||||
|
if (!SW_state && flags.butt_flag && (debounceDelta > ENC_DEBOUNCE_BUTTON)) {
|
||||||
|
if (!flags.turn_flag && !flags.hold_flag) { // если кнопка отпущена и ручка не поворачивалась
|
||||||
|
flags.turn_flag = false;
|
||||||
|
flags.isRelease_f = true;
|
||||||
|
}
|
||||||
|
if (debounceDelta > ENC_HOLD_TIMEOUT) flags.isReleaseHold_f = true;
|
||||||
|
flags.butt_flag = false;
|
||||||
|
debounce_timer = thisMls;
|
||||||
|
debounceDelta = 0;
|
||||||
|
flags.hold_flag = false;
|
||||||
|
|
||||||
|
if (flags.doubleAllow && !flags.doubleFlag) {
|
||||||
|
flags.doubleFlag = true;
|
||||||
|
flags.countFlag = false;
|
||||||
|
} else {
|
||||||
|
flags.countFlag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flags.doubleFlag && debounceDelta > ENC_DOUBLE_TIMEOUT) {
|
||||||
|
if (!flags.turn_flag) {
|
||||||
|
if (!flags.countFlag) flags.isSingle_f = true;
|
||||||
|
else flags.isDouble_f = true;
|
||||||
|
}
|
||||||
|
flags.doubleFlag = false;
|
||||||
|
}
|
||||||
|
if (flags.butt_flag && debounceDelta > ENC_HOLD_TIMEOUT && !flags.turn_flag) {
|
||||||
|
if (SW_state) {
|
||||||
|
flags.hold_flag = true;
|
||||||
|
flags.doubleAllow = false;
|
||||||
|
} else {
|
||||||
|
flags.butt_flag = false;
|
||||||
|
flags.hold_flag = false;
|
||||||
|
debounce_timer = thisMls;
|
||||||
|
debounceDelta = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(FAST_ALGORITHM)
|
||||||
|
uint8_t curState = (extTick) ? (flags.extCLK) : (_readCLK());
|
||||||
|
|
||||||
|
if (curState != prevState
|
||||||
|
#if (ENC_DEBOUNCE_TURN > 0)
|
||||||
|
&& (debounceDelta > ENC_DEBOUNCE_TURN)
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
encState = 0;
|
||||||
|
turnFlag = !turnFlag;
|
||||||
|
if (turnFlag || !flags.enc_type) {
|
||||||
|
if (( (extTick) ? (flags.extDT) : _readDT() ) != prevState) {
|
||||||
|
encState = 1;
|
||||||
|
} else {
|
||||||
|
encState = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(BINARY_ALGORITHM)
|
||||||
|
uint8_t curState = (extTick) ? (flags.extCLK | (flags.extDT << 1)) : (_readCLK() | (_readDT() << 1));
|
||||||
|
|
||||||
|
if (curState != prevState
|
||||||
|
#if (ENC_DEBOUNCE_TURN > 0)
|
||||||
|
&& (debounceDelta > ENC_DEBOUNCE_TURN)
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
encState = 0;
|
||||||
|
if (flags.rst_flag) {
|
||||||
|
if (curState == 0b11) {
|
||||||
|
flags.rst_flag = 0;
|
||||||
|
//encState = 3-prevState;
|
||||||
|
switch (prevState) {
|
||||||
|
case 0b10: encState = 1; break; // 2 - 1
|
||||||
|
case 0b01: encState = 2; break; // 1 - 2
|
||||||
|
}
|
||||||
|
} else if (!flags.enc_type && (curState == 0b00)) {
|
||||||
|
flags.rst_flag = 0;
|
||||||
|
//encState = prevState;
|
||||||
|
switch (prevState) {
|
||||||
|
case 0b01: encState = 1; break;
|
||||||
|
case 0b10: encState = 2; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (curState == 0b00 || (!flags.enc_type && curState == 0b11)) flags.rst_flag = 1;
|
||||||
|
|
||||||
|
#elif defined(PRECISE_ALGORITHM)
|
||||||
|
uint8_t curState = (extTick) ? (flags.extCLK | (flags.extDT << 1)) : (_readCLK() | (_readDT() << 1));
|
||||||
|
|
||||||
|
if (curState != prevState
|
||||||
|
#if (ENC_DEBOUNCE_TURN > 0)
|
||||||
|
&& (debounceDelta > ENC_DEBOUNCE_TURN)
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
encState = 0;
|
||||||
|
encPos += KNOBDIR[curState | (prevState << 2)];
|
||||||
|
if (flags.enc_type) {
|
||||||
|
if (curState == 0x3 && encPos != 0) {
|
||||||
|
encState = (encPos == 4) ? 1 : 2;
|
||||||
|
encPos = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((curState == 0x3 || !curState) && encPos != 0) {
|
||||||
|
encState = (encPos == 2) ? 1 : 2;
|
||||||
|
encPos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (encState != 0) {
|
||||||
|
flags.isTurn_f = true;
|
||||||
|
if (!SW_state && thisMls - fast_timer < _fast_timeout) {
|
||||||
|
if (encState == 1) flags.isFastL_f = true;
|
||||||
|
else if (encState == 2) flags.isFastR_f = true;
|
||||||
|
fast_timer = thisMls;
|
||||||
|
} else fast_timer = thisMls;
|
||||||
|
#ifdef ENC_WITH_BUTTON
|
||||||
|
if (flags.use_button && SW_state) encState += 2;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
prevState = curState;
|
||||||
|
flags.turn_flag = true;
|
||||||
|
debounce_timer = thisMls;
|
||||||
|
debounceDelta = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
175
libraries/GyverEncoder/src/GyverEncoder.h
Normal file
175
libraries/GyverEncoder/src/GyverEncoder.h
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
GyverEncoder - библиотека для расширенной работы с энкодером
|
||||||
|
ВНИМАНИЕ, БИБЛИОТЕКА УСТАРЕЛА! ИСПОЛЬЗУЙ БИБЛИОТЕКУ EncButton https://github.com/GyverLibs/EncButton
|
||||||
|
Документация: https://alexgyver.ru/encoder/
|
||||||
|
GitHub: https://github.com/GyverLibs/GyverEncoder
|
||||||
|
Возможности:
|
||||||
|
- Отработка поворота энкодера
|
||||||
|
- Отработка "нажатого поворота"
|
||||||
|
- Отработка "быстрого поворота"
|
||||||
|
- Несколько алгоритмов опроса энкодера
|
||||||
|
- Выбор подтяжки подключения энкодера
|
||||||
|
- Работа с двумя типами экнодеров
|
||||||
|
- Работа с внешним энкодером (через расширитель пинов и т.п.)
|
||||||
|
- Отработка нажатия/удержания кнопки с антидребезгом
|
||||||
|
|
||||||
|
Версии:
|
||||||
|
v3.6 от 16.09.2019 - Возвращены дефайны настроек
|
||||||
|
v4.0 от 13.11.2019
|
||||||
|
- Оптимизирован код
|
||||||
|
- Исправлены баги
|
||||||
|
- Добавлены другие алгоритмы опроса
|
||||||
|
- Добавлена возможность полностью убрать кнопку (экономия памяти)
|
||||||
|
- Добавлена возможность подключения внешнего энкодера
|
||||||
|
- Добавлена настройка подтяжки пинов
|
||||||
|
|
||||||
|
v4.1: Исправлено изменение подтяжек
|
||||||
|
|
||||||
|
v4.2
|
||||||
|
- Добавлена поддержка TYPE1 для алгоритма PRECISE_ALGORITHM
|
||||||
|
- Добавлена отработка двойного клика: isSingle / isDouble
|
||||||
|
|
||||||
|
v4.3: Исправлено ложное isSingle
|
||||||
|
v4.4: Добавлен метод resetStates, сбрасывает все is-флаги и счётчики
|
||||||
|
v4.5: Улучшен алгоритм BINARY_ALGORITHM (спасибо Ярославу Курусу)
|
||||||
|
v4.6: BINARY_ALGORITHM пофикшен для TYPE1, добавлена isReleaseHold
|
||||||
|
v4.7: Исправлен случайный нажатый поворот в BINARY_ALGORITHM
|
||||||
|
v4.8: увеличена производительность для AVR Arduino
|
||||||
|
v4.9: быстрый поворот отключен если кнопка удерживается
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GyverEncoder_h
|
||||||
|
#define GyverEncoder_h
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
// ========= КОНСТАНТЫ ==========
|
||||||
|
#define ENC_NO_BUTTON -1 // константа для работы без пина
|
||||||
|
#define TYPE1 0 // полушаговый энкодер
|
||||||
|
#define TYPE2 1 // полношаговый
|
||||||
|
#define NORM 0 // направление вращения обычное
|
||||||
|
#define REVERSE 1 // обратное
|
||||||
|
#define MANUAL 0 // нужно вызывать функцию tick() вручную
|
||||||
|
#define AUTO 1 // tick() входит во все остальные функции и опрашивается сама!
|
||||||
|
#define HIGH_PULL 0 // внутренняя подтяжка к питанию (pinMode INPUT_PULLUP)
|
||||||
|
#define LOW_PULL 1 // внешняя подтяжка к GND (pinMode INPUT)
|
||||||
|
|
||||||
|
// =========== НАСТРОЙКИ ===========
|
||||||
|
// закомментируй строку, чтобы полностью убрать отработку кнопки из кода
|
||||||
|
#define ENC_WITH_BUTTON
|
||||||
|
|
||||||
|
// тип подключения энкодера по умолчанию (LOW_PULL или HIGH_PULL)
|
||||||
|
//#define DEFAULT_ENC_PULL LOW_PULL
|
||||||
|
#define DEFAULT_ENC_PULL HIGH_PULL
|
||||||
|
|
||||||
|
// тип подключения кнопки энкодера по умолчанию (LOW_PULL или HIGH_PULL)
|
||||||
|
//#define DEFAULT_BTN_PULL LOW_PULL
|
||||||
|
#define DEFAULT_BTN_PULL HIGH_PULL
|
||||||
|
|
||||||
|
// алгоритмы опроса энкодера (раскомментировать нужный)
|
||||||
|
//#define FAST_ALGORITHM // тик 10 мкс, быстрый, не справляется с люфтами
|
||||||
|
#define BINARY_ALGORITHM // тик 14 мкс, лучше справляется с люфтами
|
||||||
|
//#define PRECISE_ALGORITHM // тик 16 мкс, работает даже с убитым энкодером (по мотивам https://github.com/mathertel/RotaryEncoder)
|
||||||
|
|
||||||
|
// настройка антидребезга энкодера, кнопки, таймаута удержания и таймаута двойного клика
|
||||||
|
#define ENC_DEBOUNCE_TURN 0
|
||||||
|
#define ENC_DEBOUNCE_BUTTON 80
|
||||||
|
#define ENC_HOLD_TIMEOUT 700
|
||||||
|
#define ENC_DOUBLE_TIMEOUT 300
|
||||||
|
|
||||||
|
#if defined(__AVR__)
|
||||||
|
#define _readCLK() bool(*_pin_reg_CLK & _bit_mask_CLK)
|
||||||
|
#define _readDT() bool(*_pin_reg_DT & _bit_mask_DT)
|
||||||
|
#define _readSW() bool(*_pin_reg_SW & _bit_mask_SW)
|
||||||
|
#else
|
||||||
|
#define _readCLK() digitalRead(_CLK)
|
||||||
|
#define _readDT() digitalRead(_DT)
|
||||||
|
#define _readSW() digitalRead(_SW)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#pragma pack(push,1)
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
bool hold_flag: 1;
|
||||||
|
bool butt_flag: 1;
|
||||||
|
bool turn_flag: 1;
|
||||||
|
bool isTurn_f: 1;
|
||||||
|
bool isPress_f: 1;
|
||||||
|
bool isRelease_f: 1;
|
||||||
|
bool isReleaseHold_f: 1;
|
||||||
|
bool isHolded_f: 1;
|
||||||
|
bool isFastR_f: 1;
|
||||||
|
bool isFastL_f: 1;
|
||||||
|
bool enc_tick_mode: 1;
|
||||||
|
bool enc_type: 1;
|
||||||
|
bool use_button : 1;
|
||||||
|
bool extCLK : 1;
|
||||||
|
bool extDT : 1;
|
||||||
|
bool extSW : 1;
|
||||||
|
bool invBtn : 1;
|
||||||
|
bool isSingle_f : 1;
|
||||||
|
bool isDouble_f : 1;
|
||||||
|
bool countFlag : 1;
|
||||||
|
bool doubleFlag : 1;
|
||||||
|
bool doubleAllow : 1;
|
||||||
|
bool rst_flag : 1;
|
||||||
|
} GyverEncoderFlags;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
// Варианты инициализации:
|
||||||
|
// Encoder enc; // не привязан к пину
|
||||||
|
// Encoder enc(пин CLK, пин DT); // энкодер без кнопки (ускоренный опрос)
|
||||||
|
// Encoder enc(пин CLK, пин DT, пин SW); // энкодер с кнопкой
|
||||||
|
// Encoder enc(пин CLK, пин DT, пин SW, тип); // энкодер с кнопкой и указанием типа
|
||||||
|
// Encoder enc(пин CLK, пин DT, ENC_NO_BUTTON, тип); // энкодер без кнопкой и с указанием типа
|
||||||
|
|
||||||
|
class Encoder {
|
||||||
|
public:
|
||||||
|
Encoder(); // для непривязанного к пинам энкодера
|
||||||
|
Encoder(uint8_t clk, uint8_t dt, int8_t sw = -1, bool type = false); // CLK, DT, SW, тип (TYPE1 / TYPE2) TYPE1 одношаговый, TYPE2 двухшаговый. Если ваш энкодер работает странно, смените тип
|
||||||
|
|
||||||
|
void tick(); // опрос энкодера, нужно вызывать постоянно или в прерывании
|
||||||
|
void tick(bool clk, bool dt, bool sw = 0); // опрос "внешнего" энкодера
|
||||||
|
void setType(bool type); // TYPE1 / TYPE2 - тип энкодера TYPE1 одношаговый, TYPE2 двухшаговый. Если ваш энкодер работает странно, смените тип
|
||||||
|
void setPinMode(bool mode); // тип подключения энкодера, подтяжка HIGH_PULL (внутренняя) или LOW_PULL (внешняя на GND)
|
||||||
|
void setBtnPinMode(bool mode); // тип подключения кнопки, подтяжка HIGH_PULL (внутренняя) или LOW_PULL (внешняя на GND)
|
||||||
|
void setTickMode(bool tickMode); // MANUAL / AUTO - ручной или автоматический опрос энкодера функцией tick(). (по умолчанию ручной)
|
||||||
|
void setDirection(bool direction); // NORM / REVERSE - направление вращения энкодера
|
||||||
|
void setFastTimeout(uint16_t 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 isReleaseHold(); // возвращает true при отпускании кнопки после удержания, сама сбрасывается в false
|
||||||
|
boolean isClick(); // возвращает true при нажатии и отпускании кнопки, сама сбрасывается в false
|
||||||
|
boolean isHolded(); // возвращает true при удержании кнопки, сама сбрасывается в false
|
||||||
|
boolean isHold(); // возвращает true при удержании кнопки, НЕ СБРАСЫВАЕТСЯ
|
||||||
|
boolean isSingle(); // возвращает true при одиночном клике (после таймаута), сама сбрасывается в false
|
||||||
|
boolean isDouble(); // возвращает true при двойном клике, сама сбрасывается в false
|
||||||
|
|
||||||
|
void resetStates(); // сбрасывает все is-флаги и счётчики
|
||||||
|
|
||||||
|
private:
|
||||||
|
GyverEncoderFlags flags;
|
||||||
|
uint8_t _fast_timeout = 50; // таймаут быстрого поворота
|
||||||
|
uint8_t prevState = 0;
|
||||||
|
uint8_t encState = 0; // 0 не крутился, 1 лево, 2 право, 3 лево нажат, 4 право нажат
|
||||||
|
uint32_t debounce_timer = 0, fast_timer = 0;
|
||||||
|
uint8_t _CLK = 0, _DT = 0, _SW = 0;
|
||||||
|
bool turnFlag = false, extTick = false, SW_state = false;
|
||||||
|
#if defined(__AVR__)
|
||||||
|
volatile uint8_t *_pin_reg_CLK;
|
||||||
|
volatile uint8_t _bit_mask_CLK;
|
||||||
|
volatile uint8_t *_pin_reg_DT;
|
||||||
|
volatile uint8_t _bit_mask_DT;
|
||||||
|
volatile uint8_t *_pin_reg_SW;
|
||||||
|
volatile uint8_t _bit_mask_SW;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
#endif
|
||||||
Loading…
x
Reference in New Issue
Block a user