如有错误,请在评论区指出,万分感谢,我也是学习者,再更系统学习一次。
NVIC
中断向量表
STM32F407有82个可屏蔽中断,13个系统中断。可屏蔽中断和部分系统中断可以配置优先级,共16个.
中断优先级
4位二进制数,配置抢占优先级(preemption priority),次优先级(subpriority)
NVIC常用配置函数
HAL_NVIC_SetPriorityGrouping()
设置4位二进制数的优先级分组策略
HAL_NVIC_SetPriority()
设置某个中断的抢占优先级和次优先级
IRQn: 要设置优先级的中断号(如:TIM1_UP_IRQn)。
Priority: 抢占优先级值,数值范围通常为 0 到 15(具体取决于微控制器的实现)。
SubPriority: 次优先级值,数值范围同样是 0 到 15。
HAL_NVIC_EnableIRQ()
启用某个中断
HAL_NVIC_DisableIRQ()
禁用某个中断
HAL_NVIC_GetPriorityGrouping()
返回当前的优先级分组策略
HAL_NVIC_GetPriority()
返回某个中断的抢占优先级、次优先级数值
HAL_NVIC_GetPendingIRQ()
检查某个中断是否被挂起
HAL_NVIC_SetPendingIRQ()
设置某个中断的挂起标志,表示发生了中断
HAL_NVIC_ClearPendingIRQ()
外部中断EXTI
外部中断功能,外部中断线
外部中断函数
_HAL_GPIO_EXTI_GET_IT(uint16_t GPIO_Pin)
if (HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET)
{
// 中断处理代码
// 清除中断标志
HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
}
_HAL_GPIO_EXTI_CLEAR_IT(uint16_t GPIO_Pin)
void EXTI0_IRQHandler(void)
{
// 检查是否是 GPIO_PIN_0 的中断
if (HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET) {
// 清除中断标志
HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
// 在此处执行中断处理逻辑,例如切换 LED 状态
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
}
}
_HAL_GPIO_EXTI_GET_FLAG()
_HAL_GPIO_EXTI_GET_IT_()
_HAL_GPIO_EXTI_GENERATE_SWIT(uint16_t GPIO_Pin)
// 在某个条件下调用该函数以生成软件中断
if (some_condition)
{
HAL_GPIO_EXTI_GENERATE_SWIT(GPIO_PIN_0);
}
HAL_GPIO_EXTIx_IRQHandler()
void EXTI0_IRQHandler(void)
{
// 检查是否是 GPIO_PIN_0 的中断
if (HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET) {
// 清除中断标志
HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
// 执行中断处理逻辑,例如切换 LED 状态
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
}
}
// 对于其他外部中断线,如 EXTI1
void EXTI1_IRQHandler(void)
{
if (HAL_GPIO_EXTI_GET_IT(GPIO_PIN_1) != RESET) {
HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_1);
// 处理 GPIO_PIN_1 的中断
}
}
确保在向 NVIC 注册中断时,正确指定了中断优先级和使能相关中断。在 ISR 中,尽量避免耗时操作,以保持响应速度。
HAL_GPIO_EXTIx_Callback()
对于0到15线的外部中断,EXTIO至EXTI4有独立的ISR,EXTIT[9:5]共用一个ISR,EXTI[15:10]共用一个ISR。在启用某个中断后,在CubeMX自动生成的中断处理程序文件stm32f4xx itc中会生成ISR的代码框架。这些外部中断ISR 的代码都是一样的,下面是几个外部中断的ISR 代码框架,只保留了其中一个ISR 的完整代码,其他的删除了代码沙箱注释。
// 中断服务例程 (ISR)
void EXTI0_IRQHandler(void)
{
// 检查 GPIO_PIN_0 是否产生中断
if (HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET) {
HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); // 清除中断标志
HAL_GPIO_EXTI_Callback(GPIO_PIN_0); // 调用用户自定义的回调函数
}
}
// 用户定义的回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_0) {
// 在这里处理 GPIO_PIN_0 的中断,例如切换 LED 状态
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
}
}
按键中断
一般来说,此方式触发外部中断,但是对于按键触发是有问题的,软件查询时,有20ms的延时设计,但这样的代码,虽然清除了挂起标志,有回调函数的延时,但是下一次抖动仍可能会触发中断再执行一次回调函数。
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
/* EXTIline interrupt detected */
//检测中断挂起标志
if( HAL_GPIO_EXTI_GET_IT(GPIO_Pin) !=RESET)
{
HAL_GPIO_EXTI_Callback(GPIOPin);
HAL_GPIO_EXTI_CLEAR_IT(GPIO Pin);
}
}
代码实例
#define KEY_UP GPIO_Pin_2
#define KEY_UP_Port GPIOE
#define KEY_UP_EXTI_IQR EXTI2_IQRn
//上升沿
#define KEY_DOWN GPIO_Pin_3
#define KEY_DOWN_Port GPIOE
#define KEY_DOWN_EXTI_IQR EXTI3_IQRn
//下降沿
#define KEY_Toggle GPIO_Pin_0
#define KEY_Toggle _Port GPIOA
#define KEY_Toggle _EXTI_IQR EXTI0_IQRn
#define LED_ON HAL_GPIO_WritePin(GPIOB,GPIO_Pin_1,ENABLE)
#define LED_OFF HAL_GPIO_WritePin(GPIOB,GPIO_Pin_1,ENABLE)
#define LED_Toggle HAL_GPIO_Togggle(HAL_GPIO_Togggle(GPIOB,GPIO_Pin_1)
void MX_GPIO_Init()
{
_HAL_RCC_GPIOE_CLK_ENABLE();
_HAL_RCC_GPIOE_CLK_ENABLE();
……
/*配置GPIO引脚:KEY_UP,下拉输入,上跳沿触发*/
GPIO_InitStruct.Pin=KEY_UP|KEY_Toggle;
GPIO_InitStruct.Mode=GPIO_MODE_IT_RISING;
GPIOInitStruct.Pull =GPIO_PULLDOWN;
HAL_GPIO_Init(KEY_UP_Port|KEY_Toggle_Port,&GPIO_InitStruct);
//上跳沿触发:
上跳沿触发意味着当引脚从低电平(0V)跳变到高电平,
//外部中断将被触发。这用于检测按键的按下事件。
//为什么使用下拉输入?
//防止浮动状态
/*如果不使用下拉电阻,按键未按下时 GPIO 引脚可能处于一个未定义的浮动状态(Floating State)。在这种状态下,由于噪声或其他干扰信号,GPIO 引脚可能会随机改变状态,这会导致误触发中断。使用下拉电阻可以确保在按键未按下时,引脚始终保持在稳定的低电平。*/
/*配置GPIO引脚:KEY_DOWN,上拉输入,下降沿触发*/
GPIO_InitStruct.Pin=KEY_DOWN;
GPIO_InitStruct.Mode=GPIO_MODE_IT_FALLING;
GPIOInitStruct.Pull =GPIO_PULLUP;
HAL_GPIO_Init(KEY_DOWN_Port,&GPIO_InitStruct);
……
HAL_NVIC_SetPriority(KEY_UP_EXTI_IQR, 3, 0); // 设置中断优先级
HAL_NVIC_EnableIRQ(KEY_UP_EXTI_IQR); // 启用中断
}
void EXTI0_IRQHandler(void){
HAL_GPIO_EXTI_IQRHandler(GPIO_PIN_0);
}
void EXTI0_GPIO_IRQHandler(uint16_t GPIO_Pin)
{
if(_HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
{
HAL_GPIO_EXTI_Callback(GPIO_Pin);
_HAL_GPIO_CLEAR_IT(GPIO_Pin);
}
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == KEY_UP)
{
HAL_Delay(10);
/*将外部中断优先级降低,低于嘀嗒系统中断优先级,这时候可以在外部中断回调函数内使用hal delay*/
LED_UP;
}
else if(GPIO_Pin == KEY_DOWN)
{
HAL_Delay(10);
LED_DOWN;
}
else if(GPIO_Pin == KEY_TOGGLE)
{
HAL_Delay(10);
LED_TOGGLE;
}
}
注意事件
外部中断优先级如果优于系统记数延时中断,那么不可以使用HAL_Delay()进行延时,否则会由于低优先级的嘀嗒中断发生在高优先级的外部中断而死机。