FreeRTOS 中斷優先級配置

NVIC 的全稱是 Nested vectored interrupt controller,即嵌套向量中斷控制器。
對於 M3 和 M4 內核的 MCU,每個中斷的優先級都是用寄存器中的 8 位來設置的。 8 位的話就可以
設置 2^8 = 256 級中斷,實際中用不了這麼多,所以芯片廠商根據自己生產的芯片做出了調整。比如 ST
的 STM32F1xx 和 F4xx 只使用了這個 8 位中的高四位[7:4],低四位取零,這樣 2^4=16,只能表示 16
級中斷嵌套。
對於這個 NVIC,有個重要的知識點就是優先級分組,搶佔優先級和子優先級,下面就以 STM32 爲
例進行介紹,STM32F1xx 和 F4xx 都是隻使用了這個 8 位寄存器的高四位[7:4]。 

從上面的表格可以看出,STM32 支持 5 種優先級分組,系統上電覆位後,默認使用的是優先級分組
0,也就是沒有搶佔式優先級,只有子優先級,關於這個搶佔優先級和這個子優先級有幾點一定要說清楚。
 具有高搶佔式優先級的中斷可以在具有低搶佔式優先級的中斷服務程序執行過程中被響應,即中
斷嵌套,或者說高搶佔式優先級的中斷可以搶佔低搶佔式優先級的中斷的執行。
 在搶佔式優先級相同的情況下,有幾個子優先級不同的中斷同時到來,那麼高子優先級的中斷優
先被響應。
 在搶佔式優先級相同的情況下,如果有低子優先級中斷正在執行,高子優先級的中斷要等待已被
響應的低子優先級中斷執行結束後才能得到響應,即子優先級不支持中斷嵌套。
 Reset、 NMI、 Hard Fault 優先級爲負數,高於普通中斷優先級,且優先級不可配置。
 對於初學者還有一個比較糾結的問題就是系統中斷(比如:PendSV,SVC,SysTick)是不是一
定比外部中斷(比如 SPI,USART)要高,答案:不是的,它們是在同一個 NVIC 下面設置的。 

具體可以點擊這裏查看之前的隨筆。

強烈推薦用戶將 Cortex-M3 內核的 STM32F103 和 Cortex-M4 內核的 STM32F407 以及
STM32F429 的 NVIC 優先級分組設置爲 4,即:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);這
樣中斷優先級的管理將非常方便。 這個也是官方強烈建議的。

(注意:一旦初始化好 NVIC 的優先級分組後,切不可以在應用中再次更改。)
設置 NVIC 的優先級分組爲 4 表示支持 0-15 級搶佔優先級 (注意, 0-15 級是 16 個級別,包含 0 級), 不支持子優先級。 
在這裏繼續強調下這一點,在 NVIC 分組爲 4 的情況下,搶佔優先級可配置範圍是 0-15,那麼數值越小,搶佔優先級的級別越高,即 0 代表最高優先級,15 代表最低優先級。 


FreeRTOS 配置選項中 NVIC 相關配置
FreeRTOSConfig.h 配置文件中設置到 NVIC 中斷的有如下幾個選項: 

複製代碼

/*
 * Cortex-M內核使用8bit來配置優先級,但是STM32只使用了高4bit,數值越小,優先級越高。
 * 在往寄存器裏面寫數值配置的時候,是按照8bit來寫的,所以真正寫的時候需要經過轉換,公式爲:
 * ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff),其中的priority就是我們配置的真正的優先級
 */
#ifdef __NVIC_PRIO_BITS  /* __NVIC_PRIO_BITS 已經在stm32f4xx.h裏面定義爲4 */
    #define configPRIO_BITS               __NVIC_PRIO_BITS
#else
    #define configPRIO_BITS               4
#endif
/*============================================== SysTick中斷優先級配置 ============================================*/
/*
 * 在往寄存器裏面寫數值配置的時候,是按照8bit來寫的,所以真正寫的時候需要經過轉換,公式爲:
 * ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff),其中的priority就是我們配置的真正的優先級。經過這個公式之後得到的是
 * 下面的這個宏:configKERNEL_INTERRUPT_PRIORITY
 * SysTick的優先級我們一般配置爲最低,即0xf 。這樣可以提高系統的實時響應能力,即其他的外部中斷可以及時的得到響應。
 */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY            0xf
