欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32 NVIC中断优先级管理

程序员文章站 2024-02-25 14:51:33
...

STM32 NVIC中断优先级管理

后面用一个具体的例子说明了中断分组、中断线、中断通道的连接关系,先看基本概念:

CM3内核支持256个中断,包括16个内核中断和240个外部中断,并且具有256级的可编程中断设置。STM32只使用了一部分CM3内核的东西。STM32有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。常用的就是这68个可屏蔽中断,但是在STM32F103系列上面只有60个(107系列有68个)。
在MDK内,与NVIC相关的寄存器,MDK为其定义了如下的结构体:

typedef struct
{
    vu32 ISER[2];
    u32 RESERVED0[30];
    vu32 ICER[2];
    u32 RSERVED1[30];
    vu32 ISPR[2];
    u32 RSERVED2[30];
    vu32 ICPR[2];
    u32 RSERVED3[30];
    vu32 IABR[2];
    u32 RSERVED4[30];
    vu32 IPR[15];
}NVIC_TypeDef;

ISER[2]:ISER全称是:Interrupt Set-Enable Registers,这是一个中断使能寄存器组。103系列可屏蔽中断有60个,这里用了2个32位寄存器,总共可以表示64个中断,STM32F103只用了其中的前60位,ISER[0]的bit0~bit31分别对应中断0~31。ISER[1]的bit0~27对应中断32~59;这样,要使能某个中断,必须设置相应的ISER位为1,使该中断被使能(这里仅是使能,还要配合中断分组、屏蔽、IO口映射等设置才算一个完整的中断设置)。
ICER[2]:全称是Interrupt Clear-Enable Registers,是一个清除中断使能寄存器组,和ISER寄存器功能相反。这里专门设置一个ICER寄存器来清除中断位,而不是向ISER写0来擦除,是因为NVIC的这些寄存器都是写1有效的,写0是无效的。
ISPR[2]:全程是Interrupt Set-Pending Registers,是一个中断挂起控制寄存器组。每个位对应的中断和ISER是一样的,通过置1,可将正在进行中的中断挂起,而执行同级或更高级别的中断,写0无效。
ICPR[2]:Interrupt Clear-Pending Registers,解除中断挂起。写1有效,写0无效。
IABR[2]:Interrupt Active Bit Registers,中断**标志位寄存器组,只读,可以读取当前正在执行的中断是哪一个。在中断执行完成后由硬件自动清零。对应位所代表的中断和ISER相同,如果为1,表示该位所对应的中断正在执行。
IPR[15]:Interrupt Priority Registers,中断优先级控制寄存器组。STM32的中断分组与这个寄存器密切相关。因为STM32的中断多达60多个,所以STM32采用中断分组的办法来确定中断的优先级。IPR寄存器由15个32bit的寄存器组成,每个可屏蔽中断占8bit,这样总共可以表示15x4=60个可屏蔽中断。IPR[0]的[31~24],[23~16],[15~8],[7~0]分别对应中断3~0,总共对应60个外部中断。而每个可屏蔽中断占用的8bit并没有全部使用,只用了高4位。这4位又分为抢占优先级和子优先级。这两个优先级要根据SCB->AIRCR中中断分组的设置来决定。
简单介绍STM32的中断分组:STM32将中断分为0~4共5个组,该组是由SCB->AIRCR寄存器的bit10~8来定义的。如下表所示:
表1 AIRCR中断分组设置表

AIRCR[10:8] bit[7:4]分配情况 分配结果
0 111 0:4 0位抢占优先级,4位响应优先级
1 110 1:3 1位抢占优先级,3位响应优先级
2 101 2:2 2位抢占优先级,2位响应优先级
3 100 3:1 3位抢占优先级,1位响应优先级
4 011 4:0 4位抢占优先级,0位响应优先级

