中断是单片机非常重要的功能,也是一个难点,本节单独讲下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 Rigister | ISER[0],ISER[1],ISER[2] | ISER0,ISER1,ISER2 |
Clear-enable 失能 | Interrupt Clear Enable Rigister | ICER[0],ICER[1],ICER[2] | ICER0,ICER1,ICER2 |
Set-pending 设置挂起 | Interrupt Set Pending Rigister |
ISPR[0],ISPR[1],ISPR[2] | ISPR0,ISPR1,ISPR2 |
Clear-pending 清除挂起 | Interrupt Clear Pending Rigister | ICPR[0],ICPR[1],ICPR[2] | ICPR0,ICPR1,ICPR2 |
Active Bits | Interrupt Active Bits Rigister | IABR[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 0000 | 0xE0 |
0x1100 0000 | 0xC0 |
0x1010 0000 | 0xA0 |
0x1000 0000 | 0x80 |
0x0110 0000 | 0x60 |
0x0101 0000 | 0x40 |
0x0010 0000 | 0x20 |
0x0000 0000 | 0x00 |
分组咋分呢。。。
分组是在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 |
0b000 | 0bxxxxxxx.y | [7:1] | [0:0] |
0b001 | 0bxxxxxx.yy | [7:2] | [1:0] |
0b002 | 0bxxxxx.yyy | [7:3] | [2:0] |
0b003 | 0bxxxx.yyyy | [7:4] | [3:0] |
0b004 | 0bxxx.yyyyy | [7:5] | [4:0] |
0b005 | 0bxx.yyyyyy | [7:6] | [5:0] |
0b006 | 0bx.yyyyyyy | [7:7] | [6:0] |
0b007 | 0byyyyyyyy | 无 | [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 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
抢占优先级 |
那我们根据Air32F103的实际情况再重新写一下:
分组位置 PRIGROUP [10:8] |
Binary Points | 表达抢占优先级的位段 Group priority bits |
抢占优先级 | 表达子优先级的位段 Subpriority bits |
子优先级 |
0b004 | 0bxxx | [7:5] | 8 | 无 | 0 |
0b005 | 0bxx.y | [7:6] | 4 | [5:5] | 2 |
0b006 | 0bx.yy | [7:7] | 2 | [6:5] | 4 |
0b007 | 0byyy | 无 | 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_000C | |
0 | 可设置 | 存储管理 (MemManage) | 存储器管理 | 0x0000_0010 | |
1 | 可设置 | 总线错误 (BusFault) | 预取指失败,存储器访问失败 | 0x0000_0014 | |
2 | 可设置 | 错误应用 (UsageFault) | 未定义的指令或非法状态 | 0x0000_0018 | |
- | - | - | 保留 | 0x0000_0018 ~0x0000_002B |
|
3 | 可设置 | SVCall | 通过SWI指令的系统服务调用 | 0x0000_002C | |
4 | 可设置 | 调试监控 (DebugMonitor) | 调试监控器 | 0x0000_0030 | |
- | - | - | 保留 | 0x0000_0034 | |
5 | 可设置 | PendSV | 可挂起的系统服务 | 0x0000_0038 | |
6 | 可设置 | SysTick | 系统滴答定时器 | 0x0000_003C | |
0 | 7 | 可设置 | WWDG | 窗口看门狗中断 | 0x0000_0040 |
1 | 8 | 可设置 | PVD | 连接到EXTI的电源电压检测(PVD) 中断 | 0x0000_0044 |
忽略 | 忽略 | 忽略 | 忽略 | 忽略 | |
59 | 66 | 可设置 | DMA2通道4_5 | DMA2通道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文章来源:https://www.toymoban.com/news/detail-793654.html
以此类推,剩余的中断向量表的名字,依次+4字节往下顺序移动文章来源地址https://www.toymoban.com/news/detail-793654.html
到了这里,关于Air32F103学习笔记-5.中断配置NVIC的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!