#define configKERNEL_INTERRUPT_PRIORITY         ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

/*===========================================可屏蔽的中斷優先級配置====================================================*/
/*
 * 用於配置STM32的特殊寄存器basepri寄存器的值,用於屏蔽中斷,當大於basepri值的優先級的中斷將被全部屏蔽。basepri只有4bit有效,
 * 默認只爲0,即全部中斷都沒有被屏蔽。configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY配置爲:5,意思就是中斷優先級大於5的中斷都被屏蔽。
 * 當把配置好的優先級寫到寄存器的時候,是按照8bit來寫的,所以真正寫的時候需要經過轉換,公式爲:
 * ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff),其中的priority就是我們配置的真正的優先級。經過這個公式之後得到的是下面的這個宏:
 * configMAX_SYSCALL_INTERRUPT_PRIORITY
 *
 * 在FreeRTOS中,關中斷是通過配置basepri寄存器來實現的,關掉的中斷由配置的basepri的值決定,小於basepri值的
 * 中斷FreeRTOS是關不掉的,這樣做的好處是可以系統設計者可以人爲的控制那些非常重要的中斷不能被關閉,在緊要的關頭必須被響應。
 * 而在UCOS中,關中斷是通過控制PRIMASK來實現的,PRIMASK是一個單1的二進制位,寫1則除能除了NMI和硬 fault的所有中斷。當UCOS關閉
 * 中斷之後,即使是你在系統中設計的非常緊急的中斷來了都不能馬上響應,這加大了中斷延遲的時間,如果是性命攸關的場合,那後果估計挺嚴重。
 * 相比UCOS的關中斷的設計,FreeRTOS的設計則顯得人性化很多。
 *
 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5
#define configMAX_SYSCALL_INTERRUPT_PRIORITY     ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

複製代碼

#define configPRIO_BITS 4
此宏定義用於配置 STM32 的 8 位優先級設置寄存器實際使用的位數。 STM32F103,STM32F407
和 STM32F429 都是使用的 4 位。 另外注意一點,這裏使用了一個條件編譯,用戶可以選擇將條件編
譯刪掉,直接定義一個#define configPRIO_BITS 4 即可。使用條件編譯的好處就是方便與系統統
一。這個__NVIC_PRIO_BITS 在 STM32F103 標準庫的頭文件 stm32f10x.h 中以及 STM32F407/439
的標準庫的頭文件 stm32f4xx.h 中分別有定義。如果用戶在 FreeRTOSConfig.h 文件裏面包含了這個
標準庫的頭文件,那麼就會執行條件編譯選項:
#define configPRIO_BITS __NVIC_PRIO_BITS 

#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x0f
此宏定義是用來配置 FreeRTOS 用到的 SysTick 中斷和 PendSV 中斷的優先級。在 NVIC 分組設置爲
4 的情況下,此宏定義的範圍就是 0-15,即專門配置搶佔優先級。這裏配置爲了 0x0f,即 SysTick
和 PendSV 都是配置爲了最低優先級,實際項目中也建議大家配置最低優先級即可。
 SVC 中斷
在 FreeRTOS 的移植文件 ports.c 中有用到 SVC 中斷的 0 號系統服務,即 SVC 0。此中斷在 FreeRTOS
中僅執行一次, 用於啓動第一個要執行的任務。 另外, 由於 FreeRTOS 沒有配置 SVC 的中斷優先級,
默認沒有配置的情況下, SVC 中斷的優先級就是最高的 0。 如果用戶在不清楚自己配置的 PendSV 和
SysTick 中斷是否跟實際情況一致時,可以進行硬件調試。 比如 MDK,我們可以在硬件調試的狀態下,
先點擊全速運行,然後查看如下調試組件: 

#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY  0x01
此宏定義比較重要,定義了受 FreeRTOS 管理的最高優先級中斷。簡單的說就是允許用戶在這個中斷
服務程序裏面調用 FreeRTOS 的 API 的最高優先級。 設置 NVIC 的優先級分組爲 4 的情況下。 配置
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 爲 0x01 表示用戶可以在搶佔式優先級爲 1 

到 15 的中斷裏面調用 FreeRTOS 的 API 函數,搶佔式優先級爲 0 的中斷裏面是不允許調用的。 不受
FreeRTOS 管理的中斷有什麼深層的含義嗎?

不受 FreeRTOS 管理中斷的深入討論
講解不受 FreeRTOS 管理的中斷之前要說一個小知識點----中斷延遲。 中斷延遲時間是衡量 RTOS 實
時操作系統的一項重要指標,那什麼又是中斷延遲呢?從中斷觸發到執行中斷服務程序的第一條指令這段
時間就是中斷延遲時間。
FreeRTOS 內核源碼中有多處開關全局中斷的地方,這些開關全局中斷會加大中斷延遲時間。 比如在
源碼的某個地方關閉了全局中斷,但是此時有外部中斷觸發,這個中斷的服務程序就需要等到再次開啓全
局中斷後纔可以得到執行。開關中斷之間的時間越長,中斷延遲時間就越大,這樣極其影響系統的實時性。
如果這是一個緊急的中斷事件,得不到及時執行的話,後果是可想而知的。
針對這種情況,FreeRTOS 就專門做了一種新的開關中斷實現機制。 關閉中斷時僅關閉受 FreeRTOS
管理的中斷,不受 FreeRTOS 管理的中斷不關閉,這些不受管理的中斷都是高優先級的中斷,用戶可以在
這些中斷裏面加入需要實時響應的程序。 FreeRTOS 能夠實現這種功能的奧祕就在於 FreeRTOS 開關中斷
使用的是寄存器 basepri,而像 uCOS 這種使用的是 primask,詳情請看下面整理的表格: 

 

#define configKERNEL_INTERRUPT_PRIORITY
宏定義 configLIBRARY_LOWEST_INTERRUPT_PRIORITY的數值經過 4bit偏移後得到一個 8bit
的優先級數值,即宏定義 configKERNEL_INTERRUPT_PRIORITY 的數值。這個 8bit 的數值纔可以
實際賦值給相應中斷的優先級寄存器。
也許初學者有疑問了,爲什麼前面 NVIC 配置的時候不是 8bit 的方式進行配置?這是因爲 ST 的
庫函數 NVIC_Init()已經爲我們做好了。 這裏的宏定義數值是供 PendSV 和 SysTick 中斷進行優先級
配置的。 比如:我們這裏配置宏定義 configLIBRARY_LOWEST_INTERRUPT_PRIORITY 是 0x0f,經
過 4bit 偏移後就是 0xf0,即 SysTick 和 PendSV 的中斷優先級就是 240。

#define configMAX_SYSCALL_INTERRUPT_PRIORITY
宏定義 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 的數值經過 4bit 偏移後得到一
個 8bit 的優先級數值,即宏定義 configMAX_SYSCALL_INTERRUPT_PRIORITY 的數值。 這個數值
是賦值給寄存器 basepri 使用的,8bit 的數值纔可以實際賦值給相應中斷的優先級寄存器。
這裏的宏定義數值賦給寄存器 basepri 後就可以實現全局的開關中斷操作了。 比如:我們這裏配
置宏定義 configLIBRARY_LOWEST_INTERRUPT_PRIORITY 是 0x01,經過 4bit 偏移後就是 0x10,
即 16。 調用了 FreeRTOS 的關中斷後,所有優先級數值大於等於 16 的中斷都會被關閉。優先級數值
小於 16 的中斷不會被關閉,對寄存器 basepri 寄存器賦值 0,那麼被關閉的中斷會被打開。

上面的優先級數值,240和16,是十進制數,是真正8bit的數值,但是ST給我們了庫函數,我們只配置4-7位,就可以了,其實就在配置這4-7位爲0xf,表示優先級爲15,實際十進制爲240,配置這4-7位爲 0x1,表示優先級爲1,實際十進制爲16.不要混淆了。
 NOTE:

這裏configMAX_SYSCALL_INTERRUPT_PRIORITY的優先級是個8bit的,之前的configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY是4bit,注意有沒有LIBRARY。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章