1、STM32的中斷
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, /*!< Window 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。
