This commit is contained in:
TheK4n 2021-08-17 16:17:49 +03:00
parent 0e1f3c1579
commit d62ecbc979
36 changed files with 2693 additions and 1055 deletions

View File

@ -0,0 +1,23 @@
<h1 align="center">PumpController</h1>
<p align="center">
<a href="https://github.com/Pendosv">
<img src="https://img.shields.io/github/followers/Pendosv?label=Follow&style=social">
</a>
<a href="https://github.com/Pendosv/PumpController">
<img src="https://img.shields.io/github/stars/Pendosv/PumpController?style=social">
</a>
</p>
<p align="center">
Contact with me
</p>
<p align="center">
<a href="https://T.me/Pend0s">
<img src="https://raw.githubusercontent.com/Pendosv/Pendosv/master/img/telegram.png" width="40" height="40">
</a>
<a href="mailto:djvlad967891@gmail.com">
<img src="https://raw.githubusercontent.com/Pendosv/Pendosv/master/img/mail.png" width="40" height="40">
</a>
</p>

View File

@ -1,29 +1,46 @@
#define pressure_Aport 0 #define pressure_Aport 0
#define relay_Dport 5 #define relay_Dport 5
#define CLK 2 #define S1 2
#define DT 3 #define S2 3
#define SW 4 #define KEY 4
#define CLK 6
#define DIO 7
#define const_pressure_min 0 #define const_pressure_min 0
#define const_pressure_max 1000 #define const_pressure_max 1000
#include <GyverEncoder.h> #include <EncButton.h>
#include <EEPROM.h> #include <EEPROM.h>
#include <GyverTM1637.h>
int pressure, pressure_min, pressure_max; int pressure, pressure_min, pressure_max;
bool flag; bool flag;
unsigned long last_time1, last_time2;
Encoder enc(CLK, DT, SW);
EncButton<EB_TICK, S1, S2, KEY> enc;
GyverTM1637 disp(CLK, DIO);
int constraint_pressure(int pressure_) {
return constrain(pressure_, 0, 1000);
}
void setup() { void setup() {
Serial.begin(9600); Serial.begin(9600);
enc.setType(TYPE2);
pinMode(relay_Dport, OUTPUT); pinMode(relay_Dport, OUTPUT);
EEPROM.get(0, pressure_min); EEPROM.get(0, pressure_min);
EEPROM.get(2, pressure_max); EEPROM.get(2, pressure_max);
disp.clear();
disp.brightness(7);
disp.clear();
} }
void loop() { void loop() {
@ -32,20 +49,24 @@ void loop() {
enc.tick(); enc.tick();
if (enc.isTurn()) { if (enc.isTurn()) {
if (enc.isRight()) { 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()) { 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()) { 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()) { 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(0, pressure_min);
EEPROM.put(2, pressure_max); EEPROM.put(2, pressure_max);
} }
@ -53,6 +74,12 @@ void loop() {
pressure = analogRead(pressure_Aport); pressure = analogRead(pressure_Aport);
if (millis() - last_time1 > 850) {
last_time1 = millis();
disp.displayInt(pressure);
}
if (pressure < pressure_min) { if (pressure < pressure_min) {
if (!flag) { if (!flag) {
digitalWrite(relay_Dport, HIGH); digitalWrite(relay_Dport, HIGH);

View File

@ -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)
<a id="install"></a>
## Установка
- Библиотеку можно найти по названию **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)
<a id="init"></a>
## Инициализация
```cpp
EncButton<MODE, A, B, KEY> enc; // энкодер с кнопкой и пинами A, B, KEY
EncButton<MODE, A, B> enc; // просто энкодер с пинами A, B
EncButton<MODE, KEY> btn; // просто кнопка с пином KEY
// MODE: EB_TICK или EB_CALLBACK - режим работы ручной или с обработчиками
// по умолчанию пины настроены в INPUT_PULLUP
// Если используется внешняя подтяжка - лучше перевести в INPUT
EncButton<EB_TICK, 2, 3, 4> enc(INPUT);
// Виртуальный режим:
EncButton<MODE, VIRT_BTN> enc; // виртуальная кнопка
EncButton<MODE, VIRT_ENCBTN> enc; // виртуальный энк с кнопкой
EncButton<MODE, VIRT_ENC> enc; // виртуальный энк
// в tick нужно передавать виртуальное значение, см. ниже!
```
<a id="usage"></a>
## Использование
```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 // таймаут накликивания, мс
```
<a id="example"></a>
## Пример
Остальные примеры смотри в **examples**!
```cpp
// Пример с прямой работой библиотеки
#include <EncButton.h>
EncButton<EB_TICK, 2, 3, 4> enc; // энкодер с кнопкой <A, B, KEY>
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");
}
```
<a id="versions"></a>
## Версии
- 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 - индивидуальная настройка таймаута удержания кнопки (была общая на всех)
<a id="feedback"></a>
## Баги и обратная связь
При нахождении багов создавайте **Issue**, а лучше сразу пишите на почту [alex@alexgyver.ru](mailto:alex@alexgyver.ru)
Библиотека открыта для доработки и ваших **Pull Request**'ов!

View File

@ -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.h>
EncButton<EB_CALLBACK, 2, 3, 4> enc; // энкодер с кнопкой <A, B, KEY>
//EncButton<EB_CALLBACK, 2, 3> enc; // просто энкодер <A, B>
//EncButton<EB_CALLBACK, 4> enc; // просто кнопка <KEY>
// по умолчанию пины настроены в INPUT_PULLUP
// Если используется внешняя подтяжка - лучше перевести в INPUT
//EncButton<EB_CALLBACK, 2, 3, 4> 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(); // обработка всё равно здесь
}

View File

@ -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.h>
EncButton<EB_TICK, 2, 3, 4> enc; // энкодер с кнопкой <A, B, KEY>
//EncButton<EB_TICK, 2, 3> enc; // просто энкодер <A, B>
//EncButton<EB_TICK, 4> enc; // просто кнопка <KEY>
// по умолчанию пины настроены в INPUT_PULLUP
// Если используется внешняя подтяжка - лучше перевести в INPUT
//EncButton<EB_TICK, 2, 3, 4> 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);
}

View File

@ -1,12 +1,12 @@
// пример с прерываниями pinChangeInterrupt (прерывания на любом пине) // пример с прерываниями pinChangeInterrupt (прерывания на любом пине)
// только для ATmega328 (UNO, Nano, Pro Mini) // только для ATmega328 (UNO, Nano, Pro Mini)
#define SW 0 #define CLK 4
#define DT 2 #define DT 5
#define CLK 3 #define SW 6
#include "GyverEncoder.h" #include <EncButton.h>
Encoder enc1(CLK, DT, SW); EncButton<EB_TICK, CLK, DT, SW> enc;
void setup() { void setup() {
Serial.begin(9600); Serial.begin(9600);
@ -17,13 +17,23 @@ void setup() {
} }
void loop() { void loop() {
enc1.tick(); // оставляем тут для работы "временных" функций и антидребезга // оставляем тут для работы "временных" функций и антидребезга
enc.tick();
if (enc1.isRight()) Serial.println("Right"); // если был поворот
if (enc1.isLeft()) Serial.println("Left");
if (enc1.isRightH()) Serial.println("Right holded"); // если было удержание + поворот if (enc.isTurn()) { // любой поворот
if (enc1.isLeftH()) Serial.println("Left holded"); 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) // функция для настройки PCINT для ATmega328 (UNO, Nano, Pro Mini)
@ -46,16 +56,15 @@ uint8_t attachPCINT(uint8_t pin) {
} }
// Векторы PCINT, нужно кинуть сюда тики // Векторы PCINT, нужно кинуть сюда тики
// не обязательно в каждый вектор, достаточно в тот, который задействован
// пины 0-7: PCINT2 // пины 0-7: PCINT2
// пины 8-13: PCINT0 // пины 8-13: PCINT0
// пины A0-A5: PCINT1 // пины A0-A5: PCINT1
ISR(PCINT0_vect) { ISR(PCINT0_vect) {
//enc1.tick();
} }
ISR(PCINT1_vect) { ISR(PCINT1_vect) {
//enc1.tick();
} }
ISR(PCINT2_vect) { ISR(PCINT2_vect) {
enc1.tick(); enc.tick();
} }

View File

@ -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.h>
EncButton<EB_TICK, 2, 3, 4> enc; // энкодер с кнопкой <A, B, KEY>
//EncButton<EB_TICK, 2, 3> enc; // просто энкодер <A, B>
//EncButton<EB_TICK, 4> enc; // просто кнопка <KEY>
// по умолчанию пины настроены в INPUT_PULLUP
// Если используется внешняя подтяжка - лучше перевести в INPUT
//EncButton<EB_TICK, 2, 3, 4> 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");
}

View File

@ -0,0 +1,35 @@
#include <EncButton.h>
EncButton<EB_TICK, VIRT_BTN> enc; // виртуальная кнопка
//EncButton<EB_TICK, VIRT_ENCBTN> enc; // виртуальный энк с кнопкой
//EncButton<EB_TICK, VIRT_ENC> 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");
}

View File

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

View File

@ -0,0 +1,9 @@
name=EncButton
version=1.8
author=AlexGyver <alex@alexgyver.ru>
maintainer=AlexGyver <alex@alexgyver.ru>
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=*

View File

@ -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 <Arduino.h>
#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

View File

@ -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
}

View File

@ -0,0 +1,13 @@
// Быстрый IO для AVR (для остальных будет digitalxxxxx)
// v1.0
#ifndef _FastIO_v2_h
#define _FastIO_v2_h
#include <Arduino.h>
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

View File

@ -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)
<a id="install"></a>
## Установка
- Библиотеку можно найти по названию **GyverEncoder** и установить через менеджер библиотек в:
- Arduino IDE
- Arduino IDE v2
- PlatformIO
- [Скачать библиотеку](https://github.com/GyverLibs/GyverEncoder/archive/refs/heads/main.zip) .zip архивом для ручной установки:
- Распаковать и положить в *C:\Program Files (x86)\Arduino\libraries* (Windows x64)
- Распаковать и положить в *C:\Program Files\Arduino\libraries* (Windows x32)
- Распаковать и положить в *Документы/Arduino/libraries/*
- (Arduino IDE) автоматическая установка из .zip: *Скетч/Подключить библиотеку/Добавить .ZIP библиотеку…* и указать скачанный архив
- Читай более подробную инструкцию по установке библиотек [здесь](https://alexgyver.ru/arduino-first/#%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA)
<a id="init"></a>
## Инициализация
```cpp
Encoder enc; // не привязан к пину
Encoder enc(пин CLK, пин DT); // энкодер без кнопки (ускоренный опрос)
Encoder enc(пин CLK, пин DT, пин SW); // энкодер с кнопкой
Encoder enc(пин CLK, пин DT, пин SW, тип); // энкодер с кнопкой и указанием типа
Encoder enc(пин CLK, пин DT, ENC_NO_BUTTON, тип); // энкодер без кнопкой и с указанием типа
```
<a id="usage"></a>
## Использование
```cpp
void tick(); // опрос энкодера, нужно вызывать постоянно или в прерывании
void setType(boolean type); // TYPE1 / TYPE2 - тип энкодера TYPE1 одношаговый, TYPE2 двухшаговый. Если ваш энкодер работает странно, смените тип
void setTickMode(boolean tickMode); // MANUAL / AUTO - ручной или автоматический опрос энкодера функцией tick(). (по умолчанию ручной)
void setDirection(boolean direction); // NORM / REVERSE - направление вращения энкодера
void setFastTimeout(int timeout); // установка таймаута быстрого поворота
void setPinMode(bool mode); // тип подключения энкодера, подтяжка HIGH_PULL (внутренняя) или LOW_PULL (внешняя на GND)
void setBtnPinMode(bool mode); // тип подключения кнопки, подтяжка HIGH_PULL (внутренняя) или LOW_PULL (внешняя на GND)
boolean isTurn(); // возвращает true при любом повороте, сама сбрасывается в false
boolean isRight(); // возвращает true при повороте направо, сама сбрасывается в false
boolean isLeft(); // возвращает true при повороте налево, сама сбрасывается в false
boolean isRightH(); // возвращает true при удержании кнопки и повороте направо, сама сбрасывается в false
boolean isLeftH(); // возвращает true при удержании кнопки и повороте налево, сама сбрасывается в false
boolean isFastR(); // возвращает true при быстром повороте
boolean isFastL(); // возвращает true при быстром повороте
boolean isPress(); // возвращает true при нажатии кнопки, сама сбрасывается в false
boolean isRelease(); // возвращает true при отпускании кнопки, сама сбрасывается в false
boolean isClick(); // возвращает true при нажатии и отпускании кнопки, сама сбрасывается в false
boolean isHolded(); // возвращает true при удержании кнопки, сама сбрасывается в false
boolean isHold(); // возвращает true при удержании кнопки, НЕ СБРАСЫВАЕТСЯ
boolean isSingle(); // возвращает true при одиночном клике (после таймаута), сама сбрасывается в false
boolean isDouble(); // возвращает true при двойном клике, сама сбрасывается в false
void resetStates(); // сбрасывает все is-флаги
```
<a id="example"></a>
## Пример
Остальные примеры смотри в **examples**!
```cpp
#define CLK 2
#define DT 3
#define SW 4
#include "GyverEncoder.h"
Encoder enc1(CLK, DT, SW); // для работы c кнопкой
void setup() {
Serial.begin(9600);
enc1.setType(TYPE2);
}
void loop() {
// обязательная функция отработки. Должна постоянно опрашиваться
enc1.tick();
if (enc1.isTurn()) { // если был совершён поворот (индикатор поворота в любую сторону)
// ваш код
}
if (enc1.isRight()) Serial.println("Right"); // если был поворот
if (enc1.isLeft()) Serial.println("Left");
if (enc1.isRightH()) Serial.println("Right holded"); // если было удержание + поворот
if (enc1.isLeftH()) Serial.println("Left holded");
//if (enc1.isPress()) Serial.println("Press"); // нажатие на кнопку (+ дебаунс)
//if (enc1.isRelease()) Serial.println("Release"); // то же самое, что isClick
if (enc1.isClick()) Serial.println("Click"); // одиночный клик
if (enc1.isSingle()) Serial.println("Single"); // одиночный клик (с таймаутом для двойного)
if (enc1.isDouble()) Serial.println("Double"); // двойной клик
if (enc1.isHolded()) Serial.println("Holded"); // если была удержана и энк не поворачивался
//if (enc1.isHold()) Serial.println("Hold"); // возвращает состояние кнопки
}
```
<a id="versions"></a>
## Версии
- v3.6 от 16.09.2019 - Возвращены дефайны настроек
- v4.0 от 13.11.2019
- Оптимизирован код
- Исправлены баги
- Добавлены другие алгоритмы опроса
- Добавлена возможность полностью убрать кнопку (экономия памяти)
- Добавлена возможность подключения внешнего энкодера
- Добавлена настройка подтяжки пинов
- v4.1: Исправлено изменение подтяжек
- v4.2
- Добавлена поддержка TYPE1 для алгоритма PRECISE_ALGORITHM
- Добавлена отработка двойного клика: isSingle / isDouble
- v4.3: Исправлено ложное isSingle
- v4.4: Добавлен метод resetStates, сбрасывает все is-флаги и счётчики
- v4.5: Улучшен алгоритм BINARY_ALGORITHM (спасибо Ярославу Курусу)
- v4.6: BINARY_ALGORITHM пофикшен для TYPE1, добавлена isReleaseHold
- v4.7: Исправлен случайный нажатый поворот в BINARY_ALGORITHM
- v4.8: увеличена производительность для AVR Arduino
- v4.9: быстрый поворот отключен если кнопка удерживается
<a id="feedback"></a>
## Баги и обратная связь
При нахождении багов создавайте **Issue**, а лучше сразу пишите на почту [alex@alexgyver.ru](mailto:alex@alexgyver.ru)
Библиотека открыта для доработки и ваших **Pull Request**'ов!

View File

@ -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");
}

View File

@ -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"); // возвращает состояние кнопки
}

View File

@ -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"); // возвращает состояние кнопки
}

View File

@ -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); // выводим значение при повороте
}
}

View File

@ -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");
}

View File

@ -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"); // возвращает состояние кнопки
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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); // выводим значение при повороте
}
}

View File

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

View File

@ -1,9 +0,0 @@
name=GyverEncoder
version=4.9
author=AlexGyver <alex@alexgyver.ru>
maintainer=AlexGyver <alex@alexgyver.ru>
sentence=Advanced encoder control library
paragraph=Advanced encoder control library
category=Sensors
url=https://github.com/GyverLibs/GyverEncoder
architectures=*

View File

@ -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;
}
}

View File

@ -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 <Arduino.h>
// ========= КОНСТАНТЫ ==========
#define ENC_NO_BUTTON -1 // константа для работы без пина
#define TYPE1 0 // полушаговый энкодер
#define TYPE2 1 // полношаговый
#define NORM 0 // направление вращения обычное
#define REVERSE 1 // обратное
#define MANUAL 0 // нужно вызывать функцию tick() вручную
#define AUTO 1 // tick() входит во все остальные функции и опрашивается сама!
#define HIGH_PULL 0 // внутренняя подтяжка к питанию (pinMode INPUT_PULLUP)
#define LOW_PULL 1 // внешняя подтяжка к GND (pinMode INPUT)
// =========== НАСТРОЙКИ ===========
// закомментируй строку, чтобы полностью убрать отработку кнопки из кода
#define ENC_WITH_BUTTON
// тип подключения энкодера по умолчанию (LOW_PULL или HIGH_PULL)
//#define DEFAULT_ENC_PULL LOW_PULL
#define DEFAULT_ENC_PULL HIGH_PULL
// тип подключения кнопки энкодера по умолчанию (LOW_PULL или HIGH_PULL)
//#define DEFAULT_BTN_PULL LOW_PULL
#define DEFAULT_BTN_PULL HIGH_PULL
// алгоритмы опроса энкодера (раскомментировать нужный)
//#define FAST_ALGORITHM // тик 10 мкс, быстрый, не справляется с люфтами
#define BINARY_ALGORITHM // тик 14 мкс, лучше справляется с люфтами
//#define PRECISE_ALGORITHM // тик 16 мкс, работает даже с убитым энкодером (по мотивам https://github.com/mathertel/RotaryEncoder)
// настройка антидребезга энкодера, кнопки, таймаута удержания и таймаута двойного клика
#define ENC_DEBOUNCE_TURN 0
#define ENC_DEBOUNCE_BUTTON 80
#define ENC_HOLD_TIMEOUT 700
#define ENC_DOUBLE_TIMEOUT 300
#if defined(__AVR__)
#define _readCLK() bool(*_pin_reg_CLK & _bit_mask_CLK)
#define _readDT() bool(*_pin_reg_DT & _bit_mask_DT)
#define _readSW() bool(*_pin_reg_SW & _bit_mask_SW)
#else
#define _readCLK() digitalRead(_CLK)
#define _readDT() digitalRead(_DT)
#define _readSW() digitalRead(_SW)
#endif
#pragma pack(push,1)
typedef struct
{
bool hold_flag: 1;
bool butt_flag: 1;
bool turn_flag: 1;
bool isTurn_f: 1;
bool isPress_f: 1;
bool isRelease_f: 1;
bool isReleaseHold_f: 1;
bool isHolded_f: 1;
bool isFastR_f: 1;
bool isFastL_f: 1;
bool enc_tick_mode: 1;
bool enc_type: 1;
bool use_button : 1;
bool extCLK : 1;
bool extDT : 1;
bool extSW : 1;
bool invBtn : 1;
bool isSingle_f : 1;
bool isDouble_f : 1;
bool countFlag : 1;
bool doubleFlag : 1;
bool doubleAllow : 1;
bool rst_flag : 1;
} GyverEncoderFlags;
#pragma pack(pop)
// Варианты инициализации:
// Encoder enc; // не привязан к пину
// Encoder enc(пин CLK, пин DT); // энкодер без кнопки (ускоренный опрос)
// Encoder enc(пин CLK, пин DT, пин SW); // энкодер с кнопкой
// Encoder enc(пин CLK, пин DT, пин SW, тип); // энкодер с кнопкой и указанием типа
// Encoder enc(пин CLK, пин DT, ENC_NO_BUTTON, тип); // энкодер без кнопкой и с указанием типа
class Encoder {
public:
Encoder(); // для непривязанного к пинам энкодера
Encoder(uint8_t clk, uint8_t dt, int8_t sw = -1, bool type = false); // CLK, DT, SW, тип (TYPE1 / TYPE2) TYPE1 одношаговый, TYPE2 двухшаговый. Если ваш энкодер работает странно, смените тип
void tick(); // опрос энкодера, нужно вызывать постоянно или в прерывании
void tick(bool clk, bool dt, bool sw = 0); // опрос "внешнего" энкодера
void setType(bool type); // TYPE1 / TYPE2 - тип энкодера TYPE1 одношаговый, TYPE2 двухшаговый. Если ваш энкодер работает странно, смените тип
void setPinMode(bool mode); // тип подключения энкодера, подтяжка HIGH_PULL (внутренняя) или LOW_PULL (внешняя на GND)
void setBtnPinMode(bool mode); // тип подключения кнопки, подтяжка HIGH_PULL (внутренняя) или LOW_PULL (внешняя на GND)
void setTickMode(bool tickMode); // MANUAL / AUTO - ручной или автоматический опрос энкодера функцией tick(). (по умолчанию ручной)
void setDirection(bool direction); // NORM / REVERSE - направление вращения энкодера
void setFastTimeout(uint16_t timeout); // установка таймаута быстрого поворота
boolean isTurn(); // возвращает true при любом повороте, сама сбрасывается в false
boolean isRight(); // возвращает true при повороте направо, сама сбрасывается в false
boolean isLeft(); // возвращает true при повороте налево, сама сбрасывается в false
boolean isRightH(); // возвращает true при удержании кнопки и повороте направо, сама сбрасывается в false
boolean isLeftH(); // возвращает true при удержании кнопки и повороте налево, сама сбрасывается в false
boolean isFastR(); // возвращает true при быстром повороте
boolean isFastL(); // возвращает true при быстром повороте
boolean isPress(); // возвращает true при нажатии кнопки, сама сбрасывается в false
boolean isRelease(); // возвращает true при отпускании кнопки, сама сбрасывается в false
boolean isReleaseHold(); // возвращает true при отпускании кнопки после удержания, сама сбрасывается в false
boolean isClick(); // возвращает true при нажатии и отпускании кнопки, сама сбрасывается в false
boolean isHolded(); // возвращает true при удержании кнопки, сама сбрасывается в false
boolean isHold(); // возвращает true при удержании кнопки, НЕ СБРАСЫВАЕТСЯ
boolean isSingle(); // возвращает true при одиночном клике (после таймаута), сама сбрасывается в false
boolean isDouble(); // возвращает true при двойном клике, сама сбрасывается в false
void resetStates(); // сбрасывает все is-флаги и счётчики
private:
GyverEncoderFlags flags;
uint8_t _fast_timeout = 50; // таймаут быстрого поворота
uint8_t prevState = 0;
uint8_t encState = 0; // 0 не крутился, 1 лево, 2 право, 3 лево нажат, 4 право нажат
uint32_t debounce_timer = 0, fast_timer = 0;
uint8_t _CLK = 0, _DT = 0, _SW = 0;
bool turnFlag = false, extTick = false, SW_state = false;
#if defined(__AVR__)
volatile uint8_t *_pin_reg_CLK;
volatile uint8_t _bit_mask_CLK;
volatile uint8_t *_pin_reg_DT;
volatile uint8_t _bit_mask_DT;
volatile uint8_t *_pin_reg_SW;
volatile uint8_t _bit_mask_SW;
#endif
};
#endif

View File

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

View File

@ -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)
<a id="install"></a>
## Установка
- Библиотеку можно найти по названию **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)
<a id="init"></a>
## Инициализация
```cpp
GyverTM1637 disp(CLK, DIO);
```
<a id="usage"></a>
## Использование
```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); // скрутка посимвольно
```
<a id="example"></a>
## Пример
Остальные примеры смотри в **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 это время в миллисекундах!
}
```
<a id="versions"></a>
## Версии
- v1.4 - поправлены типы данных и ошибки, добавлена совместимость с ESP
<a id="feedback"></a>
## Баги и обратная связь
При нахождении багов создавайте **Issue**, а лучше сразу пишите на почту [alex@alexgyver.ru](mailto:alex@alexgyver.ru)
Библиотека открыта для доработки и ваших **Pull Request**'ов!

View File

@ -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 это время в миллисекундах!
}

View File

@ -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 это время в миллисекундах!
}

View File

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

View File

@ -0,0 +1,9 @@
name=GyverTM1637
version=1.4
author=AlexGyver <alex@alexgyver.ru>
maintainer=AlexGyver <alex@alexgyver.ru>
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=*

View File

@ -0,0 +1,535 @@
#include "GyverTM1637.h"
#include <Arduino.h>
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);
}

View File

@ -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 <inttypes.h>
#include <Arduino.h>
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