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 @@
+
+
+# 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