打印
[经验分享]

基于状态机的按键扫描的实现

[复制链接]
楼主: elsaflower
手机看帖
扫描二维码
随时随地手机跟帖
41
robincotton| | 2025-2-14 19:36 | 只看该作者 回帖奖励 |倒序浏览
在设计状态机时,考虑到未来可能会有新功能的添加或按键数量的增加,预留一些扩展接口和状态空间,以便能够方便地进行功能扩展和升级。

使用特权

评论回复
42
sdlls| | 2025-2-14 19:59 | 只看该作者
基于状态机的按键扫描实现需要注意状态定义与划分、消抖处理、长按与短按区分、系统资源利用以及代码实现与测试等方面。

使用特权

评论回复
43
youtome| | 2025-2-14 21:01 | 只看该作者
消抖是按键扫描中非常重要的一步,可以防止按键抖动引起的误判。通常通过延时一定时间(如20ms)后再次检查按键电平来实现。

使用特权

评论回复
44
biechedan| | 2025-2-14 21:17 | 只看该作者
常见的消抖算法有软件延时消抖和定时器消抖。软件延时消抖会阻塞程序的执行,影响系统的实时性;定时器消抖则通过定时器定时来检测按键状态,不会阻塞程序,更适合对实时性要求较高的应用。

使用特权

评论回复
45
robincotton| | 2025-2-14 21:38 | 只看该作者
如果涉及到时间相关的状态转换,如长按、短按的区分,需要精确设置时间参数。这些时间参数应根据实际应用的需求和按键的特性来确定,以确保状态转换的准确性和稳定性。

使用特权

评论回复
46
abotomson| | 2025-2-14 22:10 | 只看该作者
在不同事件发生时,状态如何转移。例如,当检测到按键按下事件时,从初始状态转移到按下状态。

使用特权

评论回复
47
kkzz| | 2025-2-14 22:31 | 只看该作者
按键状态机通常包括未按下、确认按下、稳定按下和释放等状态。合理设计状态数量,避免过于复杂的状态转换逻辑。

使用特权

评论回复
48
kkzz| | 2025-2-17 10:17 | 只看该作者
在状态机中要设计相应的逻辑来处理长按和短按事件。可以在按键按下状态下启动一个定时器,当定时器计时达到长按时间阈值时,进入长按状态并执行长按操作对应的程序;若在达到阈值之前按键释放,则执行短按操作对应的程序。

使用特权

评论回复
49
pixhw| | 2025-2-17 10:43 | 只看该作者
为了区分按键的短按和长按操作,需要设定一个长按时间阈值。当按键按下时间超过该阈值时,判定为长按操作;否则,判定为短按操作。长按时间阈值的设定要根据具体应用需求来确定,例如在一些设备的菜单操作中,长按时间可设置为 1 - 2 秒。

使用特权

评论回复
50
minzisc| | 2025-2-17 10:58 | 只看该作者
明确每个状态的转换逻辑,确保状态机能够正确处理各种按键操作。例如:

当检测到按键按下时,从IDLE状态进入DEBOUNCE状态。
在DEBOUNCE状态中,如果电平保持低,则进入PRESSED状态。
在PRESSED状态中,如果电平保持低超过一定时间,则进入LONG_PRESS状态。
如果电平恢复高,则根据当前状态进入相应的释放状态。

使用特权

评论回复
51
sanfuzi| | 2025-2-17 12:10 | 只看该作者
如果系统中有多个按键,需要设计能够处理多个按键同时被按下的情况。
可以为每个按键单独设计状态机,或者设计一个统一的状态机来处理所有按键。

使用特权

评论回复
52
yorkbarney| | 2025-2-17 12:23 | 只看该作者
基于状态机的按键扫描实现需要注意状态定义、输入事件、动作和次态、消抖处理、定时器、状态转换逻辑、多键组合、错误处理和代码结构等方面。

使用特权

评论回复
53
linfelix| | 2025-2-17 13:05 | 只看该作者
将按键扫描代码模块化,便于维护和扩展。
定义清晰的接口函数,用于与外部模块进行交互。

使用特权

评论回复
54
mickit| | 2025-2-17 13:20 | 只看该作者
在需要低功耗的应用中,按键扫描应尽量减少CPU的占用时间。可以使用中断机制来检测按键事件,而不是持续轮询。

