0


Air32F103学习笔记-5.中断配置NVIC

中断是单片机非常重要的功能,也是一个难点,本节单独讲下NVIC,以及NVIC的配置。

一.关于NVIC

NVIC: Nested Vectored Interrupt Controller 内嵌向量中断控制器 是M3内核的一个外设

是用来总控中断的,例如中断优先级设置,中断使能等

下面看下《STM32F10xxx Cortex-M3编程手册-英文版》中关于NVIC的描述:

这部分描述了内嵌向量中断控制器以及寄存器的使用,NVIC支持:

  • 最多81个中断(具体数量取决于STM32的型号,请参考数据手册)
  • 每个中断都支持0-15个可编程的优先级。等级越高(标号越大),优先级越低,所以0是优先级最高的。
  • 中断支持电平检测以及边沿检测
  • Dynamic reprioritization of interrupts 中断的动态重新分配优先级(暂且这么翻译)
  • 优先级支持编组,并且分为主优先级和抢占优先级
  • 咬尾中断
  • 1个外部不可屏蔽中断NMI Non-Mask Interrurt

说了这么多,其实有用的就只有3点:

  1. 中断优先级支持0-15优先级,编号越小,优先级越高

  2. 中断优先级又分为两组,分别是主优先级和抢占优先级

  3. 中断支持电平触发和边沿触发(应该是说外部中断EXIT)

上面2和1是不是有点矛盾?不急我们先往下说:

看下NVIC的相关寄存器,这部分比较无聊,且用固件库编程,无需知道这些,可以略过:

NVIC的寄存器按照功能分为6大类(常用标红):

  • Set-enable 使能
  • Clear-enable 失能
  • Set-pending 设置挂起
  • Clear-pending 清除挂起
  • Active Bits
  • Interrupt Priority Registers 中断优先级寄存器

那么对应的,用以下简写,以及数组来匹配相应的寄存器;

在手册里是这么描述的,每个数组将对应其寄存器,例如使能,ISER[0]对应ISER0

完整描述看下面:
功能 寄存器全名数组寄存器Set-enable 使能Interrupt Set Enable RigisterISER[0],ISER[1],ISER[2]ISER0,ISER1,ISER2Clear-enable 失能Interrupt Clear Enable RigisterICER[0],ICER[1],ICER[2]ICER0,ICER1,ICER2Set-pending 设置挂起
Interrupt Set Pending

Rigister
ISPR[0],ISPR[1],ISPR[2]ISPR0,ISPR1,ISPR2Clear-pending 清除挂起Interrupt Clear Pending RigisterICPR[0],ICPR[1],ICPR[2]ICPR0,ICPR1,ICPR2Active BitsInterrupt Active Bits RigisterIABR[0],IABR[1],IABR[2]IABR0,IABR1,IABR2
例如常用的Set-enable 使能ISER,Clear-enable 失能ICER解释如下:

根据core_cm3.h的定义,每个ISER数组都是32位的,每一位控制1个中断的状态,并且是可读可写的。

1是使能,0是无效。复位后全部都是0,即复位后,中断默认全关,要省电嘛~~~

ISCR同理

中断优先级寄存器Interrupt Priority Registers,简称IP,M3内核的MCU有8位,理论上可以支持2的8次方,0~255,共256级,但是根本用不完,所以厂家阉割了,STM32只用了8位中的高4位用来定义优先级,就是2的4次方即16,所以中断优先级最多支持0-15级。不是16个中断的意思,多个中断可以排在同一优先级上。

但是Air32F103只用了3位定义优先级!!!所以优先级支持0~7,共8级

第二节关于中断源列出了所有中断表,Air32F103一共67个可编程中断,所以到IP[80]足够用了。

/**
  \ingroup    CMSIS_core_register
  \defgroup   CMSIS_NVIC  Nested Vectored Interrupt Controller (NVIC)
  \brief      Type definitions for the NVIC Registers
  @{
 */

/**
  \brief  Structure type to access the Nested Vectored Interrupt Controller (NVIC).
 */
