первый
This commit is contained in:
475
GyverEncoder.cpp
Normal file
475
GyverEncoder.cpp
Normal file
@@ -0,0 +1,475 @@
|
|||||||
|
#include "GyverEncoder.h"
|
||||||
|
#include "mbed.h"
|
||||||
|
#include <debounce.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(PinName clk, PinName dt, PinName sw, bool type)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (sw != NC) {
|
||||||
|
_SW = new DigitalIn(sw, PullUp);
|
||||||
|
flags.use_button = true;
|
||||||
|
} else {
|
||||||
|
flags.use_button = false;
|
||||||
|
}
|
||||||
|
flags.enc_type = type;
|
||||||
|
|
||||||
|
_CLK = new DigitalIn(clk, PullUp);
|
||||||
|
_DT = new DigitalIn(dt, PullUp);
|
||||||
|
|
||||||
|
flags.invBtn = (DEFAULT_BTN_PULL == HIGH_PULL) ? true : false;
|
||||||
|
|
||||||
|
#if defined(FAST_ALGORITHM)
|
||||||
|
prevState = digitalRead(_CLK);
|
||||||
|
#else
|
||||||
|
prevState = (_CLK->read() | _DT->read() << 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
flags.isDouble_f = false;
|
||||||
|
flags.isPress_f = false;
|
||||||
|
flags.isRelease_f = false;
|
||||||
|
flags.isHolded_f = false;
|
||||||
|
flags.butt_flag = false;
|
||||||
|
flags.hold_flag = false;
|
||||||
|
|
||||||
|
debounce_timer = Kernel::Clock::now();
|
||||||
|
|
||||||
|
encThread = new Thread(osPriorityNormal, 1024, NULL, "Encoder Thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Encoder::start()
|
||||||
|
{
|
||||||
|
// _CLK->fall(callback(this, &Encoder::encoderThread));
|
||||||
|
// _CLK->rise(callback(this, &Encoder::encoderThread));
|
||||||
|
|
||||||
|
// _SW->fall(callback(this, &Encoder::encoderThread));
|
||||||
|
// _SW->rise(callback(this, &Encoder::encoderThread));
|
||||||
|
encThread->start(callback(this, &Encoder::encoderThread));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Encoder::stop()
|
||||||
|
{
|
||||||
|
encThread->terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Encoder::encoderThread()
|
||||||
|
{
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
this->tick();
|
||||||
|
|
||||||
|
ThisThread::sleep_for(1ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================= SET =================
|
||||||
|
void Encoder::setDirection(bool direction)
|
||||||
|
{
|
||||||
|
if (direction) {
|
||||||
|
// uint8_t buf = _CLK;
|
||||||
|
// _CLK = _DT;
|
||||||
|
// _DT = buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void Encoder::setPinMode(bool mode)
|
||||||
|
{
|
||||||
|
_CLK->mode((mode) ? PullDown : PullUp);
|
||||||
|
_DT->mode((mode) ? PullDown : PullUp);
|
||||||
|
}
|
||||||
|
void Encoder::setBtnPinMode(bool mode)
|
||||||
|
{
|
||||||
|
|
||||||
|
_SW->mode((mode) ? PullDown : 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(Kernel::Clock::duration timeout)
|
||||||
|
{
|
||||||
|
_fast_timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================= IS =================
|
||||||
|
// повороты
|
||||||
|
bool Encoder::isTurn()
|
||||||
|
{
|
||||||
|
if (flags.enc_tick_mode)
|
||||||
|
Encoder::tick();
|
||||||
|
if (flags.isTurn_f) {
|
||||||
|
flags.isTurn_f = false;
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool Encoder::isRight()
|
||||||
|
{
|
||||||
|
if (flags.enc_tick_mode)
|
||||||
|
Encoder::tick();
|
||||||
|
if (encState == 2) {
|
||||||
|
encState = 0;
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool Encoder::isLeft()
|
||||||
|
{
|
||||||
|
if (flags.enc_tick_mode)
|
||||||
|
Encoder::tick();
|
||||||
|
if (encState == 1) {
|
||||||
|
encState = 0;
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool Encoder::isRightH()
|
||||||
|
{
|
||||||
|
if (flags.enc_tick_mode)
|
||||||
|
Encoder::tick();
|
||||||
|
if (encState == 4) {
|
||||||
|
encState = 0;
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool Encoder::isLeftH()
|
||||||
|
{
|
||||||
|
if (flags.enc_tick_mode)
|
||||||
|
Encoder::tick();
|
||||||
|
if (encState == 3) {
|
||||||
|
encState = 0;
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool Encoder::isFastR()
|
||||||
|
{
|
||||||
|
if (flags.enc_tick_mode)
|
||||||
|
Encoder::tick();
|
||||||
|
if (flags.isFastR_f) {
|
||||||
|
flags.isFastR_f = false;
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool Encoder::isFastL()
|
||||||
|
{
|
||||||
|
if (flags.enc_tick_mode)
|
||||||
|
Encoder::tick();
|
||||||
|
if (flags.isFastL_f) {
|
||||||
|
flags.isFastL_f = false;
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// кнопка
|
||||||
|
bool Encoder::isPress()
|
||||||
|
{
|
||||||
|
if (flags.enc_tick_mode)
|
||||||
|
Encoder::tick();
|
||||||
|
if (flags.isPress_f) {
|
||||||
|
flags.isPress_f = false;
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool Encoder::isRelease()
|
||||||
|
{
|
||||||
|
if (flags.enc_tick_mode)
|
||||||
|
Encoder::tick();
|
||||||
|
if (flags.isRelease_f) {
|
||||||
|
flags.isRelease_f = false;
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool Encoder::isClick()
|
||||||
|
{
|
||||||
|
if (flags.enc_tick_mode)
|
||||||
|
Encoder::tick();
|
||||||
|
if (flags.isRelease_f) {
|
||||||
|
flags.isRelease_f = false;
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool 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;
|
||||||
|
}
|
||||||
|
bool 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;
|
||||||
|
}
|
||||||
|
bool 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;
|
||||||
|
if (flags.isDouble_f) {
|
||||||
|
flags.isDouble_f = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool Encoder::isHold()
|
||||||
|
{
|
||||||
|
if (flags.enc_tick_mode)
|
||||||
|
Encoder::tick();
|
||||||
|
return (SW_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================= 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()
|
||||||
|
{
|
||||||
|
thisMls = Kernel::Clock::now();
|
||||||
|
debounceDelta = thisMls - debounce_timer;
|
||||||
|
// int butstate;
|
||||||
|
|
||||||
|
#ifdef ENC_WITH_BUTTON
|
||||||
|
// static etl::debounce<DEBOUNCE_COUNT, HOLD_COUNT, REPEAT_COUNT> key_state;
|
||||||
|
// static etl::debounce<DEBOUNCE_COUNT, HOLD_COUNT, REPEAT_COUNT> key_invState;
|
||||||
|
// // butstate = *_SW;
|
||||||
|
|
||||||
|
// if (key_state.add(*_SW == 0)) {
|
||||||
|
// if (key_state.is_set() && flags.isRelease_f == true) {
|
||||||
|
// flags.isPress_f = true;
|
||||||
|
// flags.butt_flag = true;
|
||||||
|
// flags.isRelease_f = false;
|
||||||
|
// }
|
||||||
|
// if (key_state.is_held()) {
|
||||||
|
// flags.isHolded_f = true;
|
||||||
|
// flags.butt_flag = false;
|
||||||
|
|
||||||
|
// } else
|
||||||
|
|
||||||
|
// if (key_state.is_repeating()) {
|
||||||
|
// flags.isDouble_f = true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // if (flags.butt_flag) {
|
||||||
|
// if (key_invState.add(*_SW == 1)) {
|
||||||
|
// if (key_invState.is_set()) {
|
||||||
|
// flags.isPress_f = false;
|
||||||
|
// flags.isRelease_f = true;
|
||||||
|
// flags.butt_flag = false;
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (flags.use_button) {
|
||||||
|
if (!extTick)
|
||||||
|
// if (_SW->read() == 0) { // читаем состояние кнопки SW
|
||||||
|
SW_state = _SW->read() ^ flags.invBtn;
|
||||||
|
// } else {
|
||||||
|
// SW_state = false ^ flags.invBtn;
|
||||||
|
// }
|
||||||
|
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 = 0ms;
|
||||||
|
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 = 0ms;
|
||||||
|
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 = 0ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(FAST_ALGORITHM)
|
||||||
|
uint8_t curState = (extTick) ? (flags.extCLK) : (digitalRead(_CLK));
|
||||||
|
|
||||||
|
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) : digitalRead(_DT)) != prevState) {
|
||||||
|
encState = 1;
|
||||||
|
} else {
|
||||||
|
encState = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(BINARY_ALGORITHM)
|
||||||
|
uint8_t curState = (extTick) ? (flags.extCLK | (flags.extDT << 1)) : (_CLK->read() | (_DT->read() << 1));
|
||||||
|
|
||||||
|
if (curState != prevState
|
||||||
|
//#if (ENC_DEBOUNCE_TURN > 0)
|
||||||
|
&& (debounceDelta > ENC_DEBOUNCE_TURN)
|
||||||
|
//#endif
|
||||||
|
) {
|
||||||
|
encState = 0;
|
||||||
|
if (curState == 0b11) {
|
||||||
|
switch (prevState) {
|
||||||
|
case 0b10:
|
||||||
|
encState = 1;
|
||||||
|
break;
|
||||||
|
case 0b01:
|
||||||
|
encState = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (curState == 0b00 && !flags.enc_type) {
|
||||||
|
switch (prevState) {
|
||||||
|
case 0b01:
|
||||||
|
encState = 1;
|
||||||
|
break;
|
||||||
|
case 0b10:
|
||||||
|
encState = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(PRECISE_ALGORITHM)
|
||||||
|
uint8_t curState = (extTick) ? (flags.extCLK | (flags.extDT << 1)) : (_CLK->read() | (_DT->read() << 1));
|
||||||
|
|
||||||
|
if (prevState != curState
|
||||||
|
//#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 (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)
|
||||||
|
// if (SW_state)
|
||||||
|
// encState += 2;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
prevState = curState;
|
||||||
|
flags.turn_flag = true;
|
||||||
|
debounce_timer = thisMls;
|
||||||
|
debounceDelta = 0ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
167
GyverEncoder.h
Normal file
167
GyverEncoder.h
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "mbed.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
GyverEncoder - библиотека для отработки энкодера. Возможности:
|
||||||
|
- Отработка поворота энкодера
|
||||||
|
- Отработка "нажатого поворота"
|
||||||
|
- Отработка "быстрого поворота"
|
||||||
|
- Несколько алгоритмов опроса энкодера
|
||||||
|
- Выбор подтяжки подключения энкодера
|
||||||
|
- Работа с двумя типами экнодеров
|
||||||
|
- Работа с внешним энкодером (через расширитель пинов и т.п.)
|
||||||
|
- Отработка нажатия/удержания кнопки с антидребезгом
|
||||||
|
|
||||||
|
Документацию читай здесь: https://alexgyver.ru/encoder/
|
||||||
|
Для максимально быстрого (в 2 раза быстрее) опроса энкодера рекомендуется использовать ядро GyverCore https://alexgyver.ru/gyvercore/
|
||||||
|
|
||||||
|
Версии:
|
||||||
|
- 3.6 от 16.09.2019 - Возвращены дефайны настроек
|
||||||
|
- 4.0 от 13.11.2019
|
||||||
|
- Оптимизирован код
|
||||||
|
- Исправлены баги
|
||||||
|
- Добавлены другие алгоритмы опроса
|
||||||
|
- Добавлена возможность полностью убрать кнопку (экономия памяти)
|
||||||
|
- Добавлена возможность подключения внешнего энкодера
|
||||||
|
- Добавлена настройка подтяжки пинов
|
||||||
|
- 4.1
|
||||||
|
- Исправлено изменение подтяжек
|
||||||
|
|
||||||
|
- 4.2
|
||||||
|
- Добавлена поддержка TYPE1 для алгоритма PRECISE_ALGORITHM
|
||||||
|
- Добавлена отработка двойного клика: isSingle / isDouble
|
||||||
|
*/
|
||||||
|
// ========= КОНСТАНТЫ ==========
|
||||||
|
#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 1ms
|
||||||
|
#define ENC_DEBOUNCE_BUTTON 10ms
|
||||||
|
#define ENC_HOLD_TIMEOUT 700ms
|
||||||
|
#define ENC_DOUBLE_TIMEOUT 300ms
|
||||||
|
|
||||||
|
// The sample time in ms.
|
||||||
|
const int SAMPLE_TIME = 1;
|
||||||
|
|
||||||
|
// The number of samples that must agree before a key state change is recognised.
|
||||||
|
// 50 = 50ms for 1ms sample time.
|
||||||
|
const int DEBOUNCE_COUNT = 50;
|
||||||
|
|
||||||
|
// The number of samples that must agree before a key held state is recognised.
|
||||||
|
// 1000 = 1s for 1ms sample time.
|
||||||
|
const int HOLD_COUNT = 1000;
|
||||||
|
|
||||||
|
// The number of samples that must agree before a key repeat state is recognised.
|
||||||
|
// 200 = 200ms for 1ms sample time.
|
||||||
|
const int REPEAT_COUNT = 200;
|
||||||
|
|
||||||
|
#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 isHolded_f : 1;
|
||||||
|
bool isFastR_f : 1;
|
||||||
|
bool isFastL_f : 1;
|
||||||
|
bool isReleaseHold_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;
|
||||||
|
} 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(PinName clk, PinName dt, PinName sw, 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(Kernel::Clock::duration timeout); // установка таймаута быстрого поворота
|
||||||
|
|
||||||
|
bool isTurn(); // возвращает true при любом повороте, сама сбрасывается в false
|
||||||
|
bool isRight(); // возвращает true при повороте направо, сама сбрасывается в false
|
||||||
|
bool isLeft(); // возвращает true при повороте налево, сама сбрасывается в false
|
||||||
|
bool isRightH(); // возвращает true при удержании кнопки и повороте направо, сама сбрасывается в false
|
||||||
|
bool isLeftH(); // возвращает true при удержании кнопки и повороте налево, сама сбрасывается в false
|
||||||
|
bool isFastR(); // возвращает true при быстром повороте
|
||||||
|
bool isFastL(); // возвращает true при быстром повороте
|
||||||
|
|
||||||
|
bool isPress(); // возвращает true при нажатии кнопки, сама сбрасывается в false
|
||||||
|
bool isRelease(); // возвращает true при отпускании кнопки, сама сбрасывается в false
|
||||||
|
bool isClick(); // возвращает true при нажатии и отпускании кнопки, сама сбрасывается в false
|
||||||
|
bool isHolded(); // возвращает true при удержании кнопки, сама сбрасывается в false
|
||||||
|
bool isHold(); // возвращает true при удержании кнопки, НЕ СБРАСЫВАЕТСЯ
|
||||||
|
bool isSingle(); // возвращает true при одиночном клике (после таймаута), сама сбрасывается в false
|
||||||
|
bool isDouble(); // возвращает true при двойном клике, сама сбрасывается в false
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Thread* encThread;
|
||||||
|
GyverEncoderFlags flags;
|
||||||
|
Kernel::Clock::duration _fast_timeout = 50ms; // таймаут быстрого поворота
|
||||||
|
uint8_t prevState = 0;
|
||||||
|
uint8_t encState = 0; // 0 не крутился, 1 лево, 2 право, 3 лево нажат, 4 право нажат
|
||||||
|
Kernel::Clock::time_point debounce_timer;
|
||||||
|
Kernel::Clock::time_point fast_timer;
|
||||||
|
DigitalIn* _CLK;
|
||||||
|
DigitalIn* _DT;
|
||||||
|
DigitalIn* _SW;
|
||||||
|
|
||||||
|
Kernel::Clock::duration debounceDelta;
|
||||||
|
Kernel::Clock::time_point thisMls;
|
||||||
|
|
||||||
|
bool turnFlag = false, extTick = false, SW_state = false;
|
||||||
|
void encoderThread();
|
||||||
|
};
|
||||||
575
debounce.h
Normal file
575
debounce.h
Normal file
@@ -0,0 +1,575 @@
|
|||||||
|
///\file
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
The MIT License(MIT)
|
||||||
|
|
||||||
|
Embedded Template Library.
|
||||||
|
https://github.com/ETLCPP/etl
|
||||||
|
https://www.etlcpp.com
|
||||||
|
|
||||||
|
Copyright(c) 2016 jwellbelove
|
||||||
|
|
||||||
|
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.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ETL_DEBOUNCE_INCLUDED
|
||||||
|
#define ETL_DEBOUNCE_INCLUDED
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// #include "platform.h"
|
||||||
|
// #include "static_assert.h"
|
||||||
|
|
||||||
|
namespace etl
|
||||||
|
{
|
||||||
|
namespace private_debounce
|
||||||
|
{
|
||||||
|
class debounce_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef uint_least8_t flags_t;
|
||||||
|
typedef uint16_t count_t;
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Adds the new sample and clears the state change flag.
|
||||||
|
/// If the sample has changed then the counter is reset.
|
||||||
|
/// The last sample state is stored as a bit in the flags.
|
||||||
|
//*************************************************************************
|
||||||
|
void add_sample(bool sample)
|
||||||
|
{
|
||||||
|
// Changed from last time?
|
||||||
|
if (sample != bool((flags & SAMPLE) != 0))
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
flags = (flags & ~SAMPLE) | (sample ? SAMPLE : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
flags &= ~CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Gets the current debouncer change state.
|
||||||
|
///\return 'true' if the debouncer has changed state.
|
||||||
|
//*************************************************************************
|
||||||
|
bool has_changed() const
|
||||||
|
{
|
||||||
|
return (flags & CHANGE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Gets the current debouncer state.
|
||||||
|
///\return 'true' if the debouncer is in the true state.
|
||||||
|
//*************************************************************************
|
||||||
|
bool is_set() const
|
||||||
|
{
|
||||||
|
return ((flags & STATE) > OFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Gets the debouncer hold state.
|
||||||
|
///\return 'true' if the debouncer is in the hold state.
|
||||||
|
//*************************************************************************
|
||||||
|
bool is_held() const
|
||||||
|
{
|
||||||
|
return (flags & STATE) > ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Gets the debouncer repeat state.
|
||||||
|
///\return 'true' if the debouncer is repeating.
|
||||||
|
//*************************************************************************
|
||||||
|
bool is_repeating() const
|
||||||
|
{
|
||||||
|
return ((flags & STATE) == REPEATING);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum states
|
||||||
|
{
|
||||||
|
OFF = 0,
|
||||||
|
ON = 1,
|
||||||
|
HELD = 2,
|
||||||
|
REPEATING = 3,
|
||||||
|
STATE = 0x03,
|
||||||
|
SAMPLE = 4,
|
||||||
|
CHANGE = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//*************************************************************************
|
||||||
|
/// Constructor.
|
||||||
|
//*************************************************************************
|
||||||
|
debounce_base(bool initial_state)
|
||||||
|
: flags(initial_state ? ON : OFF),
|
||||||
|
count(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Destructor.
|
||||||
|
//*************************************************************************
|
||||||
|
~debounce_base()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Gets the next state based on the inputs.
|
||||||
|
//*************************************************************************
|
||||||
|
void get_next(bool sample, bool condition_set, bool condition_clear, uint_least8_t state_table[][2])
|
||||||
|
{
|
||||||
|
int index1 = ((flags & STATE) * 2) + (sample ? 1 : 0);
|
||||||
|
int index2 = (sample ? (condition_set ? 0 : 1) : (condition_clear ? 0 : 1));
|
||||||
|
|
||||||
|
flags_t next = flags;
|
||||||
|
|
||||||
|
next &= ~STATE;
|
||||||
|
next |= state_table[index1][index2];
|
||||||
|
|
||||||
|
if (next != flags)
|
||||||
|
{
|
||||||
|
next |= CHANGE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next &= ~CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags_t flags;
|
||||||
|
count_t count;
|
||||||
|
};
|
||||||
|
|
||||||
|
//***************************************************************************
|
||||||
|
/// State change logic for 2 state debounce.
|
||||||
|
//***************************************************************************
|
||||||
|
class debounce2 : public debounce_base
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
debounce2(bool initial_state)
|
||||||
|
: debounce_base(initial_state)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Destructor.
|
||||||
|
//*************************************************************************
|
||||||
|
~debounce2()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
///
|
||||||
|
//*************************************************************************
|
||||||
|
void set_state(bool sample, bool condition_set, bool condition_clear)
|
||||||
|
{
|
||||||
|
static uint_least8_t state_table[4][2] =
|
||||||
|
{
|
||||||
|
/* OFF 0 */ {debounce_base::OFF, debounce_base::OFF},
|
||||||
|
/* OFF 1 */ {debounce_base::ON, debounce_base::OFF},
|
||||||
|
/* ON 0 */ {debounce_base::OFF, debounce_base::ON},
|
||||||
|
/* ON 1 */ {debounce_base::ON, debounce_base::ON},
|
||||||
|
};
|
||||||
|
|
||||||
|
get_next(sample, condition_set, condition_clear, state_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
///
|
||||||
|
//*************************************************************************
|
||||||
|
bool process(bool sample, count_t valid_count)
|
||||||
|
{
|
||||||
|
add_sample(sample);
|
||||||
|
|
||||||
|
if (count < UINT16_MAX)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
|
||||||
|
bool valid = (count == valid_count);
|
||||||
|
|
||||||
|
switch (flags & STATE)
|
||||||
|
{
|
||||||
|
case OFF:
|
||||||
|
{
|
||||||
|
set_state(sample, valid, valid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ON:
|
||||||
|
{
|
||||||
|
set_state(sample, valid, valid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & CHANGE)
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (flags & CHANGE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//***************************************************************************
|
||||||
|
/// State change logic for 3 state debounce.
|
||||||
|
//***************************************************************************
|
||||||
|
class debounce3 : public debounce_base
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
debounce3(bool initial_state)
|
||||||
|
: debounce_base(initial_state)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Destructor.
|
||||||
|
//*************************************************************************
|
||||||
|
~debounce3()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
///
|
||||||
|
//*************************************************************************
|
||||||
|
void set_state(bool sample, bool condition_set, bool condition_clear)
|
||||||
|
{
|
||||||
|
static uint_least8_t state_table[6][2] =
|
||||||
|
{
|
||||||
|
/* OFF 0 */ {debounce_base::OFF, debounce_base::OFF},
|
||||||
|
/* OFF 1 */ {debounce_base::ON, debounce_base::OFF},
|
||||||
|
/* ON 0 */ {debounce_base::OFF, debounce_base::ON},
|
||||||
|
/* ON 1 */ {debounce_base::HELD, debounce_base::ON},
|
||||||
|
/* HELD 0 */ {debounce_base::OFF, debounce_base::HELD},
|
||||||
|
/* HELD 1 */ {debounce_base::HELD, debounce_base::HELD}};
|
||||||
|
|
||||||
|
get_next(sample, condition_set, condition_clear, state_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
///
|
||||||
|
//*************************************************************************
|
||||||
|
bool process(bool sample, count_t valid_count, count_t hold_count)
|
||||||
|
{
|
||||||
|
add_sample(sample);
|
||||||
|
|
||||||
|
if (count < UINT16_MAX)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
|
||||||
|
bool valid = (count == valid_count);
|
||||||
|
bool hold = (count == hold_count);
|
||||||
|
|
||||||
|
switch (flags & STATE)
|
||||||
|
{
|
||||||
|
case OFF:
|
||||||
|
{
|
||||||
|
set_state(sample, valid, valid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ON:
|
||||||
|
{
|
||||||
|
set_state(sample, hold, valid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case HELD:
|
||||||
|
{
|
||||||
|
set_state(sample, hold, valid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & CHANGE)
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (flags & CHANGE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//***************************************************************************
|
||||||
|
/// State change logic for 4 state debounce.
|
||||||
|
//***************************************************************************
|
||||||
|
class debounce4 : public debounce_base
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
debounce4(bool initial_state)
|
||||||
|
: debounce_base(initial_state)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Destructor.
|
||||||
|
//*************************************************************************
|
||||||
|
~debounce4()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
///
|
||||||
|
//*************************************************************************
|
||||||
|
void set_state(bool sample, bool condition_set, bool condition_clear)
|
||||||
|
{
|
||||||
|
static uint_least8_t state_table[8][2] =
|
||||||
|
{
|
||||||
|
/* OFF 0 */ {debounce_base::OFF, debounce_base::OFF},
|
||||||
|
/* OFF 1 */ {debounce_base::ON, debounce_base::OFF},
|
||||||
|
/* ON 0 */ {debounce_base::OFF, debounce_base::ON},
|
||||||
|
/* ON 1 */ {debounce_base::HELD, debounce_base::ON},
|
||||||
|
/* HELD 0 */ {debounce_base::OFF, debounce_base::HELD},
|
||||||
|
/* HELD 1 */ {debounce_base::REPEATING, debounce_base::HELD},
|
||||||
|
/* REPEATING 0 */ {debounce_base::OFF, debounce_base::REPEATING},
|
||||||
|
/* REPEATING 1 */ {debounce_base::REPEATING, debounce_base::REPEATING}};
|
||||||
|
|
||||||
|
get_next(sample, condition_set, condition_clear, state_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
///
|
||||||
|
//*************************************************************************
|
||||||
|
bool process(bool sample, count_t valid_count, count_t hold_count, count_t repeat_count)
|
||||||
|
{
|
||||||
|
add_sample(sample);
|
||||||
|
|
||||||
|
if (count < UINT16_MAX)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
|
||||||
|
bool valid = (count == valid_count);
|
||||||
|
bool hold = (count == hold_count);
|
||||||
|
bool repeat = (count == repeat_count);
|
||||||
|
|
||||||
|
switch (flags & STATE)
|
||||||
|
{
|
||||||
|
case OFF:
|
||||||
|
{
|
||||||
|
set_state(sample, valid, valid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ON:
|
||||||
|
{
|
||||||
|
set_state(sample, hold, valid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case HELD:
|
||||||
|
{
|
||||||
|
set_state(sample, repeat, valid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case REPEATING:
|
||||||
|
{
|
||||||
|
set_state(sample, repeat, valid);
|
||||||
|
|
||||||
|
if (sample && repeat)
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
flags |= CHANGE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & CHANGE)
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (flags & CHANGE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace private_debounce
|
||||||
|
|
||||||
|
//***************************************************************************
|
||||||
|
/// A class to debounce signals.
|
||||||
|
/// Fixed Valid/Hold/Repeating values.
|
||||||
|
//***************************************************************************
|
||||||
|
template <const uint16_t VALID_COUNT = 0, const uint16_t HOLD_COUNT = 0, const uint16_t REPEAT_COUNT = 0>
|
||||||
|
class debounce : public private_debounce::debounce4
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//*************************************************************************
|
||||||
|
/// Constructor.
|
||||||
|
//*************************************************************************
|
||||||
|
debounce(bool initial_state = false)
|
||||||
|
: debounce4(initial_state)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Adds a new sample.
|
||||||
|
/// Returns 'true' if the debouncer changes state.
|
||||||
|
///\param sample The new sample.
|
||||||
|
///\return 'true' if the debouncer changed state.
|
||||||
|
//*************************************************************************
|
||||||
|
bool add(bool sample)
|
||||||
|
{
|
||||||
|
return process(sample, VALID_COUNT, HOLD_COUNT, REPEAT_COUNT);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//***************************************************************************
|
||||||
|
/// A class to debounce signals.
|
||||||
|
/// Fixed Valid/Hold values.
|
||||||
|
//***************************************************************************
|
||||||
|
template <const uint16_t VALID_COUNT, const uint16_t HOLD_COUNT>
|
||||||
|
class debounce<VALID_COUNT, HOLD_COUNT, 0> : public private_debounce::debounce3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//*************************************************************************
|
||||||
|
/// Constructor.
|
||||||
|
//*************************************************************************
|
||||||
|
debounce(bool initial_state = false)
|
||||||
|
: debounce3(initial_state)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Adds a new sample.
|
||||||
|
/// Returns 'true' if the debouncer changes state from...
|
||||||
|
/// 1. Clear to Set.
|
||||||
|
/// 2. Set to Clear.
|
||||||
|
/// 3. Not Held to Held.
|
||||||
|
///\param sample The new sample.
|
||||||
|
///\return 'true' if the debouncer changed state.
|
||||||
|
//*************************************************************************
|
||||||
|
bool add(bool sample)
|
||||||
|
{
|
||||||
|
return process(sample, VALID_COUNT, HOLD_COUNT);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//***************************************************************************
|
||||||
|
/// A class to debounce signals.
|
||||||
|
/// Fixed Valid value.
|
||||||
|
//***************************************************************************
|
||||||
|
template <const uint16_t VALID_COUNT>
|
||||||
|
class debounce<VALID_COUNT, 0, 0> : public private_debounce::debounce2
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//*************************************************************************
|
||||||
|
/// Constructor.
|
||||||
|
//*************************************************************************
|
||||||
|
debounce(bool initial_state = false)
|
||||||
|
: debounce2(initial_state)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Adds a new sample.
|
||||||
|
/// Returns 'true' if the debouncer changes state from...
|
||||||
|
/// 1. Clear to Set.
|
||||||
|
/// 2. Set to Clear.
|
||||||
|
///\param sample The new sample.
|
||||||
|
///\return 'true' if the debouncer changed state.
|
||||||
|
//*************************************************************************
|
||||||
|
bool add(bool sample)
|
||||||
|
{
|
||||||
|
return process(sample, VALID_COUNT);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//***************************************************************************
|
||||||
|
/// A class to debounce signals.
|
||||||
|
/// Variable Valid/Hold/Repeating values.
|
||||||
|
//***************************************************************************
|
||||||
|
template <>
|
||||||
|
class debounce<0, 0, 0> : public private_debounce::debounce4
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//*************************************************************************
|
||||||
|
/// Constructor.
|
||||||
|
///\param initial_state The initial state. Default = false.
|
||||||
|
//*************************************************************************
|
||||||
|
debounce(bool initial_state = false)
|
||||||
|
: debounce4(initial_state),
|
||||||
|
valid_count(1),
|
||||||
|
hold_count(0),
|
||||||
|
repeat_count(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Constructor.
|
||||||
|
///\param valid_count The count for a valid state..
|
||||||
|
///\param hold_count The count after valid_count for a hold state. Default = 0.
|
||||||
|
///\param repeat_count The count after hold_count for a key repeat. Default = 0.
|
||||||
|
//*************************************************************************
|
||||||
|
debounce(count_t valid, count_t hold = 0, count_t repeat = 0)
|
||||||
|
: debounce4(false)
|
||||||
|
{
|
||||||
|
set(valid, hold, repeat);
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Constructor.
|
||||||
|
//*************************************************************************
|
||||||
|
void set(count_t valid, count_t hold = 0, count_t repeat = 0)
|
||||||
|
{
|
||||||
|
valid_count = valid;
|
||||||
|
hold_count = hold;
|
||||||
|
repeat_count = repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
/// Adds a new sample.
|
||||||
|
/// Returns 'true' if the debouncer changes state from...
|
||||||
|
/// 1. Clear to Set.
|
||||||
|
/// 2. Set to Clear.
|
||||||
|
/// 3. Not Held to Held.
|
||||||
|
/// 4. Key repeats.
|
||||||
|
///\param sample The new sample.
|
||||||
|
///\return 'true' if the debouncer changed state.
|
||||||
|
//*************************************************************************
|
||||||
|
bool add(bool sample)
|
||||||
|
{
|
||||||
|
return process(sample, valid_count, hold_count, repeat_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
count_t valid_count;
|
||||||
|
count_t hold_count;
|
||||||
|
count_t repeat_count;
|
||||||
|
};
|
||||||
|
} // namespace etl
|
||||||
|
|
||||||
|
#endif
|
||||||
13
library.json
Executable file
13
library.json
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "Gyver Encoder",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/AlexGyver/GyverLibs"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"srcFilter": [
|
||||||
|
"+<*>"
|
||||||
|
],
|
||||||
|
"flags": "-I./"
|
||||||
|
}
|
||||||
|
}
|
||||||
134
platform.h
Normal file
134
platform.h
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
///\file
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
The MIT License(MIT)
|
||||||
|
|
||||||
|
Embedded Template Library.
|
||||||
|
https://github.com/ETLCPP/etl
|
||||||
|
https://www.etlcpp.com
|
||||||
|
|
||||||
|
Copyright(c) 2016 jwellbelove
|
||||||
|
|
||||||
|
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.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ETL_PLATFORM_INCLUDED
|
||||||
|
#define ETL_PLATFORM_INCLUDED
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
// Some targets do not support 8bit types.
|
||||||
|
#define ETL_8BIT_SUPPORT (CHAR_BIT == 8)
|
||||||
|
|
||||||
|
// Define a debug macro
|
||||||
|
#if !defined(ETL_DEBUG)
|
||||||
|
#if defined(_DEBUG) || defined(DEBUG)
|
||||||
|
#define ETL_DEBUG
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Determine the bit width of the platform.
|
||||||
|
#define ETL_PLATFORM_16BIT (UINT16_MAX == UINTPTR_MAX)
|
||||||
|
#define ETL_PLATFORM_32BIT (UINT32_MAX == UINTPTR_MAX)
|
||||||
|
#define ETL_PLATFORM_64BIT (UINT64_MAX == UINTPTR_MAX)
|
||||||
|
|
||||||
|
// Include the user's profile definition.
|
||||||
|
#include "etl_profile.h"
|
||||||
|
|
||||||
|
// Figure out things about the compiler, if haven't already done so in etl_profile.h
|
||||||
|
#include "profiles/determine_compiler_version.h"
|
||||||
|
#include "profiles/determine_compiler_language_support.h"
|
||||||
|
|
||||||
|
// See if we can determine the OS we're compiling on, if haven't already done so in etl_profile.h
|
||||||
|
#include "profiles/determine_development_os.h"
|
||||||
|
|
||||||
|
#if defined(ETL_FORCE_EXPLICIT_STRING_CONVERSION_FROM_CHAR)
|
||||||
|
#define ETL_EXPLICIT_STRING_FROM_CHAR explicit
|
||||||
|
#else
|
||||||
|
#define ETL_EXPLICIT_STRING_FROM_CHAR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// The macros below are dependent on the profile.
|
||||||
|
// C++11
|
||||||
|
#if ETL_CPP11_SUPPORTED && !defined(ETL_FORCE_NO_ADVANCED_CPP)
|
||||||
|
#define ETL_CONSTEXPR constexpr
|
||||||
|
#define ETL_CONST_OR_CONSTEXPR constexpr
|
||||||
|
#define ETL_DELETE = delete
|
||||||
|
#define ETL_EXPLICIT explicit
|
||||||
|
#define ETL_OVERRIDE override
|
||||||
|
#define ETL_FINAL final
|
||||||
|
#define ETL_NORETURN [[noreturn]]
|
||||||
|
|
||||||
|
#if defined(ETL_EXCEPTIONS_DISABLED)
|
||||||
|
#define ETL_NOEXCEPT
|
||||||
|
#define ETL_NOEXCEPT_EXPR(expression)
|
||||||
|
#else
|
||||||
|
#define ETL_NOEXCEPT noexcept
|
||||||
|
#define ETL_NOEXCEPT_EXPR(expression) noexcept(expression)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define ETL_CONSTEXPR
|
||||||
|
#define ETL_CONST_OR_CONSTEXPR const
|
||||||
|
#define ETL_DELETE
|
||||||
|
#define ETL_EXPLICIT
|
||||||
|
#define ETL_OVERRIDE
|
||||||
|
#define ETL_FINAL
|
||||||
|
#define ETL_NORETURN
|
||||||
|
#define ETL_NOEXCEPT
|
||||||
|
#define ETL_NOEXCEPT_EXPR(expression)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// C++14
|
||||||
|
#if ETL_CPP14_SUPPORTED && !defined(ETL_FORCE_NO_ADVANCED_CPP)
|
||||||
|
#define ETL_CONSTEXPR14 constexpr
|
||||||
|
#define ETL_DEPRECATED [[deprecated]]
|
||||||
|
#define ETL_DEPRECATED_REASON(reason) [[deprecated(reason)]]
|
||||||
|
#else
|
||||||
|
#define ETL_CONSTEXPR14
|
||||||
|
#define ETL_DEPRECATED
|
||||||
|
#define ETL_DEPRECATED_REASON(reason)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// C++17
|
||||||
|
#if ETL_CPP17_SUPPORTED && !defined(ETL_FORCE_NO_ADVANCED_CPP)
|
||||||
|
#define ETL_CONSTEXPR17 constexpr
|
||||||
|
#define ETL_IF_CONSTEXPR constexpr
|
||||||
|
#define ETL_NODISCARD [[nodiscard]]
|
||||||
|
#define ETL_FALLTHROUGH [[fallthrough]]
|
||||||
|
#else
|
||||||
|
#define ETL_CONSTEXPR17
|
||||||
|
#define ETL_IF_CONSTEXPR
|
||||||
|
#define ETL_NODISCARD
|
||||||
|
#define ETL_FALLTHROUGH
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// C++20
|
||||||
|
#if ETL_CPP20_SUPPORTED && !defined(ETL_FORCE_NO_ADVANCED_CPP)
|
||||||
|
#define ETL_LIKELY [[likely]]
|
||||||
|
#define ETL_UNLIKELY [[unlikely]]
|
||||||
|
#else
|
||||||
|
#define ETL_LIKELY
|
||||||
|
#define ETL_UNLIKELY
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Sort out namespaces for STL/No STL options.
|
||||||
|
#include "private/choose_namespace.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user