通过这个表,可以清楚的看到组0~4对应的配置关系,例如组设置为0x03,此时所有的60个中断,每个中断的中断优先级寄存器的高四位中最高3位是抢占优先级,低1位是响应优先级。每个中断都可以设置抢占优先级为0~7,响应优先级为1或0。抢占优先级的级别高于响应优先级,数值越小所代表的优先级越高
具体优先级的确定和嵌套规则:
* 1 只能高抢先优先级的中断可以打断低抢占优先级的中断服务,构成中断嵌套。
* 2 当2个(N个)相同抢占优先级的中断出现,他们之间不能构成中断嵌套,但STM32首先响应子优先级高的中断。
* 3 当2个(N个)个抢占优先级和子优先级相同的中断出现,STM32首先响应中断同道所对应的中断向量地址低的中断,就是谁先发生谁先被执行。

中断线:
STM32的每个IO口都可以作为外部中断的输入口,以F407为例,407的中断控制器支持22个外部中断请求,
EXTI线0~15:对应外部IO口的输入中断。
EXTI线16:连接到PVD输出。
EXTI线17:连接到RTC闹钟事件。
EXTI线18:连接到USB OTG FS唤醒事件。
EXTI线19:连接到以太网唤醒事件。
EXTI线20:连接到USB OTG HS(在FS中配置)唤醒事件。
EXTI线21:连接到RTC入侵和时间戳事件。
EXTI线22:连接到RTC唤醒事件。
IO口使用的中断线只有16个,但stm32的IO口却不止16个,这些中断线和IO口通过以下关系对应起来:
STM32 NVIC中断优先级管理
PA0、PB0、PC0…共用EXTI0中断标志,这样每个中断标志只能接收1个外部中断源的信号,如果接入多个IO作为中断源,只有最后配置的一个IO有效,硬件原理图设计时需要注意。EXTI0~EXTI4这5个外部中断有着自己单独的中断号(中断服务函数),EXTI5-9共用一个中断服务函数,EXTI10-15共用一个中断服务函数。可以看到Stm32f10x.h头文件中声明的中断号:
STM32 NVIC中断优先级管理
STM32 NVIC中断优先级管理

如何使用库函数实现中断分组设置及中断优先级管理:
* 1 配置中断优先级分组函数NVIC_PriorityGroupConfig。分组0~4,几位抢占优先级几位子优先级,在系统中只能被调用一次,一旦分组确定就不要修改。

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  • 2 设置好系统中断分组,对于每个中断,要设置具体的抢占优先级和子优先级的配置。可以通过NVIC_Init函数初始化,其函数声明为:
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

其中NVIC_InitTypeDef是一个结构体:

typedef struct
{
    uint8_t NVIC_IRQChannel;
    uint8_t NVIC_IRQChannelPreemptionPriority;
    uint8_t NVIC_IRQChannelSubPriority;
    FunctionalState NVIC_IRQChannelCmd;
}NVIC_InitTypeDef;

贴一段外部中断的例程来分析:

//外部中断初始化函数
void EXTIX_Init(void)
{   
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟
    KEY_Init();//初始化按键对应io模式

    //三个按键分别连到了PC5、PA15、PA0,以下对三个引脚进行配置

    //GPIOC.5 中断线以及中断初始化配置
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource5);
    EXTI_InitStructure.EXTI_Line=EXTI_Line5;//上面说了PA0,PB0为EXTI0,这里PC5中断线应该为EXTI5
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器

    //GPIOA.15  中断线以及中断初始化配置
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource15);
    EXTI_InitStructure.EXTI_Line=EXTI_Line15; // PA15中断线为EXTI15
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器

    //GPIOA.0   中断线以及中断初始化配置
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
    EXTI_InitStructure.EXTI_Line=EXTI_Line0;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器

    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;    //使能按键所在的外部中断通道;EXTI0~4单独有自己的中断号(中断服务函数),所以这里是EXTI0。
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;    //抢占优先级2,
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;   //子优先级1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
    NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

    NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;  //使能按键所在的外部中断通道;EXTI5~9共享一个中断号,所以这里是EXTI9_5_IRQn。
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;    //抢占优先级2,
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;   //子优先级1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;    //使能按键所在的外部中断通道;EXTI10~15共享一个中断号,这里EXTI15的中断号当然是EXTI15_10_IRQn。
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;    //抢占优先级2,
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;   //子优先级1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
    NVIC_Init(&NVIC_InitStructure);  
}

参考资料:
STM32不完全手册V3.0
http://www.51hei.com/bbs/dpj-89871-1.html

相关标签: stm32 中断