跳转至

5.3 STM32驱动开发-中断向量控制

前言

这里主要讲解 Breeze Mini 的中断控制驱动,完整代码在工程目录下的 Drivers 子目录下的 stm32f10x_driver_nvic.cstm32f10x_driver_nvic.h 中,该代码主要用来初始化各外设的中断和中断优先级。

相关知识

中断简介

STM32中断

Cortex-M3内核支持256个中断,其中包括16个内核中断和240个外部中断,并且其具有256级的可编程中断设置。但是STM32并且没有完全实现Cortex-M3标准中的这些中断。实际上STM32只有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。我们常用的是这68个可屏蔽中断,但是STM32的68个可屏蔽中断在STM32F103系列产品上只有60个(在F107系列上才有68个)。由于我们用的主控是 STM32F103TBU6,所以这里只介绍这60个可屏蔽中断。

NVIC简介

NVIC(Nested Vectored Interrupt Controller) 为STM32的嵌套中断向量控制器,在 core_cm3.h 中定义了与NVIC有关的寄存器变量:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
typedef struct
{
    __IO uint32_t ISER[8];
         uint32_t RESERVED0[24];
    __IO uint32_t ICER[8];
         uint32_t RSERVED1[24];
    __IO uint32_t ISPR[8];
         uint32_t RESERVED2[24];
    __IO uint32_t ICPR[8];
         uint32_t RESERVED3[24];
    __IO uint32_t IABR[8];
         uint32_t RESERVED4[56];
    __IO uint8_t  IP[240];
         uint32_t RESERVED5[644];
    __O  uint32_t STIR;
}  NVIC_Type;

只有了解这些中断寄存器变量,才能更加方便地使用STM32的中断功能来设计程序。下面重点介绍6个寄存器:

ISER寄存器

ISER(Interrupt Set-Enable Registers) 是中断使能寄存器组,组里有8个32位寄存器,共256个位,每一位控制前面说的Cortex-M3内核所支持的256个中断。但是STM32F103TBU6的可屏蔽中断只有60个,所以只用到ISER[0]和ISER[1],加起来能控制64个中断,其中多出的高4位不用。要想使能某个中断,必须要置对应ISER位为1,使能该位(光使能还不够,还需要设置中断分组、屏蔽和IO口映射等设置才能正常工作)。

ICER寄存器

ICER(Interrupt Clear-Enable Registers) 是中断失能寄存器组,寄存器数和布局都和ISER完全一样,只是向对应位写1会失能对应中断,写0无效。

ISPR寄存器

ISPR(Interrupt Set-Pending Registers) 是中断挂起控制寄存器组,寄存器数和布局都和ISER完全一样,只是向对应位写1会将正在执行的中断挂起,写0无效。

ICPR寄存器

ICPR(Interrupt Clear-Pending Registers) 是中断解挂控制寄存器组,寄存器数和布局都和ISER完全一样,只是向对应位写1会解挂对应中断,写0无效。

IABR寄存器

IABR(Interrupt Active Bit Registers) 是中断激活标志位寄存器组,寄存器数和布局都和ISER完全一样,当对应位为1时,表示该位所对应的中断正在执行,不过该寄存器是只读的,通过读对应位的值可以得到正在执行的中断是哪一个,当中断执行完成后对应位由硬件自动清零。

IP寄存器

IP(Interrupt Priority Registers) 是中断优先级控制寄存器组,这个寄存器非常重要,是用来设置STM32各外设的中断优先级的,IP寄存器由240个8位的寄存器组成,每个可屏蔽中断占用8位,这样一共可以控制240个中断,而STM32只用了其中的前60个,每个可屏蔽中断所占用的8位并没有完全使用,而只是使用了高4位,这4位中又分为 抢占优先级子优先级 位,其中抢占优先级位在前,子优先级在后。这两个优先级位的位数是可以通过 SCB-> AIRCR 中的中断分组来设置。简单来说,就是抢占和子优先级位加在一起共有4位,可以通过编程设置两个优先级位各占几位,比如设置抢占优先级位为1位,那么子优先级位就会有3位。

STM32中断分组