typedef struct
{
  __IOM uint32_t ISER[8U];               /*!< Offset: 0x000 (R/W)  Interrupt Set Enable Register */
        uint32_t RESERVED0[24U];
  __IOM uint32_t ICER[8U];               /*!< Offset: 0x080 (R/W)  Interrupt Clear Enable Register */
        uint32_t RESERVED1[24U];
  __IOM uint32_t ISPR[8U];               /*!< Offset: 0x100 (R/W)  Interrupt Set Pending Register */
        uint32_t RESERVED2[24U];
  __IOM uint32_t ICPR[8U];               /*!< Offset: 0x180 (R/W)  Interrupt Clear Pending Register */
        uint32_t RESERVED3[24U];
  __IOM uint32_t IABR[8U];               /*!< Offset: 0x200 (R/W)  Interrupt Active bit Register */
        uint32_t RESERVED4[56U];
  __IOM uint8_t  IP[240U];               /*!< Offset: 0x300 (R/W)  Interrupt Priority Register (8Bit wide) */
        uint32_t RESERVED5[644U];
  __OM  uint32_t STIR;                   /*!< Offset: 0xE00 ( /W)  Software Trigger Interrupt Register */
}  NVIC_Type;

core.cm3.h定义了非常多的中断,IP一共240个数组,能定义240个中断的优先级,牛

关于优先级分组:

Air32F103和STM32103有很大不同,并且固件库里关于分组是错误的!

STM32支持16级中断优先级(4Bits用于设置分组),AIR32F103只支持8级(3Bits用于设置分组),那么固件库里关于分组的信息就是错误的

照抄STM32改个头文件名字就全拿来用了。。。

/** @defgroup MISC_Private_Functions
  * @{
  */

/**
  * @brief  Configures the priority grouping: pre-emption priority and subpriority.
  * @param  NVIC_PriorityGroup: specifies the priority grouping bits length. 
  *   This parameter can be one of the following values:
  *     @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority
  *                                4 bits for subpriority
  *     @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority
  *                                3 bits for subpriority
  *     @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority
  *                                2 bits for subpriority
  *     @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority
  *                                1 bits for subpriority
  *     @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority
  *                                0 bits for subpriority
  * @retval None
  */
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
  /* Check the parameters */
  assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
  
  /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
  SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}

吐槽完,咱们看下优先级该怎么办。。。

首先了解下中断分组,分组分为抢占优先级和子优先级

抢占优先级:中断之间互相打断用的,优先级高的打断优先级低的去执行。如果优先级相同呢,看下面;

子优先级:抢占优先级相同时比较,子优先级,同样遵循标号越低优先级越高原则。

如果抢占优先级和子优先级全部相同时,比较中断标号,标号越低,优先级越高。

CM3权威指南-宋岩

原则上,CM3支持3个固定的高优先级和多达256级的可编程优先级,并且支持128级抢占(128的来历请见下文分解——译注)。但是,绝大多数CM3芯片都会精简设计,以致实际上支持的优先级数会更少,如8级,16级,32级等。它们在设计时会裁掉表达优先级的几个低端有效位,以减少优先级的级数(可见,不管使用多少位来表达优先级,都是以MSB对齐的——译者注)。

这是本好书啊,值得读10遍。书中举例用3位表示优先级正好对应Air32F103的情况。

也就是说,Air32F103用了高3位来表示优先级,优先级从高倒地依次为0x00,0x20,0x40,0x80,0xA0,0xC0,0xE0
0x1110 00000xE00x1100 00000xC00x1010 00000xA00x1000 00000x800x0110 00000x600x0101 00000x400x0010 00000x200x0000 00000x00
分组咋分呢。。。

分组是在SCB->AIRCR寄存器里的

这里有强调:AIRCR是用来提供分组控制的,如果想要写寄存器,必须在VECTKEY这部分中写入0X05FA,否则芯片将忽略写操作。

VECTKEY是【31:16】位,高16位的值固定了,05FA

【15】:写0

【14:11】:写0

