深入STM32中断——浅谈在HAL库中的机制

什么是中断?   

        在STM32微控制器中,基于其ARM单核架构,程序的执行遵循单线程模式。为了提高处理效率,微控制器可以利用其丰富的外设资源来优化CPU的资源分配,从而增强系统的并发处理能力。

        中断机制是首个我们学习的能够通过其特性来优化CPU资源分配,进而提升系统并发处理能力的外设。熟练掌握中断的使用对于今后编写高效且逻辑清晰的程序至关重要

        下图是它在整个程序执行过程中的逻辑图:

        在系统执行程序的时候,由于在STM32的单核ARM架构影响下,整个进行过程是单线程的。由于中断的加入,在单线程的执行过程中,程序可以通过是否触了进入中断的条件,如果成功触发条件就进入中断里面的执行中断事件,相反。(它会打断原进程)

这里就可以得出它的普遍意义和作用

意义:

1.实时控制:在确定的时间内对相应事件做出相应;例如:温度控制;

2.故障处理:检测到故障,需要第一时间进行处理;

3.数据传输:不确定数据何时会来,利用中断进行控制;

作用:

中断的作用:高效处理紧急程序,并且不会占用CPU资源。

        举一个简单的例子:
        小明在家写作业,听到了自己的手机响了就去接电话了,接完后继续写作业。

      

      在原话中有三个关键词:写作业、手机响了、接电话。它们分别对应了:原程序、发生中断条件、进入中断发生的事件。

        那么如果是电话和门铃同时响,小明他应该先干嘛嘞?下面就要引入“中断管家”的概念

一、中断的管家——NVIC中断优先级分组

1、NVIC的概念

        NVIC(Nested vectored interrupt controller),嵌入向量中断控制器,它是ARM Cortex-M系列微控制器的中的一个关键组件,负责管理和控制中断。

        对于它的降级需要引入几个概念:中断管理、向量表、优先级、中断屏蔽、活跃状态、异常处理。

        在这之前要先澄清CPU与中断服务函数与main(程序执行的主要内容的地方)。

        如下图所示:

在执行主程序main前,以STM32F103c8t6为例:在执行前,会将部分中断服务函数使能

这里的中断服务函数分为:

内核中断服务函数:这部分的中断是微控制器运行的基础,确保系统的正常运行。

外设中断服务函数:在对这部分的中断使能前是为了保证“应用程序需要与外部设备通信或者需要定时器进行精确的时序控制”

在keil5中中断服务函数应该怎么去找嘞,不管是在标准库还是HAL库,都在这个启动文件里面找到:

下图就是部分中断服务函数入口地址(中断向量表):

对于中断向量表的描述:用来查询中断服务函数地址,也是32默认的中断服务函数的执行顺序。所以对于中断向量表能提供2 个信息,一个是中断服务函数的地址,另一个就是中断服务函数默认的执行的顺序。

         执行这些中断服务函数的目的就是去抢夺ARM Cortex-M资源,也就是它的运算能力,抢夺过来来处理中断内的事件,当这些做完后STM32才会去执行mian内的程序。

        在这个抢夺就出现了顺序的问题:应该让谁来做第一个,谁来做最后一个嘞?

        这里就引回了上面在将中断概念提到的“小明”事件了,下面我将细细讲来。

2、NVIC寄存器、工作原理

NVIC寄存器:

(以下资料来自于:参考手册  STM32F10xxx Cortex-M3 编程手册.pdf)

中断使能寄存器(ISER)

位置:4.3.2—NVIC_ISERx

中断除能寄存器(ICER)

位置:4.3.3—NVIC_ICERx

应用程序中断及复位控制寄存器

位置: 4.4.5—SCB_AIRCR

中断优先级寄存器

位置: 4.3.7—NVIC_IPRx

