外观
四、按键中断控制LED灯
1 电路分析&中断介绍
在桌面温湿度仪上,有一个功能按键,通过这个按键可以用于执行各类操作,比如控制LED灯、读取温湿度或显示电池电量等信息;
本次实验通过按键中断按下来控制LED灯点亮或熄灭来测试GPIO的输入功能。
注
在按键的左侧连接是GND,右侧连接的是GPIO-PB5,也就是说,当按键按下时,是低电平。如果想要单片机能够准确的知道按下的状态,则未按下时需要时引脚保持高电平,这样才有变化。
从高电平到低电平,是一个电平的跳变,单片机可以检测这种跳变从而进行特殊的处理。
1.1 中断介绍
中断是指单片机在运行过程中,出现了某种需要紧急处理的事件,此时单片机会暂停当前运行的代码,转而执行更加紧急的事件代码。处理完毕后又返回原来被暂停的程序中继续运行。
STM32G030K6T6是Cortex ARM0+内核,具有NVIC(嵌套中断控制器)以及EXTI(外部中断事件控制器),支持4种优先级设置,且每一个GPIO均可作为外部中断/事件触发信号输入,通过这二者进行组合,使单片机能够对外部中断/事件进行快速响应。
这里对PB7也就是WAKE引脚进行下降沿读取,当发生下降沿时,单片机进入中断处理,执行相关的代码操作,这样就无需在代码中一直轮询按键引脚是否是低电平了。
1.2 中断使用
在STM32 HAL库中,有一套独立的中断/事件处理回调逻辑,假设此时按键按下,导致下降沿到来;
- 首先,系统会进入中断服务函数中(中断服务函数实现进行了弱定义,并在启动文件中进行声明);
- 在中断服务函数中,调用中断类型处理函数(一个中断服务函数会作用与多种中断类型,比如上升沿中断、下降沿中断等等)并清除该中断;该函数HAL库一般也写好了,我们仅需要找到该函数位置然后观察其中有什么内容即可;
- 获取具体的中断类型后,执行对应的处理功能函数;HAL库一般会写好一个弱定义的函数,我们需要对这个函数进行重新定义并将内容编写在其中即可。
2 代码编写
2.1 创建工程
打开STM32CubeMx软件
2.2 设置参数
根据上方原理图,设置晶振引脚、下载引脚、按键引脚与LED测试引脚的参数;
对于LED引脚,需要配置为推挽输出模式,没有使用上下拉,且一开始就输出低电平,这样方便测试LED灯是否正常工作。
对于按键引脚,需要设置为中断模式,下降沿触发,上拉(默认是高电平);
设置GPIO-PB5对应的中断线使能中断,优先级设置为1,Cortex M0+内核总共有四种优先级,数字越低,优先级越高;
在代码生成列表中,根据需要创建生成的函数及代码;
配置时钟树,这里主晶振没有使用外部而是内部晶振,外部低速晶振暂时没有使用,先不需要配置;
最后填写输出工程的名称,选择工程输出路径以及适配的IDE;
生成相关文件的.c与.h文件;
选择HAL库;
创建工程,随后打开,这个Cubemx软件就可以关闭了;
2.3 编译配置工程
正常应该是0错误0警告;
点击魔术棒,选择debug,选择合适的下载器,并配置下载器选项;
2.4 编写测试代码
在gpio.c文件的用户区编写测试代码,LED相关的代码直接照搬之前的即可,这里不再重复接收;按键中断服务函数在stm32g0xx_it.c中已经进行了编写;
根据前面所学HAL库中断回调思路;这里中断服务函数中存放的是中断类型处理函数;可以跳转该函数进入查看相关内容;
在hal_gpio.c文件中可以看到这个函数对上升沿中断以及下降沿中断进行了判断,这里我们选择的是下降沿中断,所以需要编写下降沿中断回调函数;
双击跳转到下降沿回调函数中,这里CubeMX已经帮我们写好了一个空函数,我们需要对它进行重定义将实际的功能代码编写在这函数中。
在gpio.c中重定义GPIO下降沿中断回调函数,首先判断是否是GPIO-PB5产生的; 然后再次判断是否是低电平,在中断服务函数中,不建议做死循环或长时间等待操作; 所以就不做消抖以及松手判断了,后续可以在这里设置标志位,然后在外部去做相关松手判断操作。目前这样是会有误触的可能。
C
/**
* @brief This function handles EXTI line 4 to 15 interrupts.
*/
void EXTI4_15_IRQHandler(void)
{
/* USER CODE BEGIN EXTI4_15_IRQn 0 */
/* USER CODE END EXTI4_15_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
/* USER CODE BEGIN EXTI4_15_IRQn 1 */
/* USER CODE END EXTI4_15_IRQn 1 */
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
C
/**
* @brief Handle EXTI interrupt request.
* @param GPIO_Pin Specifies the port pin connected to corresponding EXTI line.
* @retval None
*/
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if (__HAL_GPIO_EXTI_GET_RISING_IT(GPIO_Pin) != 0x00u)
{
__HAL_GPIO_EXTI_CLEAR_RISING_IT(GPIO_Pin);
HAL_GPIO_EXTI_Rising_Callback(GPIO_Pin);
}
if (__HAL_GPIO_EXTI_GET_FALLING_IT(GPIO_Pin) != 0x00u)
{
__HAL_GPIO_EXTI_CLEAR_FALLING_IT(GPIO_Pin);
HAL_GPIO_EXTI_Falling_Callback(GPIO_Pin);
}
}
/**
* @brief EXTI line detection callback.
* @param GPIO_Pin Specifies the port pin connected to corresponding EXTI line.
* @retval None
*/
__weak void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(GPIO_Pin);
/* NOTE: This function should not be modified, when the callback is needed,
the HAL_GPIO_EXTI_Falling_Callback could be implemented in the user file
*/
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
C
void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_5)
{
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_5) == GPIO_PIN_RESET)
{
//执行翻转LED函数,在中断服务函数中不建议做死循环或长时间等待操作
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_7);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11