2024.1.23随堂笔记 按键驱动
Copyright (c) 2024 by Jsir2022, All Rights Reserved.
推荐一个开源工程,作者在稚晖君的智元机器人上班。纯C语言实现的一个按键驱动,可移植性强,支持单双击、连按、连按释放、长按;采用回调处理按键事件(自定义消抖时间),使用只需3步,1:创建按键,2:按键事件与回调处理函数链接映射。然后周期检查按键。
微分的思想
智能车元素实现表(状态机)
这是在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;
}
回调函数
用法我大概理解,会以面向对象的思想重写一份按键的代码。有文章介绍链接
优点:
- 提高代码的复用性和灵活性:回调函数可以将一个函数作为参数传递给另一个函数,从而实现模块化编程,提高代码的复用性和灵活性。
- 解耦合:回调函数可以将不同模块之间的关系解耦,使得代码更易于维护和扩展。
- 可以异步执行:回调函数可以在异步操作完成后被执行,这样避免了阻塞线程,提高应用程序的效率。
缺点:
- 回调函数嵌套过多会导致代码难以维护:如果回调函数嵌套层数过多,代码会变得非常复杂,难以维护。
- 回调函数容易造成竞态条件:如果回调函数中有共享资源访问,容易出现竞态条件,导致程序出错。
- 代码可读性差:回调函数的使用可能会破坏代码的结构和可读性,尤其是在处理大量数据时。
小结:代码灵活、易于扩展,但是不易于阅读、容易出错。