在上一章节“【蓝桥杯嵌入式】状态机按键”中使用的按键程序中,按键间具有优先性(如B1按下后,B2~B4的按下将不会被识别),同时不支持双键、长短按。在这一章节中,我们升级一下,采用独立的按键检测,并实现下长短按。
一、按键的独立化
我们针对每一个按键都进行了独立的状态检测和处理,定义了一个结构体,一个数组用来存储按键的不同信息。
typedef enum
{
IDLE, // 未按下,空闲状态
PRESSED_READY, // 按下,待执行相应功能
PRESSED_FINISH, // 按下,功能已执行,等待松开按键回到空闲状态
PRESSED_COUNT // 按下,处于计时状态(用于长短按)
} ButtonState; // 按键状态
// 定义一个函数类型
typedef void (*Function)(void);
typedef struct
{
GPIO_TypeDef *port; // 按键对应的GPIO端口
uint16_t pin; // 按键对应的引脚
ButtonState key_state; // 按键的状态(空闲、按下、计时等)
unsigned char long_mode; // 按键长按模式(用于标记长按模式,1为长短按键)
unsigned int pressed_time; // 按键按下的持续时间(用于长按计时)
Function keyFunction; // 按键短按功能对应的函数指针
Function keyFunctionLong; // 按键长按功能对应的函数指针
} key_GPIO; // 按键对应信息
// 按键对应的IO口信息及功能回调函数
key_GPIO key[4];
函数指针允许我们将函数的地址存储在结构体的成员中,以便在需要时调用相应的函数。我们定义了函数指针类型Function
,用于存储按键的短按功能和长按功能对应的函数地址。在初始化 key_GPIO
结构体数组时,为每个按键的 keyFunction
和 keyFunctionLong
成员赋值为相应的函数名。这样,每个按键的结构体中就包含了对应的函数指针,可以根据按键的状态调用相应的功能函数。
// 按键对应的IO口信息及功能回调函数
key_GPIO key[4] = {
{GPIOB,GPIO_PIN_0,IDLE,0,0,function_B1},
{GPIOB,GPIO_PIN_1,IDLE,0,0,function_B2},
{GPIOB,GPIO_PIN_2,IDLE,0,0,function_B3},
{GPIOA,GPIO_PIN_0,IDLE,1,0,function_B4,function_B4_long}
};
按键的功能与上一章节基本一致,只是把B4按键改成了一个长短按键,短按:翻转LD7状态,长按:熄灭LD1~LD7。
// 按键B1的功能实现
void function_B1()
{
led_addr = led_addr | 0x03; // 更新LED位置
led_disp(led_addr); // 控制LED显示
}
// 按键B2的功能实现
void function_B2()
{
led_addr = led_addr | 0x0C; // 更新LED位置
led_disp(led_addr); // 控制LED显示
}
// 按键B3的功能实现
void function_B3()
{
led_addr = led_addr ^ 0x30; // 更新LED位置
led_disp(led_addr); // 控制LED显示
}
// 按键B4的功能实现
void function_B4()
{
led_addr = led_addr ^ 0x40; // 更新LED位置
led_disp(led_addr); // 控制LED显示
}
// 按键B4长按的功能实现
void function_B4_long()
{
led_addr = led_addr & 0x80; // 更新LED位置
led_disp(led_addr); // 控制LED显示
}
二、状态机程序
与之前的状态机类似,检测都是满足一定条件就进入下一个状态
检测按键当前的状态(key_state
):
- 如果是空闲状态(
IDLE
),检测是否有按键按下,若有则将状态切换为PRESSED_READY
。 - 如果是按下待执行状态(
PRESSED_READ