最新教程下載:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255
第21章 STM32F407的NVIC中斷分組和配置(重要)
本章節為大家講解STM32F407的中斷優先級配置。此章節非常重要,初學者經常在這里犯迷糊,因此很有必要整明白。
21.1 NVIC基礎知識
21.2 常用的NVIC API
21.3 MDK中查看實際優先級配置
21.4 開關中斷
21.5 總結
21.1 NVIC基礎知識
NVIC的全稱是Nested vectored interrupt controller,即嵌套向量中斷控制器。
對於M3/M4/M7內核的MCU,每個中斷的優先級都是用寄存器中的8位來設置的。8位的話就可以設置2^8 = 256級中斷,實際中用不了這么多,所以芯片廠商根據自己生產的芯片做出了調整。比如ST的STM32F1xx,F4xx和H7只使用了這個8位中的高四位[7:4],低四位取零,這樣2^4=16,只能表示16級中斷嵌套。
對於這個NVIC,有個重要的知識點就是優先級分組、搶占優先級和子優先級。STM32F1xx,F4xx和H7都是只使用了這個8位寄存器的高四位[7:4]。
從上面的表格可以看出,STM32支持5種優先級分組。系統上電復位后,默認使用的是優先級分組0,也就是沒有搶占式優先級,只有子優先級。關於這個搶占優先級和子優先級有幾點一定要說清楚。
- 具有高搶占式優先級的中斷可以在具有低搶占式優先級的中斷服務程序執行過程中被響應,即中斷嵌套,或者說高搶占式優先級的中斷可以搶占低搶占式優先級的中斷的執行。
- 在搶占式優先級相同的情況下,有幾個子優先級不同的中斷同時到來,那么高子優先級的中斷優先被響應。
- 在搶占式優先級相同的情況下,如果有低子優先級中斷正在執行,高子優先級的中斷要等待已被響應的低子優先級中斷執行結束后才能得到響應,即子優先級不支持中斷嵌套。
- Reset、NMI、Hard Fault 優先級為負數,高於普通中斷優先級,且優先級不可配置。
- 初學者還有一個比較糾結的問題,就是系統中斷(比如:PendSV,SVC,SysTick)是不是一定比外部中斷(比如SPI,USART)要高。答案:不是的,它們是在同一個NVIC下面設置的。
掌握了這些基礎知識基本就夠用了。另外特別注意一點,配置搶占優先級和子優先級,它們合並成的4bit數字的數值越小,優先級越高,這一點千萬不要搞錯了。
21.2 常用的NVIC API
下面將外設配置中經常用到的兩個函數做個說明。
- HAL_NVIC_SetPriority
- HAL_NVIC_EnableIRQ
21.2.1 函數HAL_NVIC_SetPriority
函數原型:
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
函數描述:
此函數主要用於設置中斷的搶占優先級和子優先級。
函數參數:
- 第1個參數IRQn是中斷號,由IRQn_Type定義的枚舉類型,原始定義在stm32f407x.h。
typedef enum { /* 部分定義,其它省略未寫 */ PendSV_IRQn = -2, SysTick_IRQn = -1, DMA1_Stream3_IRQn = 14, DMA1_Stream4_IRQn = 15, DMA1_Stream5_IRQn = 16, DMA1_Stream6_IRQn = 17, ADC_IRQn = 18, } IRQn_Type;
- 第2個參數PreemptPriority用於設置搶占優先級,范圍0 - 15。
- 第3個參數SubPriority用於設置子優先級,范圍0 -15。
使用舉例:
此函數的調用比較簡單,比如設置ADC中斷的搶占優先級是1,子優先級是0,那么此函數的設置就是:HAL_NVIC_SetPriority(ADC_IRQn, 1, 0)。
21.2.2 函數HAL_NVIC_EnableIRQ
函數原型:
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
函數描述:
此函數主要用於使能中斷。
函數參數:
- 第1個參數IRQn是中斷號,由IRQn_Type定義的枚舉類型,原始定義在stm32f407x.h。
typedef enum { /* 部分定義,其它省略未寫 */ PendSV_IRQn = -2, SysTick_IRQn = -1, DMA1_Stream3_IRQn = 14, DMA1_Stream4_IRQn = 15, DMA1_Stream5_IRQn = 16, DMA1_Stream6_IRQn = 17, ADC_IRQn = 18, } IRQn_Type;
注意事項:
調用此函數前要先調用優先級分組設置函數NVIC_PriorityGroupConfig,而這個函數會在HAL_Init里面被調用。函數基本都會在main函數里面優先被調用,所以保證HAL_Init優先被調用即可。
使用舉例:
此函數的調用比較省事,比如使能ADC中斷,那么此函數的設置就是:
HAL_NVIC_EnableIRQ (ADC_IRQn, )。
21.3 MDK中查看實際優先級配置
如果想查看配置的外設優先級是否正確設置了,MDK進入調試狀態后,先點擊全速運行,然后查看如下調試組件:
彈出如下窗口,在這個窗口里面就可以查看實際優先級了,比如滴答定時器Systick的優先級就是240,即240>>4 = 15。
21.4 開關中斷
V6開發板的BSP驅動包在bsp.h文件里面對開關中斷做了一個宏定義:
/* 開關全局中斷的宏 */ #define ENABLE_INT() __set_PRIMASK(0) /* 使能全局中斷 */ #define DISABLE_INT() __set_PRIMASK(1) /* 禁止全局中斷 */
__set_PRIMASK就是對寄存器primask做的開關設置,如下表所示:
對於寄存器basepri我們舉一個例子,幫助大家理解。比我們配置寄存器basepri(對於STM32F407,范圍0-255)的數值為16,所有優先級數值大於等於16的中斷都會被關閉,優先級數值小於16的中斷不會被關閉。但0比較特殊,對寄存器basepri寄存器賦值0,那么被關閉的中斷會被打開。
21.5 總結
最后,我們再強調一下,在NVIC分組為4的情況下,搶占優先級可配置范圍是0-15,那么數值越小,搶占優先級的級別越高,即0代表最高優先級,15代表最低優先級。