esp32墨水屏天气预测学习
onebutton.cpp,onebutton.h代码学习:代码文件如下 :
onebutton.h文件:
// -----
// OneButton.h - Library for detecting button clicks, doubleclicks and long
// press pattern on a single button. This class is implemented for use with the
// Arduino environment. Copyright (c) by Matthias Hertel,
// http://www.mathertel.de This work is licensed under a BSD style license. See
// http://www.mathertel.de/License.aspx More information on:
// http://www.mathertel.de/Arduino
// -----
// 02.10.2010 created by Matthias Hertel
// 21.04.2011 transformed into a library
// 01.12.2011 include file changed to work with the Arduino 1.0 environment
// 23.03.2014 Enhanced long press functionalities by adding longPressStart and
// longPressStop callbacks
// 21.09.2015 A simple way for debounce detection added.
// 14.05.2017 Debouncing improvements.
// 25.06.2018 Optional third parameter for deactivating pullup.
// 26.09.2018 Anatoli Arkhipenko: Included solution to use library with other
// sources of input.
// 26.09.2018 Initialization moved into class declaration.
// 26.09.2018 Jay M Ericsson: compiler warnings removed.
// 29.01.2020 improvements from ShaggyDog18
// 07.05.2023 Debouncing in one point. #118
// -----#ifndef OneButton_h
#define OneButton_h#include "Arduino.h"// ----- Callback function types -----extern "C" {typedef void (*callbackFunction)(void);typedef void (*parameterizedCallbackFunction)(void *);
}class OneButton {
public:// ----- Constructor -----/** Create a OneButton instance.* use setup(...) to specify the hardware configuration. */OneButton();/*** Create a OneButton instance and setup.* @param pin The pin to be used for input from a momentary button.* @param activeLow Set to true when the input level is LOW when the button is pressed, Default is true.* @param pullupActive Activate the internal pullup when available. Default is true.*/explicit OneButton(const int pin, const bool activeLow = true, const bool pullupActive = true);// ----- Set runtime parameters -----/*** Initialize or re-initialize the input pin. * @param pin The pin to be used for input from a momentary button.* @param mode Any of the modes also used in pinMode like INPUT or INPUT_PULLUP (default).* @param activeLow Set to true when the input level is LOW when the button is pressed, Default is true.*/void setup(const uint8_t pin, const uint8_t mode = INPUT_PULLUP, const bool activeLow = true);/*** set # millisec after safe click is assumed.*/[[deprecated("Use setDebounceMs() instead.")]]void setDebounceTicks(const unsigned int ms) {setDebounceMs(ms);}; // deprecatedvoid setDebounceMs(const int ms);/*** set # millisec after single click is assumed.*/[[deprecated("Use setClickMs() instead.")]]void setClickTicks(const unsigned int ms) {setClickMs(ms);}; // deprecatedvoid setClickMs(const unsigned int ms);/*** set # millisec after press is assumed.*/[[deprecated("Use setPressMs() instead.")]]void setPressTicks(const unsigned int ms) {setPressMs(ms);}; // deprecatedvoid setPressMs(const unsigned int ms);/*** set interval in msecs between calls of the DuringLongPress event.* 0 ms is the fastest events calls.*/void setLongPressIntervalMs(const unsigned int ms) {_long_press_interval_ms = ms;};/*** set # millisec after idle is assumed.*/void setIdleMs(const unsigned int ms);// ----- Attach events functions -----/*** Attach an event to be called immediately when a depress is detected.* @param newFunction This function will be called when the event has been detected.*/void attachPress(callbackFunction newFunction);void attachPress(parameterizedCallbackFunction newFunction, void *parameter);/*** Attach an event to be called when a single click is detected.* @param newFunction This function will be called when the event has been detected.*/void attachClick(callbackFunction newFunction);void attachClick(parameterizedCallbackFunction newFunction, void *parameter);/*** Attach an event to be called after a double click is detected.* @param newFunction This function will be called when the event has been detected.*/void attachDoubleClick(callbackFunction newFunction);void attachDoubleClick(parameterizedCallbackFunction newFunction, void *parameter);/*** Attach an event to be called after a multi click is detected.* @param newFunction This function will be called when the event has been detected.*/void attachMultiClick(callbackFunction newFunction);void attachMultiClick(parameterizedCallbackFunction newFunction, void *parameter);/*** Attach an event to fire when the button is pressed and held down.* @param newFunction*/void attachLongPressStart(callbackFunction newFunction);void attachLongPressStart(parameterizedCallbackFunction newFunction, void *parameter);/*** Attach an event to fire as soon as the button is released after a long press.* @param newFunction*/void attachLongPressStop(callbackFunction newFunction);void attachLongPressStop(parameterizedCallbackFunction newFunction, void *parameter);/*** Attach an event to fire periodically while the button is held down.* The period of calls is set by setLongPressIntervalMs(ms).* @param newFunction*/void attachDuringLongPress(callbackFunction newFunction);void attachDuringLongPress(parameterizedCallbackFunction newFunction, void *parameter);/*** Attach an event when the button is in idle position.* @param newFunction*/void attachIdle(callbackFunction newFunction);// ----- State machine functions -----/*** @brief Call this function every some milliseconds for checking the input* level at the initialized digital pin.*/void tick(void);/*** @brief Call this function every time the input level has changed.* Using this function no digital input pin is checked because the current* level is given by the parameter.* Run the finite state machine (FSM) using the given level.*/void tick(bool activeLevel);/*** Reset the button state machine.*/void reset(void);/** return number of clicks in any case: single or multiple clicks*/int getNumberClicks(void);/*** @return true if we are currently handling button press flow* (This allows power sensitive applications to know when it is safe to power down the main CPU)*/bool isIdle() const {return _state == OCS_INIT;}/*** @return true when a long press is detected*/bool isLongPressed() const {return _state == OCS_PRESS;};private:int _pin = -1; // hardware pin number.int _debounce_ms = 50; // number of msecs for debounce times.unsigned int _click_ms = 400; // number of msecs before a click is detected.unsigned int _press_ms = 800; // number of msecs before a long button press is detectedunsigned int _idle_ms = 1000; // number of msecs before idle is detectedint _buttonPressed = 0; // this is the level of the input pin when the button is pressed.// LOW if the button connects the input pin to GND when pressed.// HIGH if the button connects the input pin to VCC when pressed.// These variables will hold functions acting as event source.callbackFunction _pressFunc = NULL;parameterizedCallbackFunction _paramPressFunc = NULL;void *_pressFuncParam = NULL;callbackFunction _clickFunc = NULL;parameterizedCallbackFunction _paramClickFunc = NULL;void *_clickFuncParam = NULL;callbackFunction _doubleClickFunc = NULL;parameterizedCallbackFunction _paramDoubleClickFunc = NULL;void *_doubleClickFuncParam = NULL;callbackFunction _multiClickFunc = NULL;parameterizedCallbackFunction _paramMultiClickFunc = NULL;void *_multiClickFuncParam = NULL;callbackFunction _longPressStartFunc = NULL;parameterizedCallbackFunction _paramLongPressStartFunc = NULL;void *_longPressStartFuncParam = NULL;callbackFunction _longPressStopFunc = NULL;parameterizedCallbackFunction _paramLongPressStopFunc = NULL;void *_longPressStopFuncParam = NULL;callbackFunction _duringLongPressFunc = NULL;parameterizedCallbackFunction _paramDuringLongPressFunc = NULL;void *_duringLongPressFuncParam = NULL;callbackFunction _idleFunc = NULL;// These variables that hold information across the upcoming tick calls.// They are initialized once on program start and are updated every time the// tick function is called.// define FiniteStateMachineenum stateMachine_t : int {OCS_INIT = 0,OCS_DOWN = 1, // button is downOCS_UP = 2, // button is upOCS_COUNT = 3, // in multi press-mode, countingOCS_PRESS = 6, // button is hold downOCS_PRESSEND = 7,};/*** Run the finite state machine (FSM) using the given level.*/void _fsm(bool activeLevel);/*** Advance to a new state.*/void _newState(stateMachine_t nextState);stateMachine_t _state = OCS_INIT;bool _idleState = false;bool debouncedLevel = false;bool _lastDebounceLevel = false; // used for pin debouncingunsigned long _lastDebounceTime = 0; // millis()unsigned long now = 0; // millis()unsigned long _startTime = 0; // start time of current activeLevel changeint _nClicks = 0; // count the number of clicks with this variableint _maxClicks = 1; // max number (1, 2, multi=3) of clicks of interest by registration of event functions.unsigned int _long_press_interval_ms = 0; // interval in msecs between calls of the DuringLongPress eventunsigned long _lastDuringLongPressTime = 0; // used to produce the DuringLongPress intervalpublic:int pin() const {return _pin;};stateMachine_t state() const {return _state;};bool debounce(const bool value);int debouncedValue() const {return debouncedLevel;};/*** @brief Use this function in the DuringLongPress and LongPressStop events to get the time since the button was pressed.* @return milliseconds from the start of the button press.*/unsigned long getPressedMs() {return (millis() - _startTime);};
};#endif
onebutton.cpp文件:
/*** @file OneButton.cpp** @brief Library for detecting button clicks, doubleclicks and long press* pattern on a single button.** @author Matthias Hertel, https://www.mathertel.de* @Copyright Copyright (c) by Matthias Hertel, https://www.mathertel.de.* Ihor Nehrutsa, Ihor.Nehrutsa@gmail.com** This work is licensed under a BSD style license. See* http://www.mathertel.de/License.aspx** More information on: https://www.mathertel.de/Arduino/OneButtonLibrary.aspx** Changelog: see OneButton.h*/#include "OneButton.h"// ----- Initialization and Default Values -----/*** @brief Construct a new OneButton object but not (yet) initialize the IO pin.*/
OneButton::OneButton() {_pin = -1;// further initialization has moved to OneButton.h
}// Initialize the OneButton library.
OneButton::OneButton(const int pin, const bool activeLow, const bool pullupActive) {setup(pin, pullupActive ? INPUT_PULLUP : INPUT, activeLow);
} // OneButton// initialize or re-initialize the input pin
void OneButton::setup(const uint8_t pin, const uint8_t mode, const bool activeLow) {_pin = pin;if (activeLow) {// the button connects the input pin to GND when pressed._buttonPressed = LOW;} else {// the button connects the input pin to VCC when pressed._buttonPressed = HIGH;}pinMode(pin, mode);
}// explicitly set the number of millisec that have to pass by before a click is assumed stable.
void OneButton::setDebounceMs(const int ms) {_debounce_ms = ms;
} // setDebounceMs// explicitly set the number of millisec that have to pass by before a click is detected.
void OneButton::setClickMs(const unsigned int ms) {_click_ms = ms;
} // setClickMs// explicitly set the number of millisec that have to pass by before a long button press is detected.
void OneButton::setPressMs(const unsigned int ms) {_press_ms = ms;
} // setPressMs// explicitly set the number of millisec that have to pass by before button idle is detected.
void OneButton::setIdleMs(const unsigned int ms) {_idle_ms = ms;
} // setIdleMs// save function for click event
void OneButton::attachPress(callbackFunction newFunction) {_pressFunc = newFunction;
} // attachPress// save function for parameterized click event
void OneButton::attachPress(parameterizedCallbackFunction newFunction, void *parameter) {_paramPressFunc = newFunction;_pressFuncParam = parameter;
} // attachPress// save function for click event
void OneButton::attachClick(callbackFunction newFunction) {_clickFunc = newFunction;
} // attachClick// save function for parameterized click event
void OneButton::attachClick(parameterizedCallbackFunction newFunction, void *parameter) {_paramClickFunc = newFunction;_clickFuncParam = parameter;
} // attachClick// save function for doubleClick event
void OneButton::attachDoubleClick(callbackFunction newFunction) {_doubleClickFunc = newFunction;_maxClicks = max(_maxClicks, 2);
} // attachDoubleClick// save function for parameterized doubleClick event
void OneButton::attachDoubleClick(parameterizedCallbackFunction newFunction, void *parameter) {_paramDoubleClickFunc = newFunction;_doubleClickFuncParam = parameter;_maxClicks = max(_maxClicks, 2);
} // attachDoubleClick// save function for multiClick event
void OneButton::attachMultiClick(callbackFunction newFunction) {_multiClickFunc = newFunction;_maxClicks = max(_maxClicks, 100);
} // attachMultiClick// save function for parameterized MultiClick event
void OneButton::attachMultiClick(parameterizedCallbackFunction newFunction, void *parameter) {_paramMultiClickFunc = newFunction;_multiClickFuncParam = parameter;_maxClicks = max(_maxClicks, 100);
} // attachMultiClick// save function for longPressStart event
void OneButton::attachLongPressStart(callbackFunction newFunction) {_longPressStartFunc = newFunction;
} // attachLongPressStart// save function for parameterized longPressStart event
void OneButton::attachLongPressStart(parameterizedCallbackFunction newFunction, void *parameter) {_paramLongPressStartFunc = newFunction;_longPressStartFuncParam = parameter;
} // attachLongPressStart// save function for longPressStop event
void OneButton::attachLongPressStop(callbackFunction newFunction) {_longPressStopFunc = newFunction;
} // attachLongPressStop// save function for parameterized longPressStop event
void OneButton::attachLongPressStop(parameterizedCallbackFunction newFunction, void *parameter) {_paramLongPressStopFunc = newFunction;_longPressStopFuncParam = parameter;
} // attachLongPressStop// save function for during longPress event
void OneButton::attachDuringLongPress(callbackFunction newFunction) {_duringLongPressFunc = newFunction;
} // attachDuringLongPress// save function for parameterized during longPress event
void OneButton::attachDuringLongPress(parameterizedCallbackFunction newFunction, void *parameter) {_paramDuringLongPressFunc = newFunction;_duringLongPressFuncParam = parameter;
} // attachDuringLongPress// save function for idle button event
void OneButton::attachIdle(callbackFunction newFunction) {_idleFunc = newFunction;
} // attachIdlevoid OneButton::reset(void) {_state = OneButton::OCS_INIT;_nClicks = 0;_startTime = millis();_idleState = false;
}// ShaggyDog ---- return number of clicks in any case: single or multiple clicks
int OneButton::getNumberClicks(void) {return _nClicks;
}/*** @brief Debounce input pin level for use in SpesialInput.*/
bool OneButton::debounce(const bool value) {now = millis(); // current (relative) time in msecs.// Don't debounce going into active state, if _debounce_ms is negativeif (value && _debounce_ms < 0)debouncedLevel = value;if (_lastDebounceLevel == value) {if (now - _lastDebounceTime >= abs(_debounce_ms))debouncedLevel = value;} else {_lastDebounceTime = now;_lastDebounceLevel = value;}return debouncedLevel;
};/*** @brief Check input of the configured pin,* debounce button state and then* advance the finite state machine (FSM).*/
void OneButton::tick(void) {if (_pin >= 0) {_fsm(debounce(digitalRead(_pin) == _buttonPressed));}
} // tick()void OneButton::tick(bool activeLevel) {_fsm(debounce(activeLevel));
}/*** @brief Advance to a new state and save the last one to come back in cas of bouncing detection.*/
void OneButton::_newState(stateMachine_t nextState) {_state = nextState;
} // _newState()/*** @brief Run the finite state machine (FSM) using the given level.*/
void OneButton::_fsm(bool activeLevel) {unsigned long waitTime = (now - _startTime);// Implementation of the state machineswitch (_state) {case OneButton::OCS_INIT:// on idle for idle_ms call idle functionif (!_idleState and (waitTime > _idle_ms))if (_idleFunc) {_idleState = true;_idleFunc();}// waiting for level to become active.if (activeLevel) {_newState(OneButton::OCS_DOWN);_startTime = now; // remember starting time_nClicks = 0;if (_pressFunc) _pressFunc();if (_paramPressFunc) _paramPressFunc(_pressFuncParam);} // ifbreak;case OneButton::OCS_DOWN:// waiting for level to become inactive.if (!activeLevel) {_newState(OneButton::OCS_UP);_startTime = now; // remember starting time} else if (waitTime > _press_ms) {if (_longPressStartFunc) _longPressStartFunc();if (_paramLongPressStartFunc) _paramLongPressStartFunc(_longPressStartFuncParam);_newState(OneButton::OCS_PRESS);} // ifbreak;case OneButton::OCS_UP:// level is inactive// count as a short button down_nClicks++;_newState(OneButton::OCS_COUNT);break;case OneButton::OCS_COUNT:// dobounce time is over, count clicksif (activeLevel) {// button is down again_newState(OneButton::OCS_DOWN);_startTime = now; // remember starting time} else if ((waitTime >= _click_ms) || (_nClicks == _maxClicks)) {// now we know how many clicks have been made.if (_nClicks == 1) {// this was 1 click only.if (_clickFunc) _clickFunc();if (_paramClickFunc) _paramClickFunc(_clickFuncParam);} else if (_nClicks == 2) {// this was a 2 click sequence.if (_doubleClickFunc) _doubleClickFunc();if (_paramDoubleClickFunc) _paramDoubleClickFunc(_doubleClickFuncParam);} else {// this was a multi click sequence.if (_multiClickFunc) _multiClickFunc();if (_paramMultiClickFunc) _paramMultiClickFunc(_multiClickFuncParam);} // ifreset();} // ifbreak;case OneButton::OCS_PRESS:// waiting for pin being release after long press.if (!activeLevel) {_newState(OneButton::OCS_PRESSEND);} else {// still the button is pressedif ((now - _lastDuringLongPressTime) >= _long_press_interval_ms) {if (_duringLongPressFunc) _duringLongPressFunc();if (_paramDuringLongPressFunc) _paramDuringLongPressFunc(_duringLongPressFuncParam);_lastDuringLongPressTime = now;}} // ifbreak;case OneButton::OCS_PRESSEND:// button was released.if (_longPressStopFunc) _longPressStopFunc();if (_paramLongPressStopFunc) _paramLongPressStopFunc(_longPressStopFuncParam);reset();break;default:// unknown state detected -> reset state machine_newState(OneButton::OCS_INIT);break;} // if} // OneButton.tick()// end.
这段代码是 OneButton
库的实现,用于检测单个按钮的多种交互事件(如单击、双击、长按、多击等),适用于 Arduino 等嵌入式平台。其核心通过有限状态机(FSM) 和时间阈值判断,实现对按钮状态的精准跟踪和事件识别。以下是详细解释:
1. 核心功能与设计目标
该库的主要作用是简化按钮交互逻辑,无需手动处理防抖、事件区分(如单击 vs 双击)等复杂细节。支持的事件包括:
- 单击(Click)
- 双击(Double Click)
- 多击(Multi Click,如 3 次点击)
- 长按开始(Long Press Start)
- 长按结束(Long Press Stop)
- 长按过程中(During Long Press)
- 按钮空闲(Idle,长时间未操作)
2. 类结构与核心成员变量
关键成员变量
- 状态相关:
_state
(当前状态机状态)、_nClicks
(点击计数)、_startTime
(事件起始时间戳)。 - 时间阈值:
_debounce_ms
(防抖时间)、_click_ms
(点击间隔阈值)、_press_ms
(长按判定时间)、_idle_ms
(空闲判定时间)。 - 回调函数:如
_clickFunc
(单击回调)、_doubleClickFunc
(双击回调)等,用于事件触发时执行用户自定义逻辑。 - 引脚配置:
_pin
(按钮连接的引脚)、_buttonPressed
(按钮按下时的电平,LOW 或 HIGH)。
3. 初始化与配置方法
构造函数与 setup
方法
- 构造函数支持两种初始化方式:默认构造(后续需调用
setup
)或直接指定引脚、电平模式(activeLow
)和上拉配置(pullupActive
)。 setup
方法是核心初始化函数:- 配置引脚模式(
INPUT
或INPUT_PULLUP
)。 - 根据
activeLow
设定按钮按下时的电平(LOW 表示低电平有效,HIGH 表示高电平有效)。
- 配置引脚模式(
LOW 表示低电平有效,HIGH 表示高电平有效)。这什么意思?
src/led.cpp在电子电路和嵌入式编程中,“LOW 表示低电平有效,HIGH 表示高电平有效” 是描述电路信号与功能逻辑对应关系的常用说法,具体含义如下:
1. 高低电平的物理含义
- HIGH(高电平):通常指接近电源电压的电平(例如在 3.3V 系统中约为 3.3V,5V 系统中约为 5V)。
- LOW(低电平):通常指接近 0V 的电平(例如 0~0.8V)。
2. “有效” 的含义
“有效” 在这里表示 “触发目标功能的状态”。例如:
- 若某设备(如 LED、继电器)是 “低电平有效”,则当控制信号为LOW时,设备执行预期功能(如 LED 点亮、继电器吸合);
- 若为 “高电平有效”,则当控制信号为HIGH时,设备执行预期功能。
3. 结合你的代码理解
在你提供的 src/led.cpp
中,LED 的控制逻辑就是 “低电平有效” 的典型例子:
cpp
运行
// 例如:
case 1:digitalWrite(PIN_LED, LOW); // On(低电平点亮LED)vTaskDelay(pdMS_TO_TICKS(1000));
break;
case 0:digitalWrite(PIN_LED, HIGH); // Off(高电平关闭LED)vTaskDelay(pdMS_TO_TICKS(1000));
break;
- 当向 LED 引脚写入 LOW 时,LED 点亮(此时 LOW 是 “有效电平”,触发 “点亮” 功能);
- 当写入 HIGH 时,LED 熄灭(此时 HIGH 是 “无效电平”,不触发点亮功能)。
这种逻辑由硬件电路设计决定(例如 LED 的驱动方式是 “灌电流” 还是 “拉电流”),代码中需根据硬件设计匹配对应的电平控制逻辑。在电子电路和嵌入式编程中,“LOW 表示低电平有效,HIGH 表示高电平有效” 是描述电路信号与功能逻辑对应关系的常用说法,具体含义如下:
1. 高低电平的物理含义
- HIGH(高电平):通常指接近电源电压的电平(例如在 3.3V 系统中约为 3.3V,5V 系统中约为 5V)。
- LOW(低电平):通常指接近 0V 的电平(例如 0~0.8V)。
2. “有效” 的含义
“有效” 在这里表示 “触发目标功能的状态”。例如:
- 若某设备(如 LED、继电器)是 “低电平有效”,则当控制信号为LOW时,设备执行预期功能(如 LED 点亮、继电器吸合);
- 若为 “高电平有效”,则当控制信号为HIGH时,设备执行预期功能。
3. 结合你的代码理解
在你提供的 src/led.cpp
中,LED 的控制逻辑就是 “低电平有效” 的典型例子:
cpp
运行
// 例如:
case 1:digitalWrite(PIN_LED, LOW); // On(低电平点亮LED)vTaskDelay(pdMS_TO_TICKS(1000));
break;
case 0:digitalWrite(PIN_LED, HIGH); // Off(高电平关闭LED)vTaskDelay(pdMS_TO_TICKS(1000));
break;
- 当向 LED 引脚写入 LOW 时,LED 点亮(此时 LOW 是 “有效电平”,触发 “点亮” 功能);
- 当写入 HIGH 时,LED 熄灭(此时 HIGH 是 “无效电平”,不触发点亮功能)。
这种逻辑由硬件电路设计决定(例如 LED 的驱动方式是 “灌电流” 还是 “拉电流”),代码中需根据硬件设计匹配对应的电平控制逻辑。
时间参数配置
setDebounceMs(ms)
:设置防抖时间(默认通常为 20ms),用于过滤机械抖动导致的电平波动。setClickMs(ms)
:设置点击间隔阈值(默认约 200ms),用于区分单击和双击(两次点击间隔小于该值视为双击)。setPressMs(ms)
:设置长按判定时间(默认约 500ms),按下持续时间超过该值视为长按。setIdleMs(ms)
:设置空闲判定时间,按钮长时间未操作时触发空闲事件。
4. 事件回调注册
通过一系列 attachXXX
方法注册事件回调函数,支持两种回调类型:
- 无参数回调(
callbackFunction
):如attachClick(func)
,触发时直接调用func()
。 - 带参数回调(
parameterizedCallbackFunction
):如attachClick(func, param)
,触发时调用func(param)
,方便传递自定义参数。
支持的事件回调包括:
attachClick
:单击事件。attachDoubleClick
:双击事件。attachMultiClick
:多击事件(3 次及以上点击)。attachLongPressStart
:长按开始事件(按下时间超过_press_ms
时触发)。attachLongPressStop
:长按结束事件(长按后释放时触发)。attachDuringLongPress
:长按过程中持续触发的事件(默认每 100ms 一次)。attachIdle
:按钮空闲事件(超过_idle_ms
未操作时触发)。
5. 核心逻辑:有限状态机(FSM)
状态机是该库的核心,通过 _fsm
方法实现,用于跟踪按钮状态并触发相应事件。主要状态包括:
状态 | 说明 |
---|---|
OCS_INIT | 初始状态(空闲),等待按钮被按下。 |
OCS_DOWN | 按钮已按下,等待释放或判定为长按。 |
OCS_UP | 按钮已释放,开始计数点击次数。 |
OCS_COUNT | 计数状态,判断点击次数(单击 / 双击 / 多击),等待下一次点击或超时。 |
OCS_PRESS | 长按状态,按钮持续按下,触发 “长按过程中” 事件。 |
OCS_PRESSEND | 长按结束状态,按钮释放,触发 “长按结束” 事件。 |
有限状态机是什么意思
有限状态机(Finite State Machine,简称 FSM)是一种数学模型,用于描述一个系统在有限个 “状态” 之间,根据输入(或事件)进行规则化转移的行为。它的核心思想是:系统在某一时刻只能处于一种 “状态”,当接收到特定输入(或事件)时,会按照预设规则切换到另一种状态,并可能执行相应的动作。
核心组成部分
- 状态(State):系统可能处于的有限个离散状态(例如 “开启”“关闭”“待机”“错误” 等)。
- 事件(Event/Input):触发状态转移的外部或内部信号(例如 “按钮按下”“超时”“数据接收完成” 等)。
- 转移(Transition):定义在特定状态下,收到某事件后切换到新状态的规则(例如 “在‘关闭’状态下,收到‘开机信号’则转移到‘开启’状态”)。
- 动作(Action):状态转移时或处于某状态时执行的操作(例如 “进入‘开启’状态时启动电机”“在‘错误’状态时报警”)。
特点
- 有限性:状态数量是有限的(区别于无限状态机)。
- 确定性:在某一状态下,给定相同的事件,转移到的下一个状态是确定的(也有非确定性 FSM,但实际应用中多为确定性)。
应用场景
有限状态机在编程、电子电路、控制系统等领域广泛应用,例如:
- 交通信号灯:状态为 “红灯”“黄灯”“绿灯”,事件为 “定时结束”,转移规则为 “红灯→绿灯→黄灯→红灯” 循环。
- 电梯控制:状态包括 “上升中”“下降中”“停止”,事件为 “楼层按钮按下”,转移规则根据当前位置和目标楼层确定。
- 通信协议:例如 TCP 协议的 “建立连接”“数据传输”“断开连接” 等状态切换。
- 嵌入式设备逻辑:如按钮长按 / 短按的判断(状态:“未按下”“短按中”“长按中”,事件:“按键按下”“超时”)。
优势
- 简化复杂逻辑:将系统行为分解为清晰的状态和转移规则,避免嵌套条件判断(如多层
if-else
或switch-case
)。 - 提高可读性:状态和规则直观,便于理解和维护。
- 可验证性:有限状态可穷举,便于测试和调试。
例如,在代码中可以用enum
定义状态,用switch-case
或状态转移表实现状态逻辑,如下简单示例(伪代码):
// 定义状态
enum State {OFF, // 关闭ON, // 开启ERROR // 错误
};// 当前状态
State currentState = OFF;// 处理事件并转移状态
void handleEvent(Event event) {switch (currentState) {case OFF:if (event == POWER_ON) {currentState = ON; // 转移到开启状态startDevice(); // 执行动作}break;case ON:if (event == POWER_OFF) {currentState = OFF; // 转移到关闭状态stopDevice(); // 执行动作} else if (event == OVERHEAT) {currentState = ERROR; // 转移到错误状态triggerAlarm(); // 执行动作}break;// ... 其他状态处理}
}
状态转换流程(简化):
初始状态(
OCS_INIT
):- 若按钮被按下(电平符合
_buttonPressed
),进入OCS_DOWN
状态,记录开始时间,重置点击计数。 - 若长时间未操作(超过
_idle_ms
),触发idle
事件。
- 若按钮被按下(电平符合
按下状态(
OCS_DOWN
):- 若按钮释放,进入
OCS_UP
状态,记录释放时间。 - 若按下时间超过
_press_ms
,触发 “长按开始” 事件,进入OCS_PRESS
状态。
- 若按钮释放,进入
释放状态(
OCS_UP
):- 点击计数
_nClicks
加 1,进入OCS_COUNT
状态。
- 点击计数
计数状态(
OCS_COUNT
):- 若在
_click_ms
内再次按下按钮,回到OCS_DOWN
状态(继续计数)。 - 若超时(超过
_click_ms
)或达到最大点击数:- 1 次点击 → 触发
click
事件。 - 2 次点击 → 触发
doubleClick
事件。 - 3 次及以上 → 触发
multiClick
事件。 - 重置状态机至
OCS_INIT
。
- 1 次点击 → 触发
- 若在
长按状态(
OCS_PRESS
):- 若按钮释放,进入
OCS_PRESSEND
状态,触发 “长按结束” 事件。 - 持续按下时,每隔
_long_press_interval_ms
(默认 100ms)触发 “长按过程中” 事件。
- 若按钮释放,进入
长按结束状态(
OCS_PRESSEND
):- 触发 “长按结束” 事件后,重置状态机至
OCS_INIT
。
- 触发 “长按结束” 事件后,重置状态机至
6. 防抖处理(debounce
方法)
按钮机械结构会导致电平在按下 / 释放时产生抖动(短时间内多次跳变),debounce
方法通过以下逻辑过滤抖动:
- 记录上次电平变化时间(
_lastDebounceTime
)。 - 若当前电平与上次一致,且持续时间超过
_debounce_ms
,则认为电平稳定,更新有效电平(debouncedLevel
)。 - 若电平变化,则重置计时,等待稳定。
7. 核心驱动方法(tick
方法)
tick
是状态机的 “心脏”,需要在主循环(loop
)中频繁调用(如每 10ms 一次),功能包括:
- 读取按钮当前电平(或接受外部传入的电平)。
- 调用
debounce
方法获取稳定电平。 - 调用
_fsm
方法驱动状态机更新,实现事件检测。
总结
OneButton
库通过有限状态机和时间阈值,优雅地解决了按钮交互中的防抖、事件区分(单击 / 双击 / 长按等)问题。用户只需注册回调函数并在主循环中调用 tick
方法,即可轻松处理复杂的按钮交互,无需关心底层状态逻辑。
该库广泛应用于需要按钮输入的嵌入式项目(如智能家居控制面板、手持设备等),简化了代码复杂度,提高了交互可靠性。
下面是详细分析:
1.OneButton::OneButton() {
_pin = -1;
// further initialization has moved to OneButton.h
}
src/main.cppsrc/main.cpp这段代码是OneButton
类的默认构造函数,用于初始化OneButton
类的对象,具体解析如下:
1. 语法解析
OneButton::OneButton()
:表示这是OneButton
类的默认构造函数(无参数构造函数)。::
是作用域解析符,用于指明该函数属于OneButton
类。- 函数体内部:
_pin = -1;
表示将类的成员变量_pin
初始化为-1
。 - 注释
// further initialization has moved to OneButton.h
说明:原本可能在构造函数中进行的其他初始化操作,现在已转移到OneButton.h
头文件中(可能使用 C++ 的类内成员初始化语法)。
2. 成员变量_pin
的含义
结合代码库上下文(如src/main.cpp
中使用OneButton
处理按键),_pin
是OneButton
类中用于存储按键连接的 GPIO 引脚编号的成员变量。
- 初始化值
-1
通常表示 “未指定引脚” 或 “未初始化状态”(因为 GPIO 引脚编号一般为非负整数,如GPIO_NUM_14
)。 - 后续会通过其他方法(如
src/main.cpp
中的button.setup(PIN_BUTTON, INPUT_PULLUP, true);
)为_pin
设置实际的引脚值。
3. 初始化逻辑的迁移
注释表明 “进一步的初始化已移至OneButton.h
”,这通常是因为在 C++11 及以上标准中,支持类内成员初始化(在类定义中直接初始化成员变量),例如在OneButton.h
中可能有类似代码:
class OneButton {
private:int _pin = -1; // 类内初始化,替代构造函数中的部分初始化逻辑// 其他成员变量及声明...
public:OneButton(); // 构造函数声明// 其他方法...
};
这种方式可以简化构造函数的实现,将基础初始化逻辑集中在类定义中。
总结
该构造函数的核心作用是初始化OneButton
对象的引脚状态为 “未指定”(_pin = -1
),并通过注释说明其他初始化逻辑的位置,为后续的按键引脚配置(如setup
方法)提供基础状态。
2.
// Initialize the OneButton library.
OneButton::OneButton(const int pin, const bool activeLow, const bool pullupActive) {
setup(pin, pullupActive ? INPUT_PULLUP : INPUT, activeLow);
} // OneButton
这段代码是OneButton
类的带参数构造函数,用于初始化按键的硬件配置(引脚、输入模式、有效电平),具体解析如下:
1. 函数声明解析
OneButton::OneButton(...)
:表示这是OneButton
类的构造函数,用于创建OneButton
对象时初始化。- 参数列表:
const int pin
:按键连接的 GPIO 引脚编号(例如之前代码中的PIN_BUTTON = GPIO_NUM_14
)。const bool activeLow
:指定按键的有效电平(true
表示低电平有效,false
表示高电平有效)。const bool pullupActive
:是否启用引脚的内部上拉电阻(true
启用,false
不启用)。
2. 函数体逻辑
- 核心操作:调用
setup
方法完成初始化,传递三个参数:pin
:直接传入按键引脚号。pullupActive ? INPUT_PULLUP : INPUT
:根据pullupActive
决定引脚的输入模式:- 若
pullupActive = true
:引脚模式设为INPUT_PULLUP
(启用内部上拉电阻,此时按键未按下时引脚为高电平)。 - 若
pullupActive = false
:引脚模式设为INPUT
(普通输入模式,需外部接上下拉电阻)。
- 若
activeLow
:传入有效电平参数,用于后续判断按键状态(按下时的电平是否为有效信号)。
3. 作用与意义
- 该构造函数的核心是将按键的硬件配置参数传递给
setup
方法,由setup
方法完成具体的引脚初始化(如pinMode
配置)和状态变量设置。 - 结合
OneButton
库的功能(检测单击、双击、长按等事件),这里的初始化是后续按键事件检测的基础 —— 只有正确配置引脚模式和有效电平,才能准确判断按键的按下 / 松开状态。
示例场景
例如在src/main.cpp
中初始化按键时:
button.setup(PIN_BUTTON, INPUT_PULLUP, true);
若使用该带参构造函数,可直接在创建对象时完成配置:
OneButton button(PIN_BUTTON, true, true); // 等价于上面的setup调用
其中:
PIN_BUTTON
指定引脚,true
表示低电平有效(按键按下时引脚为低电平),true
表示启用内部上拉电阻。
综上,该构造函数是OneButton
类初始化的入口之一,通过封装硬件配置参数,简化了按键的初始化流程。