STM32F4-NVIC和EXTI

中断优先级管理

NVIC-嵌套向量中断控制器,属于内核外设,管理着包括内核和片上所有外设的中断相关的功能。管理中断嵌套,主要用于优先级的管理。

 普通外设都在标准库stmf4xx_xxx.c中。NVIC属于内核中的外设,相关的函数存放在misc.c中。

内核外设重要的库文件:Cortex-M4 内核的外设也比较多,但STM32并没有用到这么多内核外设对其进行了裁剪,STM32重要的内核外设用到的库函数放在了misc.c文件之中,所以core_cm4.c文件用的较少。

core_cm3.c:内核外设的驱动固件库。
core_cm3.h:实现了内核(CPU)里面的外设的寄存器映射,还有很多关于内核外设的库函数。
misc.h:NVIC_InitTypeDef结构体,以及库函数的参数和声明。
misc.c:NVIC(嵌套向量中断控制器)、SysTick(系统滴答定时器)相关函数主体。

中断向量表

Cortex-M4 内核支持 256 个中断,其中包含 16 个内核中断和 240 个外部中断,并且具有256 级的可编程中断设置。STM32F4 并没有使用CM4 内核的全部东西,只用了它的一部分。STM32F40xx/STM32F41xx总共有 92 个中断, STM32F42xx/STM32F43xx 总共有 96 个中断,STM32F40xx/STM32F41xx 的 92 个中断,包含10个内核中断和82个可屏蔽中断,具有16级可编程的中断优先级,我们常用的就是这 82 个可屏蔽中断

STM32F4xx中文参考手册,P234。

 NVIC中断优先级分组

中断管理方法:对STM32中断进行分组。同时,对每个中断设置抢占优先级和响应优先级值。STM32F4 将中断分为 5 个组,组 0~4。该分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。 每个中断还有个IP寄存器,它的bit[4:7]位这四个位来确定几位抢占几位响应。

比如:当设置了分组2,对每个中断,它是有2个位可以设置抢占优先级,2个位可以设置响应优先级。2个位的话bit[0~1]:值的范围就是0~3 (二进制11)。

抢占优先级和响应优先级:响应优先级只有在两个中断抢占优先级相同的时候才生效。

高优先级的抢占优先级的中断是可以打断正在进行的低优先级的抢占优先级的中断的。
抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
抢占优先级相同的中断,当两个中断同时发生的情況下,哪个响应优先级高,哪个先执行。
如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行。

优先级值设置时,数值越小的说明优先级越高。

举例:假设当设置了中断优先级组为2。然后设置中断3(RTC中断)的抢占优先级为2,响应优先级为1。中断6(外部中断0)的抢占优先级为3,响应优先级为0。中断7(外部中断1)的抢占优先级为2,响应优先级为0。那么这3个中断的优先级顺序为:中断7>中断3>中断6。

注意:一般情况下,代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的后果。

中断优先级分组函数:此函数本质上也是控制SCB->AIRCR[10:8]寄存器。

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

中断优先级设置

MDK中NVIC寄存器结构体

typedef struct
{
  __IO uint32_t ISER[8];      //中断使能寄存器数组           
       uint32_t RESERVED0[24];                    //未用到,reserved保留
  __IO uint32_t ICER[8];      //中断失能寄存器数组          
       uint32_t RSERVED1[24];                    //未用到,reserved保留
  __IO uint32_t ISPR[8];      //中断挂起寄存器数组              
       uint32_t RESERVED2[24];                    //未用到,reserved保留
  __IO uint32_t ICPR[8];      //中断解挂寄存器数组          
       uint32_t RESERVED3[24];                    //未用到,reserved保留
  __IO uint32_t IABR[8];      //中断激活标志位寄存器数组        
       uint32_t RESERVED4[56];                    //未用到,reserved保留
  __IO uint8_t  IP[240];      //中断优先级控制的寄存器数组          
       uint32_t RESERVED5[644];                    //未用到,reserved保留
  __O  uint32_t STIR;                    
}  NVIC_Type;

IP[240]:全称: Interrupt Priority Registers,是一个中断优先级控制的寄存器组。STM32F4 的中断分组与这个寄存器组密切相关。IP 寄存器组由 240 个8bit的寄存器组成,每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断。 而 STM32F4只用到了其中的 82 个。 IP[81]~IP[0]分别对应中断 81~0。 而每个可屏蔽中断占用的 8bit 并没有全部使用,而是只用了高 4 位。这 4 位,又分为抢占优先级和响应优先级。抢占优先级在前,响应优先级在后。而这两个优先级各占几个位又要根据 SCB->AIRCR 中的中断分组设置来决定。

ISER[8]:Interrupt Set-Enable Registers,这是一个中断使能寄存器组。里面有8个32位寄存器。上面说了 CM4 内核支持 256 个中断,这里用 8 个 32 位寄存器来控制,每个位控制一个中断。但是STM32F4 的可屏蔽中断最多只有 82 个,所以对我们来说,有用的就是三个(ISER[0~2]),总共可以表示 96 个中断。而 STM32F4 只用了其中的前 82 个。 ISER[0]的 bit0~31 分别对应中断0~31; ISER[1]的 bit0~32 对应中断 32~63; ISER[2]的 bit0~17 对应中断 64~81;这样总共 82 个中断就分别对应。要使能某个中断,必须设置相应的 ISER 位为 1,使该中断被使能(这里仅仅是使能,还要配合中断分组、屏蔽、 IO 口映射等设置才算是一个完整的中断设置)。具体每一位对应哪个中断,请参考 stm32f4xx.h 里面的第 188 行处。ICER[8]:是一个中断除能寄存器组。该寄存器组与 ISER 的作用恰好相反,是用来清除某个中断的使能。其对应位的功能,也和 ICER 一样。这里要专门设置一个 ICER 来清除中断位,而不是向 ISER 写 0 来清除,是因为 NVIC 的这些寄存器都是写 1 有效的, 写 0 是无效的。