【10:8】:用来进行分组,这里PRIGROUP的值很有意思,乍一看不好理解,但是如果我们把这里的所有值都列出来,对比下就清晰了。

分组位置

PRIGROUP

[10:8]
Binary Points
表达抢占优先级的位段

Group priority bits

表达子优先级的位段

Subpriority bits
0b0000bxxxxxxx.y[7:1][0:0]0b0010bxxxxxx.yy[7:2][1:0]0b0020bxxxxx.yyy[7:3][2:0]0b0030bxxxx.yyyy[7:4][3:0]0b0040bxxx.yyyyy[7:5][4:0]0b0050bxx.yyyyyy[7:6][5:0]0b0060bx.yyyyyyy[7:7][6:0]0b0070byyyyyyyy无[7:0](所有位)
其实这里的值代表了,子优先级的位数。

例如当【10:8】全部写0,即0b000,表示子优先级是【0:0】位,即只有0位用来表示子优先级,那么抢占优先级就是【7:0】位了。即分组值0b000,=0时,抢占优先级共7位,子优先级共1位。

但是Air32F103一共才3位用来表示优先级,所以【10:8】位,写0,1,2,3,4结果都是一样的,即抢占优先级用全部高3位表示,子优先级1位也没,所以抢占优先级共二的三次方共8级,子优先级0.
Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0抢占优先级
那我们根据Air32F103的实际情况再重新写一下:

分组位置

PRIGROUP

[10:8]
Binary Points
表达抢占优先级的位段

Group priority bits
抢占优先级
表达子优先级的位段

Subpriority bits
子优先级0b0040bxxx[7:5]8无00b0050bxx.y[7:6]4[5:5]20b0060bx.yy[7:7]2[6:5]40b0070byyy无0[7:5](所有位)8
至此【10:8】的值,我们就能确定了

这里以IP共4位来进行分组示例(因为是STM32的手册)作为对比参考。

【7:3】:保留了,需要清除。即写0。

【1】:VECTCLRACTIVE,写时必须置0.

【0】:VECTRESET,写时必须置0.

至此我们能确定AIRCR寄存器的值了,只有【10:8】这三位的值是变化的,其余都是固定的。

我们再看下固件库:misc.h,这个头文件定义了几个分组的值,这些是错误的。

分组0,值是7,0位抢占优先级,4位子优先级

分组1,值是6,1位抢占优先级,3位子优先级

分组2,值是5,2位抢占优先级,2位子优先级

分组3,值是4,3位抢占优先级,1位子优先级

分组4,值是3,4位抢占优先级,0位子优先级

#define NVIC_PriorityGroup_0         ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
                                                            4 bits for subpriority */
#define NVIC_PriorityGroup_1         ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
                                                            3 bits for subpriority */
#define NVIC_PriorityGroup_2         ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
                                                            2 bits for subpriority */
#define NVIC_PriorityGroup_3         ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
                                                            1 bits for subpriority */
#define NVIC_PriorityGroup_4         ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
                                                            0 bits for subpriority */

我们根据上面的表格,写个对的:

#define NVIC_PriorityGroup_0         ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
                                                            3 bits for subpriority */
#define NVIC_PriorityGroup_1         ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
                                                            2 bits for subpriority */
#define NVIC_PriorityGroup_2         ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
                                                            1 bits for subpriority */
#define NVIC_PriorityGroup_3         ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
                                                            0 bits for subpriority */

也就是说,我们可以使用分组0,1,2,3,分别对应

分组0,值是7,0位抢占优先级,3位子优先级

分组1,值是6,1位抢占优先级,2位子优先级

分组2,值是5,2位抢占优先级,1位子优先级

分组3,值是4,3位抢占优先级,0位子优先级

关于分组设定的库函数,我们看下原型:

1.先用断言检查了下中断分组库函数的形参是否在范围内,超范围会警告,严谨。

2.SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;

这句是在配置SCB-AIRCR的值,这个将决定中断分组。

我们查下AIR_VECKEY_MASK的值

