本章節為大家講解ThreadX中斷優先級配置,此章節非常重要,初學者經常在這裡犯迷糊。對于初學者來說,本章節務必要整明白。
11.1 NVIC基礎知識
11.2 使用ThreadX時如何配置外設NVIC
11.3 ThreadX配置選項中NVIC相關配置
11.4 不受ThreadX管理中的的深入讨論
11.5 實驗例程
11.6 總結
11.1 初學者重要提示
- ThreadX官方配套開發中斷方案是用的PRIMASK寄存器,本章節我們分享一種使用BasePri寄存器開關中斷的玩法,這樣可以讓高優先級中斷實作零中斷延遲。
- 使用這種方法,不可在不受ThreadX管理的中斷裡面再調用ThreadX的API函數。
11.2 NVIC基礎知識
NVIC的全稱是Nested vectored interrupt controller,即嵌套向量中斷控制器。
對于M3和M4核心的MCU,每個中斷的優先級都是用寄存器中的8位來設定的。8位的話就可以設定2^8 = 256級中斷,實際中用不了這麼多,是以晶片廠商根據自己生産的晶片做出了調整。比如ST的STM32F1xx,F4xx,H7xx隻使用了這個8位中的高四位[7:4],低四位取零,這樣2^4=16,隻能表示16級中斷嵌套。
對于這個NVIC,有個重要的知識點就是優先級分組,搶占優先級和子優先級,下面就以STM32為例進行介紹,STM32F1xx,F4xx,H7xx都是隻使用了這個8位寄存器的高四位[7:4]。
從上面的表格可以看出,STM32支援5種優先級分組,系統上電複位後,預設使用的是優先級分組0,也就是沒有搶占式優先級,隻有子優先級,關于這個搶占優先級和這個子優先級有幾點一定要說清楚。
- 具有高搶占式優先級的中斷可以在具有低搶占式優先級的中斷服務程式執行過程中被響應,即中斷嵌套,或者說高搶占式優先級的中斷可以搶占低搶占式優先級的中斷的執行。
- 在搶占式優先級相同的情況下,有幾個子優先級不同的中斷同時到來,那麼高子優先級的中斷優先被響應。
- 在搶占式優先級相同的情況下,如果有低子優先級中斷正在執行,高子優先級的中斷要等待已被響應的低子優先級中斷執行結束後才能得到響應,即子優先級不支援中斷嵌套。
- Reset、NMI、Hard Fault 優先級為負數,高于普通中斷優先級,且優先級不可配置。
- 對于初學者還有一個比較糾結的問題就是系統中斷(比如:PendSV,SVC,SysTick)是不是一定比外部中斷(比如SPI,USART)要高,答案:不是的,它們是在同一個NVIC下面設定的。
掌握了這些基礎知識基本就夠用了。另外特别注意一點,配置搶占優先級和子優先級,他們合并成的4bit數字的數值越小,優先級越高,這一點千萬不要搞錯了,下面通過11.2小節舉一個執行個體。
11.3 使用ThreadX時如何配置外設NVIC
強烈推薦使用者将STM32的NVIC優先級分組設定為4,即:這樣中斷優先級的管理将非常友善。此函數在程式優先調用:(注意:一旦初始化好NVIC的優先級分組後,切不可以在應用中再次更改。)
設定NVIC的優先級分組為4表示支援0-15級搶占優先級(注意,0-15級是16個級别,包含0級),不支援子優先級。反映在STM32的HAL配置上就是如下:
/*
*********************************************************************************************************
* 函 數 名: System_Init
* 功能說明: 系統初始化,主要是MPU,Cache和系統時鐘配置
* 形 參:無
* 返 回 值: 無
*********************************************************************************************************
*/
void System_Init(void)
{
/* 配置MPU */
MPU_Config();
/* 使能L1 Cache */
CPU_CACHE_Enable();
/*
STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鐘:
- 調用函數HAL_InitTick,初始化滴答時鐘中斷1ms。
- 設定NVIC優先級分組為4。
*/
HAL_Init();
/*
配置系統時鐘到400MHz
- 切換使用HSE。
- 此函數會更新全局變量SystemCoreClock,并重新配置HAL_InitTick。
*/
SystemClock_Config();
/*
Event Recorder:
- 可用于代碼執行時間測量,MDK5.25及其以上版本才支援,IAR不支援。
- 預設不開啟,如果要使能此選項,務必看V7開發闆使用者手冊第8章
*/
#if Enable_EventRecorder == 1
/* 初始化EventRecorder并開啟 */
EventRecorderInitialize(EventRecordAll, 1U);
EventRecorderStart();
#endif
#if Enable_RTTViewer == 1
/* 配置通道0,上行配置*/
SEGGER_RTT_ConfigUpBuffer(0, "RTTUP", NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
/* 配置通道0,下行配置*/
SEGGER_RTT_ConfigDownBuffer(0, "RTTDOWN", NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
#endif
}
在這裡繼續強調下這一點,在NVIC分組為4的情況下,搶占優先級可配置範圍是0-15,那麼數值越小,搶占優先級的級别越高,即0代表最高優先級,15代表最低優先級。
11.4 不受ThreadX管理中斷的深入讨論
11.4.1 實作原理
講解不受ThreadX管理的中斷之前要說一個小知識點----中斷延遲。中斷延遲時間是衡量RTOS實時作業系統的一項重要名額,那什麼又是中斷延遲呢?從中斷觸發到執行中斷服務程式的第一條指令這段時間就是中斷延遲時間。
ThreadX核心源碼中有多處開關全局中斷的地方,這些開關全局中斷會加大中斷延遲時間。比如在源碼的某個地方關閉了全局中斷,但是此時有外部中斷觸發,這個中斷的服務程式就需要等到再次開啟全局中斷後才可以得到執行。開關中斷之間的時間越長,中斷延遲時間就越大,這樣極其影響系統的實時性。如果這是一個緊急的中斷事件,得不到及時執行的話,後果是可想而知的。
針對這種情況,我們為ThreadX就專門做了一種新的開關中斷實作機制。關閉中斷時僅關閉受ThreadX管理的中斷,不受ThreadX管理的中斷不關閉,這些不受管理的中斷都是高優先級的中斷,使用者可以在這些中斷裡面加入需要實時響應的程式。ThredX能夠實作這種功能的奧秘就在于ThreadX開關中斷使用的是寄存器basepri,而非官方預設配套使用的primask,詳情請看下面整理的表格:
對寄存器basepri我們舉一個例子,幫助大家了解,比我們配置寄存器basepri的數值為16,所有優先級數值大于等于16的中斷都會被關閉,優先級數值小于16的中斷不會被關閉。對寄存器basepri寄存器指派0,那麼被關閉的中斷會被打開。這個就是ThreadX開關中斷的實作方案。
11.4.2 實作代碼(AC5,AC6,IAR和GCC均支援)
實作代碼如下,大家僅需修改tx_port.h檔案中的TX_DISABLE,TX_RESTORE和_tx_thread_system_return_inline實作為如下即可:
#include "stm32f4xx_hal.h"
#define ThreadX_MAX_INTERRUPT_PRIORITY (0x10)
#define TX_INTERRUPT_SAVE_AREA uint32_t was_masked;
#define TX_DISABLE was_masked = __get_BASEPRI(); __set_BASEPRI(ThreadX_MAX_INTERRUPT_PRIORITY);
#define TX_RESTORE __set_BASEPRI(was_masked);
#define _tx_thread_system_return _tx_thread_system_return_inline
static void _tx_thread_system_return_inline(void)
{
unsigned int was_masked;
/* Set PendSV to invoke ThreadX scheduler. */
*((ULONG *) 0xE000ED04) = ((ULONG) 0x10000000);
if (__get_IPSR() == 0)
{
was_masked = __get_BASEPRI();
__set_BASEPRI(0);
__set_BASEPRI(was_masked);
}
}
注意:我們這裡設定宏定義ThreadX_MAX_INTERRUPT_PRIORITY為0x10,表示調用函數TX_DISABLE關閉中斷的時候,僅關閉搶占優先級1到15,搶占優先級0未不關閉(NVIC的優先級分組為4,STM32僅使用高4bit)。大家可以根據自己的情況做修改調整。
11.5 實驗例程
配套例子:
V6-3007_ThreadX Task Priority Change
實驗目的:
- 學習ThreadX的開關中斷使用BasePri寄存器方案,這樣使用ThreadX可以僅開關受ThreadX管理的中斷,進而可以讓高優先級中斷實作零中斷延遲。
實驗内容:
1、共建立了如下幾個任務,通過按下按鍵K1可以通過序列槽或者RTT列印任務堆棧使用情況
===================================================
OS CPU Usage = 1.94%
===================================================
Prio StackSize CurStack MaxStack Taskname
2 4092 383 391 App Task Start
3 4092 543 659 App Msp Pro
4 4092 391 391 App Task UserIF
5 4092 543 659 App Task COM
30 1020 519 519 App Task STAT
31 1020 143 71 App Task IDLE
0 1020 391 391 System Timer Thread
序列槽軟體可以使用SecureCRT或者H7-TOOL RTT檢視列印資訊。
App Task Start任務 :啟動任務,這裡用作BSP驅動包處理。
App Task MspPro任務 :消息處理,這裡未使用。
App Task UserIF任務 :按鍵消息處理。
App Task COM任務 :這裡用作LED閃爍。
App Task STAT任務 :統計任務
App Task IDLE任務 :空閑任務
System Timer Thread任務:系統定時器任務
2、 (1) 凡是用到printf函數的全部通過函數App_Printf實作。
(2) App_Printf函數做了信号量的互斥操作,解決資源共享問題。
3、預設上電是通過序列槽列印資訊,如果使用RTT列印資訊
(1) MDK AC5,MDK AC6或IAR通過使能bsp.h檔案中的宏定義為1即可
#define Enable_RTTViewer 1
(2) Embedded Studio繼續使用此宏定義為0, 因為Embedded Studio僅制作了調試狀态RTT方式檢視。
實驗操作:
- K1按鍵按下列印任務執行情況。
序列槽列印資訊方式(AC5,AC6和IAR):
波特率 115200,資料位 8,奇偶校驗位無,停止位 1
RTT列印資訊方式(AC5,AC6和IAR):
Embedded Studio僅支援調試狀态RTT列印:
由于Embedded Studio不支援中文,是以中文部分顯示亂碼,不用管。
程式執行框圖:
11.6 總結
本章節為大家講解ThreadX中斷優先級配置,特别是不受ThreadX管理中斷的實作方案,望大家熟練掌握。