STM32CubeMX与HAL库开发教程三(外部中断与中断系统)

目录

1、什么是中断系统

 2、我们为什么要使用中断

3、STM32中断优先级

4、STM32外部中断EXTI

5、STM32CubeMX配置外部中断


前言

        中断是MCU一个基础的功能,而什么是中断,我们为什么要使用中断,本节我们来介绍什么中断的相关概念与外部中断如何使用

1、什么是中断系统

        到中午饭点了,切菜炒菜做点饭吧,卧槽憋不住了,快去上个厕所,在这个过程中,做饭可以代表我的主程序,而上厕所这个打断我主程序的行为就可以称为中断

        而在单片机系统中,如果遇到需要紧急处理的突发事件时,CPU需要迅速的作出反应,暂停正在运行的程序来处理突发事件,这时就需要中断,发生突发事件从而打断当前程序,转而去处理这一事件,当处理完成后再回到原来被打断出继续执行原程序的过程

                      

 2、我们为什么要使用中断

        在蒸饭的过程中,我需要做的也仅仅是等待饭蒸好,而这个过程我不可能一直站在这里等待它,我想去做别的事情,比如说舒舒服服的坐在沙发上看电视,在这个场景中,我是唯一具有处理能力的主体,不管是蒸饭、关水龙头还是看电视,同一个时间点上我只能干一件事情。但是,在我专心致志干一件事情时,总有许多或紧迫或不紧迫的事情突然出现在面前,都需要去关注,有些还需要我停下手头的工作马上去处理。只有在处理完之后,方能回头完成先前的任务。

        中断机制不仅赋予了我处理意外情况的能力,如果我能充分发挥这个机制的妙用,就可以“同时”完成多个任务了。正是由于中断机制,MCU才能有条不紊地“同时”完成多个任务,中断机制实质上帮助MCU提高了并发“处理”能力。

3、STM32中断优先级

        在现实生活中,一件事很急,但总有比他更急迫的事,中断也是如此,分为高优先级中断与低优先级中断,一旦同时触发中断,一定是先执行优先级高的中断任务,再去执行低优先级的中断任务,最后再回到主程序继续执行,如果低优先级中断已经触发了,高优先级也会打断低优先级的中断先执行。

        而STM32F系列的MCU的NVIC(嵌套向量中断控制器)采用4位二进制数设置中断优先级,并且分为抢占优先级次优先级优先级数字越小表示优先级别越高,中断0优先级要大于中断1,如果两个优先级相同的中断触发,那就谁先触发就执行谁,先判断抢占优先级,相同的情况下再判断次优先级。

        HAL库中断管理常用函数

函数名功能
HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)设置4位二进制数的优先级分组策略
HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)设置某个中断的抢占优先级和次优先级
HAL_NVIC_EnableIRQ(IRQn_Type IRQn)启用某个中断
HAL_NVIC_DisableIRQ(IRQn_Type IRQn)禁用某个中断
HAL_NVIC_GetPriorityGrouping(void)返回当前的优先级分组策略
HAL_NVIC_GetPriority(IRQn_Type IRQn, uint32_t PriorityGroup, uint32_t* pPreemptPriority, uint32_t* pSubPriority)返回某个中断的抢占优先级和次优先级的数值
HAL_NVIC_GetPendingIRQ(IRQn_Type IRQn)检查某个中断是否被挂起
HAL_NVIC_SetPendingIRQ(IRQn_Type IRQn)设置某个中断的挂起标志,表示发生了中断
HAL_NVIC_ClearPendingIRQ(IRQn_Type IRQn)清除某个中断的挂起标志

以上函数相关的驱动程序的头文件在 stm32f1xx_hal_cortex.h 中,其中前三个用于CubeMX自动生成的代码,其他函数用于用户代码,几个常用函数的详细介绍如下,其中一些函数的详细定义和功能可查看源程序中的头文件

函数 HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)

        其中参数uint32_t PriorityGroup是用于设置优先级分组策略,可使用头文件中定义的几个宏定义常量,入夏表示,他们表示不同的分组策略

#define IS_NVIC_PRIORITY_GROUP(GROUP) (((GROUP) == NVIC_PRIORITYGROUP_0) || \
                                       ((GROUP) == NVIC_PRIORITYGROUP_1) || \
                                       ((GROUP) == NVIC_PRIORITYGROUP_2) || \
                                       ((GROUP) == NVIC_PRIORITYGROUP_3) || \
                                       ((GROUP) == NVIC_PRIORITYGROUP_4))

