STM32 的中断管理是属于内核部分的, 所以中断管理的寄存器也是属于内核组, 不属于芯片外设, 在查看相关资料的时候, 需要查看相对应的内核手册.
STM32F103ZET6 是 Cortex-M3 内核的 IC.Cortex-M3 内核支持 256 个中断, 其中包含了 16 个内核中断和 240 个外部中断, 并且具有 256 级的可编程中断设置. 但是 STM32 并没有完全使用 Cortex-M3 内核的全部中断, 只是用了其中的一部分.
在 STM32 中, 有时候中断也称为异常, 系统异常, 把它们统一理解为中断就可以了.
2, 中断编号
STM32 不同类型的芯片 IC 所具有的中断个数是不一样的, 在 HAL 库中, 可以通过查找 IRQn_Type 这个结构体来查看该 IC 所具有的中断. IRQn_Tyepe 对该芯片的中断进行了编号. STM32F103ZET6 的 IRQn_Type 结构体定义在 stm32f103xe.h 头文件中, 如下:
- typedef enum
- {
- /****** Cortex-M3 Processor Exceptions Numbers ***************************************************/
- NonMaskableInt_IRQn = -14, /*!<2 Non Maskable Interrupt */
- HardFault_IRQn = -13, /*!< 3 Cortex-M3 Hard Fault Interrupt */
- MemoryManagement_IRQn = -12, /*!< 4 Cortex-M3 Memory Management Interrupt */
- BusFault_IRQn = -11, /*!< 5 Cortex-M3 Bus Fault Interrupt */
- UsageFault_IRQn = -10, /*!< 6 Cortex-M3 Usage Fault Interrupt */
- SVCall_IRQn = -5, /*!< 11 Cortex-M3 SV Call Interrupt */
- DebugMonitor_IRQn = -4, /*!< 12 Cortex-M3 Debug Monitor Interrupt */
- PendSV_IRQn = -2, /*!< 14 Cortex-M3 Pend SV Interrupt */
- SysTick_IRQn = -1, /*!< 15 Cortex-M3 System Tick Interrupt */
- /****** STM32 specific Interrupt Numbers *********************************************************/
- WWDG_IRQn = 0, /*!< Windows WatchDog Interrupt */
- PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */
- TAMPER_IRQn = 2, /*!< Tamper Interrupt */
- RTC_IRQn = 3, /*!< RTC global Interrupt */
- FLASH_IRQn = 4, /*!< FLASH global Interrupt */
- RCC_IRQn = 5, /*!< RCC global Interrupt */
- EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */
- EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */
- EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */
- EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */
- EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */
- DMA1_Channel1_IRQn = 11, /*!< DMA1 Channel 1 global Interrupt */
- DMA1_Channel2_IRQn = 12, /*!< DMA1 Channel 2 global Interrupt */
- DMA1_Channel3_IRQn = 13, /*!< DMA1 Channel 3 global Interrupt */
- DMA1_Channel4_IRQn = 14, /*!< DMA1 Channel 4 global Interrupt */
- DMA1_Channel5_IRQn = 15, /*!< DMA1 Channel 5 global Interrupt */
- DMA1_Channel6_IRQn = 16, /*!< DMA1 Channel 6 global Interrupt */
- DMA1_Channel7_IRQn = 17, /*!< DMA1 Channel 7 global Interrupt */
- ADC1_2_IRQn = 18, /*!< ADC1 and ADC2 global Interrupt */
- USB_HP_CAN1_TX_IRQn = 19, /*!< USB Device High Priority or CAN1 TX Interrupts */
- USB_LP_CAN1_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN1 RX0 Interrupts */
- CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */
- CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */
- EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */
- TIM1_BRK_IRQn = 24, /*!< TIM1 Break Interrupt */
- TIM1_UP_IRQn = 25, /*!< TIM1 Update Interrupt */
- TIM1_TRG_COM_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt */
- TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */
- TIM2_IRQn = 28, /*!< TIM2 global Interrupt */
- TIM3_IRQn = 29, /*!< TIM3 global Interrupt */
- TIM4_IRQn = 30, /*!< TIM4 global Interrupt */
- I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */
- I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */
- I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */
- I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */
- SPI1_IRQn = 35, /*!< SPI1 global Interrupt */
- SPI2_IRQn = 36, /*!< SPI2 global Interrupt */
- USART1_IRQn = 37, /*!< USART1 global Interrupt */
- USART2_IRQn = 38, /*!< USART2 global Interrupt */
- USART3_IRQn = 39, /*!< USART3 global Interrupt */
- EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */
- RTC_Alarm_IRQn = 41, /*!< RTC Alarm through EXTI Line Interrupt */
- USBWakeUp_IRQn = 42, /*!< USB Device WakeUp from suspend through EXTI Line Interrupt */
- TIM8_BRK_IRQn = 43, /*!< TIM8 Break Interrupt */
- TIM8_UP_IRQn = 44, /*!< TIM8 Update Interrupt */
- TIM8_TRG_COM_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt */
- TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */
- ADC3_IRQn = 47, /*!< ADC3 global Interrupt */
- FSMC_IRQn = 48, /*!< FSMC global Interrupt */
- SDIO_IRQn = 49, /*!< SDIO global Interrupt */
- TIM5_IRQn = 50, /*!< TIM5 global Interrupt */
- SPI3_IRQn = 51, /*!< SPI3 global Interrupt */
- UART4_IRQn = 52, /*!< UART4 global Interrupt */
- UART5_IRQn = 53, /*!< UART5 global Interrupt */
- TIM6_IRQn = 54, /*!< TIM6 global Interrupt */
- TIM7_IRQn = 55, /*!< TIM7 global Interrupt */
- DMA2_Channel1_IRQn = 56, /*!< DMA2 Channel 1 global Interrupt */
- DMA2_Channel2_IRQn = 57, /*!< DMA2 Channel 2 global Interrupt */
- DMA2_Channel3_IRQn = 58, /*!< DMA2 Channel 3 global Interrupt */
- DMA2_Channel4_5_IRQn = 59, /*!< DMA2 Channel 4 and Channel 5 global Interrupt */
- } IRQn_Type;
- 3,NVIC
NVIC 的简称是嵌套中断向量控制器, 它控制着整个芯片中断相关的功能. NVIC 是 Cortex-M3 内核里面的一个外设, 它们共同完成对中断的响应. 但是 ST 在设计芯片的时候对 Cortex-M3 内核里面的 NVIC 进行了裁剪, 把不需要的部分去掉, 所以说 STM32 的 NVIC 是 Cortex-M3 的 NVIC 的一个子集.
在 HAL 库中, 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 RSERVED1[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;
NVIC_Type 结构体在 core_cm3.h 文件中定义 .
NVIC_Type 结构体中的 RSERVED 都是保留位, 即给每个寄存器都预留了很多位, 方便以后可以扩展功能.
ISER[8] 中断使能寄存器组:
Cortxe-M3 内核支持 256 个中断, ISER 寄存器的每一个 bit 位控制一个中断使能, ISER 是 32 位寄存器, 8 个 32 位的寄存器组成了 256 个中断控制位.
但需要注意的是 STM32F103 的可屏蔽中断只有 60 个, 所以用不到 8 个 ISER 寄存器, 只用到了 ISER[0] 和 ISER[1] 这两个寄存器, ISER[0] 和 ISER[1] 可以组成成 64 个中断使能位.
ISER[0] 的 bit0~bit31 分别对应中断 0~31;ISER[1] 的 bit0~bit31 分别对应中断 32~59.
当需要使能某个外设中断时, 就必须找到 ISER 寄存器的对应位, 并将其置 1, 具体是置位哪一个位, 可以根据结构体 IRQn_Type 来查询. 比如说 WWDG_IRQn 在 IRQn_Type 中的值为 0, 那么在 ISER 寄存器中的中断使能位就是 ISER[0] 中的 bit0 位.
假设某外设中的在 IRQn_Type 的中断编号为 IRQn, 则可以根据如下操作置位该外设的中断使能位:
ISER[ IRQn>> 5 ] = (1<<(IRQn & 31))
IRQn & 31 相当于只取低 5 位的值, 因为只有对 ISER 寄存器写 1 才能使能中断, 但是写 0 是不会有任何效果的, 所以这里直接用等于号而不需要用 "|" 号.
ICER[8] 中断失能寄存器组:
ICER 寄存器的作用刚好与 ISER 寄存器的作用相反, ICER 寄存器是用来清除某个中断的使能的. ICER 寄存器的使用跟 ISER 是一样的, 只是功能相反.
因为 NIVC 的寄存器都是写 1 有效, 而写 0 是无效的, 不能通过给 ISER 寄存器写 0 来清除中断使能位, 所以增加了 ICER 寄存器与 ISER 寄存器对应.
ICER 寄存器的使用方式可以参考 ISER.
ISPR[8] 中断挂起寄存器组:
每个位的中断与 ISER 是一样的, 只是功能不同. ISPR 寄存器如果置 1 是将一个正在进行的中断挂起, 转而执行同级别或更高级别的中断. 同 ISER 一样, 写 0 无效.
ICPR[8] 中断解除挂起寄存器组:
ICPR 寄存器与 ISPR 寄存器功能刚好相反, 通过置位 ICPR 寄存器来解除 ISPR 寄存器挂起的中断. 同 ISER 一样, 写 0 无效.
一般很少用到 ISPR 寄存器和 ICPR 寄存器.
IABR[8] 中断激活标志位寄存器组:
IABR 寄存器是一个状态寄存器, 它是一个只读寄存器. 通过读取 IABR 寄存器的值, 可以知道当前执行的中断是哪一个. IABR 寄存器的对应位在中断执行完之后又硬件自动清零. IABR 寄存器的中断位与 ISER 是一样的.
IP[240] 中断优先级控制寄存器组:
IP 寄存器与 STM32 的中断分组密切相关. IP 寄存器组由 240 个 8bit 的寄存器组成, 每个可屏蔽中断占用 8bit, 总共可以表示 240 个可屏蔽中断, 但是 STM32F103 只用到了 IP[59]~IP[0] 这 60 个.
IP 寄存器的 8 个 bit 并没有全部使用, 只用了高 4 位, 也就是 bit4~bit7 位. Bit4~bit7 位又分为抢占优先级和响应先级, 抢占优先级在前, 响应先级在后. 而对于抢占优先级和响应优先级各占几位, 则需要根据 SCB 的 AIRCR 寄存器中的中断分组设置有决定.
4, 中断分组
STM32 将中断分为 5 个组, 组 0~ 组 4.STM32 中断的分组设置是由 SCB 的 AIRCR 寄存器的 bit8~bit10 为来设置的. 而抢占优先级和响应优先级则需要根据分组来决定, 如下:
中断分组 0:AIRCR[10:8] = 111, 那么对于 IP 寄存器的 bit7~bit4 的占位情况为: 0 位抢占优先级和 4 位响应优先级.
中断分组 1:AIRCR[10:8] = 110, 那么对于 IP 寄存器的 bit7~bit4 的占位情况为: 1 位抢占优先级和 3 位响应优先级.
中断分组 2:AIRCR[10:8] = 101, 那么对于 IP 寄存器的 bit7~bit4 的占位情况为: 2 位抢占优先级和 2 位响应优先级.
中断分组 3:AIRCR[10:8] = 100, 那么对于 IP 寄存器的 bit7~bit4 的占位情况为: 3 位抢占优先级和 1 位响应优先级.
中断分组 4:AIRCR[10:8] = 011, 那么对于 IP 寄存器的 bit7~bit4 的占位情况为: 4 位抢占优先级和 0 位响应优先级.
举个例子来说, 如果中断分组设置为 3, 即 AIRCR[10:8] = 100, 那么 STM32 的所有可屏蔽中断, 每个中断的 IP 寄存器的 bit7~bit5 位是抢占优先级, bit4 位是响应优先级, 每个可屏蔽中断, 可以设置为 0~7 的抢占优先级, 0~1 的响应优先级.
需要注意的是 SCB 的 AIRCR 寄存具有写保护, 需要对 SCB 的 AIRCR 寄存的高 16 位写入 0x05FA 这个密钥才能修改 AIIRCR 寄存器.
抢占优先级和响应优先级的区别:
抢占优先级别高于响应优先级, 而数值越小所代表的优先级就越高.
如果两个中断的抢占优先级和响应优先级都是一样的, 则哪个中断先发生就先执行.
如果抢占优先级高不一样, 那么抢占优先级高的中断可以打断正在进行的抢占优先级比较低的中断.
如果抢占优先级和响应优先级都相同的话, 就根据中断编号来决定谁先执行, 中断编号越小, 优先级越高.
如果中断的抢占优先级相同, 响应优先级不同的中断是不可以相互打断的.
举例说明如下: 设定中断分组为 2, 将 RTC 的中断的抢占优先级设为 2, 响应优先级设为 1.EXTI0 的中断的抢占优先级设为 3, 响应优先级设为 0.EXTI1 的抢占优先级设为 2, 响应优先级设为 0. 因为数值越小所代表的优先级就越高, 那么这 3 个中断的优先级顺序为: EXTI1 中断 > RTC 中断 > EXTI0 中断. EXTI1 和 RTC 中断都可以打断 EXTI0 中断; 而 EXTI1 和 RTC 中断却不能相互打断.
5, 中断编程
首先使能某个外设中断, 这个具体由每个外设的相关中断使能控制位控制. 比如串口由发送完成中断, 接收完成中断, 这两个中断都是由串口控制寄存器的相关中断使能位控制.
然后设置中断的分组和中断优先级, 关于中断分组, 一般只设置一次就好了, 设置过后就不再更改, 重复设置为同一个组号也可以.
使能中断, 这里使能的是 ISER 寄存器的中断使能位.
在 HAL 库函数中, 对 NVIC 的操作封装在 stm32f1xx_hal_cortex.c 文件中, 其函数的声明如下:
- void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup);
- void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority);
- void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);
- void HAL_NVIC_DisableIRQ(IRQn_Type IRQn);
- void HAL_NVIC_SystemReset(void);
HAL_NVIC_SetPriorityGrouping 函数是设置中断分组:
HAL_NVIC_SetPriority 函数是设置中断的优先级. IRQn 是对应的中断编号; PreemptPriority 是设置抢占优先级; SubPriority 是设置响应优先级.
HAL_NVIC_EnableIRQ 函数是使能中断, 操作的是 ISER 寄存器.
HAL_NVIC_DisableIRQ 函数是失能中断, 操作的是 ICER 寄存器.
void HAL_NVIC_SystemReset 函数是软件复位操作.
6, 中断服务函数
在 STM32 的启动文件当中, 为每个中断都写了一个中断服务函数, 只是启动文件中的终端服务函数都是空的, 为的只是初始化中断向量表. 实际的中断服务函数都需要重新编写.
在 HAL 库中系统异常和中断服务函数一般写在 stm32f10x_it.c 文件中, 当然也可以写在其它文件, 但是必须保证不能出现两个文件具有同一个中断服务函数, 否者会报错.
还需要注意的就是中断服务函数的函数名必须跟启动文件里面预先设置的函数名一样, 如果不一样, 当触发中断时, 系统在中断向量表中找不到中断服务函数的入口, 就会直接跳转到启动文件里预先写好的空函数, 并且在里面循环, 造成死机.
7, 中断屏蔽功能
PRIMASK 与 FAULTMASK 特殊功能寄存器
PRIMASK 用于禁止除了 NMI 和硬 fault 之外的所有异常和中断. 也就是说 PRIMASK 能禁止中断, 可以当做关总中断来使用, 但是不能关闭 NMI 和硬 fault 的中断. PRIMASK 的使用方式如下:
关闭中断:
- MOV R0, #1
- MSR PRIMASK, R0
将 1 的值写入 PRIMASK 中, 这样就关闭除了 NMI 和硬 fault 之外的中断.
也可以通过 CPS 指令快速完成关闭中断:
CPSID i
开中断:
- MOV R0, #1
- MSR PRIMASK, R0
将 PRIMASK 的值清 0, 开启中断.
也可以通过 CPS 指令快速完成关闭中断:
CPSIE i
而 FAULTMSK 寄存器能直接把当前优先级改为 - 1, 这样一来, 连硬 fault 中断都被屏蔽了, 但是 FAULTMSK 寄存器不能屏蔽 NMI 中断, 而且 FAULTMASK 会在异常退出时自动清 0,FAULTMASK 的使用方式如下:
关闭中断:
- MOV R0, #1
- MSR FAULTMASK, R0
将 1 的值写入 FAULTMASK 中, 这样就关闭除了 NMI 之外的中断.
开中断
- MOV R0, #1
- MSR FAULTMASK, R0
将 FAULTMASK 的值清 0, 开启中断.
BASEPRI 寄存器
使用 BASEPRI 寄存器能根据优先级屏蔽部分中断. 中断的优先级设置的数值越小, 那么优先级就越高, 如果将 BASEPRI 的值设为 0x40, 优先级设置由于是高 4 位有效, 也就相当于 BASEPRI 的值设为 2, 那么低于或等于优先级 2 的中断教会被屏蔽, 即优先级 0 和优先级 1 的中断可响应; 优先级 2, 优先级 3 以及以上的优先级中断将被屏蔽.
编程如下:
- MOV R0, #0x40
- MSR BASEPRI,R0
如果要取消 BASEPRI 对中断的屏蔽, 则如下:
- MOV R0, #0
- MSR BASEPRI,R0
系统中表达优先级的位数, 也同样影响 BASEPRI 中有意义的位数. 如果系统中只使用 3 个位来表达优先级, 则 BASEPRI 有意义的值仅为 0x00, 0x20, 0x40, 0x60, 0x80, 0xA0, 0xC0 以及 0xE0.
来源: http://www.bubuko.com/infodetail-3087227.html