STM32的GPIO_蓝桥杯按键实验_实现松手检测/长按短按/单击双击
按键实验简介
按键实验原理
按键的硬件结构
蓝桥杯开发板上的按键默认为抬起状态,此时经上拉电阻接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;//清零标志位
}
}
