STM32的GPIO_蓝桥杯按键实验_实现松手检测/长按短按/单击双击

2025-10-14 02:07:21 1477

按键实验简介

按键实验原理

按键的硬件结构

蓝桥杯开发板上的按键默认为抬起状态,此时经上拉电阻接VDD,连接引脚状态为高电平;按下后线路导通接地,转变为低电平

检测按键的方法

使用查询法,不断读入按键相连GPIO输入状态,从而判断按键的状态

按键的GPIO引脚配置

按键的GPIO引脚连接

开发板有4个独立按键B1-B4,分别连接PB0,PB1,PB2,PA0

按键的GPIO初始化配置

将PB0,PB1,PB2,PA0配置为GPIO_Input

按键的使用

按键使用的HAL库函数

读取引脚电平状态HAL_GPIO_ReadPin

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)

/*

GPIOx:端口号,如GPIOA,GPIOB,GPIOC……

GPIO_Pin:引脚号,如GPIO_PIN_0,GPIO_PIN_1,GPIO_PIN_2……

返回值:GPIO_PinState,即引脚状态GPIO_PIN_SET或GPIO_PIN_RESET

*/

代码实现

按键查询函数

也就是通过不断查询按键引脚电平状态,来判断按键是否被按下了

uint8_t Key_Scan(void)

{

uint8_t key_val=0;

if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET)

{

HAL_Delay(20);//加入延时去抖的写法,也可不写

if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET)

{

key_val=1;

}

}

if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==GPIO_PIN_RESET)

{

HAL_Delay(20);

if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==GPIO_PIN_RESET)

{

key_val=2;

}

}

if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==GPIO_PIN_RESET)

{

HAL_Delay(20);

if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==GPIO_PIN_RESET)

{

key_val=3;

}

}

if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)

{

HAL_Delay(20);

if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)

{

key_val=4;

}

}

return key_val;//返回key_val来判断按下的是哪一个键

}

//在main函数中使用时,注意C语言参数传递不传变量本身的特性:

key_val=Key_Scan();

定时查询法

例如将按键扫描改为定时扫描法,减少对资源的占用

//使用flag标记按键的扫描

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

//0.001秒定时器中断,也可放在Systick中

{

flag=1;

//不直接把按键检测函数放在中断中,避免停留在中断内太久妨碍其他程序执行

}

void key_scan()

{

if(flag==1)

{

//按键的检测

}

flag=0;

}

功能拓展

带有松手检测的完整按键处理函数

按键松手检测

//为按下按键增加抬手检测功能

uint8_t key_val;//读取按键值

uint8_t key_down;//按键下降沿检测,只在按键按下瞬间为按键值,其他时刻全为0

uint8_t key_up;//按键上升沿检测,只在按键抬起瞬间为按键值,其他时刻全为0

uint8_t key_old;//保存上一次检测按键值

void Key_Proc(void)

{

key_val = Key_Scan();//查询哪一个键被按下

key_down = key_val & (key_val ^ key_old);

//新旧键值异或,两者不同时得1,由此检测哪些键发生了变化

//与当前键值作按位与运算,只有按键被按下的情况下才保留变化位

//这样就把那些被按下的按键的键值保存到key_up

key_up = ~key_val & (key_val ^ key_old);

//同上,但加入取反符号,检测哪些按键被释放,把释放的按键保存到key_up

key_old = key_val;//保存当前的按下的键值用以下一次判断

if(key_down==1)

{

//此处写按键1按下后执行的操作

}

}

按键处理函数用例

void Key_Proc(void)

{

key_val=Key_Scan();

key_down = key_val & (key_val^key_old);

key_up = ~key_val & (key_val^key_old);

key_old=key_val;

if(key_down==3)//如果key3被按下

{

enled=0x01;

}

if(key_up==3)//如果key3松开

{

enled=0x00;

}

}

区分更多按键操作

长按与短按

在按键处理函数中增加一个计时变量(使用SysTick实现),按下后开始计时,超过某个时间没有抬起就是长按

void Key_Proc(void)

{

key_val=Key_Scan();

key_down = key_val & (key_val^key_old);

key_up = ~key_val & (key_val^key_old);

key_old=key_val;

if(key_down)//按下后开始使用SysTick计时

{

uskey=0;

}

if(uskey<1000)//1s内抬起为短按,务必注意uskey的数据类型取值是否足够

{

if(key_up==1)

{

//按键1短按操作

}

if(key_up==2)

{

//按键2短按操作

}

if(key_up==3)

{

//按键3短按操作

}

if(key_up==4)

{

//按键4短按操作

}

}

else//1s后未抬起为长按

{

if(key_val==1)

{

//按键1长按操作

}

if(key_val==2)

{

//按键2长按操作

}

if(key_val==3)

{

//按键3长按操作

}

if(key_val==4)

{

//按键4长按操作

}

}

}

单击与双击

在区分长按短按基础上,再增加检测规定时间内是否有再次按下同一按键

但是当按键抬起后变量key_up会清零,无从判断两次点击按键值是否相同,所以要增添一个临时变量保存按键值

计时(用以检测是否有第二次按下的行为)并非一直运行,而是在上电后第一次按下,双击结束后的下一次按下以及超时后(当前操作为单击)的下一次按下才需要计时,计时的启停通过设置标志位实现

void Key_Proc(void)

{

key_val=Key_Scan();

key_down = key_val & (key_val^key_old);

key_up = ~key_val & (key_val^key_old);

key_old=key_val;

if(key_up)//如果有按键处在抬起阶段

{

key_temp=key_up;//记下这个键

if(key_flag==0)//key_flag为0表示没有按键抬起,可以计时

{

uskey=0;

key_flag=1;//将标志位改为1以表示有按键抬起并开始计时

}

else//key_up且key_flag=1,代表双击结束后的第二次上升沿

key_flag=0;

}

if(key_flag==1)//如果在计时状态(已经按下了一次)

{

if(uskey<300)//300ms内有按键按下为双击

{

if(key_down==1&&key_temp==1)

{

//按键1双击操作

}

if(key_down==2&&key_temp==2)

{

//按键2双击操作

}

if(key_down==3&&key_temp==3)

{

//按键3双击操作

}

if(key_down==4&&key_temp==4)

{

//按键4双击操作

}

}

else//300ms内无按键为单击

{

if(key_temp==1)

{

//按键1单击操作

}

if(key_temp==2)

{

//按键2单击操作

}

if(key_temp==3)

{

//按键3单击操作

}

if(key_temp==4)

{

//按键4单击操作

}

key_flag=0;//清零标志位

}

}