函数 HAL_NVIC_EnableIRQ(IRQn_Type IRQn) 

        函数的功能是在NVIC控制器中开启某个中断,只有在NVIC中开启某个中断后,NVIC才会对这个中断请求做出响应,执行相应的ISR,其中枚举类型IRQn_Type的参数IRQn是中断号的枚举值

4、STM32外部中断EXTI

        基础的外部中断是MCU上GPIO引脚作为输入引脚时,由引脚上的电平变化所产生的中断,例如连接按键输入的引脚就可以由按键长生的不外部中断信号,此外还有一些内部信号作为EXTI中断线的输入,例如RTC唤醒事件信号连续在EXTI线22上

        EXTI0至EXTI15这16个外部中断以GPIO引脚作为输入线,EXTO0可以选择PA0,PB0至PI0的某个引脚作为输入线,如果设置了PA0为EXTI0的输入线,那么PB0和PC0等就不能再作为EXTI0的输入线,以GPIO引脚作为输入线的EXTI可以用于检测外部输入事件,例如按键连续的GPIO引脚信号,通过外部中断方式检测比轮询模式更有效

        HAL库外部中断相关函数

函数名功能描述
__HAL_GPIO_EXTI_GET_IT(__EXTI_LINE__) (EXTI->PR & (__EXTI_LINE__))检查某个外部中断线是否有挂起的中断
__HAL_GPIO_EXTI_CLEAR_IT(__EXTI_LINE__) (EXTI->PR = (__EXTI_LINE__))清除某个外部中断线的挂起标志位
__HAL_GPIO_EXTI_GET_FLAG(__EXTI_LINE__) (EXTI->PR & (__EXTI_LINE__))和函数1一样的功能
__HAL_GPIO_EXTI_CLEAR_FLAG(__EXTI_LINE__) (EXTI->PR = (__EXTI_LINE__))和函数2一样的功能
__HAL_GPIO_EXTI_GENERATE_SWIT(__EXTI_LINE__) (EXTI->SWIER |= (__EXTI_LINE__))在某个外部中断线上产生软中断
HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)外部中断ISR中调用的通用处理函数
HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)外部中断处理的回调函数,需要用户自己重新实现

外部中断相关函数的定义在文件stm32f1xx_hal_gpio.h中,相关参数可以跳转头文件查询

        在HAL库中,以 __HAL  为前缀的都是宏函数,表中前几个函数都是宏函数,例如__HAL_GPIO_EXTI_GET_IT(__EXTI_LINE__) (EXTI->PR & (__EXTI_LINE__))  ,它的功能为检查外部中断挂起寄存器(EXTI_PR)中某个中断线的挂起标志为是否置位,参数(__EXTI_LINE__) 是某个外部中断线,用GPIO_PIN_0、GPIO_PIN_1等宏定义常量来表示。

        函数的返回值只要不等于0(用宏RESET表示0),就表示外部中断线挂起标志位被置位,有未处理的中断事件

        而函数 __HAL_GPIO_EXTI_CLEAR_IT(__EXTI_LINE__) (EXTI->PR = (__EXTI_LINE__)) 用于清除某个中断线的标志位,向外部中断挂起寄存器(EXTI_PR)的某个中断线写入1,就可以清除该中断线的挂起标志,在外部中断ISR里处理完中断后,我们需要调用这个函数清除挂起标志位,以便于再次相应下次中断

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

        这个函数的代码很简单,如果检测到中断线GPIO_PIN的中断挂起标志位不为0,就清除中断挂起标志位,然后执行函数HAL_GPIO_EXTI_Callback(),这个函数是对中断进行相应处理的回调函数,它的代码框架在文件stm32f1xx_hal_gpio.c中,代码如下

__weak void HAL_GPIO_EXTI_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_Callback could be implemented in the user file
   */
}

       这个函数的前面有个修饰符__weak  就是用来定义弱函数的,所谓弱函数就是HAL库中预先定义的带有__weak修饰符的函数,如果用户没有重新实现这些函数,编译时就编译这些弱函数,如果在用户程序中重新实现了这些函数,那么就编译用户自己写的函数

        在STM32CubeMX中,用户只需要搞清楚与中断事件对应的回调函数,然后重新实现回调函数即可,对于外部中断,只有一个中断事件源,所以只有一个回调函数HAL_GPIO_EXTI_Callback(),在对外部中断进行处理时,只需要重新实验这个函数即可。

