2024.1.23随堂笔记 按键驱动

Copyright (c) 2024 by Jsir2022, All Rights Reserved.

推荐一个开源工程,作者在稚晖君的智元机器人上班。纯C语言实现的一个按键驱动,可移植性强,支持单双击、连按、连按释放、长按;采用回调处理按键事件(自定义消抖时间),使用只需3步,1:创建按键,2:按键事件与回调处理函数链接映射。然后周期检查按键。

微分的思想

Pasted image 20240123000636.png

智能车元素实现表(状态机)

这是在23年智能车写下的各个元素响应代码,这样写的参数方便易改。当然,我是在23年蓝桥杯看了老师的页面回调函数表领会到的,24年蓝桥杯拿出来讲按键回调函数表更有意思了。(面向对象思想,抽象出它们的共同点,然后重构它使得简洁明了)

typedef struct
{
    uint8 State;//执行状态,如左转右转直行
    uint8 Step;//步骤序号
    int8 Angle;//目标旋转角度
    int16 Speed;//目标速度
    int16 Displace;//目标路程
} Response_type __attribute__((aligned(2)));
//然后用二维结构体指针统一说明
Response_type edata Response[6][5] = {
	// Block
	{{TURN_LEFT, 0, 70, 60, 8000},
	 {TURN_RIGHT, 1, -70, 90, 7600},
	 {TURN_RIGHT, 2, -50, 90, 7200},
	 {TURN_LEFT, 3, 70, 90, 400},
	 {QUIT, 0, 0, 0, 0}},
	// Round_In
	{{GO_STRAIGHT, 0, -14, 150, 4600},
	 {TURN_LEFT, 1, 44, 150, 4000},
	 {QUIT, 0, 0, 0, 0}},
	// Round_Out
	{{TURN_LEFT, 0, 0, 150, 9000},
	 {QUIT, 0, 0, 0, 0}},
	// Into_The_Garage_And_Stop
	{{GO_STRAIGHT, 0, 0, 10, 2000},
	 {GO_STRAIGHT, 1, 0, -80, -1},
	 {TURN_LEFT, 2, 90, -80, -4600},
	 {QUIT, 0, 0, 0, 0}},
	// Turn_Right_Into_The_Garage
	{{TURN_RIGHT, 0, -106, 70, 10000},
	 {QUIT, 0, 0, 0, 0}},
	// Start_And_Out_Of_The_Garage
	{{GO_STRAIGHT, 0, 0, 150, 1000},
	 {TURN_RIGHT, 1, -50, 150, 2000},
	 {QUIT, 0, 0, 0, 0}}};

多功能按键(状态机)

23年智能车对按键参数、按键状态和按键对象的代码。

#define BTN_DOUBLE_CLICK_TIME 100 // 双击间隔时间
#define BTN_LONG_CLICK_TIME 200   // 长按起步时间
#define BTN_SCAN_TIME 2           // 按键扫描时间(单位ms)

typedef enum
{
  BUTTON_CLICKED = 0,
  BUTTON_DOUBLE,
  BUTTON_LONG,
  BUTTON_NONE = 0xff
} Button_Event;

typedef struct
{
  uint8 btnStatus;
  int8 encoRealVal;
  uint16 btnSustainTime;
  uint16 btnSpacingTime;
} EC11_type;

extern EC11_type EC11;

void EC11_Init(void);
uint8 EC11_Process(void);

按键状态轮询函数,是状态机思想,但没消抖。

uint8 EC11_Process(void)
{
    static bool PrevState, ClickState;

    if (!EC11_BTN_Pin)
    {
        if (PrevState)
        {
            EC11.btnSustainTime += BTN_SCAN_TIME;
            if (EC11.btnSustainTime >= BTN_LONG_CLICK_TIME)
            {
                ClickState = 0;
                // 长按
                EC11.btnSustainTime = 0;
                return BUTTON_LONG;
            }
            return BUTTON_NONE;
        }
        else
        {
            ClickState++;
            PrevState = TRUE;
            EC11.btnSpacingTime = 0;
            EC11.btnSustainTime = 0;
            return BUTTON_NONE;
        }
    }

    if (ClickState == 1)
    {
        if (EC11.btnSpacingTime >= BTN_DOUBLE_CLICK_TIME)
        {
            // 单击
            ClickState = 0;
            return BUTTON_CLICKED;
        }
    }
    else if (ClickState >= 2)
    {
        if (EC11.btnSpacingTime <= BTN_DOUBLE_CLICK_TIME)
        {
            // 双击
            ClickState = 0;
            return BUTTON_DOUBLE;
        }
    }

    EC11.btnSpacingTime += BTN_SCAN_TIME;
    PrevState = FALSE;
    return BUTTON_NONE;
}

回调函数

用法我大概理解,会以面向对象的思想重写一份按键的代码。有文章介绍链接

优点:

  1. 提高代码的复用性和灵活性:回调函数可以将一个函数作为参数传递给另一个函数,从而实现模块化编程,提高代码的复用性和灵活性。
  2. 解耦合:回调函数可以将不同模块之间的关系解耦,使得代码更易于维护和扩展。
  3. 可以异步执行:回调函数可以在异步操作完成后被执行,这样避免了阻塞线程,提高应用程序的效率。

缺点:

  1. 回调函数嵌套过多会导致代码难以维护:如果回调函数嵌套层数过多,代码会变得非常复杂,难以维护。
  2. 回调函数容易造成竞态条件:如果回调函数中有共享资源访问,容易出现竞态条件,导致程序出错。
  3. 代码可读性差:回调函数的使用可能会破坏代码的结构和可读性,尤其是在处理大量数据时。

小结:代码灵活、易于扩展,但是不易于阅读、容易出错。