电子说
处理器:GD32F103VET6
开发环境:MDK(keil 5) + STM32CubeMX
1.1 中断的作用
中断的主要作用就是允许一些紧急的任务“插队”,提前进行。比如,大家在排队做核酸检测,但是,现在来了一位男士,他说他有紧急情况,希望能够先做,在征得同意后,他先做了核酸,他做完了跑了,但大家仍然按照原来的顺序排队进行,这就是中断。
又如,你在玩游戏,但是一个重要电话进来了,你只得暂停当前的游戏,先接电话,接完电话继续玩。这也是中断。
在各种电子产品中,中断用得非常非常多,在我看来,它和GPIO、串口、定时器一起,是各种处理器学习的最重要的四个模块之一,我称之为“四大金刚”。
1.2 GD32的中断的执行及其优先级
对于GD32的中断,我们需要关注以下问题:
1.那些模块可以申请中断?绝大部分的模块,比如串口、定时器、SPI接口等都可以申请中断,这些可以申请中断的模块我们叫中断源。
2.某个模块申请中断,并获得系统同意后,系统将会做什么?中断申请并获得通过后,将会执行中断函数,中断函数执行完后又回到原来的地方继续执行。
GD32的各个模块的中断函数的函数名厂家已经帮我们取好了,在启动文件(.s文件)中,具体如图1所示。
图1 GD32的中断函数
3.如果有多个中断同时到来,处理器该怎么办呢?它会先去执行哪个中断的中断函数呢?通过每个中断的优先级才裁决先响应谁!优先级高的中断,它的中断函数将会获得优先执行。GD32的每个中断的优先级分为抢占式优先级和子优先级两部分,在实际应用中,一般我们都是只需要设置抢占式优先级即可,子优先级不用理会。抢占式优先级可以设置的值为0-15,值越小优先级越高,这点跟freertos操作系统的优先级刚好相反,freertos的是值越高优先级越高。
GD32的中断由一个叫NVIC的模块统一处理,NVIC全称叫嵌套中断向量控制器,抢占式优先级、子优先级、中断允许、中断失能都在它里面设置。
最后补充说明两点:
1.GD32的每一个模块都会有至少一个中断开关,只有这个开关处于使能状态,该模块的中断信号才能进入到NVIC内部。
2.中断函数中尽量不要有延时,如必须要延时,则延时要尽量小,要快进快出,不要影响后续的中断的响应!
1.3 GD32的外部中断
GD32F103VET6支持19个外部中断,注意,这里虽然用了外部两个字,但真正只有16来自于芯片的外部,有3个仍然是在芯片的内部。外部的16个中断分别为EXTI0~EXTI15,其中EXTI0可以从PA0、PB0、PC0等标号为0的引脚进入,EXTI1可以从PA1、PB1、PC1等标号为1引脚进入,其他外部中断同理。
在这16个从IO引脚进入的中断中,他们的中断函数分别为
●EXTI0-EXTI4都有自己独立的中断函数,他们的函数名分别为EXTI0_IRQHandler~ EXTI4_IRQHandler;
●外部中断5到9共用一个中断函数,函数名为EXTI9_5_IRQHandler;
●外部中断10到15共用一个中断函数,函数名为EXTI15_10_IRQHandler。
对于这些共用中断函数的外部中断,需要在中断函数的内部进一步判断是那个外部中断。
外部中断的触发方式可以有下降沿触发和上升沿触发,这个可以根据具体情况来选择。
1.4 外部中断应用示例
下面我们通过一个例子来学习外部中断的应用。
【例1】使用PE2外部的按键KEY0产生外部中断,每发生一次外部中断将LED0的状态反转一次。在整个过程中,LED1以1秒的频率闪烁。
【实现过程】
1.设置系统时钟源和系统时钟的频率为72MHz;
2.设置调试方式为Serial Wire;
3.设置与LED0相连的PE12,与LED1相连的PE13工作方式为输出;
4.设置与KEY0相连的PE2引脚的为外部中断引脚,抢占式优先级为3(由于系统滴答定时器的优先级为0,所以这个优先级比0低即可)。整个过程的步骤为:
(1)设置PE2的工作模式为外部中断,如图2所示。
图2 PE2引脚设置为外部中断示意图
(2)在GPIO中设置PE2的中断触发方式为下降沿触发(按键按下就触发),如图3所示。
图3 设置下降沿触发示意图
(3)设置外部中断2的抢占式优先级的值为3,实际上由于本工程中除了系统滴答定时器的中断,其他的中断都没有,所以这里设置不与滴答定时器的中断优先级相同即可(滴答定时器的中断优先级为0)。设置步骤和结果如图4所示。
图4 优先级设置示意图
注意,如果EXTI2的中断的优先级采用默认,也就是抢占式优先级为0,子优先级也为0,这两个优先级跟滴答定时器(System tick timer)的优先级一样,则可能在中断中使用延时函数HAL_Delay()时,有可能会使系统死机(延时函数HAL_Delay()通过tick 定时器的中断来工作,当进入EXTI2中断函数执行时,tick 定时器的中断得不到及时执行,所以会出现死机现象!)。
5.配置好中断后,接下来是配置工程名、工程存放路径等信息,然后点击生成代码,生成代码后,我们需要补充如下功能:
(1)在主函数的while循环中补充是的LED1闪烁的程序段,具体如图5所示。
图5 LED1闪烁程序段示意图
(2)编写中断服务回调函数,注意不是中断服务函数,该函数的内容如图6所示。
图6 EXTI2的中断回调(callback)函数
程序编写好后,编译程序并将程序下载到开发板中,可以看到LED1闪烁,LED0则是按下一次KEY0键LE0的状态反转一次,任务目标完成。
1.5 HAL库中中断函数的执行流程
在刚才的步骤中,有一步为编写中断服务回调函数,注意,为回调函数而不是中断服务函数,这跟我们前面的介绍是不是互相矛盾呢?我们通过观察CubeMX生成的工程的中断的执行流程来回答这个问题。这个流程如下:
①系统接收到EXTI2的中断请求并响应后,到.s文件中寻找到该中断函数的入口,如图7所示。
图7 外部中断EXTI2的中断服务函数的入口示意图
②由于函数的名字就是函数的入口,所以找到入口后,接下来执行函数EXTI2_IRQHandler,如图8所示。函数EXTI2_IRQHandler就是EXTI2的中断服务函数。
图8 中断服务函数EXTI2_IRQHandler的内容
③执行通用IO口外部中断函数HAL_GPIO_EXTI_IRQHandler(),这个函数是一个通用函数,其他的GPIO口的外部中断都调用这个函数,它的参数只有一个,就是中断的输入引脚标号,实际上就是外部中断线编号。函数HAL_GPIO_EXTI_IRQHandler的内容如图9所示。
图9 通用外部中断函数的内容示意图
由图9可以看到,在该函数中,在判断GPIO_Pin引脚对应的标志位为真后执行两个动作,分别是
●使用宏__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin)来清除标志位,以便下一次的中断能够进来;
●调用中断回调函数HAL_GPIO_EXTI_Callback(),我们要实现的中断需要做的事情就在中断回调函数中实现,所以在前面的步骤中,我们编写的是中断回调函数的代码,而不是中断服务函数的代码,当然,大家也可以将代码内容直接写到中断服务函数中。
1.6 外部中断实验中涉及到的HAL库的函数/宏及其他相关知识
1.获取外部中断标志位和清除外部中断标志位
中断标志位被置1说明该中断获得了响应,获得响应后要在中断服务函数中清除该标志位,以使得下次该中断有中断申请到来时能获得响应。HAL库中,获取中断标志位和清除中断标志位分别用下面两个宏来完成。
●获取中断标志位:__HAL_GPIO_EXTI_GET_IT()
●清除中断标志位: __HAL_GPIO_EXTI_CLEAR_IT()
这两个宏都只有一个参数,就是外部中断输入引脚的编号。
注意,与HAL库中的函数不用,HAL库中宏名用两个下划线开始,其格式为
“__” + “HAL” + 模块名 + 执行动作名
2.外部中断回调函数HAL_GPIO_EXTI_Callback()
HAL库中回调函数使用Callback结尾,对于外部中断回调函数,它只有一个参数,这个参数就是外部中断的编号—也即外部中断输入IO引脚的标号。
3.HAL库初始化函数HAL_Init()
每次我们用STM32CubeMX生成工程时,主函数中的第一条语句就是执行函数HAL_Init()的内容,这个函数具体做什么呢?我们打开看一下,可以看到这个函数的内容如图10所示。
图10 HAL库初始化函数HAL_Init()的内容
由图可见,函数HAL_Init()主要所两个事,分别是:
①设置抢占式优先级的位数,这里是设置为4位,其值可以从015,所以抢占式优先级可以设置为015的任意数字。
②配置tick timer定时器,并设置它的优先级。
1.7 结论及注意事项
在本章的学习中,主要注意以下几点:
1.发生外部中断后,在系统提供的中断服务函数中已经清除了中断标志位,所以大家在编写中断回调函数的时候不需要再次清除中断标志位。
2.中断的优先级不要跟tick timer,也就是滴答定时器的中断优先级一样,否则在中断回调函数中使用HAL_Delay函数时会出现死机现象。
3.GD32/STM32的优先级是:优先级值越小的中断,它的中断优先级越高。
全部0条评论
快来发表一下你的评论吧 !