5、STM32CubeMX配置外部中断

点击对应管脚,设置对应模式,外部中断为GPIO输入模式,原理为检测高低电平触发中断

而下面这就是外部中断的六种模式,上升沿就是低电平变为高电平的过程,而下降沿就是高电平变为低电平的过程,我们的按键选择下降沿触发

然后我们来配置外部中断的使能和优先级

然后我们将时钟树和项目管理配置完之后点击右上角的蓝色小按钮GENERATE CODE生成文件,将自己编写的代码放进代码沙盒中,这样下次再次更改CubeMX配置生成的文件就不会将用户的代码给清除了

        在HAL库中,中断运行结束后不会立刻退出,而是会先进入相对应的中断回调函数,处理该函数中的代码之后,才会退出中断,所以在HAL库中我们一般将中断需要处理代码放在中断回调函数中,接下来我们编写一段重新实现中断回调函数的代码

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if(GPIO_Pin == KeyUP_Pin) //PA0 = KeyUP
    {
        HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_2); //翻转LED
        HAL_Delay(500); //软件消除按键抖动
    }
    else if(GPIO_Pin == KeyRight_Pin) //PE2 = KeyRight
    {
        HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_4);  //翻转另外一个LED
        HAL_Delay(1000);  //观察优先级的作用
    }
    else if(GPIO_Pin == KeyDown_Pin) //PE3 = KeyDown
    {
        __HAL_GPIO_EXTI_GENERATE_SWIT(GPIO_PIN_0); //产生EXTI0软中断
        HAL_Delay(1000);  //这个延迟是必要的,否则会由于抖动触发两次
    }
}

        最后,我们需要改写函数 HAL_GPIO_EXTI_IRQHandler() 来增强代码的性能,源代码它在检测到中断挂起标志后,先清除中断挂起标志后,再执行回调函数,一般的中断通用处理函数都是这样的处理流程,是为了硬件能及时响应下一次中断,但是对于检测按键输入的外部中断,这样是有问题的,因为清除中断挂起标志后,按键的抖动就会触发下一次中断,并将中断挂起标志置位,虽然再回调函数中使用了延时,但是回调函数退出后,NVIC检测到中断位被挂起,又会执行一次回调函数。

        所以对于外部中断的按键检测输入,我们需要修改一下的 HAL_GPIO_EXTI_IRQHandler()  代码,将清除中断挂起标志位的功能放在后面,既修改为如下的代码,这样修改后就不存在问题了

// 未修改!!!
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

// 已修改!!!
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
  {
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
  }
}

        但要注意,这个函数是头文件中自带的原始驱动文件,不存在代码沙箱,修改完这个函数后,重新生成代码会将这个代码变回为原来的样子,所以在使用CubeMX时,用户一定要将代码写进代码沙箱中,如果要更改HAL库原始文件,重新生成后也一定要再次更改!!

  • 42
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要配置外部中断,你需要使用HAL库的相关函数。以下是一个简单的步骤指南: 1. 首先,在CubeMX中配置外部中断。打开CubeMX,选择你的微控制器型号,然后转到"Pinout & Configuration"选项卡。在左侧的引脚列表中选择你要使用的引脚,并在右侧的"GPIO Pin Configuration"部分将其配置为外部中断模式。 2. 在CubeMX的"Configuration"选项卡中,找到"NVIC"(Nested Vectored Interrupt Controller)设置。启用你选择的外部中断通道,并选择优先级。 3. 在生成代码之后,打开你的IDE(如Keil或IAR)并打开生成的项目。 4. 找到与你配置的引脚对应的GPIO外部中断处理函数。这个函数通常位于"stm32fxxx_it.c"或类似的文件中。例如,如果你配置的是PA0引脚,处理函数可能是"void EXTI0_IRQHandler(void)"。 5. 在处理函数中,你可以执行你想要的操作。例如,你可以读取GPIO的状态,处理中断事件等。 这里是一个简单的例子,演示如何使用HAL库配置外部中断(以PA0引脚为例): ```c // 配置外部中断 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; // 上升沿触发 GPIO_InitStruct.Pull = GPIO_PULLDOWN; // 下拉电阻 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 使能外部中断 HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI0_IRQn); // 外部中断处理函数 void EXTI0_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); } // HAL库外部中断回调函数 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == GPIO_PIN_0) { // 处理中断事件 } } ``` 请根据你的特定需求进行适当的修改。希望这可以帮助到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值