出处:http://blog.sina.com.cn/s/blog_72605ba50102vv87.html
0.前言
本文想解决的问题有:
- 如何开启、关闭中断
- 如何开启、关闭异常
- LPC177x/8x支持的中断优先级个数
- 复位后,异常/中断默认的优先级
- 如何设置异常/中断的优先级
- 什么是优先级组,如何设置优先级组,复位后的优先级组
1. Cortex-M3的异常/中断屏蔽寄存器组
注:只有在特权级下,才允许访问这3个寄存器。
名 字 | 功能描述 |
PRIMASK | 只有单一比特的寄存器。置为1后,就关掉所有可屏蔽异常,只剩下NMI和硬Fault可以响应。默认值是0,表示没有关闭中断。 |
FAULTMASK | 只有单一比特的寄存器。置为1后,只有NMI可以响应。默认值为0,表示没有关异常。 |
BASEPRI | 该寄存器最多有9位(由表达优先级的位数决定)。定义了被屏蔽优先级的阈值。当它被设置为某个值后,所有优先级号大于等于此值的中断都被关。若设置成0,则不关断任何中断,0为默认值。 |
注:寄存器BASEPRI的有效位数受系统中表达优先级的位数影响,如果系统中只使用3个位来表达优先级,则BASEPRI有意义的值仅为0x00、0x20、0x40、0x60、0x80、0xA0、0xC0和0xE0
使用MRS/MSR指令访问这三个寄存器,比如:
MRS R0, BASEPRI ;读取BASEPRI到R0中
MSR BASEPRI, R0 ;将R0数据写入到BASEPRI中
为了快速的开关中断,CM3还专门设置了一条CPS指令,有四种用法:
CPSID I ;PRIMASK=1,关中断
CPSIE I ;PRIMASK=0,开中断
CPSID F ;FAULTMASK=1,关异常
CPSIE F ;FAULTMASK=0,开异常
CMSIS-M3微控制器软件接口标准中的core_cm3.h给出了开关中断或异常的函数:
1.1 开/关中断
1:
8: static __INLINE void __set_PRIMASK(uint32_t priMask)
9: {
10: register uint32_t __regPriMask __ASM("primask");
11: __regPriMask = (priMask);
12: }
register type var-name __asm(reg);
keil编译器已命名的寄存器变量为:
寄存器 | __asm修饰的字符串 | 处理器 |
| " | All processors |
| | All processors |
| | Cortex-M3, Cortex-M4 |
| | Cortex-M3, Cortex-M4 |
| | Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 |
| | Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 |
| | Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 |
| | Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 |
| | Cortex-M3, Cortex-M4 |
| | Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 |
| | Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 |
| | Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 |
| | Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 |
| | Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 |
| | Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 |
| | Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 |
| | All processors |
| | All processors |
| "r13" "sp" | All processors |
| "r15" "pc" | All processors |
| | All processors, apart from Cortex-M series processors. |
| | Cortex-M0, Cortex-M1, Cortex-M3, Cortex-M4 |
1.2 开/关异常
1:
8: static __INLINE void __set_FAULTMASK(uint32_t faultMask)
9: {
10: register uint32_t __regFaultMask __ASM("faultmask");
11: __regFaultMask = (faultMask & 1);
12: }
使用__set_FAULTMASK(1)来关闭中断和异常;使用__set_FAULTMASK(0)开启中断和异常.
1.3 更精确的优先级屏蔽
1:
8: static __INLINE void __set_BASEPRI(uint32_t basePri)
9: {
10: register uint32_t __regBasePri __ASM("basepri");
11: __regBasePri = (basePri & 0xff);
12: }
比如想屏蔽优先级不高于0x60的中断,则使用代码:__set_BASEPRI(0x60);如果想取消中断屏蔽,则使用__set_BASEPRI(0)即可。
2.异常/中断和优先级
注:关于异常和中断,想要分个清清楚楚实在有点困难。异常和中断都可以“中断”正常执行的代码流,区别在于,异常是Cortex-M3内核产生的“中断”信号,而中断是Cortex-M3内核外部(片上外设或外部中断信号)产生的“中断”信号。希望你看懂了,有时候你心里明白,但要讲的清清楚楚着实难!
表2-1:系统异常
编号 | 类型 | 优先级 | 简介 |
0 | N/A | N/A | 无 |
1 | 复位 | -3(最高) | 复位 |
2 | NMI | -2 | 不可屏蔽中断(来自外部NMI输入脚) |
3 | 硬Fault | -1 | 只要FAULTMASK没有置位,硬Fault服务例程会被强制执行 |
4 | 存储器管理Fault | 可编程 | MPU访问违例以及访问非法位置均可引发。企图在“非执行区”取址也会引发此Fault。 |
5 | 总线Fault | 可编程 | 总线收到了错误响应,原因可以使预取流产或数据流产,企图访问协处理器也会引发此Fault |
6 | 用法Fault | 可编程 | 由于程序错误导致的异常。通常是使用了一条无效指令,或者是非法的状态转换,例如尝试切换到ARM状态 |
7~10 | 保留 | 保留 | 保留 |
11 | SVCall | 可编程 | 执行系统服务调用指令(SVC)引发的异常 |
12 | 调试监视器 | 可编程 | 调试器(断点、数据观察点,或者是外部调试请求) |
13 | 保留 | 保留 | 保留 |
14 | PendSV | 可编程 | 为系统设备而设的“可挂起请求” |
15 | SysTick | 可编程 | 系统节拍时钟定时器(SysTick) |
表2-2:外设中断
编号 | 类型 | 优先级 | 简介 |
16 | IRQ #0 | 可编程 | 外设中断#0 |
17 | IRQ #1 | 可编程 | 外设中断#1 |
... | ... | 可编程 | ... |
255 | IRQ #239 | 可编程 | 外设中断#239 |
注:表2-1和2-2中的“编号”有着特殊的意义,一是特殊功能寄存器IPSR中会记录当前正在服务的异常并给出了它的编号;二是优先级完全相同的多个异常同时挂起时,则先响应异常编号最小的那一个。
2.1 设置异常/中断的优先级
2.1.1 系统异常优先级设置
表2-3:SHPR1寄存器的位分配
位 | 名称 | 功能 |
[31:24] | PRI_7 | 保留 |
[23:16] | PRI_6 | 系统处理程序6的优先级,用法Fault |
[15:8] | PRI_5 | 系统处理程序5的优先级,总线Fault |
[7:0] | PRI_4 | 系统处理程序4的优先级,存储器管理Fault |
表2-3:SHPR2寄存器的位分配
位 | 名称 | 功能 |
[31:24] | PRI_11 | 系统处理程序11的优先级,SVCall |
[23:0] | - | 保留 |
表2-4:SHPR3寄存器的位分配
位 | 名称 | 功能 |
[31:24] | PRI_15 | 系统处理程序15的优先级,SysTick 异常 |
[23:16] | PRI_14 | 系统处理程序14的优先级,PendSV |
[15:0] | - | 保留 |
注:每个PRI_N域为8位宽,但是处理器仅实现每个域的位[7:3],位[2:0]读取值为零并忽略写入值。
2.1.2 外设中断优先级设置
2.1.3 系统异常/外设中断优先级设置C代码
1:
12: static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
13: {
14: if(IRQn < 0) {
15: SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);
16: }
17: else {
18: NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);
19: }
20: }
系统异常ID:
标号 | 中断ID | 描述 |
NonMaskableInt_IRQn | -14 | 不可屏蔽中断 |
MemoryManagement_IRQn | -12 | Cortex-M3内存管理中断 |
BusFault_IRQn | -11 | Cortex-M3 总线Fault中断 |
UsageFault_IRQn | -10 | Cortex-M3 用法Fault 中断 |
SVCall_IRQn | -5 | Cortex-M3 SV Call中断 |
DebugMonitor_IRQn | -4 | Cortex-M3 调试监视中断 |
PendSV_IRQn | -2 | Cortex-M3 Pend SV中断 |
SysTick_IRQn | -1 | Cortex-M3 系统Tick中断 |
标号 | 中断ID | 描述 | 标号 | 中断ID | 描述 |
WDT_IRQn | 0 | 看门狗 | EINT3_IRQn | 21 | 外中断3 |
TIMER0_IRQn | 1 | 定时器0 | ADC_IRQn | 22 | AD转换 |
TIMER1_IRQn | 2 | 定时器1 | BOD_IRQn | 23 | 欠压检测 |
TIMER2_IRQn | 3 | 定时器2 | USB_IRQn | 24 | USB |
TIMER3_IRQn | 4 | 定时器3 | CAN_IRQn | 25 | CAN |
UART0_IRQn | 5 | UART0 | DMA_IRQn | 26 | 通用DMA |
UART1_IRQn | 6 | UART1 | I2S_IRQn | 27 | I2S |
UART2_IRQn | 7 | UART2 | ENET_IRQn | 28 | 以太网 |
UART3_IRQn | 8 | UART3 | MCI_IRQn | 29 | SD/MMC卡I/F |
PWM1_IRQn | 9 | PWM1 | MCPWM_IRQn | 30 | 电机控制PWM |
I2C0_IRQn | 10 | I2C0 | QEI_IRQn | 31 | 正交编码接口 |
I2C1_IRQn | 11 | I2C1 | PLL1_IRQn | 32 | PLL1锁存 |
I2C2_IRQn | 12 | I2C2 | USBActivity_IRQn | 33 | USB活动 |
Reserved0_IRQn | 13 | 保留 | CANActivity_IRQn | 34 | CAN活动 |
SSP0_IRQn | 14 | SSP0 | UART4_IRQn | 35 | UART4 |
SSP1_IRQn | 15 | SSP1 | SSP2_IRQn | 36 | SSP2 |
PLL0_IRQn | 16 | PLL0锁存 | LCD_IRQn | 37 | LCD |
RTC_IRQn | 17 | RTC | GPIO_IRQn | 38 | GPIO |
EINT0_IRQn | 18 | 外中断0 | PWM0_IRQn | 39 | PWM0 |
EINT1_IRQn | 19 | 外中断1 | EEPROM_IRQn | 40 | EEPROM |
EINT2_IRQn | 20 | 外中断2 | | | |
2.2 设置异常/中断的优先级组
2.2.1 设置优先级寄存器组的C代码
1:
12: static __INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
13: {
14: uint32_t reg_value;
15:
16: uint32_t PriorityGroupTmp = (PriorityGroup & 0x07);
17:
18: reg_value = SCB->AIRCR;
19:
20: reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk);
21:
22: reg_value = (reg_value |
23: (0x5FA << SCB_AIRCR_VECTKEY_Pos) |
24: (PriorityGroupTmp << 8));
25: SCB->AIRCR = reg_value;
26: }