STM32将所有的中断分成5组,组号0~4。分组的设置是由 SCB->AIRCR 寄存器的8~10位的值决定的,具体情况如下表所示:

1
2
3
4
5
6
        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位子优先级

通过上面这个表我们可以知道如果组设置为3,则此时所有的60个中断的IP寄存器的高4位中最高3位是用来设置抢占优先级,低1位用来设置子优先级,此时对于每个中断可以设置07的抢占和01的子优先级。抢占优先级的响应优先高于子优先级,数值越小表示优先级越高。

中断优先级打断规则:如果两个中断的的抢占和子优先级都完全一样,则哪个中断先产生就先执行。此外高优先级的抢占优先级可以打断正在执行的低抢占优先级的中断,若两个中断的抢占优先级相同,则哪个中断先产生就先执行。比如现设置中断优先级组为2,然后设置中断3的抢占优先级为2,子优先级为1,中断6的抢占优先级为3,子优先级位0,中断7的抢占优先级为2,子优先级位0,则优先级顺序为 ** 中断7 > 中断3 > 中断6**。此时中断3和7都可以打断中断6的执行,但是中断3和7不能相互打断。

固件库配置中断

STM32与中断相关的库函数在 FWLib 目录下的 misc.cmisc.h 中。

首先介绍的是中断优先级分组函数 NVIC_PriorityGroupConfig,其原型为:

1
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

这个函数的作用是对中断的优先级进行分组,一旦分好组后就不要再更改了,这个函数在程序中最好只调用一次(理论上也可以调用多次,但是需要保证每次调用时设置的分组都一样),这个函数的具体实现为:

1
2
3
4
5
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
    assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
    SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}

通过上述定义可以看出这个函数的唯一目的就是通过设置 SCB-> AIRCR 寄存器来设置中断优先级分组,这在前面已经介绍过了,选中 "IS_NVIC_PRIORITY_GROUP",然后右键 "Go to defition of..." 可以跳转到其定义处:

1
2
3
4
5
#define IS_NVIC_PRIORITY_GROUP(GROUP) (((GROUP) == NVIC_PriorityGroup_0) || \
                                       ((GROUP) == NVIC_PriorityGroup_1) || \
                                       ((GROUP) == NVIC_PriorityGroup_2) || \
                                       ((GROUP) == NVIC_PriorityGroup_3) || \
                                       ((GROUP) == NVIC_PriorityGroup_4))

这是一个含参数的宏定义,这样如果要设置整个系统的中断优先级组为2,则代码为:

1
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

设置好整个系统的中断分组后,还要对于每个外设中断设置它的抢占和子优先级,这里需要用到中断初始化函数 NVIC_Init,其原型为:

1
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

其中 NVIC_InitTypeDef 定义为:

1
2
3
4
5
6
7
typedef struct
{
    uint8_t NVIC_IRQChannel;
    uint8_t NVIC_IRQChannelPreemptionPriority;
    uint8_t NVIC_IRQChannelSubPriority;
    FunctionalState NVIC_IRQChannelCmd;
} NVIC_InitTypeDef;

NVIC_IRQChannel 表示待初始化中断名,所有STM32支持的各外设中断名可以从 stm32f10x.h 中找到,NVIC_IRQChannelPreemptionPriority 表示中断的抢占优先级别,NVIC_IRQChannelSubPriority 表示中断的子优先级别,NVIC_IRQChannelCmd 表示中断使能控制。

比如若要使能串口1的中断,要求设置抢占优先级为1,子优先级为0,具体代码为:

1
2
3
4
5
6
7
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel                   = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 1;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
NVIC_Init(&NVIC_InitStructure);

这里我们仅介绍的是中断分组和设置优先级的方法,至于每个外设的对应中断状态如何清除、查询,这个在后面具体介绍每个外设时会再详细介绍。总结来说就是设置中断优先级分为两步为:

  1. 在系统初始化时设置一次中断分组,确定抢占、子优先级的分配位数。

  2. 设置所用中断的中断优先级别,对每个中断调用 NVIC_Init() 初始化。

硬件连接

外设中断控制属于 Breeze Mini 主微控制器STM32F103TBU6自身提供的中断管理功能,是没有外部电路连接的。

软件设计

参考