NVIC工作原理

        (1)这里的AIRCR寄存器是用来规定中断优先级(抢占优先级与响应优先级(又称子优先级))的个数的的总个数的;

        (2)寄存器ISER、ICER,分别用1bit控制中断的使能、失能(外设中断),使能后才能对该中断的中断优先级设置;

        (3)IPR寄存器在AIRCR寄存器分配后进行中断优先级的数量的设置,8bit控制一个中断优先级;

        (4)SHPR寄存器是只对内核中断的中断优先级(抢占优先级)进行数量的控制,所以在内核中断与外设中断执行顺序的比较中只会比较抢占优先级,而且内核中断是一直使能,它不需要经过ISER、ICER的寄存器的控制。

        下图是各个STM32型号里的内核中断、外部中断、中断有优先级的总数量。        

 在这里的分组(抢占优先级:响应优先级)有这几种情况:

         [4:0]:16个抢占优先级,0个响应优先级

         [3:1]:12个抢占优先级,4个响应优先级

         [2:2]:8个抢占优先级,8个响应优先级

         [1:3]:4个抢占优先级,12个响应优先级

         [0:4]:0个抢占优先级,16个响应优先级

NVIC中断优先级分组规则:

(1).抢占优先级:高抢占优先级可以打断正在执行的低抢占优先级中断

(2).响应优先级:当抢占优先级相同时,响应优先级高的先执行,但不能互相打断

(3).抢占和响应都相同的情况下,自然优先级高的,先执行

(4).自然优先级:中断向量表的优先级

补充:

(1).上述的高是指的是顺序高,不是数值高,也就是说数值越小,表示优先级越高

(2).先抢占再响应!

例如:

        这里的抢占优先级和响应优先级的位数是通过AIRCR寄存器控制分组、IPR寄存器设置前4位数来实现的。

        额。。。如果这里你还没理解的话,下面我叫一个通俗易懂的:

        现在我有两个外部中断 EXTIO和EXTI1:

我的NVIC分组为[0:4]——代表现在的外部中断只能设置响应优先级,也就是16给优先级

现在只能设置EXTI0 、 EXTI1的响应优先级

设置:EXTIO   12 个响应优先级

           EXTI1   10  个响应优先级

比较下:两个外部中断的执行顺序为EXTI1 在 EXTI0 之前

3、在HAL中对于在CUBE MX里的配置和在kile5 中 NVIC相关的函数

在CUBE MX 中的配置方法:

先使能两个EXTI0和EXTI1两个外部中断

设置NVIC:

        先说主要的

        Priority Group中断优先级分组:这里可以选择抢占优先级与响应优先级分组方式,这里对应着我上面说的NVIC分组来的

        Enabled,勾选这个就能直接使能对应的中断,也可以用来检查是否将对应的中断段使能了。

        Preemption Priority,设置抢占优先级;Sub Priority,设置响应优先级

补充:具体的数量是根据优先级分组来定的,特别说明,它这里的个数是从0开始的也就是说0作为第一个,0-15代表16个中断优先级,但如果是在[4:0]或者[0:4]的情况下,这时候0就对应了没有抢占优先级、没有响应优先级

其他选项的说明:

Search,这里可以直接搜寻,对应的中断名

Sort by Premption Priority and Sub Priorrity,勾选这个进行,抢占与响应优先级的排序

Sort by interrupts names,勾选这个进行按中断名称排序

Force DMA channels Interrupts,指定是否强制使能DMA通道的中断。(这个我会在下面将中断的在HAL库中的机制时具体讲解)

HAL库,在kile5中相关函数

(注意:在CUBE MX配置后这些会自动生成,如果你翔去手动修改直接按照下面函数的作用传入参数就行):

使能函数,传入参数为对应的需要使能的中断

例如:这样,使能EXTI0外设中断

失能也一样的用法。

规定NVIC中断分组情况,传入参数为抢占与响应优先级的分组情况

例如:这样分组抢占优先级:响应优先级[0:4]

设置优先级数量,对抢占优先级与响应优先级数量的设置。

例如:设置外部中断EXTI0的抢占优先级为8、响应优先级为8。

        再次提醒以上的对NVIC的配置均可以在CUBE MX里面直接配置,上面讲解是为了帮助大家理解NVIC的配置的由来,所以不是很建议,在已经配置完后再去手动配置一遍NVIC!!!!

二、在HAL库中的“中断服务函数”——中断回调机制

