From 59a740b94759a18f7615db6cdec79ba29042de1c Mon Sep 17 00:00:00 2001 From: pendos Date: Sun, 15 Aug 2021 21:28:09 +0300 Subject: [PATCH] add lib --- libraries/GyverEncoder/LICENSE | 21 ++ libraries/GyverEncoder/README.md | 149 ++++++++ .../enc_pinChangeInt/enc_pinChangeInt.ino | 61 +++ .../encoder_2_impulse/encoder_2_impulse.ino | 28 ++ .../examples/encoder_auto/encoder_auto.ino | 30 ++ .../examples/encoder_demo/encoder_demo.ino | 47 +++ .../examples/encoder_fast/encoder_fast.ino | 32 ++ .../encoder_interrupt/encoder_interrupt.ino | 33 ++ .../examples/external_enc/external_enc.ino | 40 ++ .../examples/timer_isr/timer_isr.ino | 31 ++ .../examples/two_encoders/two_encoders.ino | 20 + .../examples/value_change/value_change.ino | 31 ++ libraries/GyverEncoder/keywords.txt | 52 +++ libraries/GyverEncoder/library.properties | 9 + libraries/GyverEncoder/src/GyverEncoder.cpp | 352 ++++++++++++++++++ libraries/GyverEncoder/src/GyverEncoder.h | 175 +++++++++ 16 files changed, 1111 insertions(+) create mode 100644 libraries/GyverEncoder/LICENSE create mode 100644 libraries/GyverEncoder/README.md create mode 100644 libraries/GyverEncoder/examples/enc_pinChangeInt/enc_pinChangeInt.ino create mode 100644 libraries/GyverEncoder/examples/encoder_2_impulse/encoder_2_impulse.ino create mode 100644 libraries/GyverEncoder/examples/encoder_auto/encoder_auto.ino create mode 100644 libraries/GyverEncoder/examples/encoder_demo/encoder_demo.ino create mode 100644 libraries/GyverEncoder/examples/encoder_fast/encoder_fast.ino create mode 100644 libraries/GyverEncoder/examples/encoder_interrupt/encoder_interrupt.ino create mode 100644 libraries/GyverEncoder/examples/external_enc/external_enc.ino create mode 100644 libraries/GyverEncoder/examples/timer_isr/timer_isr.ino create mode 100644 libraries/GyverEncoder/examples/two_encoders/two_encoders.ino create mode 100644 libraries/GyverEncoder/examples/value_change/value_change.ino create mode 100644 libraries/GyverEncoder/keywords.txt create mode 100644 libraries/GyverEncoder/library.properties create mode 100644 libraries/GyverEncoder/src/GyverEncoder.cpp create mode 100644 libraries/GyverEncoder/src/GyverEncoder.h diff --git a/libraries/GyverEncoder/LICENSE b/libraries/GyverEncoder/LICENSE new file mode 100644 index 0000000..353b7ee --- /dev/null +++ b/libraries/GyverEncoder/LICENSE @@ -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. diff --git a/libraries/GyverEncoder/README.md b/libraries/GyverEncoder/README.md new file mode 100644 index 0000000..9261b11 --- /dev/null +++ b/libraries/GyverEncoder/README.md @@ -0,0 +1,149 @@ +![License: MIT](https://img.shields.io/badge/License-MIT-green.svg) +![author](https://img.shields.io/badge/author-AlexGyver-informational.svg) +# GyverEncoder +Библиотека для расширенной работы с энкодером +**ВНИМАНИЕ, БИБЛИОТЕКА УСТАРЕЛА! ИСПОЛЬЗУЙ БИБЛИОТЕКУ [EncButton](https://github.com/GyverLibs/EncButton)** +- Отработка поворота энкодера +- Отработка "нажатого поворота" +- Отработка "быстрого поворота" +- Несколько алгоритмов опроса энкодера +- Выбор подтяжки подключения энкодера +- Работа с двумя типами экнодеров +- Работа с внешним энкодером (через расширитель пинов и т.п.) +- Отработка нажатия/удержания кнопки с антидребезгом + +### Совместимость +Совместима со всеми Arduino платформами (используются Arduino-функции) + +### Документация +К библиотеке есть [расширенная документация](https://alexgyver.ru/encoder/) + +## Содержание +- [Установка](#install) +- [Инициализация](#init) +- [Использование](#usage) +- [Пример](#example) +- [Версии](#versions) +- [Баги и обратная связь](#feedback) + + +## Установка +- Библиотеку можно найти по названию **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) + + +## Инициализация +```cpp +Encoder enc; // не привязан к пину +Encoder enc(пин CLK, пин DT); // энкодер без кнопки (ускоренный опрос) +Encoder enc(пин CLK, пин DT, пин SW); // энкодер с кнопкой +Encoder enc(пин CLK, пин DT, пин SW, тип); // энкодер с кнопкой и указанием типа +Encoder enc(пин CLK, пин DT, ENC_NO_BUTTON, тип); // энкодер без кнопкой и с указанием типа +``` + + +## Использование +```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-флаги +``` + + +## Пример +Остальные примеры смотри в **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"); // возвращает состояние кнопки +} +``` + + +## Версии +- 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: быстрый поворот отключен если кнопка удерживается + + +## Баги и обратная связь +При нахождении багов создавайте **Issue**, а лучше сразу пишите на почту [alex@alexgyver.ru](mailto:alex@alexgyver.ru) +Библиотека открыта для доработки и ваших **Pull Request**'ов! \ No newline at end of file diff --git a/libraries/GyverEncoder/examples/enc_pinChangeInt/enc_pinChangeInt.ino b/libraries/GyverEncoder/examples/enc_pinChangeInt/enc_pinChangeInt.ino new file mode 100644 index 0000000..1423f86 --- /dev/null +++ b/libraries/GyverEncoder/examples/enc_pinChangeInt/enc_pinChangeInt.ino @@ -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(); +} diff --git a/libraries/GyverEncoder/examples/encoder_2_impulse/encoder_2_impulse.ino b/libraries/GyverEncoder/examples/encoder_2_impulse/encoder_2_impulse.ino new file mode 100644 index 0000000..99814e8 --- /dev/null +++ b/libraries/GyverEncoder/examples/encoder_2_impulse/encoder_2_impulse.ino @@ -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"); +} diff --git a/libraries/GyverEncoder/examples/encoder_auto/encoder_auto.ino b/libraries/GyverEncoder/examples/encoder_auto/encoder_auto.ino new file mode 100644 index 0000000..59eb5d2 --- /dev/null +++ b/libraries/GyverEncoder/examples/encoder_auto/encoder_auto.ino @@ -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"); // возвращает состояние кнопки +} diff --git a/libraries/GyverEncoder/examples/encoder_demo/encoder_demo.ino b/libraries/GyverEncoder/examples/encoder_demo/encoder_demo.ino new file mode 100644 index 0000000..cbab6a6 --- /dev/null +++ b/libraries/GyverEncoder/examples/encoder_demo/encoder_demo.ino @@ -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"); // возвращает состояние кнопки +} diff --git a/libraries/GyverEncoder/examples/encoder_fast/encoder_fast.ino b/libraries/GyverEncoder/examples/encoder_fast/encoder_fast.ino new file mode 100644 index 0000000..a526add --- /dev/null +++ b/libraries/GyverEncoder/examples/encoder_fast/encoder_fast.ino @@ -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); // выводим значение при повороте + } +} diff --git a/libraries/GyverEncoder/examples/encoder_interrupt/encoder_interrupt.ino b/libraries/GyverEncoder/examples/encoder_interrupt/encoder_interrupt.ino new file mode 100644 index 0000000..3a23bf4 --- /dev/null +++ b/libraries/GyverEncoder/examples/encoder_interrupt/encoder_interrupt.ino @@ -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"); +} diff --git a/libraries/GyverEncoder/examples/external_enc/external_enc.ino b/libraries/GyverEncoder/examples/external_enc/external_enc.ino new file mode 100644 index 0000000..d0c5714 --- /dev/null +++ b/libraries/GyverEncoder/examples/external_enc/external_enc.ino @@ -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"); // возвращает состояние кнопки +} diff --git a/libraries/GyverEncoder/examples/timer_isr/timer_isr.ino b/libraries/GyverEncoder/examples/timer_isr/timer_isr.ino new file mode 100644 index 0000000..b4c21f6 --- /dev/null +++ b/libraries/GyverEncoder/examples/timer_isr/timer_isr.ino @@ -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"); + +} diff --git a/libraries/GyverEncoder/examples/two_encoders/two_encoders.ino b/libraries/GyverEncoder/examples/two_encoders/two_encoders.ino new file mode 100644 index 0000000..7a08fee --- /dev/null +++ b/libraries/GyverEncoder/examples/two_encoders/two_encoders.ino @@ -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"); +} diff --git a/libraries/GyverEncoder/examples/value_change/value_change.ino b/libraries/GyverEncoder/examples/value_change/value_change.ino new file mode 100644 index 0000000..3e96ec5 --- /dev/null +++ b/libraries/GyverEncoder/examples/value_change/value_change.ino @@ -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); // выводим значение при повороте + } +} diff --git a/libraries/GyverEncoder/keywords.txt b/libraries/GyverEncoder/keywords.txt new file mode 100644 index 0000000..9e9138e --- /dev/null +++ b/libraries/GyverEncoder/keywords.txt @@ -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 \ No newline at end of file diff --git a/libraries/GyverEncoder/library.properties b/libraries/GyverEncoder/library.properties new file mode 100644 index 0000000..6ee5fdc --- /dev/null +++ b/libraries/GyverEncoder/library.properties @@ -0,0 +1,9 @@ +name=GyverEncoder +version=4.9 +author=AlexGyver +maintainer=AlexGyver +sentence=Advanced encoder control library +paragraph=Advanced encoder control library +category=Sensors +url=https://github.com/GyverLibs/GyverEncoder +architectures=* \ No newline at end of file diff --git a/libraries/GyverEncoder/src/GyverEncoder.cpp b/libraries/GyverEncoder/src/GyverEncoder.cpp new file mode 100644 index 0000000..67d8156 --- /dev/null +++ b/libraries/GyverEncoder/src/GyverEncoder.cpp @@ -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; + } + } \ No newline at end of file diff --git a/libraries/GyverEncoder/src/GyverEncoder.h b/libraries/GyverEncoder/src/GyverEncoder.h new file mode 100644 index 0000000..5205231 --- /dev/null +++ b/libraries/GyverEncoder/src/GyverEncoder.h @@ -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 + +// ========= КОНСТАНТЫ ========== +#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 \ No newline at end of file