1 异常类型
F103内核有一个异常响应系统,支持众多的系统异常和外部中断。其中系统异常有8个(如果把 Reset和 HardFault 也算上的话就是 10 个),外部中断有 60 个。
除了个别异常的优先级被定死外,其它异常的优先级都是可编程的。有关具体的系统异常和外部中断可在标准库文件 stm32f10x.h 这个头文件查询到,在 IRQn_Type 这个结构体里面包含了 F103系列全部的异常声明。
表1 其它STM32F10xxx产品(小容量、中容量和大容量)的向量表
(灰色部分是系统异常清单其余是外部中断清单)
位置 | 优先 | 优先级 | 名称 | 说明 | 地址 |
级 | 类型 | ||||
- | - | - | 保留 | 0x0000_0000 | |
-3 | 固定 | Reset | 复位 | 0x0000_0004 | |
-2 | 固定 | NMI | 不可屏蔽中断 | 0x0000_0008 | |
RCC时钟安全系统(CSS)联接到NMI向量 | |||||
-1 | 固定 | 硬件失效(HardFault) | 所有类型的失效 | 0x0000_000C | |
0 | 可设置 | 存储管理(MemManage) | 存储器管理 | 0x0000_0010 | |
1 | 可设置 | 总线错误(BusFault) | 预取指失败,存储器访问失败 | 0x0000_0014 | |
2 | 可设置 | 错误应用(UsageFault) | 未定义的指令或非法状态 | 0x0000_0018 | |
- | - | - | 保留 | 0x0000_001C | |
~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 |
2 | 9 | 可设置 | TAMPER | 侵入检测中断 | 0x0000_0048 |
3 | 10 | 可设置 | RTC | 实时时钟(RTC)全局中断 | 0x0000_004C |
4 | 11 | 可设置 | FLASH | 闪存全局中断 | 0x0000_0050 |
5 | 12 | 可设置 | RCC | 复位和时钟控制(RCC)中断 | 0x0000_0054 |
6 | 13 | 可设置 | EXTI0 | EXTI线0中断 | 0x0000_0058 |
7 | 14 | 可设置 | EXTI1 | EXTI线1中断 | 0x0000_005C |
8 | 15 | 可设置 | EXTI2 | EXTI线2中断 | 0x0000_0060 |
9 | 16 | 可设置 | EXTI3 | EXTI线3中断 | 0x0000_0064 |
10 | 17 | 可设置 | EXTI4 | EXTI线4中断 | 0x0000_0068 |
11 | 18 | 可设置 | DMA1通道1 | DMA1通道1全局中断 | 0x0000_006C |
12 | 19 | 可设置 | DMA1通道2 | DMA1通道2全局中断 | 0x0000_0070 |
13 | 20 | 可设置 | DMA1通道3 | DMA1通道3全局中断 | 0x0000_0074 |
14 | 21 | 可设置 | DMA1通道4 | DMA1通道4全局中断 | 0x0000_0078 |
15 | 22 | 可设置 | DMA1通道5 | DMA1通道5全局中断 | 0x0000_007C |
16 | 23 | 可设置 | DMA1通道6 | DMA1通道6全局中断 | 0x0000_0080 |
17 | 24 | 可设置 | DMA1通道7 | DMA1通道7全局中断 | 0x0000_0084 |
18 | 25 | 可设置 | ADC1_2 | ADC1和ADC2的全局中断 | 0x0000_0088 |
19 | 26 | 可设置 | USB_HP_CAN_TX | USB高优先级或CAN发送中断 | 0x0000_008C |
20 | 27 | 可设置 | USB_LP_CAN_RX0 | USB低优先级或CAN接收0中断 | 0x0000_0090 |
21 | 28 | 可设置 | CAN_RX1 | CAN接收1中断 | 0x0000_0094 |
22 | 29 | 可设置 | CAN_SCE | CANSCE中断 | 0x0000_0098 |
23 | 30 | 可设置 | EXTI9_5 | EXTI线[9:5]中断 | 0x0000_009C |
24 | 31 | 可设置 | TIM1_BRK | TIM1刹车中断 | 0x0000_00A0 |
25 | 32 | 可设置 | TIM1_UP | TIM1更新中断 | 0x0000_00A4 |
26 | 33 | 可设置 | TIM1_TRG_COM | TIM1触发和通信中断 | 0x0000_00A8 |
27 | 34 | 可设置 | TIM1_CC | TIM1捕获比较中断 | 0x0000_00AC |
28 | 35 | 可设置 | TIM2 | TIM2全局中断 | 0x0000_00B0 |
29 | 36 | 可设置 | TIM3 | TIM3全局中断 | 0x0000_00B4 |
30 | 37 | 可设置 | TIM4 | TIM4全局中断 | 0x0000_00B8 |
31 | 38 | 可设置 | I2C1_EV | I2C1事件中断 | 0x0000_00BC |
32 | 39 | 可设置 | I2C1_ER | I2C1错误中断 | 0x0000_00C0 |
33 | 40 | 可设置 | I2C2_EV | I2C2事件中断 | 0x0000_00C4 |
34 | 41 | 可设置 | I2C2_ER | I2C2错误中断 | 0x0000_00C8 |
35 | 42 | 可设置 | SPI1 | SPI1全局中断 | 0x0000_00CC |
36 | 43 | 可设置 | SPI2 | SPI2全局中断 | 0x0000_00D0 |
37 | 44 | 可设置 | USART1 | USART1全局中断 | 0x0000_00D4 |
38 | 45 | 可设置 | USART2 | USART2全局中断 | 0x0000_00D8 |
39 | 46 | 可设置 | USART3 | USART3全局中断 | 0x0000_00DC |
40 | 47 | 可设置 | EXTI15_10 | EXTI线[15:10]中断 | 0x0000_00E0 |
41 | 48 | 可设置 | RTCAlarm | 连到EXTI的RTC闹钟中断 | 0x0000_00E4 |
42 | 49 | 可设置 | USB唤醒 | 连到EXTI的从USB待机唤醒中断 | 0x0000_00E8 |
43 | 50 | 可设置 | TIM8_BRK | TIM8刹车中断 | 0x0000_00EC |
44 | 51 | 可设置 | TIM8_UP | TIM8更新中断 | 0x0000_00F0 |
45 | 52 | 可设置 | TIM8_TRG_COM | TIM8触发和通信中断 | 0x0000_00F4 |
46 | 53 | 可设置 | TIM8_CC | TIM8捕获比较中断 | 0x0000_00F8 |
47 | 54 | 可设置 | ADC3 | ADC3全局中断 | 0x0000_00FC |
48 | 55 | 可设置 | FSMC | FSMC全局中断 | 0x0000_0100 |
49 | 56 | 可设置 | SDIO | SDIO全局中断 | 0x0000_0104 |
50 | 57 | 可设置 | TIM5 | TIM5全局中断 | 0x0000_0108 |
51 | 58 | 可设置 | SPI3 | SPI3全局中断 | 0x0000_010C |
52 | 59 | 可设置 | UART4 | UART4全局中断 | 0x0000_0110 |
53 | 60 | 可设置 | UART5 | UART5全局中断 | 0x0000_0114 |
54 | 61 | 可设置 | TIM6 | TIM6全局中断 | 0x0000_0118 |
55 | 62 | 可设置 | TIM7 | TIM7全局中断 | 0x0000_011C |
56 | 63 | 可设置 | DMA2通道1 | DMA2通道1全局中断 | 0x0000_0120 |
57 | 64 | 可设置 | DMA2通道2 | DMA2通道2全局中断 | 0x0000_0124 |
58 | 65 | 可设置 | DMA2通道3 | DMA2通道3全局中断 | 0x0000_0128 |
59 | 66 | 可设置 | DMA2通道4_5 | DMA2通道4和DMA2通道5全局中断 | 0x0000_012C |
2 NVIC嵌套向量中断控制器
2.1 NVIC 寄存器简介
NVIC 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,是内核里面的一个外设。芯片厂商在设计芯片的时候会对 Cortex-M3 内核里面的 NVIC 进行裁剪,把不需要的部分去掉,所以说 STM32的 NVIC是 Cortex-M 3 的 NVIC 的一个子集,相关内容在msic.c和msic.h文件中。
2.2 NVIC 中断配置固件库
固件库文件core_cm3.h的最后,还提供了 NVIC 的一些函数,这些函数遵循 CMSIS 规则,只要是 Cortex-M3 的处理器都可以使用,具体如下:
表2 NVIC库函数
这些库函数在编程的时候用的都比较少,甚至基本都不用。在配置中断的时候还有更简洁的方法(相关内容在msic.c和msic.h文件中)。
3 优先级的定义
3.1 优先级定义
中断优先级寄存器 NVIC_IPRx,用来配置外部中断的优先级,IPR宽度为 8bit,数值越小,优先级越高。但绝大多数 CM3 芯片都会精简设计,实际上支持的优先级数减少,在F103中,只使用了高 4bit,如下所示:
表3 中断优先级寄存器 NVIC_IPRx
用于表达优先级的4bit,又被分组成抢占优先级和子优先级。如果有多个中断同时响应:
- 抢占优先级高的就会优先得到执行;
- 如果抢占优先级相同,就比较子优先级,子优先级高的先执行;
- 如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号(自然优先级),编号越小,优先级越高。
当中断系统正在执行一个中断服务时,有另一个优先级更高的中断提出中断请求,这时会暂时中止当前正在执行的级别较低的中断源的服务程序,去处理级别更高的中断源,待处理完毕,再返回到被中断了的中断服务程序继续执行,这个过程叫做中断嵌套。
注意:
- 高抢占优先级的中断可以嵌套低抢占优先级的中断;
- 在抢占式优先级相同的情况下,如果有低子优先级中断正在执行,高子优先级的中断要等待已被响应的低子优先级中断执行结束后才能得到响应,即子优先级不支持中断嵌套。
优先级的分组由内核外设SCB的应用程序中断及复位控制寄存器AIRCR的 PRIGROUP[10:8]位决定,F103分为了5组,具体如下:主优先级 = 抢占优先级。
表4 应用程序中断及复位控制寄存器AIRCR
设置优先级分组可调用库函数 NVIC_PriorityGroupConfig()实现,STM32有关NVIC中断相关的库函数都在库文件 misc.c 和 misc.h 中。
表5 中断优先级分组真值表
4 NVIC中断编程
在配置每个中断时包含的内容:
1. 使能外设某个中断,具体由每个外设的相关中断使能位控制。比如串口有发送完成中断,接收完成中断,这两个中断都由串口控制寄存器的相关中断使能位控制;
2.配置中断优先级分组,调用库函数 NVIC_PriorityGroupConfig()即可;注意NVIC_PriorityGroupConfig()是整个程序中只需要设置一次。当设置好了中断优先级分组,其他各种外设对应的中断向量的中断优先级就是基于目前设置分组来解读,从代码布局逻辑来说,NVIC_PriorityGroupConfig()适合放在 main()函数中。
3.定义NVIC_InitTypeDef 结构体类型变量和设置变量成员,包含设置中断源,设置抢占优先级和子优先级,使能中断请求,最后调用库函数NVIC_Init()写入配置的寄存器参数;
a.NVIC_IROChannel:用来设置中断源,不同的中断中断源不一样,且不可写错,即使写错程序也不会报错,只会导致不响应中断。具体的成员配置可参考 stm32f10x.h 头文件里面的 IRQn_Type结构体定义,这个结构体包含了所有的中断源。
图1 IRQn_Type结构体
b.NVIC_IRQChannelPreemptionPriority,NVIC_IRQChannelSubPriority分别对应抢占优先级和子优先级,具体的值要根据表5 优先级分组真值表来确定。
c.NVIC_IRQChannelCmd:中断使能(ENABLE)或者失能(DISABLE)。操作的是 NVIC_ISER和 NVIC_ICER 这两个寄存器。
4.编写中断服务函
在启动文件startup_stm32f10x_hd.s 中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。
实际的中断服务函数都需要我们重新编写,为了方便管理我们把中断服务函数统一写在 stm32f10x_it.c 这个库文件中。关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口,直接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。
5.代码举例
- 在main.c中调用NVIC_PriorityGroupConfig();
- 在相关文件中配置NVIC,如下图2所示:
图2 配置NVIC
- 流程示意图