diff --git a/README.md b/README.md index e69de29..72ce488 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,23 @@ +

PumpController

+ +

+ + + + + + +

+ + +

+ Contact with me +

+

+ + + + + + +

diff --git a/firmware/pump_encoder/pump_encoder.ino b/firmware/pump_encoder/pump_encoder.ino index 08afee2..1041f94 100644 --- a/firmware/pump_encoder/pump_encoder.ino +++ b/firmware/pump_encoder/pump_encoder.ino @@ -1,29 +1,46 @@ #define pressure_Aport 0 #define relay_Dport 5 -#define CLK 2 -#define DT 3 -#define SW 4 +#define S1 2 +#define S2 3 +#define KEY 4 + +#define CLK 6 +#define DIO 7 #define const_pressure_min 0 #define const_pressure_max 1000 -#include +#include #include +#include int pressure, pressure_min, pressure_max; bool flag; +unsigned long last_time1, last_time2; -Encoder enc(CLK, DT, SW); +EncButton enc; +GyverTM1637 disp(CLK, DIO); + + + +int constraint_pressure(int pressure_) { + return constrain(pressure_, 0, 1000); +} void setup() { Serial.begin(9600); - enc.setType(TYPE2); + pinMode(relay_Dport, OUTPUT); + EEPROM.get(0, pressure_min); EEPROM.get(2, pressure_max); + + disp.clear(); + disp.brightness(7); + disp.clear(); } void loop() { @@ -32,20 +49,24 @@ void loop() { enc.tick(); if (enc.isTurn()) { if (enc.isRight()) { - pressure_min = constrain(pressure_min+10, const_pressure_min, const_pressure_max); + pressure_min = constraint_pressure(pressure_min+10); + disp.displayInt(pressure_min); } if (enc.isLeft()) { - pressure_min = constrain(pressure_min-10, const_pressure_min, const_pressure_max); + pressure_min = constraint_pressure(pressure_min-10); + disp.displayInt(pressure_min); } if (enc.isRightH()) { - pressure_min = constrain(pressure_max+10, const_pressure_min, const_pressure_max); + pressure_max = constraint_pressure(pressure_max+10); + disp.displayInt(pressure_max); } if (enc.isLeftH()) { - pressure_min = constrain(pressure_max-10, const_pressure_min, const_pressure_max); + pressure_max = constraint_pressure(pressure_max-10); + disp.displayInt(pressure_max); } } - if (enc.isClick()) { + if (enc.isHolded()) { EEPROM.put(0, pressure_min); EEPROM.put(2, pressure_max); } @@ -53,6 +74,12 @@ void loop() { pressure = analogRead(pressure_Aport); + if (millis() - last_time1 > 850) { + last_time1 = millis(); + disp.displayInt(pressure); + } + + if (pressure < pressure_min) { if (!flag) { digitalWrite(relay_Dport, HIGH); diff --git a/libraries/GyverEncoder/LICENSE b/libraries/EncButton/LICENSE similarity index 100% rename from libraries/GyverEncoder/LICENSE rename to libraries/EncButton/LICENSE diff --git a/libraries/EncButton/README.md b/libraries/EncButton/README.md new file mode 100644 index 0000000..4b7818f --- /dev/null +++ b/libraries/EncButton/README.md @@ -0,0 +1,194 @@ +![License: MIT](https://img.shields.io/badge/License-MIT-green.svg) +![author](https://img.shields.io/badge/author-AlexGyver-informational.svg) +# EncButton +Ультра лёгкая и быстрая библиотека для энкодера, энкодера с кнопкой или просто кнопки +- Максимально быстрое чтение пинов для AVR (ATmega328/ATmega168, ATtiny85/ATtiny13) +- Оптимизированный вес +- Быстрые и лёгкие алгоритмы кнопки и энкодера +- Энкодер: поворот, нажатый поворот, быстрый поворот, счётчик +- Кнопка: антидребезг, клик, несколько кликов, счётчик кликов, удержание, режим step +- Подключение - **только с подтяжкой к питанию** (внешней или внутренней)! +- Опциональный режим callback (+22б SRAM на каждый экземпляр) +- Виртуальный режим (кнопка, энк, энк с кнопкой) + +### Совместимость +Совместима со всеми Arduino платформами (используются Arduino-функции) + +## Содержание +- [Установка](#install) +- [Инициализация](#init) +- [Использование](#usage) +- [Пример](#example) +- [Версии](#versions) +- [Баги и обратная связь](#feedback) + + +## Установка +- Библиотеку можно найти по названию **EncButton** и установить через менеджер библиотек в: + - Arduino IDE + - Arduino IDE v2 + - PlatformIO +- [Скачать библиотеку](https://github.com/GyverLibs/EncButton/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 +EncButton enc; // энкодер с кнопкой и пинами A, B, KEY +EncButton enc; // просто энкодер с пинами A, B +EncButton btn; // просто кнопка с пином KEY +// MODE: EB_TICK или EB_CALLBACK - режим работы ручной или с обработчиками + +// по умолчанию пины настроены в INPUT_PULLUP +// Если используется внешняя подтяжка - лучше перевести в INPUT +EncButton enc(INPUT); + +// Виртуальный режим: +EncButton enc; // виртуальная кнопка +EncButton enc; // виртуальный энк с кнопкой +EncButton enc; // виртуальный энк +// в tick нужно передавать виртуальное значение, см. ниже! +``` + + +## Использование +```cpp +void pullUp(); // подтянуть пины внутренней подтяжкой +void holdEncButton(bool state); // виртуально зажать кнопку энкодера +void setHoldTimeout(int tout); // установить время удержания кнопки, мс (до 30 000) + +// тикер, вызывать как можно чаще или в прерывании +// вернёт отличное от нуля значение, если произошло какое то событие +uint8_t tick(); + +// tick может принимать виртуальный сигнал при режиме VIRT_: +// (сигнал кнопки) +// (сигнал энкодера А, сигнал энкодера B) +// (сигнал энкодера А, сигнал энкодера B, сигнал кнопки) + +uint8_t getState(); // получить статус +void resetState(); // сбросить статус + +// обработка энкодера +bool isTurn(); // факт поворота +bool isRight(); // вправо +bool isLeft(); // влево +bool isRightH(); // вправо нажатый +bool isLeftH(); // влево нажатый +bool isFast(); // быстрый +int counter; // доступ к счётчику энкодера + +// обработка кнопки +bool isPress(); // нажатие +bool isRelease(); // отпускание +bool isClick(); // клик +bool isHolded(); // было удержание (однократное срабатывание) +bool isHold(); // удерживается (постоянное срабатывание) +bool isStep(); // режим шага +bool state(); // текущий статус кнопки +bool hasClicks(uint8_t numClicks); // проверка на наличие указанного количества кликов +uint8_t hasClicks(); // вернёт количество кликов, если они есть +uint8_t clicks; // доступ к счётчику кликов + +// для режима с обработчиком +void attach(eb_callback type, void (*handler)()); // подключить обработчик +void detach(eb_callback type); // отключить обработчик +void attachClicks(uint8_t amount, void (*handler)()); // подключить обработчик на количество кликов (может быть только один!) +void detachClicks(); // отключить обработчик на количество кликов + +// eb_callback может быть: +TURN_HANDLER +RIGHT_HANDLER +LEFT_HANDLER +RIGHT_H_HANDLER +LEFT_H_HANDLER +CLICK_HANDLER +HOLDED_HANDLER +STEP_HANDLER +HOLD_HANDLER +CLICKS_HANDLER +PRESS_HANDLER +RELEASE_HANDLER +// СМОТРИ ПРИМЕР + +// дефайны настроек (дефайнить ПЕРЕД ПОДКЛЮЧЕНИЕМ БИБЛИОТЕКИ) +// показаны значения по умолчанию +//#define EB_FAST 30 // таймаут быстрого поворота, мс +//#define EB_DEB 80 // дебаунс кнопки, мс +//#define EB_HOLD 1000 // таймаут удержания кнопки, мс +//#define EB_STEP 500 // период срабатывания степ, мс +//#define EB_CLICK 400 // таймаут накликивания, мс +``` + + +## Пример +Остальные примеры смотри в **examples**! +```cpp +// Пример с прямой работой библиотеки + +#include +EncButton enc; // энкодер с кнопкой + +void setup() { + Serial.begin(9600); + enc.counter = 100; // изменение счётчика +} + +void loop() { + enc.tick(); + + if (enc.isTurn()) { // любой поворот + Serial.print("turn "); + Serial.println(enc.counter); // вывод счётчика + } + + if (enc.isLeft()) { + if (enc.isFast()) Serial.println("fast left"); + else Serial.println("left"); + } + + if (enc.isRight()) { + if (enc.isFast()) Serial.println("fast right"); + else Serial.println("right"); + } + + if (enc.isLeftH()) Serial.println("leftH"); + if (enc.isRightH()) Serial.println("rightH"); + if (enc.isClick()) Serial.println("click"); + if (enc.isHolded()) Serial.println("holded"); + if (enc.isStep()) Serial.println("step"); + + if (enc.isPress()) Serial.println("press"); + if (enc.isClick()) Serial.println("click"); + if (enc.isRelease()) Serial.println("release"); + + if (enc.hasClicks(1)) Serial.println("1 click"); + if (enc.hasClicks(2)) Serial.println("2 click"); + if (enc.hasClicks(3)) Serial.println("3 click"); + if (enc.hasClicks(5)) Serial.println("5 click"); + + if (enc.hasClicks()) Serial.println(enc.clicks); + //if (enc.isHold()) Serial.println("hold"); +} +``` + + +## Версии +- v1.1 - пуллап отдельныи методом +- v1.2 - можно передать конструктору параметр INPUT_PULLUP / INPUT(умолч) +- v1.3 - виртуальное зажатие кнопки энкодера вынесено в отдельную функцию + мелкие улучшения +- v1.4 - обработка нажатия и отпускания кнопки +- v1.5 - добавлен виртуальный режим +- v1.6 - оптимизация работы в прерывании +- v1.6.1 - подтяжка по умолчанию INPUT_PULLUP +- v1.7 - большая оптимизация памяти, переделан FastIO +- v1.8 - индивидуальная настройка таймаута удержания кнопки (была общая на всех) + + +## Баги и обратная связь +При нахождении багов создавайте **Issue**, а лучше сразу пишите на почту [alex@alexgyver.ru](mailto:alex@alexgyver.ru) +Библиотека открыта для доработки и ваших **Pull Request**'ов! \ No newline at end of file diff --git a/libraries/EncButton/examples/callbackMode/callbackMode.ino b/libraries/EncButton/examples/callbackMode/callbackMode.ino new file mode 100644 index 0000000..ec26d6a --- /dev/null +++ b/libraries/EncButton/examples/callbackMode/callbackMode.ino @@ -0,0 +1,74 @@ +// Пример с обработчиками + +// Опциональные дефайн-настройки (показаны по умолчанию) +//#define EB_FAST 30 // таймаут быстрого поворота, мс +//#define EB_DEB 80 // дебаунс кнопки, мс +//#define EB_HOLD 1000 // таймаут удержания кнопки, мс +//#define EB_STEP 500 // период срабатывания степ, мс +//#define EB_CLICK 400 // таймаут накликивания, мс + +// подключение - только PULL-UP, внешний или внутренний! +// для изменения направления энкодера поменяй A и B при инициализации + +#include +EncButton enc; // энкодер с кнопкой +//EncButton enc; // просто энкодер +//EncButton enc; // просто кнопка + +// по умолчанию пины настроены в INPUT_PULLUP +// Если используется внешняя подтяжка - лучше перевести в INPUT +//EncButton enc(INPUT); + +void setup() { + Serial.begin(9600); + enc.counter = 100; // изменение счётчика + //enc.setHoldTimeout(500); // установка таймаута удержания кнопки + + enc.attach(TURN_HANDLER, myTurn); + enc.attach(RIGHT_HANDLER, myRight); + enc.attach(CLICK_HANDLER, myClick); + enc.attach(HOLDED_HANDLER, myHolded); + enc.attach(STEP_HANDLER, myStep); + enc.attach(CLICKS_HANDLER, myClicks); + enc.attachClicks(5, fiveClicks); +} + +void myTurn() { + Serial.print("turn "); + Serial.println(enc.counter); // вывод счётчика + + // также можно опросить здесь (isRight, isLeft, isRightH, isLeftH) + if (enc.isLeft()) { + if (enc.isFast()) Serial.println("fast left"); + else Serial.println("left"); + } + if (enc.isLeftH()) { + if (enc.isFast()) Serial.println("fast left hold"); + else Serial.println("left"); + } +} + +void myRight() { + if (enc.isFast()) Serial.println("fast right"); + else Serial.println("right"); +} + +void myClick() { + Serial.println("click"); +} +void myHolded() { + Serial.println("holded"); +} +void myStep() { + Serial.println("step"); +} +void myClicks() { + Serial.println(enc.clicks); +} +void fiveClicks() { + Serial.println("kek"); +} + +void loop() { + enc.tick(); // обработка всё равно здесь +} diff --git a/libraries/EncButton/examples/isr/isr.ino b/libraries/EncButton/examples/isr/isr.ino new file mode 100644 index 0000000..baf907d --- /dev/null +++ b/libraries/EncButton/examples/isr/isr.ino @@ -0,0 +1,49 @@ +// Пример с прямой работой библиотеки на прерываниях + +// Опциональные дефайн-настройки (показаны по умолчанию) +//#define EB_FAST 30 // таймаут быстрого поворота, мс +//#define EB_DEB 80 // дебаунс кнопки, мс +//#define EB_HOLD 1000 // таймаут удержания кнопки, мс +//#define EB_STEP 500 // период срабатывания степ, мс +//#define EB_CLICK 400 // таймаут накликивания, мс + +// подключение - только PULL-UP, внешний или внутренний! +// для изменения направления энкодера поменяй A и B при инициализации + +#include +EncButton enc; // энкодер с кнопкой +//EncButton enc; // просто энкодер +//EncButton enc; // просто кнопка + +// по умолчанию пины настроены в INPUT_PULLUP +// Если используется внешняя подтяжка - лучше перевести в INPUT +//EncButton enc(INPUT); + +void setup() { + Serial.begin(9600); + + // желательно подключить оба пина энкодера на внешние прерывания по CHANGE + // можно использовать PCINT https://github.com/NicoHood/PinChangeInterrupt + attachInterrupt(0, isr, CHANGE); // D2 + attachInterrupt(1, isr, CHANGE); // D3 + // подключил оба прерывания на одну функцию +} + +// в прерывании вызываем тик +void isr() { + enc.tick(); +} + +void loop() { + // тут тоже вызываем тик, нужен для + // корректной работы дебаунсов и прочих таймеров!!! + enc.tick(); + + if (enc.isTurn()) { // любой поворот + Serial.print("turn "); + Serial.println(enc.counter); // вывод счётчика + } + + // имитация загруженной программы, обработка происходит в прерывании + delay(50); +} diff --git a/libraries/GyverEncoder/examples/enc_pinChangeInt/enc_pinChangeInt.ino b/libraries/EncButton/examples/pcint/pcint.ino similarity index 57% rename from libraries/GyverEncoder/examples/enc_pinChangeInt/enc_pinChangeInt.ino rename to libraries/EncButton/examples/pcint/pcint.ino index 1423f86..a8494b1 100644 --- a/libraries/GyverEncoder/examples/enc_pinChangeInt/enc_pinChangeInt.ino +++ b/libraries/EncButton/examples/pcint/pcint.ino @@ -1,12 +1,12 @@ // пример с прерываниями pinChangeInterrupt (прерывания на любом пине) // только для ATmega328 (UNO, Nano, Pro Mini) -#define SW 0 -#define DT 2 -#define CLK 3 +#define CLK 4 +#define DT 5 +#define SW 6 -#include "GyverEncoder.h" -Encoder enc1(CLK, DT, SW); +#include +EncButton enc; void setup() { Serial.begin(9600); @@ -17,13 +17,23 @@ void setup() { } void loop() { - enc1.tick(); // оставляем тут для работы "временных" функций и антидребезга - - if (enc1.isRight()) Serial.println("Right"); // если был поворот - if (enc1.isLeft()) Serial.println("Left"); + // оставляем тут для работы "временных" функций и антидребезга + enc.tick(); - if (enc1.isRightH()) Serial.println("Right holded"); // если было удержание + поворот - if (enc1.isLeftH()) Serial.println("Left holded"); + if (enc.isTurn()) { // любой поворот + Serial.print("turn "); + Serial.println(enc.counter); // вывод счётчика + } + + if (enc.isLeft()) { + if (enc.isFast()) Serial.println("fast left"); + else Serial.println("left"); + } + + if (enc.isRight()) { + if (enc.isFast()) Serial.println("fast right"); + else Serial.println("right"); + } } // функция для настройки PCINT для ATmega328 (UNO, Nano, Pro Mini) @@ -46,16 +56,15 @@ uint8_t attachPCINT(uint8_t pin) { } // Векторы 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(); + enc.tick(); } diff --git a/libraries/EncButton/examples/tickMode/tickMode.ino b/libraries/EncButton/examples/tickMode/tickMode.ino new file mode 100644 index 0000000..5283b15 --- /dev/null +++ b/libraries/EncButton/examples/tickMode/tickMode.ino @@ -0,0 +1,63 @@ +// Пример с прямой работой библиотеки + +// Опциональные дефайн-настройки (показаны по умолчанию) +//#define EB_FAST 30 // таймаут быстрого поворота, мс +//#define EB_DEB 80 // дебаунс кнопки, мс +//#define EB_HOLD 1000 // таймаут удержания кнопки, мс +//#define EB_STEP 500 // период срабатывания степ, мс +//#define EB_CLICK 400 // таймаут накликивания, мс + +// подключение - только PULL-UP, внешний или внутренний! +// для изменения направления энкодера поменяй A и B при инициализации + +#include +EncButton enc; // энкодер с кнопкой +//EncButton enc; // просто энкодер +//EncButton enc; // просто кнопка + +// по умолчанию пины настроены в INPUT_PULLUP +// Если используется внешняя подтяжка - лучше перевести в INPUT +//EncButton enc(INPUT); + +void setup() { + Serial.begin(9600); + enc.counter = 100; // изменение счётчика + //enc.setHoldTimeout(500); // установка таймаута удержания кнопки +} + +void loop() { + enc.tick(); + + if (enc.isTurn()) { // любой поворот + Serial.print("turn "); + Serial.println(enc.counter); // вывод счётчика + } + + if (enc.isLeft()) { + if (enc.isFast()) Serial.println("fast left"); + else Serial.println("left"); + } + + if (enc.isRight()) { + if (enc.isFast()) Serial.println("fast right"); + else Serial.println("right"); + } + + if (enc.isLeftH()) Serial.println("leftH"); + if (enc.isRightH()) Serial.println("rightH"); + if (enc.isClick()) Serial.println("click"); + if (enc.isHolded()) Serial.println("holded"); + if (enc.isStep()) Serial.println("step"); + + if (enc.isPress()) Serial.println("press"); + if (enc.isClick()) Serial.println("click"); + if (enc.isRelease()) Serial.println("release"); + + if (enc.hasClicks(1)) Serial.println("1 click"); + if (enc.hasClicks(2)) Serial.println("2 click"); + if (enc.hasClicks(3)) Serial.println("3 click"); + if (enc.hasClicks(5)) Serial.println("5 click"); + + if (enc.hasClicks()) Serial.println(enc.clicks); + //if (enc.isHold()) Serial.println("hold"); +} diff --git a/libraries/EncButton/examples/virtual/virtual.ino b/libraries/EncButton/examples/virtual/virtual.ino new file mode 100644 index 0000000..792d409 --- /dev/null +++ b/libraries/EncButton/examples/virtual/virtual.ino @@ -0,0 +1,35 @@ +#include + +EncButton enc; // виртуальная кнопка +//EncButton enc; // виртуальный энк с кнопкой +//EncButton enc; // виртуальный энк + +void setup() { + Serial.begin(9600); + pinMode(4, INPUT_PULLUP); // подтянем пин + //enc.setHoldTimeout(500); // установка таймаута удержания кнопки +} + +void loop() { + // tick может принимать виртуальный сигнал: + // (сигнал кнопки) + // (сигнал энкодера А, сигнал энкодера B) + // (сигнал энкодера А, сигнал энкодера B, сигнал кнопки) + enc.tick(!digitalRead(4)); + + if (enc.isClick()) Serial.println("click"); + if (enc.isHolded()) Serial.println("holded"); + if (enc.isStep()) Serial.println("step"); + + if (enc.isPress()) Serial.println("press"); + if (enc.isClick()) Serial.println("click"); + if (enc.isRelease()) Serial.println("release"); + + if (enc.hasClicks(1)) Serial.println("1 click"); + if (enc.hasClicks(2)) Serial.println("2 click"); + if (enc.hasClicks(3)) Serial.println("3 click"); + if (enc.hasClicks(5)) Serial.println("5 click"); + + if (enc.hasClicks()) Serial.println(enc.clicks); + //if (enc.isHold()) Serial.println("hold"); +} diff --git a/libraries/EncButton/keywords.txt b/libraries/EncButton/keywords.txt new file mode 100644 index 0000000..772f7aa --- /dev/null +++ b/libraries/EncButton/keywords.txt @@ -0,0 +1,66 @@ +####################################### +# Syntax Coloring Map For EncButton +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### +EncButton KEYWORD1 + +EB_FAST KEYWORD1 +EB_DEB KEYWORD1 +EB_HOLD KEYWORD1 +EB_STEP KEYWORD1 +EB_CLICK KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +tick KEYWORD2 +getState KEYWORD2 +resetState KEYWORD2 +isPress KEYWORD2 +isRelease KEYWORD2 +isFast KEYWORD2 +isTurn KEYWORD2 +isRight KEYWORD2 +isLeft KEYWORD2 +isRightH KEYWORD2 +isLeftH KEYWORD2 +isClick KEYWORD2 +isHolded KEYWORD2 +isHeld KEYWORD2 +isHold KEYWORD2 +isStep KEYWORD2 +counter KEYWORD2 +state KEYWORD2 +hasClicks KEYWORD2 +clicks KEYWORD2 +attach KEYWORD2 +detach KEYWORD2 +attachClicks KEYWORD2 +detachClicks KEYWORD2 +pullUp KEYWORD2 +holdEncButton KEYWORD2 +setHoldTimeout KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### +EB_TICK LITERAL1 +EB_CALLBACK LITERAL1 +VIRT_BTN LITERAL1 +VIRT_ENC LITERAL1 +VIRT_ENCBTN LITERAL1 +TURN_HANDLER LITERAL1 +RIGHT_HANDLER LITERAL1 +LEFT_HANDLER LITERAL1 +RIGHT_H_HANDLER LITERAL1 +LEFT_H_HANDLER LITERAL1 +CLICK_HANDLER LITERAL1 +HOLDED_HANDLER LITERAL1 +STEP_HANDLER LITERAL1 +HOLD_HANDLER LITERAL1 +CLICKS_HANDLER LITERAL1 +PRESS_HANDLER LITERAL1 +RELEASE_HANDLER LITERAL1 \ No newline at end of file diff --git a/libraries/EncButton/library.properties b/libraries/EncButton/library.properties new file mode 100644 index 0000000..ed77e1f --- /dev/null +++ b/libraries/EncButton/library.properties @@ -0,0 +1,9 @@ +name=EncButton +version=1.8 +author=AlexGyver +maintainer=AlexGyver +sentence=Light and fast library for button and encoder control +paragraph=Light and fast library for button and encoder control +category=Sensors +url=https://github.com/GyverLibs/EncButton +architectures=* \ No newline at end of file diff --git a/libraries/EncButton/src/EncButton.h b/libraries/EncButton/src/EncButton.h new file mode 100644 index 0000000..f8345d1 --- /dev/null +++ b/libraries/EncButton/src/EncButton.h @@ -0,0 +1,383 @@ +/* + Ультра лёгкая и быстрая библиотека для энкодера, энкодера с кнопкой или просто кнопки + Документация: + GitHub: https://github.com/GyverLibs/EncButton + Возможности: + - Максимально быстрое чтение пинов для AVR (ATmega328/ATmega168, ATtiny85/ATtiny13) + - Оптимизированный вес + - Быстрые и лёгкие алгоритмы кнопки и энкодера + - Энкодер: поворот, нажатый поворот, быстрый поворот, счётчик + - Кнопка: антидребезг, клик, несколько кликов, счётчик кликов, удержание, режим step + - Подключение - только HIGH PULL! + - Опциональный режим callback (+22б SRAM на каждый экземпляр) + + AlexGyver, alex@alexgyver.ru + https://alexgyver.ru/ + MIT License + + Версии: + v1.1 - пуллап отдельныи методом + v1.2 - можно передать конструктору параметр INPUT_PULLUP / INPUT(умолч) + v1.3 - виртуальное зажатие кнопки энкодера вынесено в отдельную функцию + мелкие улучшения + v1.4 - обработка нажатия и отпускания кнопки + v1.5 - добавлен виртуальный режим + v1.6 - оптимизация работы в прерывании + v1.6.1 - PULLUP по умолчанию + v1.7 - большая оптимизация памяти, переделан FastIO + v1.8 - индивидуальная настройка таймаута удержания кнопки (была общая на всех) +*/ + +#ifndef EncButton_h +#define EncButton_h + +// =========== НАСТРОЙКИ (можно передефайнить из скетча) ============ +#define _EB_FAST 30 // таймаут быстрого поворота +#define _EB_DEB 80 // дебаунс кнопки +#define _EB_HOLD 1000 // таймаут удержания кнопки +#define _EB_STEP 500 // период срабатывания степ +#define _EB_CLICK 400 // таймаут накликивания + +// =========== НЕ ТРОГАЙ ============ +#include +#include "FastIO_v2.h" +// флаг макро +#define _setFlag(x) (flags |= 1 << x) +#define _clrFlag(x) (flags &= ~(1 << x)) +#define _readFlag(x) ((flags >> x) & 1) + +#ifndef EB_FAST +#define EB_FAST _EB_FAST +#endif +#ifndef EB_DEB +#define EB_DEB _EB_DEB +#endif +#ifndef EB_HOLD +#define EB_HOLD _EB_HOLD +#endif +#ifndef EB_STEP +#define EB_STEP _EB_STEP +#endif +#ifndef EB_CLICK +#define EB_CLICK _EB_CLICK +#endif + +enum eb_callback { + TURN_HANDLER, + RIGHT_HANDLER, + LEFT_HANDLER, + RIGHT_H_HANDLER, + LEFT_H_HANDLER, + CLICK_HANDLER, + HOLDED_HANDLER, + STEP_HANDLER, + HOLD_HANDLER, + CLICKS_HANDLER, + PRESS_HANDLER, + RELEASE_HANDLER, +}; + +// константы +#define EB_TICK 0 +#define EB_CALLBACK 1 + +#define EB_NO_PIN 255 + +#define VIRT_ENC 254 +#define VIRT_ENCBTN 253 +#define VIRT_BTN 252 + +// класс +template < uint8_t _EB_MODE, uint8_t _S1 = EB_NO_PIN, uint8_t _S2 = EB_NO_PIN, uint8_t _KEY = EB_NO_PIN > +class EncButton { +public: + // можно указать режим работы пина + EncButton(const uint8_t mode = INPUT_PULLUP) { + if (_S1 < 252 && mode == INPUT_PULLUP) pullUp(); + } + + // подтянуть пины внутренней подтяжкой + void pullUp() { + if (_S1 < 252) { // реальное устройство + if (_S2 == EB_NO_PIN) { // обычная кнопка + pinMode(_S1, INPUT_PULLUP); + } else if (_KEY == EB_NO_PIN) { // энк без кнопки + pinMode(_S1, INPUT_PULLUP); + pinMode(_S2, INPUT_PULLUP); + } else { // энк с кнопкой + pinMode(_S1, INPUT_PULLUP); + pinMode(_S2, INPUT_PULLUP); + pinMode(_KEY, INPUT_PULLUP); + } + } + } + + // установить таймаут удержания кнопки для isHold(), мс (до 30 000) + void setHoldTimeout(int tout) { + _holdT = tout >> 7; + } + + // виртуально зажать кнопку энкодера + void holdEncButton(bool state) { + if (state) _setFlag(7); + else _clrFlag(7); + } + + // тикер, вызывать как можно чаще или в прерывании + // вернёт отличное от нуля значение, если произошло какое то событие + uint8_t tick(uint8_t s1 = 0, uint8_t s2 = 0, uint8_t key = 0) { + if (!_isrFlag) { + _isrFlag = 1; + + // обработка энка (компилятор вырежет блок если не используется) + // если объявлены два пина или выбран вирт. энкодер или энкодер с кнопкой + if ((_S1 < 252 && _S2 < 252) || _S1 == VIRT_ENC || _S1 == VIRT_ENCBTN) { + uint8_t state; + if (_S1 >= 252) state = s1 | (s2 << 1); // получаем код + else state = F_fastRead(_S1) | (F_fastRead(_S2) << 1); // получаем код + poolEnc(state); + } + + // обработка кнопки (компилятор вырежет блок если не используется) + // если S2 не указан (кнопка) или указан KEY или выбран вирт. энкодер с кнопкой или кнопка + if ((_S1 < 252 && _S2 == EB_NO_PIN) || _KEY != EB_NO_PIN || _S1 == VIRT_BTN || _S1 == VIRT_ENCBTN) { + if (_S1 < 252 && _S2 == EB_NO_PIN) _btnState = !F_fastRead(_S1); // обычная кнопка + if (_KEY != EB_NO_PIN) _btnState = !F_fastRead(_KEY); // энк с кнопкой + if (_S1 == VIRT_BTN) _btnState = s1; // вирт кнопка + if (_S1 == VIRT_ENCBTN) _btnState = key; // вирт энк с кнопкой + poolBtn(); + } + + if (_EB_MODE) { + if (*_callback[0] && isTurn()) _callback[0](); + switch (EBState) { + case 1: if (*_callback[1]) _callback[1](); break; // isRight + case 2: if (*_callback[2]) _callback[2](); break; // isLeft + case 3: if (*_callback[3]) _callback[3](); break; // isRightH + case 4: if (*_callback[4]) _callback[4](); break; // isLeftH + case 5: if (*_callback[5]) _callback[5](); break; // isClick + case 6: if (*_callback[6]) _callback[6](); break; // isHolded + case 7: if (*_callback[7]) _callback[7](); break; // isStep + case 8: if (*_callback[11]) _callback[11](); break; // isPress + case 9: if (*_callback[12]) _callback[12](); break; // isRelease + } + EBState = 0; + if (*_callback[8] && _readFlag(4)) _callback[8](); // isHold + if (_readFlag(6)) { + if (*_callback[9]) _callback[9](); // clicks + if (*_callback[10] && clicks == _amount) _callback[10](); + _clrFlag(6); + } + } + } + _isrFlag = 0; + return EBState; + } + + // подключить обработчик + void attach(eb_callback type, void (*handler)()) { + _callback[type] = *handler; + } + + // отключить обработчик + void detach(eb_callback type) { + _callback[type] = NULL; + } + + // подключить обработчик на количество кликов (может быть только один!) + void attachClicks(uint8_t amount, void (*handler)()) { + _amount = amount; + _callback[10] = *handler; + } + + // отключить обработчик на количество кликов + void detachClicks() { + _callback[10] = NULL; + } + + // получить статус + uint8_t getState() { return EBState; } + + // сбросить статус + void resetState() { EBState = 0; } + + // поворот вправо + bool isRight() { return _EB_MODE ? (_dir == 1 ? 1 : 0) : checkState(1); } + + // поворот влево + bool isLeft() { return _EB_MODE ? (_dir == 2 ? 1 : 0) : checkState(2); } + + // поворот вправо нажатый + bool isRightH() { return _EB_MODE ? (_dir == 3 ? 1 : 0) : checkState(3); } + + // поворот влево нажатый + bool isLeftH() { return _EB_MODE ? (_dir == 4 ? 1 : 0) : checkState(4); } + + // быстрый поворот + bool isFast() { return _readFlag(1); } + + // энкодер повёрнут + bool isTurn() { + if (_readFlag(0)) { + _clrFlag(0); + return true; + } return false; + } + + // кнопка нажата + bool isPress() { return checkState(8); } + + // кнопка отпущена + bool isRelease() { return checkState(9); } + + // клик по кнопке + bool isClick() { return checkState(5); } + + // кнопка удержана + bool isHolded() { return checkState(6); } + + // кнопка удержана (грамотный аналог holded =) + bool isHeld() { return checkState(6); } + + // кнопка удерживается + bool isHold() { return _readFlag(4); } + + // режим импульсного удержания + bool isStep() { return checkState(7); } + + // статус кнопки + bool state() { return !F_fastRead(_S1); } + + // имеются клики + bool hasClicks(uint8_t numClicks) { + if (clicks == numClicks && _readFlag(6)) { + _clrFlag(6); + return 1; + } + return 0; + } + + // имеются клики + uint8_t hasClicks() { + if (_readFlag(6)) { + _clrFlag(6); + return clicks; + } return 0; + } + + // счётчик энкодера + int counter = 0; + + // счётчик кликов + uint8_t clicks = 0; + +private: + void poolEnc(uint8_t state) { + if (_encRST && state == 0b11) { // ресет и энк защёлкнул позицию + if (_S2 == EB_NO_PIN || _KEY != EB_NO_PIN) { // энкодер с кнопкой + if (!_readFlag(4)) { // если кнопка не "удерживается" + if (_lastState == 0b10) EBState = (_btnState || _readFlag(7)) ? 3 : 1, counter++; + else if (_lastState == 0b01) EBState = (_btnState || _readFlag(7)) ? 4 : 2, counter--; + } + } else { // просто энкодер + if (_lastState == 0b10) EBState = 1, counter++; + else if (_lastState == 0b01) EBState = 2, counter--; + } + if (EBState > 0) { + if (_EB_MODE) _dir = EBState; + if (millis() - _debTimer < EB_FAST) _setFlag(1); // быстрый поворот + else _clrFlag(1); // обычный поворот + if (EBState < 5) _setFlag(0); // флаг поворота для юзера + } + + _encRST = 0; + _debTimer = millis(); + } + if (state == 0b00) _encRST = 1; + _lastState = state; + } + + void poolBtn() { + uint32_t thisMls = millis(); + uint32_t debounce = thisMls - _debTimer; + if (_btnState) { // кнопка нажата + if (!_readFlag(3)) { // и не была нажата ранее + if (debounce > EB_DEB) { // и прошел дебаунс + _setFlag(3); // флаг кнопка была нажата + _debTimer = thisMls; // сброс таймаутов + EBState = 8; // кнопка нажата + } + if (debounce > EB_CLICK) { // кнопка нажата после EB_CLICK + clicks = 0; // сбросить счётчик и флаг кликов + flags &= ~0b01100000; + } + } else { // кнопка уже была нажата + if (!_readFlag(4)) { // и удержание ещё не зафиксировано + if (debounce < (_holdT << 7)) { // прошло меньше удержания + if (EBState != 0 && EBState != 8) _setFlag(2); // но энкодер повёрнут! Запомнили + } else { // прошло больше времени удержания + if (!_readFlag(2)) { // и энкодер не повёрнут + EBState = 6; // значит это удержание (сигнал) + _setFlag(4); // запомнили что удерживается + _debTimer = thisMls; // сброс таймаута + } + } + } else { // удержание зафиксировано + if (debounce > EB_STEP) { // таймер степа + EBState = 7; // сигналим + _debTimer = thisMls; // сброс таймаута + } + } + } + } else { // кнопка не нажата + if (_readFlag(3)) { // но была нажата + if (debounce > EB_DEB && !_readFlag(4) && !_readFlag(2)) { // энкодер не трогали и не удерживали - это клик + EBState = 5; + clicks++; + } else EBState = 9; // кнопка отпущена + flags &= ~0b00011100; // clear 2 3 4 + _debTimer = thisMls; // сброс таймаута + } else if (clicks > 0 && debounce > EB_CLICK && !_readFlag(5)) flags |= 0b01100000; // флаг на клики + } + } + + bool checkState(uint8_t val) { + if (EBState == val) { + EBState = 0; + return 1; + } return 0; + } + uint32_t _debTimer = 0; + uint8_t _lastState = 0, EBState = 0; + bool _btnState = 0, _encRST = 0, _isrFlag = 0; + uint8_t flags = 0; + uint8_t _holdT = EB_HOLD >> 7; + + uint8_t _dir = 0; + void (*_callback[_EB_MODE ? 13 : 0])() = {}; + uint8_t _amount = 0; + + + // flags + // 0 - enc turn + // 1 - enc fast + // 2 - enc был поворот + // 3 - флаг кнопки + // 4 - hold + // 5 - clicks flag + // 6 - clicks get + // 7 - enc button hold + + // EBState + // 0 - idle + // 1 - right + // 2 - left + // 3 - rightH + // 4 - leftH + // 5 - click + // 6 - holded + // 7 - step + // 8 - press + // 9 - release +}; + +#endif \ No newline at end of file diff --git a/libraries/EncButton/src/FastIO_v2.cpp b/libraries/EncButton/src/FastIO_v2.cpp new file mode 100644 index 0000000..2e1ecdf --- /dev/null +++ b/libraries/EncButton/src/FastIO_v2.cpp @@ -0,0 +1,93 @@ +#include "FastIO_v2.h" + +bool F_fastRead(const uint8_t pin) { +#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) + if (pin < 8) return bitRead(PIND, pin); + else if (pin < 14) return bitRead(PINB, pin - 8); + else if (pin < 20) return bitRead(PINC, pin - 14); + +#elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny13__) + return bitRead(PINB, pin); + +#elif defined(AVR) + uint8_t *_pin_reg = portInputRegister(digitalPinToPort(pin)); + uint8_t _bit_mask = digitalPinToBitMask(pin); + return bool(*_pin_reg & _bit_mask); + +#else + return digitalRead(pin); + +#endif + return 0; +} + +void F_fastWrite(const uint8_t pin, bool val) { +#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) + if (pin < 8) bitWrite(PORTD, pin, val); + else if (pin < 14) bitWrite(PORTB, (pin - 8), val); + else if (pin < 20) bitWrite(PORTC, (pin - 14), val); + +#elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny13__) + bitWrite(PORTB, pin, val); + +#elif defined(AVR) + uint8_t *_port_reg = portInputRegister(digitalPinToPort(pin)); + uint8_t _bit_mask = digitalPinToBitMask(pin); + _port_reg = portOutputRegister(digitalPinToPort(pin)); + _bit_mask = digitalPinToBitMask(pin); + if (val) *_port_reg |= _bit_mask; // HIGH + else *_port_reg &= ~_bit_mask; // LOW + +#else + digitalWrite(pin, val); + +#endif +} + +uint8_t F_fastShiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) { +#if defined(AVR) + volatile uint8_t *_clk_port = portOutputRegister(digitalPinToPort(clockPin)); + volatile uint8_t *_dat_port = portInputRegister(digitalPinToPort(dataPin)); + uint8_t _clk_mask = digitalPinToBitMask(clockPin); + uint8_t _dat_mask = digitalPinToBitMask(dataPin); + uint8_t data = 0; + for (uint8_t i = 0; i < 8; i++) { + *_clk_port |= _clk_mask; + if (bitOrder == MSBFIRST) { + data <<= 1; + if (bool(*_dat_port & _dat_mask)) data |= 1; + } else { + data >>= 1; + if (bool(*_dat_port & _dat_mask)) data |= 1 << 7; + } + *_clk_port &= ~_clk_mask; + } + return data; +#else + return shiftIn(dataPin, clockPin, bitOrder); +#endif +} + +void F_fastShiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t data) { +#if defined(AVR) + volatile uint8_t *_clk_port = portOutputRegister(digitalPinToPort(clockPin)); + volatile uint8_t *_dat_port = portOutputRegister(digitalPinToPort(dataPin)); + uint8_t _clk_mask = digitalPinToBitMask(clockPin); + uint8_t _dat_mask = digitalPinToBitMask(dataPin); + for (uint8_t i = 0; i < 8; i++) { + if (bitOrder == MSBFIRST) { + if (data & (1 << 7)) *_dat_port |= _dat_mask; + else *_dat_port &= ~_dat_mask; + data <<= 1; + } else { + if (data & 1) *_dat_port |= _dat_mask; + else *_dat_port &= ~_dat_mask; + data >>= 1; + } + *_clk_port |= _clk_mask; + *_clk_port &= ~_clk_mask; + } +#else + shiftOut(dataPin, clockPin, bitOrder, data); +#endif +} \ No newline at end of file diff --git a/libraries/EncButton/src/FastIO_v2.h b/libraries/EncButton/src/FastIO_v2.h new file mode 100644 index 0000000..e83265c --- /dev/null +++ b/libraries/EncButton/src/FastIO_v2.h @@ -0,0 +1,13 @@ +// Быстрый IO для AVR (для остальных будет digitalxxxxx) +// v1.0 + +#ifndef _FastIO_v2_h +#define _FastIO_v2_h +#include + +bool F_fastRead(const uint8_t pin); // быстрое чтение пина +void F_fastWrite(const uint8_t pin, bool val); // быстрая запись +uint8_t F_fastShiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder); // быстрый shiftIn +void F_fastShiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t data); // быстрый shiftOut + +#endif \ No newline at end of file diff --git a/libraries/GyverEncoder/README.md b/libraries/GyverEncoder/README.md deleted file mode 100644 index 9261b11..0000000 --- a/libraries/GyverEncoder/README.md +++ /dev/null @@ -1,149 +0,0 @@ -![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/encoder_2_impulse/encoder_2_impulse.ino b/libraries/GyverEncoder/examples/encoder_2_impulse/encoder_2_impulse.ino deleted file mode 100644 index 99814e8..0000000 --- a/libraries/GyverEncoder/examples/encoder_2_impulse/encoder_2_impulse.ino +++ /dev/null @@ -1,28 +0,0 @@ -/* - В последнее время китайцы стали делать одинаковые модули (ку 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 deleted file mode 100644 index 59eb5d2..0000000 --- a/libraries/GyverEncoder/examples/encoder_auto/encoder_auto.ino +++ /dev/null @@ -1,30 +0,0 @@ -#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 deleted file mode 100644 index cbab6a6..0000000 --- a/libraries/GyverEncoder/examples/encoder_demo/encoder_demo.ino +++ /dev/null @@ -1,47 +0,0 @@ -#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 deleted file mode 100644 index a526add..0000000 --- a/libraries/GyverEncoder/examples/encoder_fast/encoder_fast.ino +++ /dev/null @@ -1,32 +0,0 @@ -#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 deleted file mode 100644 index 3a23bf4..0000000 --- a/libraries/GyverEncoder/examples/encoder_interrupt/encoder_interrupt.ino +++ /dev/null @@ -1,33 +0,0 @@ -/* - Пример работы с энкодером с прерыванием. Максимальная чёткость работы - в любом быдлокоде! -*/ - -#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 deleted file mode 100644 index d0c5714..0000000 --- a/libraries/GyverEncoder/examples/external_enc/external_enc.ino +++ /dev/null @@ -1,40 +0,0 @@ -// подключаем "внешний" энкодер, для работы с расширителями пинов например - -#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 deleted file mode 100644 index b4c21f6..0000000 --- a/libraries/GyverEncoder/examples/timer_isr/timer_isr.ino +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Отработка по прерыванию таймера - */ - -#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 deleted file mode 100644 index 7a08fee..0000000 --- a/libraries/GyverEncoder/examples/two_encoders/two_encoders.ino +++ /dev/null @@ -1,20 +0,0 @@ -// два энкодера - -#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 deleted file mode 100644 index 3e96ec5..0000000 --- a/libraries/GyverEncoder/examples/value_change/value_change.ino +++ /dev/null @@ -1,31 +0,0 @@ -#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 deleted file mode 100644 index 9e9138e..0000000 --- a/libraries/GyverEncoder/keywords.txt +++ /dev/null @@ -1,52 +0,0 @@ -####################################### -# 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 deleted file mode 100644 index 6ee5fdc..0000000 --- a/libraries/GyverEncoder/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 67d8156..0000000 --- a/libraries/GyverEncoder/src/GyverEncoder.cpp +++ /dev/null @@ -1,352 +0,0 @@ -#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 deleted file mode 100644 index 5205231..0000000 --- a/libraries/GyverEncoder/src/GyverEncoder.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - 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 diff --git a/libraries/GyverTM1637/LICENSE b/libraries/GyverTM1637/LICENSE new file mode 100644 index 0000000..353b7ee --- /dev/null +++ b/libraries/GyverTM1637/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/GyverTM1637/README.md b/libraries/GyverTM1637/README.md new file mode 100644 index 0000000..1d6e8ab --- /dev/null +++ b/libraries/GyverTM1637/README.md @@ -0,0 +1,337 @@ +![License: MIT](https://img.shields.io/badge/License-MIT-green.svg) +![author](https://img.shields.io/badge/author-AlexGyver-informational.svg) +# GyverTM1637 +GyverTM1637 - бибилотека для 7 сегментного дисплея на чипе TM1637 с кучей приколюх +- Вывод цифр массивом или прицельно +- Вывод букв из списка доступных (листай ниже) массивом или прицельно +- Отдельная функция вывода часов и минут (часы без нуля слева, минуты с нулём) +- Вывод числа от -999 до 9999 с учётом знака +- Готовая функция бегущей строки +- Функции смены яркости и состояния двоеточия автоматически обновляют дисплей +- Функция обновления значения с эффектом вертикальной прокрутки +- Функция обновления значения с эффектом скручивания (лучше один раз увидеть) + +### Совместимость +Совместима со всеми Arduino платформами (используются Arduino-функции) + +### Документация +К библиотеке есть [расширенная документация](https://alexgyver.ru/tm1637_display/) + +## Содержание +- [Установка](#install) +- [Инициализация](#init) +- [Использование](#usage) +- [Пример](#example) +- [Версии](#versions) +- [Баги и обратная связь](#feedback) + + +## Установка +- Библиотеку можно найти по названию **GyverTM1637** и установить через менеджер библиотек в: + - Arduino IDE + - Arduino IDE v2 + - PlatformIO +- [Скачать библиотеку](https://github.com/GyverLibs/GyverTM1637/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 +GyverTM1637 disp(CLK, DIO); +``` + + +## Использование +```cpp +void display(uint8_t DispData[]); // выводит цифры массивом по ячейкам. От 0 до 9 (byte values[] = {3, 5, 9, 0}; ) +void display(uint8_t BitAddr, uint8_t DispData); // выводит цифру DispData в указанную ячейку дисплея BitAddr +void display(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3); // если лень создавать массив, выводит цифры в ячейки + +void displayByte(uint8_t DispData[]); // выводит байт вида 0xe6 и буквы-константы вида _a , _b .... массивом +void displayByte(uint8_t BitAddr, uint8_t DispData); // выводит байт вида 0xe6 и буквы-константы вида _a , _b .... в ячейку +void displayByte(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3); // если лень создавать массив, выводит байты в ячейки + +void displayClock(uint8_t hrs, uint8_t mins); // выводит часы и минуты +void displayClockScroll(uint8_t hrs, uint8_t mins, int delayms); // выводит часы и минуты с эффектом прокрутки +void displayClockTwist(uint8_t hrs, uint8_t mins, int delayms); // выводит часы и минуты с эффектом скрутки + +void displayInt(int value); // выводит число от -999 до 9999 (да, со знаком минус) +void runningString(uint8_t DispData[], byte amount, int delayMs); // бегущая строка (array, sizeof(array), задержка в мс) +void clear(void); // очистить дисплей +void point(boolean PointFlag); // вкл / выкл точку (POINT_ON / POINT_OFF) +void brightness(uint8_t bright, uint8_t = 0x40, uint8_t = 0xc0); // яркость 0 - 7 + +void scroll(uint8_t BitAddr, uint8_t DispData, int delayms); // обновить значение прокруткой (адрес, ЦИФРА, задержка в мс) +void scroll(uint8_t DispData[], int delayms); // обновить значение прокруткой (массив ЦИФР, задержка в мс) +void scroll(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3, int delayms); // прокрутка посимвольно +void scrollByte(uint8_t BitAddr, uint8_t DispData, int delayms); // обновить значение прокруткой (адрес, БАЙТ, задержка в мс) +void scrollByte(uint8_t DispData[], int delayms); // обновить значение прокруткой (массив БАЙТ, задержка в мс) +void scrollByte(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3, int delayms); // прокрутка посимвольно + +void twist(uint8_t BitAddr, uint8_t DispData, int delayms); // обновить значение скручиванием (адрес, ЦИФРА, задержка в мс) +void twist(uint8_t DispData[], int delayms); // обновить значение скручиванием (массив ЦИФР, задержка в мс) +void twist(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3, int delayms); // скрутка посимвольно +void twistByte(uint8_t BitAddr, uint8_t DispData, int delayms); // обновить значение скручиванием (адрес, БАЙТ, задержка в мс) +void twistByte(uint8_t DispData[], int delayms); // обновить значение скручиванием (массив БАЙТ, задержка в мс) +void twistByte(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3, int delayms); // скрутка посимвольно +``` + + +## Пример +Остальные примеры смотри в **examples**! +```cpp +/* + Пример вывода на дисплей с регистром TM1637 + показывает все возможности библиотеки GyverTM1637 + AlexGyver Technologies http://alexgyver.ru/ +*/ + +#define CLK 2 +#define DIO 3 + +#include "GyverTM1637.h" +GyverTM1637 disp(CLK, DIO); + +uint32_t Now, clocktimer; +boolean flag; + +void setup() { + Serial.begin(9600); + disp.clear(); + disp.brightness(7); // яркость, 0 - 7 (минимум - максимум) + +} + +void loop() { + runningText(); + scrolls(); + scrollClock(); + twists(); + twistClock(); + ints(); + bytes(); + fadeBlink(); + normClock(); +} + +void twists() { + // скручивание массив ЦИФР + byte digs[4] = {3, 5, 7, 1}; + disp.twist(digs, 50); // скорость прокрутки 100 + delay(1000); + + // скручивание прицельно (ячейка, БАЙТ, скорость) + disp.twistByte(0, _1, 50); + delay(1000); + + // скручивание прицельно (ячейка, ЦИФРА, скорость) + disp.twist(0, 8, 70); + delay(1000); + + disp.clear(); + delay(200); + for (byte i = 0; i < 10; i++) { + disp.twist(3, i, 20); + delay(200); + } + + // скручивание массива БАЙТ + byte troll[4] = {_t, _r, _o, _l}; + disp.twistByte(troll, 50); + delay(1000); + + // прицельное скручивание БАЙТА (ячейка, байт, скорость) + disp.twistByte(2, _G, 50); + delay(1000); +} + +void twistClock() { + byte hrs = 21, mins = 55; + uint32_t tmr; + Now = millis(); + while (millis () - Now < 10000) { // каждые 10 секунд + if (millis() - tmr > 500) { // каждые полсекунды + tmr = millis(); + flag = !flag; + disp.point(flag); // выкл/выкл точки + + if (flag) { + // ***** часы! **** + mins ++; + if (mins > 59) { + mins = 0; + hrs++; + if (hrs > 24) hrs = 0; + } + // ***** часы! **** + disp.displayClockTwist(hrs, mins, 35); // выводим время + } + } + } + disp.point(0); // выкл точки +} + +void scrolls() { + // прокрутка массив ЦИФР + byte digs[4] = {3, 5, 7, 1}; + disp.scroll(digs, 100); // скорость прокрутки 100 + delay(1000); + + // прокрутка прицельно (ячейка, ЦИФРА, скорость) + disp.scroll(0, 8, 200); + delay(1000); + + disp.clear(); + delay(1000); + for (byte i = 0; i < 10; i++) { + disp.scroll(3, i, 50); + delay(400); + } + + // прокрутка массива БАЙТ + byte troll[4] = {_t, _r, _o, _l}; + disp.scrollByte(troll, 100); + delay(1000); + + // прицельная прокрутка БАЙТА (ячейка, байт, скорость) + disp.scrollByte(2, _G, 50); + delay(1000); +} + +void bytes() { + // выводим байты из массива + byte troll[4] = {_t, _r, _o, _l}; + disp.displayByte(troll); + delay(1000); + + // выводим байты напрямую (4 в скобках) + disp.displayByte(_L, _O, _L, _empty); + delay(1000); + + // выводим байты "прицельно" + disp.displayByte(3, _O); // 3 ячейка, буква О + delay(1000); + + // выводим цифры из массива + byte hell[4] = {6, 6, 6, 6}; + disp.display(hell); + delay(1000); + + // выводим цифры напрямую (4 в скобках) + disp.display(1, 2, 3, 4); + delay(1000); + + // выводим цифры "прицельно" + disp.display(0, 9); // 0 ячейка, цифра 9 + delay(1000); +} + +void fadeBlink() { + // пишем HELL + disp.displayByte(_H, _E, _L, _L); + + Now = millis(); + while (millis () - Now < 3000) { // 3 секунды + for (int i = 7; i > 0; i--) { + disp.brightness(i); // меняем яркость + delay(40); + } + for (int i = 0; i < 8; i++) { + disp.brightness(i); // меняем яркость + delay(40); + } + } +} + +void scrollClock() { + byte hrs = 15, mins = 0; + uint32_t tmr; + Now = millis(); + while (millis () - Now < 10000) { // каждые 10 секунд + if (millis() - tmr > 500) { // каждые полсекунды + tmr = millis(); + flag = !flag; + disp.point(flag); // выкл/выкл точки + + if (flag) { + // ***** часы! **** + mins ++; + if (mins > 59) { + mins = 0; + hrs++; + if (hrs > 24) hrs = 0; + } + // ***** часы! **** + disp.displayClockScroll(hrs, mins, 70); // выводим время + } + } + } + disp.point(0); // выкл точки +} + +void normClock() { + byte hrs = 15, mins = 0; + uint32_t tmr; + Now = millis(); + while (millis () - Now < 10000) { // каждые 10 секунд + if (millis() - tmr > 500) { // каждые полсекунды + tmr = millis(); + flag = !flag; + disp.point(flag); // выкл/выкл точки + + // ***** часы! **** + mins ++; + if (mins > 59) { + mins = 0; + hrs++; + if (hrs > 24) hrs = 0; + } + // ***** часы! **** + disp.displayClock(hrs, mins); // выводим время функцией часов + } + } + disp.point(0); // выкл точки +} + +void ints() { + // тупо отправляем цифры + disp.displayInt(-999); + delay(500); + disp.displayInt(-99); + delay(500); + disp.displayInt(-9); + delay(500); + disp.displayInt(0); + delay(500); + disp.displayInt(6); + delay(500); + disp.displayInt(66); + delay(500); + disp.displayInt(666); + delay(500); + disp.displayInt(6666); + delay(500); +} + +void runningText() { + byte welcome_banner[] = {_H, _E, _L, _L, _O, _empty, _empty, + _e, _n, _j, _o, _y, _empty, _empty, + _1, _6, _3, _7, _empty, _d, _i, _S, _P, _l, _a, _y + }; + disp.runningString(welcome_banner, sizeof(welcome_banner), 200); // 200 это время в миллисекундах! +} + +``` + + +## Версии +- v1.4 - поправлены типы данных и ошибки, добавлена совместимость с ESP + + +## Баги и обратная связь +При нахождении багов создавайте **Issue**, а лучше сразу пишите на почту [alex@alexgyver.ru](mailto:alex@alexgyver.ru) +Библиотека открыта для доработки и ваших **Pull Request**'ов! \ No newline at end of file diff --git a/libraries/GyverTM1637/examples/GyverTM1637_demo/GyverTM1637_demo.ino b/libraries/GyverTM1637/examples/GyverTM1637_demo/GyverTM1637_demo.ino new file mode 100644 index 0000000..7935c0c --- /dev/null +++ b/libraries/GyverTM1637/examples/GyverTM1637_demo/GyverTM1637_demo.ino @@ -0,0 +1,240 @@ +/* + Пример вывода на дисплей с регистром TM1637 + показывает все возможности библиотеки GyverTM1637 + AlexGyver Technologies http://alexgyver.ru/ +*/ + +#define CLK 2 +#define DIO 3 + +#include "GyverTM1637.h" +GyverTM1637 disp(CLK, DIO); + +uint32_t Now, clocktimer; +boolean flag; + +void setup() { + Serial.begin(9600); + disp.clear(); + disp.brightness(7); // яркость, 0 - 7 (минимум - максимум) + +} + +void loop() { + runningText(); + scrolls(); + scrollClock(); + twists(); + twistClock(); + ints(); + bytes(); + fadeBlink(); + normClock(); +} + +void twists() { + // скручивание массив ЦИФР + byte digs[4] = {3, 5, 7, 1}; + disp.twist(digs, 50); // скорость прокрутки 100 + delay(1000); + + // скручивание прицельно (ячейка, БАЙТ, скорость) + disp.twistByte(0, _1, 50); + delay(1000); + + // скручивание прицельно (ячейка, ЦИФРА, скорость) + disp.twist(0, 8, 70); + delay(1000); + + disp.clear(); + delay(200); + for (byte i = 0; i < 10; i++) { + disp.twist(3, i, 20); + delay(200); + } + + // скручивание массива БАЙТ + byte troll[4] = {_t, _r, _o, _l}; + disp.twistByte(troll, 50); + delay(1000); + + // прицельное скручивание БАЙТА (ячейка, байт, скорость) + disp.twistByte(2, _G, 50); + delay(1000); +} + +void twistClock() { + byte hrs = 21, mins = 55; + uint32_t tmr; + Now = millis(); + while (millis () - Now < 10000) { // каждые 10 секунд + if (millis() - tmr > 500) { // каждые полсекунды + tmr = millis(); + flag = !flag; + disp.point(flag); // выкл/выкл точки + + if (flag) { + // ***** часы! **** + mins ++; + if (mins > 59) { + mins = 0; + hrs++; + if (hrs > 24) hrs = 0; + } + // ***** часы! **** + disp.displayClockTwist(hrs, mins, 35); // выводим время + } + } + } + disp.point(0); // выкл точки +} + +void scrolls() { + // прокрутка массив ЦИФР + byte digs[4] = {3, 5, 7, 1}; + disp.scroll(digs, 100); // скорость прокрутки 100 + delay(1000); + + // прокрутка прицельно (ячейка, ЦИФРА, скорость) + disp.scroll(0, 8, 200); + delay(1000); + + disp.clear(); + delay(1000); + for (byte i = 0; i < 10; i++) { + disp.scroll(3, i, 50); + delay(400); + } + + // прокрутка массива БАЙТ + byte troll[4] = {_t, _r, _o, _l}; + disp.scrollByte(troll, 100); + delay(1000); + + // прицельная прокрутка БАЙТА (ячейка, байт, скорость) + disp.scrollByte(2, _G, 50); + delay(1000); +} + +void bytes() { + // выводим байты из массива + byte troll[4] = {_t, _r, _o, _l}; + disp.displayByte(troll); + delay(1000); + + // выводим байты напрямую (4 в скобках) + disp.displayByte(_L, _O, _L, _empty); + delay(1000); + + // выводим байты "прицельно" + disp.displayByte(3, _O); // 3 ячейка, буква О + delay(1000); + + // выводим цифры из массива + byte hell[4] = {6, 6, 6, 6}; + disp.display(hell); + delay(1000); + + // выводим цифры напрямую (4 в скобках) + disp.display(1, 2, 3, 4); + delay(1000); + + // выводим цифры "прицельно" + disp.display(0, 9); // 0 ячейка, цифра 9 + delay(1000); +} + +void fadeBlink() { + // пишем HELL + disp.displayByte(_H, _E, _L, _L); + + Now = millis(); + while (millis () - Now < 3000) { // 3 секунды + for (int i = 7; i > 0; i--) { + disp.brightness(i); // меняем яркость + delay(40); + } + for (int i = 0; i < 8; i++) { + disp.brightness(i); // меняем яркость + delay(40); + } + } +} + +void scrollClock() { + byte hrs = 15, mins = 0; + uint32_t tmr; + Now = millis(); + while (millis () - Now < 10000) { // каждые 10 секунд + if (millis() - tmr > 500) { // каждые полсекунды + tmr = millis(); + flag = !flag; + disp.point(flag); // выкл/выкл точки + + if (flag) { + // ***** часы! **** + mins ++; + if (mins > 59) { + mins = 0; + hrs++; + if (hrs > 24) hrs = 0; + } + // ***** часы! **** + disp.displayClockScroll(hrs, mins, 70); // выводим время + } + } + } + disp.point(0); // выкл точки +} + +void normClock() { + byte hrs = 15, mins = 0; + uint32_t tmr; + Now = millis(); + while (millis () - Now < 10000) { // каждые 10 секунд + if (millis() - tmr > 500) { // каждые полсекунды + tmr = millis(); + flag = !flag; + disp.point(flag); // выкл/выкл точки + + // ***** часы! **** + mins ++; + if (mins > 59) { + mins = 0; + hrs++; + if (hrs > 24) hrs = 0; + } + // ***** часы! **** + disp.displayClock(hrs, mins); // выводим время функцией часов + } + } + disp.point(0); // выкл точки +} + +void ints() { + // тупо отправляем цифры + disp.displayInt(-999); + delay(500); + disp.displayInt(-99); + delay(500); + disp.displayInt(-9); + delay(500); + disp.displayInt(0); + delay(500); + disp.displayInt(6); + delay(500); + disp.displayInt(66); + delay(500); + disp.displayInt(666); + delay(500); + disp.displayInt(6666); + delay(500); +} + +void runningText() { + byte welcome_banner[] = {_H, _E, _L, _L, _O, _empty, _empty, + _e, _n, _j, _o, _y, _empty, _empty, + _1, _6, _3, _7, _empty, _d, _i, _S, _P, _l, _a, _y + }; + disp.runningString(welcome_banner, sizeof(welcome_banner), 200); // 200 это время в миллисекундах! +} diff --git a/libraries/GyverTM1637/examples/GyverTM1637_demo_mini/GyverTM1637_demo_mini.ino b/libraries/GyverTM1637/examples/GyverTM1637_demo_mini/GyverTM1637_demo_mini.ino new file mode 100644 index 0000000..563c100 --- /dev/null +++ b/libraries/GyverTM1637/examples/GyverTM1637_demo_mini/GyverTM1637_demo_mini.ino @@ -0,0 +1,182 @@ +/* + Пример вывода на дисплей с регистром TM1637 + показывает все возможности библиотеки GyverTM1637 + AlexGyver Technologies http://alexgyver.ru/ +*/ + +#define CLK 2 +#define DIO 3 + +#include "GyverTM1637.h" +GyverTM1637 disp(CLK, DIO); + +uint32_t Now, clocktimer; +boolean flag; + +void setup() { + Serial.begin(9600); + disp.clear(); + disp.brightness(7); // яркость, 0 - 7 (минимум - максимум) + +} + +void loop() { + runningText(); + normClock(); + scrollClock(); + twistClock(); + scrolls(); + twists(); + ints(); + fadeBlink(); +} + +void twists() { + disp.clear(); + delay(200); + for (byte i = 0; i < 10; i++) { + disp.twist(3, i, 20); + delay(300); + } + + // скрутка массива БАЙТ + byte troll[4] = {_L, _O, _L, _empty}; + disp.scrollByte(troll, 100); + delay(500); +} + +void twistClock() { + byte hrs = 21, mins = 55; + uint32_t tmr; + Now = millis(); + while (millis () - Now < 5000) { // каждые 10 секунд + if (millis() - tmr > 500) { // каждые полсекунды + tmr = millis(); + flag = !flag; + disp.point(flag); // выкл/выкл точки + + if (flag) { + // ***** часы! **** + mins ++; + if (mins > 59) { + mins = 0; + hrs++; + if (hrs > 24) hrs = 0; + } + // ***** часы! **** + byte dispTime[] = {(byte)hrs / 10, (byte)hrs % 10, (byte)mins / 10, (byte)mins % 10}; + disp.twist(dispTime, 35); // выводим время массивом + } + } + } + disp.point(0); // выкл точки +} + +void scrolls() { + disp.clear(); + delay(1000); + for (byte i = 0; i < 10; i++) { + disp.scroll(3, i, 50); + delay(300); + } + + // прокрутка массива БАЙТ + byte troll[4] = {_t, _r, _o, _l}; + disp.scrollByte(troll, 100); + delay(500); +} + +void fadeBlink() { + // пишем HELL + disp.displayByte(_H, _E, _L, _L); + + Now = millis(); + while (millis () - Now < 3000) { // 3 секунды + for (int i = 7; i > 0; i--) { + disp.brightness(i); // меняем яркость + delay(40); + } + for (int i = 0; i < 8; i++) { + disp.brightness(i); // меняем яркость + delay(40); + } + } +} + +void scrollClock() { + byte hrs = 15, mins = 0; + uint32_t tmr; + Now = millis(); + while (millis () - Now < 5000) { // каждые 10 секунд + if (millis() - tmr > 500) { // каждые полсекунды + tmr = millis(); + flag = !flag; + disp.point(flag); // выкл/выкл точки + + if (flag) { + // ***** часы! **** + mins ++; + if (mins > 59) { + mins = 0; + hrs++; + if (hrs > 24) hrs = 0; + } + // ***** часы! **** + byte dispTime[] = {(byte)hrs / 10, (byte)hrs % 10, (byte)mins / 10, (byte)mins % 10}; + disp.scroll(dispTime, 70); // выводим время массивом + } + } + } + disp.point(0); // выкл точки +} + +void normClock() { + byte hrs = 15, mins = 0; + uint32_t tmr; + Now = millis(); + while (millis () - Now < 5000) { // 10 секунд + if (millis() - tmr > 500) { // каждые полсекунды + tmr = millis(); + flag = !flag; + disp.point(flag); // выкл/выкл точки + + // ***** часы! **** + mins ++; + if (mins > 59) { + mins = 0; + hrs++; + if (hrs > 24) hrs = 0; + } + // ***** часы! **** + disp.displayClock(hrs, mins); // выводим время функцией часов + } + } + disp.point(0); // выкл точки +} + +void ints() { + // тупо отправляем цифры + disp.displayInt(-999); + delay(200); + disp.displayInt(-99); + delay(200); + disp.displayInt(-9); + delay(200); + disp.displayInt(0); + delay(200); + disp.displayInt(6); + delay(200); + disp.displayInt(66); + delay(200); + disp.displayInt(666); + delay(200); + disp.displayInt(6666); + delay(200); +} + +void runningText() { + byte welcome_banner[] = {_H, _E, _L, _L, _O, _empty, _empty, + _T, _M, _1, _6, _3, _7, + }; + disp.runningString(welcome_banner, sizeof(welcome_banner), 150); // 200 это время в миллисекундах! +} diff --git a/libraries/GyverTM1637/keywords.txt b/libraries/GyverTM1637/keywords.txt new file mode 100644 index 0000000..4729d01 --- /dev/null +++ b/libraries/GyverTM1637/keywords.txt @@ -0,0 +1,91 @@ + +####################################### +# Syntax Coloring Map For GyverTM1637 +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +GyverTM1637 KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +display KEYWORD2 +displayByte KEYWORD2 +displayInt KEYWORD2 + +displayClock KEYWORD2 +displayClockTwist KEYWORD2 +displayClockScroll KEYWORD2 + +runningString KEYWORD2 +scroll KEYWORD2 +scrollByte KEYWORD2 +twist KEYWORD2 +twistByte KEYWORD2 + +clear KEYWORD2 +point KEYWORD2 +brightness KEYWORD2 +digToHEX KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +POINT_ON LITERAL1 +POINT_OFF LITERAL1 + +_A LITERAL1 +_B LITERAL1 +_C LITERAL1 +_D LITERAL1 +_E LITERAL1 +_F LITERAL1 +_G LITERAL1 +_H LITERAL1 +_J LITERAL1 +_L LITERAL1 +_N LITERAL1 +_O LITERAL1 +_P LITERAL1 +_S LITERAL1 +_U LITERAL1 +_Y LITERAL1 +_a LITERAL1 +_b LITERAL1 +_c LITERAL1 +_d LITERAL1 +_e LITERAL1 +_f LITERAL1 +_h LITERAL1 +_i LITERAL1 +_j LITERAL1 +_l LITERAL1 +_n LITERAL1 +_o LITERAL1 +_q LITERAL1 +_r LITERAL1 +_t LITERAL1 +_u LITERAL1 +_y LITERAL1 + +_dash LITERAL1 +_under LITERAL1 +_equal LITERAL1 +_empty LITERAL1 +_degree LITERAL1 + +_0 LITERAL1 +_1 LITERAL1 +_2 LITERAL1 +_3 LITERAL1 +_4 LITERAL1 +_5 LITERAL1 +_6 LITERAL1 +_7 LITERAL1 +_8 LITERAL1 +_9 LITERAL1 \ No newline at end of file diff --git a/libraries/GyverTM1637/library.properties b/libraries/GyverTM1637/library.properties new file mode 100644 index 0000000..3332d7c --- /dev/null +++ b/libraries/GyverTM1637/library.properties @@ -0,0 +1,9 @@ +name=GyverTM1637 +version=1.4 +author=AlexGyver +maintainer=AlexGyver +sentence=Library for advanced control of TM1637 7-segment display +paragraph=Library for advanced control of TM1637 7-segment display +category=Display +url=https://github.com/GyverLibs/GyverTM1637 +architectures=* \ No newline at end of file diff --git a/libraries/GyverTM1637/src/GyverTM1637.cpp b/libraries/GyverTM1637/src/GyverTM1637.cpp new file mode 100644 index 0000000..f9cddb6 --- /dev/null +++ b/libraries/GyverTM1637/src/GyverTM1637.cpp @@ -0,0 +1,535 @@ +#include "GyverTM1637.h" +#include +const uint8_t digitHEX[] = {0x3f, 0x06, 0x5b, 0x4f, + 0x66, 0x6d, 0x7d, 0x07, + 0x7f, 0x6f, 0x00, 0x40 +};//0~9, ,- + +GyverTM1637::GyverTM1637(uint8_t clk, uint8_t dio) +{ + Clkpin = clk; + Datapin = dio; + pinMode(Clkpin, OUTPUT); + pinMode(Datapin, OUTPUT); +} + +uint8_t digToHEX(uint8_t digit) { + return digitHEX[digit]; +} + +int GyverTM1637::writeByte(int8_t wr_data) +{ + uint8_t i, count1; + for (i = 0; i < 8; i++) //sent 8bit data + { + digitalWrite(Clkpin, LOW); + if (wr_data & 0x01)digitalWrite(Datapin, HIGH); //LSB first + else digitalWrite(Datapin, LOW); + wr_data >>= 1; + digitalWrite(Clkpin, HIGH); + + } + digitalWrite(Clkpin, LOW); //wait for the ACK + digitalWrite(Datapin, HIGH); + digitalWrite(Clkpin, HIGH); + pinMode(Datapin, INPUT); + + delayMicroseconds(50); + uint8_t ack = digitalRead(Datapin); + if (ack == 0) + { + pinMode(Datapin, OUTPUT); + digitalWrite(Datapin, LOW); + } + delayMicroseconds(50); + pinMode(Datapin, OUTPUT); + delayMicroseconds(50); + + return ack; +} +//send start signal to GyverTM1637 +void GyverTM1637::start(void) +{ + digitalWrite(Clkpin, HIGH); //send start signal to GyverTM1637 + digitalWrite(Datapin, HIGH); + digitalWrite(Datapin, LOW); + digitalWrite(Clkpin, LOW); +} +//End of transmission +void GyverTM1637::stop(void) +{ + digitalWrite(Clkpin, LOW); + digitalWrite(Datapin, LOW); + digitalWrite(Clkpin, HIGH); + digitalWrite(Datapin, HIGH); +} + +// ************************** ФОРМИРУЕМ ДАННЫЕ ***************************** +void GyverTM1637::display(uint8_t DispData[]) +{ + uint8_t SegData[4]; + for (byte i = 0; i < 4; i ++) { + //if (DispData[i] == 0x7f) SegData[i] = 0x00; + //else + //{ + lastData[i] = digitHEX[DispData[i]]; + SegData[i] = digitHEX[DispData[i]] + PointData; + //} + } + sendArray(SegData); +} +void GyverTM1637::displayByte(uint8_t DispData[]) +{ + uint8_t SegData[4]; + for (byte i = 0; i < 4; i ++) { + //if (DispData[i] == 0x7f) SegData[i] = 0x00; + //else SegData[i] = DispData[i]; + //{ + lastData[i] = DispData[i]; + SegData[i] = DispData[i] + PointData; + //} + } + sendArray(SegData); +} + +void GyverTM1637::display(uint8_t BitAddr, uint8_t DispData) +{ + uint8_t SegData; + //if (DispData == 0x7f) SegData = 0x00; + //else + //{ + lastData[BitAddr] = digitHEX[DispData]; + SegData = digitHEX[DispData] + PointData; + //} + sendByte(BitAddr, SegData); +} + +void GyverTM1637::displayByte(uint8_t BitAddr, uint8_t DispData) +{ + uint8_t SegData; + //if (DispData == 0x7f) SegData = 0x00; + //else + //{ + lastData[BitAddr] = DispData; + SegData = DispData + PointData; + //} + sendByte(BitAddr, SegData); +} + +// ************************** ОТПРАВКА НА ДИСПЛЕЙ ***************************** +void GyverTM1637::sendByte(uint8_t BitAddr, int8_t sendData) { + start(); //start signal sent to GyverTM1637 from MCU + writeByte(ADDR_FIXED);// + stop(); // + start(); // + writeByte(BitAddr | 0xc0); // + writeByte(sendData);// + stop(); // + start(); // + writeByte(Cmd_DispCtrl);// + stop(); // +} + +void GyverTM1637::sendArray(uint8_t sendData[]) { + start(); //start signal sent to GyverTM1637 from MCU + writeByte(ADDR_AUTO);// + stop(); // + start(); // + writeByte(Cmd_SetAddr);// + for (byte i = 0; i < 4; i ++) { + writeByte(sendData[i]); // + } + stop(); // + start(); // + writeByte(Cmd_DispCtrl);// + stop(); // +} +// ****************************************** +void GyverTM1637::displayByte(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3) { + uint8_t dispArray[] = {bit0, bit1, bit2, bit3}; + displayByte(dispArray); +} +void GyverTM1637::display(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3) { + uint8_t dispArray[] = {bit0, bit1, bit2, bit3}; + display(dispArray); +} + +void GyverTM1637::clear(void) +{ + display(0x00, 0x7f); + display(0x01, 0x7f); + display(0x02, 0x7f); + display(0x03, 0x7f); + lastData[0] = 0x00; + lastData[1] = 0x00; + lastData[2] = 0x00; + lastData[3] = 0x00; +} +void GyverTM1637::update(void) +{ + displayByte(lastData); +} + +void GyverTM1637::brightness(uint8_t brightness, uint8_t SetData, uint8_t SetAddr) +{ + Cmd_SetData = SetData; + Cmd_SetAddr = SetAddr; + Cmd_DispCtrl = 0x88 + brightness;//Set the brightness and it takes effect the next time it displays. + update(); +} + + +void GyverTM1637::point(boolean PointFlag) +{ + if (PointFlag) PointData = 0x80; + else PointData = 0; + update(); +} + +// ************************** ВСЯКИЕ ФУНКЦИИ ***************************** +void GyverTM1637::displayClock(uint8_t hrs, uint8_t mins) { + if (hrs > 99 || mins > 99) return; + uint8_t disp_time[4]; + if ((hrs / 10) == 0) disp_time[0] = 10; + else disp_time[0] = (hrs / 10); + disp_time[1] = hrs % 10; + disp_time[2] = mins / 10; + disp_time[3] = mins % 10; + GyverTM1637::display(disp_time); +} +void GyverTM1637::displayClockScroll(uint8_t hrs, uint8_t mins, int delayms) { + if (hrs > 99 || mins > 99) return; + uint8_t disp_time[4]; + if ((hrs / 10) == 0) disp_time[0] = 10; + else disp_time[0] = (hrs / 10); + disp_time[1] = hrs % 10; + disp_time[2] = mins / 10; + disp_time[3] = mins % 10; + scroll(disp_time, delayms); +} +void GyverTM1637::displayClockTwist(uint8_t hrs, uint8_t mins, int delayms) { + if (hrs > 99 || mins > 99) return; + uint8_t disp_time[4]; + if ((hrs / 10) == 0) disp_time[0] = 10; + else disp_time[0] = (hrs / 10); + disp_time[1] = hrs % 10; + disp_time[2] = mins / 10; + disp_time[3] = mins % 10; + twist(disp_time, delayms); +} + +void GyverTM1637::displayInt(int value) { + if (value > 9999 || value < -999) return; + boolean negative = false; + boolean neg_flag = false; + byte digits[4]; + if (value < 0) negative = true; + value = abs(value); + digits[0] = (int)value / 1000; // количесто тысяч в числе + uint16_t b = (int)digits[0] * 1000; // вспомогательная переменная + digits[1] = ((int)value - b) / 100; // получем количество сотен + b += digits[1] * 100; // суммируем сотни и тысячи + digits[2] = (int)(value - b) / 10; // получем десятки + b += digits[2] * 10; // сумма тысяч, сотен и десятков + digits[3] = value - b; // получаем количество единиц + + if (!negative) { + for (byte i = 0; i < 3; i++) { + if (digits[i] == 0) digits[i] = 10; + else break; + } + } else { + for (byte i = 0; i < 3; i++) { + if (digits[i] == 0) { + if (digits[i + 1] == 0){ + digits[i] = 10; + } else { + digits[i] = 11; + break; + } + } + } + } + GyverTM1637::display(digits); +} + +void GyverTM1637::runningString(uint8_t DispData[], byte amount, int delayMs) { + uint8_t segm_data[amount + 8]; // оставляем место для 4х пустых слотов в начале и в конце + for (byte i = 0; i < 4; i++) { // делаем первые 4 символа пустыми + segm_data[i] = 0x00; + } + for (byte i = 0; i < amount; i++) { // далее забиваем тем что на входе (сам текст строки) + segm_data[i + 4] = DispData[i]; + } + for (byte i = amount + 4; i < amount + 8; i++) { // и последние 4 тоже забиваем пустыми символами + segm_data[i] = 0x00; + } + for (byte i = 0; i <= amount + 4; i++) { // выводим + displayByte(segm_data[i], segm_data[i + 1], segm_data[i + 2], segm_data[i + 3]); + delay(delayMs); + } +} + + +void GyverTM1637::scroll(uint8_t DispData[], int delayms) { + byte DispDataByte[4]; + for (byte i = 0; i < 4; i++) { + DispDataByte[i] = digitHEX[DispData[i]]; + } + scrollByte(DispDataByte, delayms); +} + +void GyverTM1637::scroll(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3, int delayms) { + byte DispData[] = {digitHEX[bit0], digitHEX[bit1], digitHEX[bit2], digitHEX[bit3]}; + GyverTM1637::scrollByte(DispData, delayms); +} + +void GyverTM1637::scroll(uint8_t BitAddr, uint8_t DispData, int delayms) { + byte DispDataByte = digitHEX[DispData]; + scrollByte(BitAddr, DispDataByte, delayms); +} + +void GyverTM1637::scrollByte(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3, int delayms) { + byte DispData[] = {bit0, bit1, bit2, bit3}; + GyverTM1637::scrollByte(DispData, delayms); +} + +void GyverTM1637::scrollByte(uint8_t DispData[], int delayms) { + byte lastBytes[4]; + byte step; + byte stepArray[4]; + boolean changeByte[4] = {0, 0, 0, 0}; + + for (byte i = 0; i < 4; i++) { + if (DispData[i] != lastData[i]) changeByte[i] = 1; + lastBytes[i] = lastData[i]; + } + + for (byte i = 0; i < 4; i++) { + byte lastByte = lastData[i]; + stepArray[i] = lastByte; + + if (changeByte[i]) { + step = 0; + swapBytes(&step, lastByte, 6, 0); + swapBytes(&step, lastByte, 2, 1); + swapBytes(&step, lastByte, 4, 5); + swapBytes(&step, lastByte, 3, 6); + stepArray[i] = step; + } + } + displayByte(stepArray); + delay(delayms); + + for (byte i = 0; i < 4; i++) { + byte lastByte = lastBytes[i]; + stepArray[i] = lastByte; + + if (changeByte[i]) { + step = 0; + swapBytes(&step, lastByte, 3, 0); + stepArray[i] = step; + } + } + displayByte(stepArray); + delay(delayms); + + for (byte i = 0; i < 4; i++) { + if (changeByte[i]) { + stepArray[i] = 0; + } + } + displayByte(stepArray); + delay(delayms); + + for (byte i = 0; i < 4; i++) { + byte lastByte = lastBytes[i]; + byte newByte = DispData[i]; + stepArray[i] = lastByte; + + if (changeByte[i]) { + step = 0; + swapBytes(&step, newByte, 0, 3); + stepArray[i] = step; + } + } + displayByte(stepArray); + delay(delayms); + + for (byte i = 0; i < 4; i++) { + byte newByte = DispData[i]; + stepArray[i] = lastBytes[i]; + + if (changeByte[i]) { + step = 0; + swapBytes(&step, newByte, 0, 6); + swapBytes(&step, newByte, 1, 2); + swapBytes(&step, newByte, 5, 4); + swapBytes(&step, newByte, 6, 3); + stepArray[i] = step; + } + } + displayByte(stepArray); + delay(delayms); + + for (byte i = 0; i < 4; i++) { + displayByte(DispData); + } +} + +void GyverTM1637::scrollByte(uint8_t BitAddr, uint8_t DispData, int delayms) { + byte oldByte = lastData[BitAddr]; + byte newByte = DispData; + byte step; + + step = 0; + swapBytes(&step, oldByte, 6, 0); + swapBytes(&step, oldByte, 2, 1); + swapBytes(&step, oldByte, 4, 5); + swapBytes(&step, oldByte, 3, 6); + displayByte(BitAddr, step); + delay(delayms); + + step = 0; + swapBytes(&step, oldByte, 3, 0); + swapBytes(&step, newByte, 0, 3); + displayByte(BitAddr, step); + delay(delayms); + + step = 0; + swapBytes(&step, newByte, 0, 6); + swapBytes(&step, newByte, 1, 2); + swapBytes(&step, newByte, 5, 4); + swapBytes(&step, newByte, 6, 3); + displayByte(BitAddr, step); + delay(delayms); + + displayByte(BitAddr, newByte); +} + +void swapBytes(byte* newByte, byte oldByte, byte newP, byte oldP) { + byte newBit = 0; + if (oldByte & (1 << oldP)) newBit = 1; + else newBit = 0; + *newByte = *newByte | (newBit << newP); +} + +void GyverTM1637::twist(uint8_t BitAddr, uint8_t DispData, int delayms) { + twistByte(BitAddr, digitHEX[DispData], delayms); +} + +void GyverTM1637::twist(uint8_t DispData[], int delayms) { + byte newData[4]; + for (byte i = 0; i < 4; i++) { + newData[i] = digitHEX[DispData[i]]; + } + twistByte(newData, delayms); +} + +void GyverTM1637::twist(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3, int delayms) { + byte DispData[] = {digitHEX[bit0], digitHEX[bit1], digitHEX[bit2], digitHEX[bit3]}; + GyverTM1637::twistByte(DispData, delayms); +} + +void GyverTM1637::twistByte(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3, int delayms) { + byte DispData[] = {bit0, bit1, bit2, bit3}; + GyverTM1637::twistByte(DispData, delayms); +} + +void GyverTM1637::twistByte(uint8_t DispData[], int delayms) { + byte step; + byte stepArray[4]; + boolean changeByte[4] = {0, 0, 0, 0}; + + for (byte i = 0; i < 4; i++) { + if (DispData[i] != lastData[i]) changeByte[i] = 1; + stepArray[i] = DispData[i]; + } + + step = step & 0b00111111; // выкл центральную + for (byte i = 0; i < 5; i++) { + step |= (1 << i); // зажигаем по одной + for (byte k = 0; k < 4; k++) { + if (changeByte[k]) + stepArray[k] = step; + } + displayByte(stepArray); + delay(delayms); + } + //for (byte r = 0; r < 1; r++) { + for (byte i = 0; i < 6; i++) { + step = 0b11000000; + step = ~(step | (1 << i) | (1 << i + 1)); // бегает дырка + for (byte k = 0; k < 4; k++) { + if (changeByte[k]) stepArray[k] = step; + } + displayByte(stepArray); + delay(delayms); + } + //} + step = 0b11000000; + for (byte i = 0; i < 6; i++) { + step |= (1 << i); + for (byte k = 0; k < 4; k++) { + if (changeByte[k]) + stepArray[k] = ~step; + } + displayByte(stepArray); + delay(delayms); + } + for (byte k = 0; k < 4; k++) { + if (changeByte[k]) + stepArray[k] = 0b0000000; + } + for (byte i = 0; i < 7; i++) { + for (byte k = 0; k < 4; k++) { + if (changeByte[k]) { + byte newBit = 0; + if (DispData[k] & (1 << i)) newBit = 1; + else newBit = 0; + stepArray[k] |= (newBit << i); + } + } + displayByte(stepArray); + delay(delayms); + } +} + +void GyverTM1637::twistByte(uint8_t BitAddr, uint8_t DispData, int delayms) { + byte oldByte = lastData[BitAddr]; + byte newByte = DispData; + byte step = oldByte; + + step = step & 0b0111111; // выкл центральную + + for (byte i = 0; i < 5; i++) { + step |= (1 << i); // зажигаем по одной + displayByte(BitAddr, step); + delay(delayms); + } + //for (byte r = 0; r < 1; r++) { + for (byte i = 0; i < 6; i++) { + step = 0b1000000; + step = ~(step | (1 << i) | (1 << i + 1)); // бегает дырка + + displayByte(BitAddr, step); + delay(delayms); + } + //} + step = 0b1000000; + for (byte i = 0; i < 6; i++) { + step |= (1 << i); + displayByte(BitAddr, ~step); + delay(delayms); + } + step = 0; + for (byte i = 0; i < 7; i++) { + byte newBit = 0; + if (newByte & (1 << i)) newBit = 1; + else newBit = 0; + step |= (newBit << i); + displayByte(BitAddr, step); + delay(delayms); + } + displayByte(BitAddr, newByte); +} \ No newline at end of file diff --git a/libraries/GyverTM1637/src/GyverTM1637.h b/libraries/GyverTM1637/src/GyverTM1637.h new file mode 100644 index 0000000..8095251 --- /dev/null +++ b/libraries/GyverTM1637/src/GyverTM1637.h @@ -0,0 +1,214 @@ +/* + GyverTM1637 - бибилотека для 7 сегментного дисплея на чипе TM1637 с кучей приколюх + Документация: + GitHub: https://github.com/GyverLibs/GyverTM1637 + Возможности: + - Вывод цифр массивом или прицельно + - Вывод букв из списка доступных (листай ниже) массивом или прицельно + - Отдельная функция вывода часов и минут (часы без нуля слева, минуты с нулём) + - Вывод числа от -999 до 9999 с учётом знака + - Готовая функция бегущей строки + - Функции смены яркости и состояния двоеточия автоматически обновляют дисплей + - Функция обновления значения с эффектом вертикальной прокрутки + - Функция обновления значения с эффектом скручивания (лучше один раз увидеть) + + AlexGyver, alex@alexgyver.ru + https://alexgyver.ru/ + MIT License + + Основано на оригинальной библиотеке "Grove_4Digital_Display" от Frankie.Chu + https://github.com/Seeed-Studio/Grove_4Digital_Display + + Версии: + v1.4 - поправлены типы данных и ошибки, добавлена совместимость с ESP +*/ + +#ifndef GyverTM1637_h +#define GyverTM1637_h +#include +#include +class GyverTM1637 { +public: + + GyverTM1637(uint8_t clk, uint8_t dio); // объявление и инициализация + + void display(uint8_t DispData[]); // выводит цифры массивом по ячейкам. От 0 до 9 (byte values[] = {3, 5, 9, 0}; ) + void display(uint8_t BitAddr, uint8_t DispData); // выводит цифру DispData в указанную ячейку дисплея BitAddr + void display(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3); // если лень создавать массив, выводит цифры в ячейки + + void displayByte(uint8_t DispData[]); // выводит байт вида 0xe6 и буквы-константы вида _a , _b .... массивом + void displayByte(uint8_t BitAddr, uint8_t DispData); // выводит байт вида 0xe6 и буквы-константы вида _a , _b .... в ячейку + void displayByte(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3); // если лень создавать массив, выводит байты в ячейки + + void displayClock(uint8_t hrs, uint8_t mins); // выводит часы и минуты + void displayClockScroll(uint8_t hrs, uint8_t mins, int delayms); // выводит часы и минуты с эффектом прокрутки + void displayClockTwist(uint8_t hrs, uint8_t mins, int delayms); // выводит часы и минуты с эффектом скрутки + + void displayInt(int value); // выводит число от -999 до 9999 (да, со знаком минус) + + void runningString(uint8_t DispData[], byte amount, int delayMs); // бегущая строка (array, sizeof(array), задержка в мс) + + void clear(void); // очистить дисплей + + void point(boolean PointFlag); // вкл / выкл точку (POINT_ON / POINT_OFF) + + void brightness(uint8_t bright, uint8_t = 0x40, uint8_t = 0xc0); // яркость 0 - 7 + + void scroll(uint8_t BitAddr, uint8_t DispData, int delayms); // обновить значение прокруткой (адрес, ЦИФРА, задержка в мс) + void scroll(uint8_t DispData[], int delayms); // обновить значение прокруткой (массив ЦИФР, задержка в мс) + void scroll(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3, int delayms); // прокрутка посимвольно + void scrollByte(uint8_t BitAddr, uint8_t DispData, int delayms); // обновить значение прокруткой (адрес, БАЙТ, задержка в мс) + void scrollByte(uint8_t DispData[], int delayms); // обновить значение прокруткой (массив БАЙТ, задержка в мс) + void scrollByte(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3, int delayms); // прокрутка посимвольно + + void twist(uint8_t BitAddr, uint8_t DispData, int delayms); // обновить значение скручиванием (адрес, ЦИФРА, задержка в мс) + void twist(uint8_t DispData[], int delayms); // обновить значение скручиванием (массив ЦИФР, задержка в мс) + void twist(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3, int delayms); // скрутка посимвольно + void twistByte(uint8_t BitAddr, uint8_t DispData, int delayms); // обновить значение скручиванием (адрес, БАЙТ, задержка в мс) + void twistByte(uint8_t DispData[], int delayms); // обновить значение скручиванием (массив БАЙТ, задержка в мс) + void twistByte(uint8_t bit0, uint8_t bit1, uint8_t bit2, uint8_t bit3, int delayms); // скрутка посимвольно + +private: + uint8_t lastData[4]; + void update(); + int writeByte(int8_t wr_data); + void start(void); + void stop(void); + + void sendByte(uint8_t BitAddr, int8_t sendData); + void sendArray(uint8_t sendData[]); + + uint8_t Cmd_SetData; + uint8_t Cmd_SetAddr; + uint8_t Cmd_DispCtrl; + uint8_t PointData; + + uint8_t Clkpin; + uint8_t Datapin; +}; + +void swapBytes(byte* newByte, byte oldByte, byte oldP, byte newP); +uint8_t digToHEX(uint8_t digit); // вернёт код цифры для displayByte + +//************definitions for TM1637********************* +#define ADDR_AUTO 0x40 +#define ADDR_FIXED 0x44 +#define STARTADDR 0xc0 + +/**** definitions for the clock point of the digit tube *******/ +#define POINT_ON 1 +#define POINT_OFF 0 + +/**************definitions for brightness***********************/ +#define BRIGHT_DARKEST 0 +#define BRIGHT_TYPICAL 2 +#define BRIGHTEST 7 + +/************** БУКВЫ И СИМВОЛЫ *****************/ + +#if defined(ESP8266) +#define _A 0x77 +#define _B 0x7f +#define _C 0x39 +#define _D 0x3f +#define _E 0x79 +#define _F 0x71 +#define _G 0x3d +#define _H 0x76 +#define _J 0x1e +#define _L 0x38 +#define _N 0x37 +#define _O 0x3f +#define _P 0x73 +#define _S 0x6d +#define _U 0x3e +#define _Y 0x6e +#define _a 0x5f +#define _b 0x7c +#define _c 0x58 +#define _d 0x5e +#define _e 0x7b +#define _f 0x71 +#define _h 0x74 +#define _i 0x10 +#define _j 0x0e +#define _l 0x06 +#define _n 0x54 +#define _o 0x5c +#define _q 0x67 +#define _r 0x50 +#define _t 0x78 +#define _u 0x1c +#define _y 0x6e +#define _dash 0x40 +#define _under 0x08 +#define _equal 0x48 +#define _empty 0x00 +#define _degree 0x63 + +#define _0 0x3f +#define _1 0x06 +#define _2 0x5b +#define _3 0x4f +#define _4 0x66 +#define _5 0x6d +#define _6 0x7d +#define _7 0x07 +#define _8 0x7f +#define _9 0x6f + +#else + +enum TM1637_letters { + _A = 0x77, + _B = 0x7f, + _C = 0x39, + _D = 0x3f, + _E = 0x79, + _F = 0x71, + _G = 0x3d, + _H = 0x76, + _J = 0x1e, + _L = 0x38, + _N = 0x37, + _O = 0x3f, + _P = 0x73, + _S = 0x6d, + _U = 0x3e, + _Y = 0x6e, + _a = 0x5f, + _b = 0x7c, + _c = 0x58, + _d = 0x5e, + _e = 0x7b, + _f = 0x71, + _h = 0x74, + _i = 0x10, + _j = 0x0e, + _l = 0x06, + _n = 0x54, + _o = 0x5c, + _q = 0x67, + _r = 0x50, + _t = 0x78, + _u = 0x1c, + _y = 0x6e, + _dash = 0x40, + _under = 0x08, + _equal = 0x48, + _empty = 0x00, + _degree = 0x63, + + _0 = 0x3f, + _1 = 0x06, + _2 = 0x5b, + _3 = 0x4f, + _4 = 0x66, + _5 = 0x6d, + _6 = 0x7d, + _7 = 0x07, + _8 = 0x7f, + _9 = 0x6f, +}; +#endif +#endif \ No newline at end of file