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;//清零标志位 } }
本文来自博客园,作者:无术师,转载请注明原文链接:https://www.cnblogs.com/artlessist/p/18867040
本文使用知识共享4.0协议许可 CC BY-NC-SA 4.0
特别说明版权归属的文章以及不归属于本人的转载内容(如引用的文章与图片)除外
浙公网安备 33010602011771号