中断管理函数CM3内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。
但STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。
STM32有76个中断,包括16个内核中断和60个可屏蔽中断,具有16级可编程的中断优先级。
而我们常用的就是这60个可屏蔽中断,所以我们就只针对这60个可屏蔽中断进行介绍。
在MDK内,与NVIC相关的寄存器,MDK为其定义了如下的结构体:typedef struct{vu32 ISER[2];u32 RESERVED0[30];vu32 ICER[2];u32 RSERVED1[30];vu32 ISPR[2];u32 RESERVED2[30];vu32 ICPR[2];u32 RESERVED3[30];vu32 IABR[2];u32 RESERVED4[62];vu32 IPR[15];} NVIC_TypeDef;STM32的中断在这些寄存器的控制下有序的执行的。
了解这些中断寄存器,你才能方便的使用STM32的中断。
下面重点介绍这几个寄存器:ISER[2]:ISER全称是:Interrupt Set-Enable Registers,这是一个中断使能寄存器组。
上面说了STM32的可屏蔽中断只有60个,这里用了2个32位的寄存器,总共可以表示64个中断。
而STM32只用了其中的前60位。
ISER[0]的bit0~bit31分别对应中断0~31。
ISER[1]的bit0~27对应中断32~59;这样总共60个中断就分别对应上了。
你要使能某个中断,必须设置相应的ISER位为1,使该中断被使能(这里仅仅是使能,还要配合中断分组、屏蔽、IO口映射等设置才算是一个完整的中断设置)。
具体每一位对应哪个中断,请参考stm32f10x_nvic..h里面的第36行处。
ICER[2]:全称是:Interrupt Clear-Enable Registers,是一个中断除能寄存器组。
该寄存器组与ISER的作用恰好相反,是用来清除某个中断的使能的。
其对应位的功能,也和ICER一样。
这里要专门设置一个ICER来清除中断位,而不是向ISER写0来清除,是因为NVIC的这些寄存器都是写1有效的,写0是无效的。
具体为什么这么设计,请看《CM3权威指南》第125页,NVIC概览一章。
ISPR[2]:全称是:Interrupt Set-Pending Registers,是一个中断挂起控制寄存器组。
每个位对应的中断和ISER是一样的。
通过置1,可以将正在进行的中断挂起,而执行同级或更高级别的中断。
写0是无效的。
ICPR[2]:全称是:Interrupt Clear-Pending Registers,是一个中断解挂控制寄存器组。
其作用与ISPR相反,对应位也和ISER是一样的。
通过设置1,可以将挂起的中断接挂。
写0无效。
IABR[2]:全称是:Active Bit Registers,是一个中断激活标志位寄存器组。
对应位所代表的中断和ISER一样,如果为1,则表示该位所对应的中断正在被执行。
这是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。
在中断执行完了由硬件自动清零。
IPR[15]:全称是:Interrupt Priority Registers,是一个中断优先级控制的寄存器组。
这个寄存器组相当重要!STM32的中断分组与这个寄存器组密切相关。
IPR寄存器组由15个32bit的寄存器组成,每个可屏蔽中断占用8bit,这样总共可以表示15*4=60个可屏蔽中断。
刚好和STM32的可屏蔽中断数相等。
IPR[0]的[31~24],[23~16],[15~8],[7~0]分别对应中中断3~0,依次类推,总共对应60个外部中断。
而每个可屏蔽中断占用的8bit并没有全部使用,而是只用了高4位。
这4位,又分为抢占优先级和子优先级。
抢占优先级在前,子优先级在后。
而这两个优先级各占几个位又要根据SCB->AIRCR中中断分组的设置来决定。
这里简单介绍一下STM32的中断分组:STM32将中断分为5个组,组0~4。
该分组的设置是由SCB->AIRCR寄存器的bit10~8来定义的。
具体的分配关系如下表所示:表2.7.2.1 AIRCR中断分组设置表通过这个表,我们就可以清楚的看到组0~4对应的配置关系,例如组设置为3,那么此时所有的60个中断,每个中断的中断优先寄存器的高四位中的最高3位是抢占优先级,低1位是响应优先级。
每个中断,你可以设置抢占优先级为0~7,响应优先级为1或0。
抢占优先级的级别高于响应优先级。
而数值越小所代表的优先级就越高。
结合实例说明一下:假定设置中断优先级组为2,然后设置中断3(RTC中断)的抢占优先级为3,响应优先级为1。
中断6(外部中断0)的抢占优先级为4,响应优先级为0。
中断7(外部中断1)的抢占优先级为3,响应优先级为0。
那么这3个中断的优先级顺序为:中断7>3>中断6。
这里需要注意2点:如果两个中断的响应优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行。
高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
而抢占优先级相同的中断,上面例子中的中断3和中断7都可以打断中断6的中断。
而中断7和中断3却不可以相互打断!通过以上介绍,我们熟悉了STM32中断设置的大致过程。
接下来我们介绍如何使用函数实现以上中断设置,使得我们以后的中断设置简单化。
第一个介绍的是NVIC的分组函数MY_NVIC_PriorityGroupConfig,该函数的参数NVIC_Group0~4,总共5组。
如果参数非法,将可能导致不可预料的结果。
MY_NVIC_PriorityGroupConfig函数代码如下://设置NVIC分组//NVIC_Group:NVIC分组 0~4 总共5组void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group){u32 temp,temp1;temp1=(~NVIC_Group)&0x07;//取后三位temp1<<=8;temp=SCB->AIRCR; //读取先前的设置temp&=0X0000F8FF; //清空先前分组temp|=0X05FA0000; //写入钥匙temp|=temp1;SCB->AIRCR=temp; //设置分组}通过前面的介绍,我们知道STM32的5个分组是通过设置SCB->AIRCR的BIT[10:8]来实现的,而通过7.2.3的介绍我们知道SCB->AIRCR的修改需要通过在高16位写入0X05FA这个密钥才能修改的,故在设置AIRCR之前,应该把密钥加入到要写入的内容的高16位,以保证能正常的写入AIRCR。
在修改AIRCR的时候,我们一般采用读->改->写的步骤,来实现不改变AIRCR原来的其他设置。
以上就是MY_NVIC_PriorityGroupConfig函数设置中断优先级分组的思路。
第二个函数是NVIC设置函数MY_NVIC_Init,该函数有4个参数,分别为:NVIC_PreemptionPriority、NVIC_SubPriority、NVIC_Channel、NVIC_Group。
第一个参数NVIC_PreemptionPriority为中断抢占优先级数值,第二个参数NVIC_SubPriority为中断子优先级数值,前两个参数的值必须在规定范围内,否则也可能产生意想不到的错误。
第三个参数NVIC_Channel为中断的编号(范围为0~59),最后一个参数NVIC_Group为中断分组设置(范围为0~4)。
该函数代码如下://设置NVIC//NVIC_PreemptionPriority:抢占优先级//NVIC_SubPriority :响应优先级//NVIC_Channel :中断编号//NVIC_Group :中断分组 0~4//注意优先级不能超过设定的组的范围!否则会有意想不到的错误//组划分://组0:0位抢占优先级,4位响应优先级//组1:1位抢占优先级,3位响应优先级//组2:2位抢占优先级,2位响应优先级//组3:3位抢占优先级,1位响应优先级//组4:4位抢占优先级,0位响应优先级//NVIC_SubPriority和NVIC_PreemptionPriority的原则是,数值越小,越优先void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8 NVIC_Channel,u8 NVIC_Group){u32 temp;u8 IPRADDR=NVIC_Channel/4; //每组只能存4个,得到组地址u8 IPROFFSET=NVIC_Channel%4;//在组内的偏移IPROFFSET=IPROFFSET*8+4; //得到偏移的确切位置MY_NVIC_PriorityGroupConfig(NVIC_Group);//设置分组temp=NVIC_PreemptionPriority<<(4-NVIC_Group);temp|=NVIC_SubPriority&(0x0f>>NVIC_Group);temp&=0xf;//取低四位if(NVIC_Channel<32)NVIC->ISER[0]|=1<<NVIC_Channel;//使能中断位(要清除的话,相反操作就OK)else NVIC->ISER[1]|=1<<(NVIC_Channel-32);NVIC->IPR[IPRADDR]|=temp<<IPROFFSET;//设置响应优先级和抢断优先级}通过前面的介绍,我们知道每个可屏蔽中断的优先级的设置是在IPR寄存器组里面的,每个中断占8位,但只用了其中的4个位,以上代码就是根据中断分组情况,来设置每个中断对应的高4位的数值的。
当然在该函数里面还引用了MY_NVIC_PriorityGroupConfig这个函数来设置分组。
其实这个分组函数在每个系统里面只要设置一次就够了,设置多次,则是以最后的那一次为准。
但是只要多次设置的组号都是一样,就没事。
否则前面设置的中断会因为后面组的变化优先级会发生改变,这点在使用的时候要特别注意!一个系统代码里面,所有的中断分组都要统一!!,以上代码对要配置的中断号默认是开启中断的。
也就是ISER中的值设置为1了。
通过以上两个函数就实现了对NVIC的管理和配置。