目录
原理图解析
设置STM32CubeMX
按键配置
定时器配置
手搓代码
中断回调(服务)函数
按键判断函数
按键单击判断函数
按键双击判断函数
按键长按判断函数
正文
原理图解析
我们以PB1为例来分析,假如按键没有被按下,那么PB1的电平就与左上角的VDD相等,也就是PB1 = 1;如果按键被按下,那么右下角的接地就会被导通,PB1的电平3就与GND相等,也就是PB1 = 2。
这样我们就能通过以上原理对按键进行判断,但是按键判断是一个事件触发程序,所以我们要使用定时器来使开发板能在任意时间都能对按键进行判断。因此我们再下一步设置STM32CubeMX中需要对定时器初始化。
设置STM32CubeMX
按键配置
首先我们先根据按键的原理图配置好引脚,需要注意的是,再前几篇文章中讲LCD与LED我们讲引脚都是设置为GPIO_Output,但是按键的四个引脚,我们需要设置为GPIO_input,如图:
然后在左侧选择GPIO中的按键的四个引脚,上下拉模式设为上拉,为以下状态:
定时器配置
如果大家有去做过省赛或国赛题目,就都会看到过对按键响应时间是有要求的,一般都是响应时间在0.1秒内,所以我们的定时器可以就设置为0.01秒。
以下是使能中断
手搓代码
中断回调(服务)函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
以下就是中断回调函数,很长,记不住怎么办。别担心,还有办法,我们打开keil5,在Project下找到stm32g4xx_it.c,在里面找到stm32g4xx_hal-tim.h并打开他,翻到最下面,再往上滑一点点,大概两千五百多行的位置,就可以找到这个函数啦,我们就可以直接复制使用。
按键判断函数
实现创建一个按键的结构体,其含义写在注释中了:
struct keys { uchar judge_sta; //判断按键按键按下的动作到了第几步 bool key_sta; //如果按键被按下,为0 bool key_flag; //如果确认被按下,为1 };
具体实现按键判断函数的思路:
1.判断中断回调函数收到的中断信号是不是我们刚刚给按键设置的定时器3的信号,如果是就进入按键判断函数
2.读取每个按键这一时刻的电平
3.判断有哪些按键为按下的状态(低电平)
4.按键抖动判断
5.状态重置
按键单击判断函数
struct keys key[4]={0,0,0,0}; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance==TIM3) //判断中断信号是否来自定时器3 { key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0); //读按键PB0现在的状态,如果被按下,PB0 = 0;如果没有被按下,PB0 = 1; key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1); key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2); key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0); for(int i = 0;i < 4; i++ ) //确认是哪个或哪些按键被按下了 { switch (key[i].judge_sta) { case 0: { if(key[i].key_sta==0) key[i].judge_sta = 1; //第一次判断是否按下 } break; case 1: { if(key[i].key_sta==0) //进入下一次定时器扫描,按键还是按下状态,那么就确认为按下,以此来消抖 { key[i].judge_sta = 2; key[i].key_flag = 1; } else //否则就是抖动,本次不算按键被按下 key[i].judge_sta = 0; } break; case 2: { if(key[i].key_sta==1) key[i].judge_sta = 0; //判断是否松手,松手后按键状态重置 } break; } } } }
按键双击判断函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance==TIM3) //判断中断信号是否来自定时器3 { key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0); //读按键PB0现在的状态,如果被按下,PB0 = 0;如果没有被按下,PB0 = 1; key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1); key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2); key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0); for(int i = 0;i < 4; i++ ) //确认是哪个或哪些按键被按下了 { switch (key[i].judge_sta) { case 0: { if(key[i].key_sta==0) key[i].judge_sta = 1; //第一次判断是否按下 } break; case 1: { if(key[i].key_sta==0) //进入下一次定时器扫描,按键还是按下状态,那么就确认为按下,以此来消抖 { if(a == i && key[a].key_time < 70) //小于70,说明上次按下后到这次按下时间间隔小于0.7秒 { key[i].double_key_flag = 1; //这是一次双击事件 } else { key[i].key_flag = 1; a = i; //记录这一次是上面按键被按下 } key[i].judge_sta = 2; } else //否则就是抖动,本次不算按键被按下 key[i].judge_sta = 0; } break; case 2: { if(key[i].key_sta==1) key[i].judge_sta = 0; //判断是否松手,松手后按键状态重置 key[i].key_time = 0; } break; } } key[a].key_time++; //第一次被按下之后,开始计时 } }
按键长按判断函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance==TIM3) { key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0); key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1); key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2); key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0); for(int i = 0;i < 4; i++ ) { switch (key[i].judge_sta) { case 0: { if(key[i].key_sta==0) { key[i].judge_sta = 1; //第一次判断是否按下 key[i].key_time = 0; } } break; case 1: { if(key[i].key_sta==0) //进入下一次定时器扫描,按键还是按下状态,那么就确认为按下,以此来消抖 { key[i].judge_sta = 2; } else key[i].judge_sta = 0; } break; case 2: { if(key[i].key_sta==1) //判断是否松手 { if(key[i].key_time < 100) { key[i].key_flag = 1; } // if(key[i].key_time > 100) //一次扫描10毫秒,100次1000毫秒,就是判断是否长按超过1000毫秒 // //松手后,才会执行相应反应 // { // key[i].long_flag = 1; // } key[i].judge_sta = 0; } else { key[i].key_time++; if(key[i].key_time > 100) //一次扫描10毫秒,100次1000毫秒,就是判断是否长按超过1000毫秒//未松手时,就会执行相应反应 { key[i].long_flag = 1; } } } break; } } } }