用来设置每个中断的抢占优先级和响应优先级以及中断使能的函数:

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)

结构体成员变量:

typedef struct
{
  uint8_t NVIC_IRQChannel;          //用来设置中断源,不同中断的中断源不同  82选1
  uint8_t NVIC_IRQChannelPreemptionPriority;   //设置抢占优先级
  uint8_t NVIC_IRQChannelSubPriority;          //设置响应优先级
  FunctionalState NVIC_IRQChannelCmd;          //设置使能或者失能
} NVIC_InitTypeDef;

配置实例:

NVIC_InitTypeDef   NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//响应优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能
NVIC_Init(&NVIC_InitStructure);//根据上面指定参数初始化NVIC寄存器

ISPR[8]:Interrupt Set-Pending Registers,是一个中断挂起控制寄存器组。每个位对应的中断和 ISER 是一样的。通过置 1,可以将正在进行的中断挂起,而执行同级或更高级别的中断。写 0 是无效的。
ICPR[8]:Interrupt Clear-Pending Registers,是一个中断解挂控制寄存器组。其作用与 ISPR 相反,对应位也和 ISER 是一样的。通过设置 1,可以将挂起的中断解挂。写 0 无效。

中断挂起和解挂相关库函数:

static_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn);

static_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn);

static_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn)

IABR[8]:Interrupt Active Bit Registers,是一个中断激活标志位寄存器组。对应位所代表的中断和 ISER 一样,如果为 1,则表示该位所对应的中断正在被执行。这是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。在中断执行完了由硬件自动清零。

static_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn)
中断优先级设置步骤:

1.系统运行后先设置中断优先级分组,调用函数:NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);整个系统执行过程中,只设置一次中断分组。

2.针对每个中断,设置对应的抢占优先级和响应优先级,调用函数: NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)

3.如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。

编写中断服务函数

在启动文件 startup_stm32f40_41xxx.s中我们预先为每个中断都写了一个中断服务函数,只是这些中断服务函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要我们重新编写。如果一切配置完毕,当中断来临时,CPU会根据相应的中断去中断向量表找到对应的中断函数的地址,然后调用执行中断服务函数。

外部中断-EXTI

外部中断基本结构

 普通的GPIO作为输入输出,EXTI则是把普通的GPIO复用作为检测口,根据EXTI寄存器配置是中断模式还是事件模式。来控制引脚的输入信号是到NVIC中去,还是到脉冲发生器中去。如果配置为中断,则引脚的输入信号会通过上升/下降沿触发选择寄存器、软件中断事件寄存器、请求挂起寄存器、中断屏蔽寄存器共同设置最后达到NVIC中。CPU则会响应这个中断,去处理它的中断服务函数。事件的话,最后到达脉冲发生器,不需要CPU响应。

STM32F4 的每个 IO 都可以作为外部中断的中断输入口。STM32F407 的中断控制器支持 23个外部中断/事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。

STM32F407 的 23 个外部中断为:
        EXTI 线 0~15:对应外部 IO 口的输入中断。(以此重点展开描述)

EXTI16~22作为EXTI外设的输入线:

        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 唤醒事件。

每个外部中断线可以独立的配置触发方式(上升,下降,双边沿触发),触发 / 屏蔽,专用的状态位。

STM32F4 供 IO 口使用的中断线只有 16 个,STM32F4 的 IO 口远不止 16 个。因此做如下设计:

GPIO的管脚 GPIOx0~GPIOx15(x=A,B,C,D,E,F,G,H,I)分别对应中断线 0~15。这样每个中断线对应了最多 9 个 IO 口,以EXTI 0这根中断线为例:它对应了 GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0、GPIOH.0、GPIOI.0。而中断线每次只能连接到 1 个 IO口上,这样就需要通过配置来决定对应的中断线连接到哪个 GPIO口上了。

16个中断线的不是每个中断都有独立的中断服务函数,IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数。从表中可以看出,外部中断线5~9分配一个中断向量,共用一个服务函数。外部中断线10~15分配一个中断向量,共用一个中断服务函数。

EXTI内部框图:

  中断服务函数列表:

EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler

 外部中断常用库函数:
void SYSCFG_EXTILineConfig(unit8_t EXTI_PortSourceGPIOx,unit8_t EXTI_PinSourcex);
//设置IO口与中断线的映射关系
使用范例:SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);//初始化中断线
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);//判断中断线中断状态,是否发生
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);//清除中断线上的中断标志位
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//此函数很重要,在使用外部中断的时候一定要先使能SYSCFG时钟
外部中断的一般配置步骤(使用 IO 口外部中断的一般步骤):

1) 使能 IO 口时钟, 初始化 IO 口为输入。

GPIO_Init();

2) 使能 SYSCFG 时钟, 设置 IO 口与中断线的映射关系。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

void SYSCFG_EXTILineConfig(unit8_t EXTI_PortSourceGPIOx,unit8_t EXTI_PinSourcex);

3)初始化线上中断,设置触发条件等。

EXTI_Init();

4)配置中断分组(NVIC),并使能中断。

NVIC_Init();

5)编写中断服务函数。

EXTIx_IRQHandler();

6)清除中断标志位

EXTI_ClearITPendingBit();

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值