commit de59e62d68d29a305f3e8c7d55e1a94f85a76e55 Author: Beslan Date: Sat Nov 28 23:20:52 2020 +0300 первый diff --git a/GyverEncoder.cpp b/GyverEncoder.cpp new file mode 100644 index 0000000..a5f85d2 --- /dev/null +++ b/GyverEncoder.cpp @@ -0,0 +1,475 @@ +#include "GyverEncoder.h" +#include "mbed.h" +#include + +#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 key_state; + // static etl::debounce 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; + } +} diff --git a/GyverEncoder.h b/GyverEncoder.h new file mode 100644 index 0000000..67c59ba --- /dev/null +++ b/GyverEncoder.h @@ -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(); +}; diff --git a/debounce.h b/debounce.h new file mode 100644 index 0000000..612fc58 --- /dev/null +++ b/debounce.h @@ -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 + +// #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 +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 +class debounce : 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 +class debounce : 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 diff --git a/library.json b/library.json new file mode 100755 index 0000000..1b5d39d --- /dev/null +++ b/library.json @@ -0,0 +1,13 @@ +{ + "name": "Gyver Encoder", + "repository": { + "type": "git", + "url": "https://github.com/AlexGyver/GyverLibs" + }, + "build": { + "srcFilter": [ + "+<*>" + ], + "flags": "-I./" + } +} \ No newline at end of file diff --git a/platform.h b/platform.h new file mode 100644 index 0000000..6947bc9 --- /dev/null +++ b/platform.h @@ -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 +#include + +// 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