STM32CubeMX教程4 EXTI 按键外部中断
读者可访问 GitHub - lc-guo/STM32CubeMX-Series-Tutorial 获取原始工程代码
1、准备材料
开发板(STM32F407G-DISC1)
STM32CubeMX软件(Version 6.10.0)
keil µVision5 IDE(MDK-Arm)
2、实验目标
使用STM32CubeMX软件配置STM32F407开发板通过用户按键产生外部中断,然后在中断中翻转绿色LED灯的状态
3、中断系统概述
3.1、中断向量表
STM32F4系列有10个系统中断和82个可屏蔽的外部中断 (注释1),除系统Reset中断外每个中断均有对应的中断服务函数,如下图所示为中断向量表
3.2、中断优先级
嵌套向量中断控制器(NVIC)采用4位二进制数表示中断优先级,这4位二进制数表示的中断优先级又分为了抢占优先级和次优先级,其中根据抢占优先级所使用的二进制位数分成了5个组,分为NVIC_PRIORITYGROUP_0 ~ NVIC_PRIORITYGROUP_4,分别表示抢占优先级使用0~4位二进制数表示,具体规则如下几点所述:
- 高抢占优先级的中断可以打断低抢占优先级的中断
- 抢占优先级和次优先级均相同的两个中断谁先发生谁就先执行
- 抢占优先级相同但次优先级不同的两个中断,后触发的中断需要等待先触发的中断执行完毕才可以执行
4、实验流程
4.0、前提知识
STM32F407一共有23个外部中断,其中包含16个外部中断(EXTI0~EXTI15)和7个对应不同的事件的中断,如下表所示
每一组GPIO的0号引脚均可以作为EXTI0的输入,同理,每一组GPIO的15号引脚均可以作为EXTI15的输入,具体映射如下图所示 (注释2)
4.1、CubeMX相关配置
4.1.0、工程基本配置
打开STM32CubeMX软件,单击ACCESS TO MCU SELECTOR选择开发板MCU(选择你使用开发板的主控MCU型号),选中MCU型号后单击页面右上角Start Project开始工程,具体如下图所示
开始工程之后在配置主页面System Core/RCC中配置HSE/LSE晶振,在System Core/SYS中配置Debug模式,具体如下图所示
详细工程建立内容读者可以阅读“STM32CubeMX教程1 工程建立”
4.1.1、时钟树配置
系统时钟使用8MHz外部高速时钟HSE,HCLK、PCLK1和PCLK2均设置为STM32F407能达到的最高时钟频率,具体如下图所示
4.1.2、外设参数配置
在Pinout & Configuration页面右边单片机引脚预览Pinout view中,寻找需要设置的具体GPIO,这里我们仍然选择用户按键的PA0引脚,但是右键单击将其配置为GPIO_EXTI0
在页面的中间GPIO Mode and Configuration栏中选中PA0引脚,然后在下方对其引脚进行配置,外部中断主要配置 GPIO mode 和 GPIO Pull-up/Pull-down
GPIO mode :有外部中断上升沿、下降沿、上/下降沿触发、外部事件上升沿、下降沿和上/下降沿触发共计6种模式
GPIO Pull-up/Pull-down :选择无上/下拉,根据“STM32CubeMX教程3 GPIO输入 - 按键响应”实验“3.1.2、外设参数配置”小节可知开发板上的按键已经使用电阻进行外部下拉
我们使用的是外部中断,并且在按键按下松开时既会产生上升沿也会产生下降沿,因此笔者这里选择下降沿触发,无上/下拉的外部中断模式,具体配置如下图所示
4.1.3、外设中断配置
将引脚配置为中断模式后,还需要在NVIC中启动其具体的中断,单击页面左侧的NVIC配置,选择合适的中断优先级组,然后启动对应的中断,这里即用户按键引脚PA0的中断EXTline0 interrupt,具体配置如下图所示
4.2、生成代码
4.2.0、配置Project Manager页面
单击进入Project Manager页面,在左边Project分栏中修改工程名称、工程目录和工具链,然后在Code Generator中勾选“Gnerate peripheral initialization as a pair of 'c/h' files per peripheral”,最后单击页面右上角GENERATE CODE生成工程,具体如下图所示
详细Project Manager配置内容读者可以阅读“STM32CubeMX教程1 工程建立”实验3.4.3小节
4.2.1、外设初始化调用流程
当仅仅配置了引脚为外部中断模式,而不启动对应的外部中断时,在生成的代码引脚初始化函数MX_GPIO_Init中只会增加如下图框中所示的代码,此时由于未使能中断,因此中断不能正常响应,如下图所示
4.2.2、外设中断调用流程
当配置了引脚为外部中断模式,且使能了引脚对应的中断,首先会在HAL_Init(void)函数中设置中断优先级组(系统默认开启了一些中断,因此即使我们不使能外部中断该函数也会被其他开启的中断调用从而设置中断优先级组),如下图所示
另外除了上述的引脚模式设置代码外,还会在引脚初始化函数MX_GPIO_Init中新增加中断优先级设置及对应中断使能的函数,如下图所示
然后在stm32f4xx_it.c文件中会出现对应的中断服务子函数void EXTI0_IRQHandler(void) (注释3),中断服务子函数中调用了HAL_GPIO_EXTI_IRQHandler(GPIO_Pin)函数,跳转过去在函数内部执行了
- 检测该中断线是否有挂起的外部中断
- 如果有则清除挂起的外部中断线
- 然后调用外部中断回调函数HAL_GPIO_EXTI_Callback(GPIO_Pin)(此函数为虚函数,可由用户自定义)
具体流程如下图所示
4.2.3、添加其他必要代码
此时用户只需要重新实现HAL_GPIO_EXTI_Callback(GPIO_Pin)函数即可,在该函数体内可以实现中断触发后想要实现的功能代码,笔者将该函数重新实现在了gpio.c中
值得注意的是在外部中断回调函数中使用到了HAL库延时函数来对按键消抖,此处可能会存在严重的问题 (注释4)
具体代码如下图所示
源代码如下
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == USER_KEY_Pin)
{
HAL_Delay(10);
HAL_GPIO_TogglePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin);
}
}
5、常用函数
/*所有外部中断触发回调服务子函数,通过GPIO_Pin判断是哪个中断线,然后在函数体内做相应动作*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
6、烧录验证
烧录程序,然后可以观察到当开发板上电后,四个颜色LED全部点亮,然后每按下一次用户按键,绿色LED灯状态均会发生翻转
7、注释解析
注释1:具体的中断向量表可以在keil工程目录启动文件startup_stm32f407xx.s中查看,这里的中断数量笔者是根据启动文件中的数量计算的,其中Reserved的中断向量未计数
注释2:图片来自STM32F4xx中文参考手册.pdf
注释3:以后对于每一个外设中断,启用后都会在stm32f4xx_it.c文件中找到对应的中断服务函数
注释4:HAL库延时函数HAL_Delay使用的是系统滴答定时器作为时间基准,而系统滴答定时器同为中断,在外部中断中触发系统滴答定时器中断会涉及中断优先级的问题,此时需保证滴答定时器的抢占优先级高于我们所使用的外部中断,否则会出现卡死的现象发生