使用特权

评论回复
55
abotomson| | 2025-2-17 13:44 | 只看该作者
状态机的实现可能需要使用定时器来进行消抖、长按计时等操作。要合理分配定时器资源,避免多个状态机或其他功能模块争夺同一个定时器,导致系统运行不稳定。

使用特权

评论回复
56
mmbs| | 2025-2-17 14:23 | 只看该作者
按键在按下和释放时会产生机械抖动,可能导致多次触发按键事件。通过状态机进入消抖状态来处理抖动,消抖时间的设置非常关键。消抖时间过短,无法有效消除抖动;消抖时间过长,则会影响按键响应的及时性。一般来说,消抖时间设置在 10 - 20ms 较为合适,但具体时间可根据按键的特性和实际应用场景进行调整。

使用特权

评论回复
57
jonas222| | 2025-2-17 15:00 | 只看该作者
为状态机的关键部分添加详细的注释,解释每个状态的含义、转换条件以及相应的操作。同时,编写相关的文档说明按键扫描功能的实现原理和使用方法,方便其他开发人员理解和使用。

使用特权

评论回复
58
jimmhu| | 2025-2-17 15:11 | 只看该作者
在S2状态,通过计时器来判断是否进入长按状态(如S4)。若未进入长按状态且按键释放,则返回S0状态。

使用特权

评论回复
59
cemaj| | 2025-2-17 15:47 | 只看该作者
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

// 定义状态
typedef enum {
    IDLE,
    DEBOUNCE,
    PRESSED,
    SHORT_PRESS,
    LONG_PRESS
} State;

// 定义事件
typedef enum {
    KEY_DOWN,
    KEY_UP
} Event;

// 定义按键状态机
typedef struct {
    State currentState;
    uint32_t debounceTimer;
    uint32_t longPressTimer;
} KeyStateMachine;

// 初始化状态机
void KeyStateMachine_Init(KeyStateMachine *sm) {
    sm->currentState = IDLE;
    sm->debounceTimer = 0;
    sm->longPressTimer = 0;
}

// 检测按键电平
bool IsKeyPressed(void) {
    // 模拟读取按键电平
    // 返回true表示按键按下,false表示未按下
    return true; // 替换为实际读取按键电平的代码
}

// 状态机处理函数
void KeyStateMachine_Process(KeyStateMachine *sm) {
    Event event = IsKeyPressed() ? KEY_DOWN : KEY_UP;

    switch (sm->currentState) {
        case IDLE:
            if (event == KEY_DOWN) {
                sm->currentState = DEBOUNCE;
                sm->debounceTimer = 20; // 消抖时间20ms
            }
            break;

        case DEBOUNCE:
            if (event == KEY_DOWN && --sm->debounceTimer == 0) {
                sm->currentState = PRESSED;
                sm->longPressTimer = 1000; // 长按时间1000ms
            } else if (event == KEY_UP) {
                sm->currentState = IDLE;
            }
            break;

        case PRESSED:
            if (event == KEY_UP) {
                sm->currentState = SHORT_PRESS;
                // 执行短按操作
                printf("Short press detected\n");
            } else if (--sm->longPressTimer == 0) {
                sm->currentState = LONG_PRESS;
                // 执行长按操作
                printf("Long press detected\n");
            }
            break;

        case SHORT_PRESS:
            if (event == KEY_DOWN) {
                sm->currentState = PRESSED;
                sm->longPressTimer = 1000; // 重置长按时间
            }
            break;

        case LONG_PRESS:
            if (event == KEY_UP) {
                sm->currentState = IDLE;
            }
            break;

        default:
            sm->currentState = IDLE;
            break;
    }
}

int main() {
    KeyStateMachine sm;
    KeyStateMachine_Init(&sm);

    while (1) {
        KeyStateMachine_Process(&sm);
        // 模拟延时
        for (volatile uint32_t i = 0; i < 1000000; i++);
    }

    return 0;
}

使用特权

评论回复
60
olivem55arlowe| | 2025-2-17 16:11 | 只看该作者
在检测到按键按下或释放时,通过软件延时(如10ms或20ms)来消除抖动。
延时期间不进行按键状态判断,以避免误判。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则