在进行按键开发时首先来了解轮询和中断两种工作模式。
轮询模式
很多I/O设备都有一个状态寄存器,用于描述设备当前的工作状态,每当设备状态发生改变时,设备将修改相应状态寄存器位。通过不断查询设备的状态寄存器,CPU就可以了解设备的状态,从而进行必要的I/O操作。为了节约CPU资源,查询工作往往不是连续的,而是定时进行,即轮询即间隔一段时间去判断寄存器的状态。
轮询方式具有简单、易实现、易控制等优势,在对实时面感性不高,且有大量CPU资源中大量应用。比如说功能较为单一的单片机中就大量使用轮询模式。轮询方式主要存在以下不足:
增加系统开销。无论是任务轮询还是定时器轮询都需要消耗对应的系统资源。
无法及时感知设备状态变化。在轮询间隔内的设备状态变化只有在下次轮询时才能被发现,这将无法满足对实时性敏感的应用场合。
浪费CPU资源。无论设备是否发生状态改变,轮询总在进行。在实际情况中,大多数设备的状态改变通常不会那么频繁,轮询空转将白白浪费CPU时间片。
中断模式
中断就是由硬件或者软件发出的一种IRQ(中断请求)信号,一旦CPU接收到中断信号,CPU就会暂停当前执行的任务,并且保留现场,去响应外设的中断请求。
中断的工作流程:CPU安装中断处理程序,发生中断,保存现场,识别中断,判断中断号,执行中断服务处理程序,返回现场。
了解了这两种工作模式后我们使用这两种对进行开发。
根据电路图可知按键通过PB12\13\14IO端口进行控制,这些端口通过一个上拉电阻将按键的电压钳制于高电平,当按键按下时端口则为低电平。
因此对端口进行如下设置:
1、将端口设置为输出模式(以PB12为例)
2、设置内部上拉,并对管脚进行命名
4、后续其他设置同跑马灯设置,后生成代码。
3、在main.c中while()循环中添加如下代码:
if(HAL_GPIO_ReadPin(Key1_GPIO_Port,Key1_Pin) == GPIO_PIN_RESET)
{
Blink_led(RedLed, 500);//当按键Key1按下红色LED亮500ms
}
if(HAL_GPIO_ReadPin(Key2_GPIO_Port,Key2_Pin) == GPIO_PIN_RESET)
{
Blink_led(BlueLed, 500);//当按键Key2按下蓝色LED亮500ms
}
if(HAL_GPIO_ReadPin(Key3_GPIO_Port,Key3_Pin) == GPIO_PIN_RESET)
{
Blink_led(GreenLed, 500);//当按键Key3按下绿色LED亮500ms
}
然后对代码进行编译烧录,并验证实验现象:当按键Key1按下红色LED亮,当按键Key2按下蓝色LED亮,当按键Key3按下绿色LED亮。后将LED亮灯时间延长,例如延迟至2s,重新编译烧录,再次观察实验现象,会发现当按下Key1后,马上按Key2,蓝色LED并不会亮。而这便是轮询模式的特点:轮询模式下,如果CPU正在执行其他的代码,来不及查询按键状态的代码,而导致错过事件的错过。因此类似这样事件的处理,我们一般选用中断的工作模式。进行如下设置:
1、修改管脚的输入模式为中断输入,并对管脚进行命名
2、使能中断,并设置中断优先级
3、后续其他设置同跑马灯设置,后生成代码。
打开工程,发现HAL_GPIO_EXTI_IRQHandler(中断服务函数) 在stm32f1xx_it.c文件下,接着右键HAL_GPIO_EXTI_IRQHandler(中断服务函数),点击Go To Definition Of " "进入此函数
HAL_GPIO_EXTI_IRQHandler此函数的作用是清除中断标志位,之后进入HAL_GPIO_EXTI_Callback(中断回调函数)中断运行结束后不会立马退出,而是进入HAL_GPIO_EXTI_Callback(中断回调函数),处理完中断回调函数的事件后,再退出中断,所以可以将需要响应的事件代码写入中断回调函数中。
继续进入HAL_GPIO_EXTI_Callback(中断回调函数)
这是中断回调函数的原型,我们发现该函数定义前有两个下划线,提示我们该函数为虚函数,意味着需自行编写。因此我们通常不将需要响应的事件代码在这写入,而是重新定义一个中断回调函数。
因此我们在gpio.c中添加一个中断回调函数。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
switch(GPIO_Pin)
{
case Key1_Pin:
Blink_led(RedLed, 500);
break;
case Key2_Pin:
Blink_led(RedLed, 500);
break;
case Key3_Pin:
Blink_led(RedLed, 500);
break;
default:
break;
}
}
最后编译,烧录,观察实验现象。
补充说明
上拉电阻和下拉电阻
上拉电阻:将一个单片机的IO口通过一个电阻与电源VCC相连,固定在高电平。
下拉电阻:将一个单片机的IO口通过一个电阻与地GND相连,固定在低电平。
上拉电阻和下拉电阻2者共同的作用是:避免电压的“悬浮”,造成电路的不稳定。对于上拉电阻和下拉电阻的选择,应结合开关管特性和下级电路的输入特性进行设定;考虑的因素包括:驱动能力与功耗的平衡,下级电路的驱动需求,高低电平的设定,频率特性等等。
其他详情可以参考此博科:http://t.csdn.cn/CVTx0
HAL_GPIO_ReadPin()函数
该函数的作用时读取我们想要知道的引脚的电平状态、函数返回值为0或1。
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
函数参数:GPIOx: 是GPIO_TypeDef * 类型,是GPIO寄存器类型。
函数参数: GPIO_Pin:是unit16_t类型,是GPIO寄存器的位端口。返回值:输入端口引脚值,0或1。