【外部中断理论可参考:【小趴菜STM32开发笔记2】---- 外部中断】
目录
1 Cubemx新建工程
(略)
2 配置系统时钟
3 配置引脚
3.1 配置4个LED引脚为GPIO_Output
【Tip】自定义引脚标签
3.2 配置4个按键为中断模式(GPIO_EXTIx)
3.3 设置中断优先级
【这里中断号5和中断号9共用一条中断线】
4 生成代码
(略)
5 进入Keil,补充代码
5.1 HAL库的中断处理流程
在 MX_GPIO_Init() 中调用HAL库使能中断函数和设置中断优先级:
在 HAL_NVIC_EnableIRQ() 中调用 NVIC_EnableIRQ()
当触发中断时,处理器查表跳转到中断服务程序 EXTIx_IRQHandler(void)
中断服务程序 EXTIx_IRQHandler (void) 中调用外部中断通用处理函数HAL_GPIO_EXTI_IRQHandler (uint16_t GPIO_Pin) ,该函数作为HAL库提供的外部中断接口函数,参量为触发中断的引脚
系统自带了一个弱引用的中断回调函数,通过__weak修饰,没有任何执行代码,仅有一个避免编译警告的语句:UNUSED(GPIO_Pin)
需要在main.c中重写中断回调函数 HAL_GPIO_EXTI_Callback() ,并补全中断所需要执行的任务函数
5.2 工程源码
5.2.1 main函数
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
//定义4个计数变量,用于LED闪烁次数计数
uint8_t LED0_Count;
uint8_t LED1_Count;
uint8_t LED2_Count;
uint8_t LED3_Count;
void SystemClock_Config(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
while (1)
{
//初始时刻4个LED同时闪烁
HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);
HAL_GPIO_TogglePin(LED3_GPIO_Port,LED3_Pin);
HAL_Delay(500);
}
}
5.2.2 中断回调函数
//微秒级的延时
void delay_us(uint32_t delay_us)
{
volatile unsigned int num;
volatile unsigned int t;
for (num = 0; num < delay_us; num++)
{
t = 11;
while (t != 0)
{
t--;
}
}
}
//毫秒级的延时
void delay_ms(uint16_t delay_ms)
{
volatile unsigned int num;
for (num = 0; num < delay_ms; num++)
{
delay_us(1000);
}
}
/* USER CODE BEGIN 4 */
//重写中断回调服务函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == KEY0_Pin) //判断中断源为KEY0
{
while(KEY0_Pin ==0);
for(LED0_Count=0;LED0_Count<20;LED0_Count++) //翻转20次,闪烁10次
{
delay_ms(100);
HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
}
}
if(GPIO_Pin == KEY1_Pin) //判断中断源为KEY1
{
while(KEY1_Pin ==0);
for(LED1_Count=0;LED1_Count<20;LED1_Count++)
{
delay_ms(100);
HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
}
}
if(GPIO_Pin == KEY2_Pin) //判断中断源为KEY2
{
while(KEY2_Pin ==0);
for(LED2_Count=0;LED2_Count<20;LED2_Count++)
{
delay_ms(100);
HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);
}
}
if(GPIO_Pin == KEY3_Pin) //判断中断源为KEY3
{
while(KEY3_Pin ==0);
for(LED3_Count=0;LED3_Count<20;LED3_Count++)
{
delay_ms(100);
HAL_GPIO_TogglePin(LED3_GPIO_Port,LED3_Pin);
}
}
}
/* USER CODE END 4 */
这里在中断回调函数里调用了重构的阻塞延时函数,那为什么不直接用HAL库自带的HAL_Delay 进行延时呢?
答:因为HAL_Delay 的延时是通过嘀嗒定时器(system tick timer)的定时器中断实现的,而嘀嗒定时器的中断优先级在默认情况下是低于外部中断优先级的,因此在触发外部中断执行回调函数时,遇到优先级更低的HAL_Delay ,那么请问这时候低优先级的HAL_Delay 能否打断高优先级的中断抢先执行呢?肯定是不能的吧,因此程序就卡死在HAL_Delay这一步,无法往下继续运行。
当然,除了重构延时函数外,也可以增大嘀嗒定时器的中断优先级(需确保该操作不能影响系统其他功能的正常执行)
6 实验现象
外部中断嵌套
4个按键分别控制4个LED翻转10次,key0和key1可以抢占key2和key3的执行