1、概念

        在HAL库里面用来对于进入中断后执行内部的事件的这个过程不叫做“中断服务函数”,这个概念是来之标准库的。

        从逻辑上面来讲:

        标准库的处理逻辑,进入中断里开始执行程序,执行完后,先清除标志位然后在退出中断;而在HAL库里面处理的逻辑就截然不同,在HAL库里面想要执行中断服务函数,它不像标准库直接编写中断服务函数就行。在HAL库里面,你需要通过HAL库中断处理公用函数、HAL库数据处理回调函数来实现具体流程为可以表为:

实现一个中断内的时间 =中断服务例程——>HAL库中断处理公用函数——>HAL库数据处理回调函数——>HAL库中断处理公用函数——>中断服务例程

特别注意:

        (1)中断例程:是指中断向量表中地址映射的函数,这个函数如果在没有使能前,是为被定义的,它只是在提前为地址做了个标识,这个需要自己使能后,才能对这个函数进行操作,当然这个函数会在,你CUBE MX生成工程过程中直接生成这个函数。

        (2)HAL库中断处理公用函数:

中断向量表中,可以看出,去使用那个中断,就需要先去定义那个中断例程

在自动生成的中断例程中,它会生成指向中断处理公用的函数

在中断处理公用函数里,执行着异常处理、清中断标志等处理

最后就是需要设计进入中断后执行的事件,需要你在回调函数中编程,注意在每个外设只能定义一个回调函数,但像EXTI这种外部中断有多个的时候,但只有一个回调函数的情况,可以在回调函数里面,可以通过条件编程来实现,应用多个EXTI外部中断,如下图:

       

       下图是它一个工作的逻辑图,便于大家理解:

2、代码实例(这一部分博主会在下一张用一个简单和一个进阶的代码实例,来细细讲解)

        (博主用的是正点原子的mini板)。

        代码逻辑:

        按下Key_0,再按下按键Key_1,观察是那个灯亮,按照EXTI0先执行EXTI1后执行的逻辑,
        正常结果:会出现LED_0发生闪烁而不是LED_发生闪烁,
        错误结果:如果是EXTI1先执行的话当LED_1闪烁完后再次重复按键操作会出现LED_0长亮

      先选择芯片型号,STM32F103RCT6

        使能LED0与LED1引脚PA8与PD2:初始化为推完输出与高电平,电平触发方式为电平下降沿触发。

        使能EXTI0与EXTI1外部中断引脚PA0与PA1:初始化为下降沿电平触发EXTI0与EXTI1外部中断。

        

        设置NVIC中断优先级分组:

        (1)分组为[4:0],16位抢占优先级,0位响应优先级

        (2)分配优先级数量:EXTI0抢占优先级数量为0,EXTI1抢占优先级的数量为1,既如果同时触发外部中断EXTI0与EXTI1的条件时先执行外部中断EXTI0里的事件再执行后者。

        设置按键Key_0与Key_1的引脚为PA15与PC5:初始化为上拉输出,电平下降沿触发

        创建工程,打开Keli5进行编程:

宏定义两个按键对应的引脚,创建第二个中断触发的条件变量

#define Key_0   HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_15)
#define Key_1   HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5)

int num=0;

编写EXTI中断回调函数

void  HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  switch (GPIO_Pin)
	{
	  case GPIO_PIN_0:	
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8,GPIO_PIN_RESET);
		    HAL_Delay(100);
		    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8,GPIO_PIN_SET);
		 break;
		
		case GPIO_PIN_1:
			  HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
		    HAL_Delay(100);
		    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8,GPIO_PIN_SET);
		    num = 1;
		 break;
	}
}

编写自建逻辑

		/*这里的自检逻辑
		按下Key_0,再按下按键Key_1,观察是那个灯亮,按照EXTI0先执行EXTI1后执行的逻辑,
		正常结果:会出现LED_0发生闪烁而不是LED_发生闪烁,
		错误结果:如果是EXTI1先执行的话当LED_1闪烁完后再次重复按键操作会出现LED_0长亮
		*/
		if(Key_0 == RESET)
		{
			
			if(num ==1&&Key_1 == 0)
			{
			while(1)
			{
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);
			}
	   	}
			
			else 
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);
			HAL_Delay(10);
				
		}
		if(Key_0 == RESET)
		{
			if(Key_1 == 0)
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);
		}
		

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值