这个掩码的值太熟悉了,0x05FA是手册中要求我们写AIRCR寄存器时,必须要写在高16位的值,并且除了【10:8】位,其余位都为0。

所以NVIC_PriorityGroup的值上AIRCR_VECTKEY_MASK的值,就是我们将要配置的结果。

换句话说,NVIC_PriorityGroup定义了【10:8】的值,其余无关位用掩码的形式和他或在一起即可,简单可靠。

中断分组设置只需要设置1次即可,一般放在main函数里:

例如,我们想要设置中断优先级分组为0组,即0位抢占优先级,3位子优先级。

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

二.关于中断源

Air32F103 有8个可编程的优先等级(使用了3位中断优先级)

中断源及中断优先级如下:红字为内核中断,其余为芯片厂商的外设中断(自己设计的,即使都用M3内核,但每个厂商的中断可能不同,具体要看用户手册)
位置优先级优先级类型名称说明地址--保留 0x0000_0000-3固定Reset复位 0x0000_0004-2固定 NMI不可屏蔽中断 RCC时钟安全系统(CSS)连接到NMI 向量0x0000_0008-1固定硬件失效 (HardFault)所有类型的失效0x0000_000C0可设置存储管理 (MemManage)存储器管理0x0000_00101可设置总线错误 (BusFault)预取指失败,存储器访问失败0x0000_00142可设置错误应用 (UsageFault)未定义的指令或非法状态0x0000_0018---保留
0x0000_0018

~0x0000_002B
3可设置SVCall通过SWI指令的系统服务调用0x0000_002C4可设置调试监控 (DebugMonitor)调试监控器0x0000_0030---保留0x0000_00345可设置PendSV可挂起的系统服务0x0000_00386可设置SysTick系统滴答定时器0x0000_003C07可设置WWDG窗口看门狗中断0x0000_004018可设置PVD连接到EXTI的电源电压检测(PVD) 中断0x0000_0044忽略忽略忽略忽略忽略5966可设置DMA2通道4_5DMA2通道4和DMA2通道5全局中断0x0000_012C
我数了一下,一共有70个中断源,除了前三个是固定的优先级,剩下67个都是可自己设置使用的。

结合上一节:

Air32F103学习笔记-4.看看SDK中startup_air32f10x.s, .sct, .map-CSDN博客

我看下启动文件关于中断向量表的地址:

; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY    //开辟了1个区域,名字叫REST,数据类型,只读
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

发现没,启动文件中关于中断的,关于中断向量的顺序和手册里的一模一样,因为启动文件是根据手册写的。

AREA 定义一块代码段,段名字是RESET,READONLY 表示只读。

还记得上一节的分散加载吗? RESET段放在最前面,从哪里开始?从外部存储器,即FLASH的起始地址开始放中断向量表。起始地址是0x8000 0000

#1.sct

LR_IROM1 0x08000000 0x00040000 { ; load region size_region
ER_IROM1 0x08000000 0x00040000 { ; load address = execution address
*.o (RESET, +First) //RESET段放在最前面
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x20000000 0x00018000 { ; RW data
.ANY (+RW +ZI)
}
}

例如:

启动文件,第一行: DCD __initial_sp ; Top of Stack 栈顶地址

手册第二行,保留,地址是0x8000 0000,会被映射到0x0000 0000,作为启动后的第一条命令,即MSP的地址。上一节我们知道了,MSP指向了0x2000 0400,即栈顶地址;

启动文件,第二行: DCD Reset_Handler ; Reset Handler 复位中断

手册第二行,复位,地址是0x8000 0004,会被映射到0x0000 0004,作为启动后读取的第二条命令,指向了Reset_Handler,即0x8000 023D

以此类推,剩余的中断向量表的名字,依次+4字节往下顺序移动

标签: 学习 笔记

本文转载自: https://blog.csdn.net/weixin_45417939/article/details/134565343
版权归原作者 Suliang2013 所有, 如有侵权,请联系我们删除。

“Air32F103学习笔记-5.中断配置NVIC”的评论:

还没有评论