1. 程式人生 > 其它 >【STM32H7】第11章 ThreadX中斷優先順序配置,含BasePri配置方案

【STM32H7】第11章 ThreadX中斷優先順序配置,含BasePri配置方案

論壇原始地址(持續更新):http://www.armbbs.cn/forum.php?mod=viewthread&tid=99514

第11章 ThreadX中斷優先順序配置,含BasePri配置方案

本章節為大家講解ThreadX中斷優先順序配置,此章節非常重要,初學者經常在這裡犯迷糊。對於初學者來說,本章節務必要整明白。

11.1 NVIC基礎知識

11.2 使用ThreadX時如何配置外設NVIC

11.3 ThreadX配置選項中NVIC相關配置

11.4 不受ThreadX管理中的的深入討論

11.5 實驗例程

11.6 總結

11.1 初學者重要提示

  1. ThreadX官方配套開發中斷方案是用的PRIMASK暫存器,本章節我們分享一種使用BasePri暫存器開關中斷的玩法,這樣可以讓高優先順序中斷實現零中斷延遲。
  2. 使用這種方法,不可在不受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,HIS時鐘:
       - 呼叫函式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 "stm32h7xx_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 實驗例程

配套例子:

V7-3006_ThreadX BasePri Interrupt

實驗目的:

  1. 學習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方式檢視。

實驗操作:

  1. K1按鍵按下列印任務執行情況。

串列埠列印資訊方式(AC5,AC6和IAR):

波特率 115200,資料位 8,奇偶校驗位無,停止位 1

RTT列印資訊方式(AC5,AC6和IAR):

Embedded Studio僅支援除錯狀態RTT列印:

由於Embedded Studio不支援中文,所以中文部分顯示亂碼,不用管。

程式執行框圖:

11.6 總結

本章節為大家講解ThreadX中斷優先順序配置,特別是不受ThreadX管理中斷的實現方案,望大家熟練掌握。