【STM32】蓝桥杯嵌入式开发板实现按键单击、双击、长按(通用模板)
按键实现单击、双击、长按
前言
基于蓝桥杯嵌入式开发板实现按键的单击,双击,长按检测与处理,使用定时器后台检测,防止占用前台资源,可以随便移植到任何单片机上。
一、硬件原理图

二、构造按键结构体
#define KEY_B1 HAL_GPIO_ReadPin(KEY_B1_GPIO_Port, KEY_B1_Pin)
#define KEY_B2 HAL_GPIO_ReadPin(KEY_B2_GPIO_Port, KEY_B2_Pin)
#define KEY_B3 HAL_GPIO_ReadPin(KEY_B3_GPIO_Port, KEY_B3_Pin)
#define KEY_B4 HAL_GPIO_ReadPin(KEY_B4_GPIO_Port, KEY_B4_Pin)
/* 按键长按检测时间(单位:次数) 一次为定时器扫描一次的时间 */
#define KEY_LONG_PRESS_TIME 80
/* 按键双击间隔时间(单位:次数) 一次为定时器扫描一次的时间 */
#define KEY_DOUBLE_GAP_TIME 40
enum KEY_STATE
{
key_state_0 = 0,
key_state_1 = 1,
key_state_2 = 2,
key_state_3 = 3
};
enum KEY_EVENT
{
key_no = 0, /* 无按键按下 */
key_click = 1, /* 单击 */
key_double = 2, /* 双击 */
key_long = 3 /* 长按 */
};
struct key{
bool key_input_val;
enum KEY_STATE key_state_buff1; /* 按键执行状态1 */
enum KEY_STATE key_state_buff2; /* 按键执行状态2 */
enum KEY_EVENT key_real_result; /* 最终判断结果 */
unsigned char key_time_cnt1; /* 定时器1 */
unsigned char key_time_cnt2; /* 定时器2 */
};
三、在定时器中断回调函数中检测
static enum KEY_EVENT key_driver(int key_num)
{
enum KEY_EVENT key_result = key_no;
switch (key[key_num].key_state_buff1)
{
case key_state_0:
if (key[key_num].key_input_val == 0)
{
key[key_num].key_state_buff1 = key_state_1;
/* 如果按键被按下,状态切换到按键消抖和确认状态 */
}
break;
case key_state_1:
if (key[key_num].key_input_val == 0)
{
key[key_num].key_time_cnt1 = 0;
key[key_num].key_state_buff1 = key_state_2;
/* 按键仍处于按下状态 */
/* 消抖完成,计时器key_time_cnt1 开始计时 */
/* 状态切换到计时状态 */
}
else
key[key_num].key_state_buff1 = key_state_0;
break;
case key_state_2:
if (key[key_num].key_input_val == 1)
{
key_result = key_click; /* 按键抬起,产生一次click操作 */
key[key_num].key_state_buff1 = key_state_0;
}
else if (++key[key_num].key_time_cnt1 >= KEY_LONG_PRESS_TIME)
{ /* 按键继续按下超过1000ms */
key_result = key_long; /* 返回长按操作 */
key[key_num].key_state_buff1 = key_state_3; /* 状态切换到按键释放状态 */
}
break;
case key_state_3:
if (key[key_num].key_input_val == 1)
/* 没按键按下了,回正常状态,防止按一次检测出两次单击 */
key[key_num].key_state_buff1 = key_state_0;
break;
}
return key_result;
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM3)
{
enum KEY_EVENT key_result;
key[0].key_input_val = KEY_B1;/* 读取按键值 */
key[1].key_input_val = KEY_B2;
key[2].key_input_val = KEY_B3;
key[3].key_input_val = KEY_B4;
for (int i = 0; i < 4; i++)
{
key_result = key_driver(i);
switch (key[i].key_state_buff2)
{
case key_state_0:
if (key_result == key_click)
{
key[i].key_time_cnt2 = 0;
key[i].key_state_buff2 = key_state_1;
/* 第一次单击,不返回,到下一个状态判断是否有双击 */
}
else
key[i].key_real_result = key_result;
/* 对于无键,长按,返回原事件 */
break;
case key_state_1:
if (key_result == key_click) /* 又一次单击,时间间隔小于500ms */
{
key[i].key_real_result = key_double; /* 返回双击事件 */
key[i].key_state_buff2 = key_state_0;
}
else if (++key[i].key_time_cnt2 >= KEY_DOUBLE_GAP_TIME)
{
key[i].key_real_result = key_click;
/* 500ms内没有再次出现单击事件,返回单击事件 */
key[i].key_state_buff2 = key_state_0;
}
break;
}
}
}
}
四、按键处理函数
void key_proc()
{
if (key[0].key_real_result == key_click)
{
LCD_ClearLine(Line1);
LCD_DisplayStringLine(Line1, (uint8_t *)" key0_click ");
key[0].key_real_result = key_no;/* 清除按键事件,置为无键事件 */
}
else if (key[0].key_real_result == key_double)
{
LCD_ClearLine(Line1);
LCD_DisplayStringLine(Line1, (uint8_t *)" key0_double ");
key[0].key_real_result = key_no;
}
else if (key[0].key_real_result == key_long)
{
LCD_ClearLine(Line1);
LCD_DisplayStringLine(Line1, (uint8_t *)" key0_long ");
key[0].key_real_result = key_no;
}
if (key[1].key_real_result == key_click)
{
LCD_ClearLine(Line2);
LCD_DisplayStringLine(Line2, (uint8_t *)" key1_click ");
key[1].key_real_result = key_no;
}
else if (key[1].key_real_result == key_double)
{
LCD_ClearLine(Line2);
LCD_DisplayStringLine(Line2, (uint8_t *)" key1_double ");
key[1].key_real_result = key_no;
}
else if (key[1].key_real_result == key_long)
{
LCD_ClearLine(Line2);
LCD_DisplayStringLine(Line2, (uint8_t *)" key1_long ");
key[1].key_real_result = key_no;
}
}
五、现象
作者:ssq不是上上签