1. 程式人生 > 其它 >在Tricore上移植μC/OS-III(三)—— Trap和中斷

在Tricore上移植μC/OS-III(三)—— Trap和中斷

技術標籤:嵌入式微控制器rtos

本文介紹了在英飛凌Tricore核心微控制器陷阱(Trap)和中斷(Interrupt)。例程程式碼可在公眾號“汽車軟體雜談”後臺回覆“Tricore uCOS”獲取。

本文是《在Tricore上移植μC/OS-III》系列文章的第三篇,前兩篇分別介紹了Tricore核心的CSA機制和上下文切換的具體使用方法。這篇文章主要講用Trap機制來呼叫上下文切換函式(OSCtxSw()),以及系統時鐘的實現等內容。

1. Tricore的Trap機制

Trap簡單來說,就是在發生一些異常情況或錯誤操作的時候,系統自動進入一個類似於中斷的函式中,以防止錯誤的操作對系統產生損害,並允許使用者對系統當時的執行狀態進行跟蹤檢視,和ARM的HardFault類似。例如我們前兩篇文章提到過的,CSA使用不正確、指令呼叫不正確這一類的操作,都會觸發Trap。

Trap是不能在應用程式中被禁止的。

關於Trap的詳細介紹可在TricoreArchitecture_UM_VO1的第六章中找到。共有8個大類,包含了指令錯誤、記憶體管理錯誤、CSA使用錯誤等多種型別,我這裡就不詳細說了,有需要了解的可以檢視手冊。

在這些大類之下,還根據觸發Trap的原因不同來劃分了很多子類,這些子類通過TIN(Trap Identification Number)來進行標識,在觸發Trap的時候,TIN被存入系統暫存器D[15]中,進入Trap函式後,我們可以通過呼叫暫存器D[15]中的值來獲取TIN,進而獲取觸發Trap的詳細原因。

2. System Call

下面我們來說一下System Call這個特殊的Trap。其實除了我們上文說到的系統錯誤或指令錯誤會觸發Trap外,Tricore還為使用者提供了一個可以人為觸發Trap的機制

,就是System Call。調用匯編指令“SYSCALL”,系統就會立即跳轉到System Call Trap中,我們同樣可以傳遞一個8位無符號數的TIN進去,在Trap中通過判斷TIN,來實現不同的功能。

我們之前說過的OSCtxSw()這個任務切換函式,就是放在System Call 的Trap函式中,之所以採用這種方法,就是因為進入Trap的時候會自動儲存之前任務的UCX和LCX,省去了手動儲存任務執行狀態的麻煩,而且在Trap中,我們使用“RFE”指令就可以返回上一個任務的執行狀態。

3. System Call的使用方法

iLLD庫中已經為我們寫好了Trap函式,我們需要做的就是使能並編寫一個Hook函式。

① 首先要在Ifx_Cfg.h中,使能 IFX_CFG_EXTEND_TRAP_HOOKS 這個巨集定義:

/*********************************************************************************************************************/
/*---------------------------------Configuration for Trap Hook Functions' Extensions---------------------------------*/
/*********************************************************************************************************************/
 #define IFX_CFG_EXTEND_TRAP_HOOKS  /* Decomment this line if the project needs to extend trap hook functions */

② 然後在Ifx_Cpu_Trap.c中,新增包含一個“Ifx_Cfg_Trap.h”,這個檔案需要我們自己建立,並在裡面編寫Hook函式。

/*******************************************************************************
**                      Includes                                              **
*******************************************************************************/
#include "IfxCpu_Trap.h"
#include "Cpu/Std/IfxCpu.h"
#include "Cpu/Std/IfxCpu_Intrinsics.h"
#include "IfxCpu_reg.h"
#include "Ifx_Cfg.h"
#ifdef IFX_CFG_EXTEND_TRAP_HOOKS
#include "Ifx_Cfg_Trap.h"
#endif

③ Ifx_Cfg_Trap.h 中的內容如下圖所示,這個Hook函式就是我們在SystemCall Trap中將要呼叫的函式,注意這個函式必須是 inline 函式,以避免函式巢狀而產生的CSA連結串列變化。trapInfo這個結構體中包含著我們傳遞進去的TIN,我們這次使用的是0。

#include "Ifx_Types.h"
#include "IfxCpu_Intrinsics.h"
#include "IfxPort_Io.h"
#include "os_cpu.h"

IFX_INLINE void SysCallExtensionHook(IfxCpu_Trap trapInfo)
{
    switch (trapInfo.tId)
    {
        case 0:     //切換任務Trap
        {
            /*__svlcx();    lower context was stored in the trap vector */
            OSCtxSw();
            break;
        }
        default:
        {
            break;
        }
    }
}
#define IFX_CFG_CPU_TRAP_SYSCALL_CPU0_HOOK(trapInfo)  SysCallExtensionHook(trapInfo)

④ 呼叫SystemCall。μC/OS-III中的任務切換介面是OS_TASK_SW(),也就是說每次執行任務切換的時候都會呼叫這個語句,我們把這個語句定義成呼叫SystemCall,用的是iLLD的__syscall()介面。這樣就可以實現:需要執行任務切換的時候,觸發SystemCall Trap並自動儲存任務狀態,然後呼叫OSCtxSw()函式執行任務切換。

#define  OS_SYSCALL_CTXSW       0
#define  OS_TASK_SW()           __syscall(OS_SYSCALL_CTXSW)

4. 系統時鐘的實現

在RTOS中,通常都需要一個系統時鐘中斷,以固定的頻率觸發,以為系統提供計時和任務排程功能。本次的系統中斷採用Tricore的STM0定時器,每1ms觸發一次,即系統時鐘頻率是1KHz。相關函式在Bsp.c中。

作業系統中與系統時鐘相關的兩個函式分別是:

  • 初始化函式:OS_CPU_SysTickInit (CPU_INT32U cnts),傳入的引數cnt就是時鐘振盪多少次後觸發中斷,關於這個數值我們後面再說。
  • 時鐘滴答函式:OS_CPU_SysTickHandler (void),每次觸發系統中斷的時候要呼叫這個函式。
void  OS_CPU_SysTickHandler (void)
{
    OSIntEnter ();                                          /* Tell uC/OS-III that we are starting an ISR             */
    OSTimeTick();                                           /* Call uC/OS-III's OSTimeTick()                          */
    OSIntExit();                                            /* Tell uC/OS-III that we are leaving the ISR             */
}

void  OS_CPU_SysTickInit (CPU_INT32U  cnts)
{
    InitSTM0(cnts);
}

在初始化系統時鐘的時候初始化STM0模組:

static void InitSTM0(CPU_INT32U  cnts)
{
    IfxStm_initCompareConfig(&g_STMConf);                   /* Initialize the configuration structure with default values   */
    g_STMConf.triggerPriority = OS_CFG_TICK_TASK_PRIO;      /* Set the priority of the interrupt                            */
    g_STMConf.typeOfService = IfxSrc_Tos_cpu0;              /* Set the service provider for the interrupts                  */
    g_STMConf.ticks = cnts;                                 /* Set the number of ticks after which the timer triggers an
                                                             * interrupt for the first time                                 */
    IfxStm_initCompare(BSP_DEFAULT_TIMER, &g_STMConf);      /* Initialize the STM with the user configuration               */
}

宣告STM中斷,並定義中斷入口函式。中斷優先順序配置為系統的TickTask相同的優先順序,當然這倆優先順序其實不是一個概念,一個是對微控制器來說的,一個是對μC/OS來說的,不要混淆,配置成不同的優先順序也完全可以。在中斷函式中重新配置STM0暫存器值,以設定下一次觸發中斷的時間。然後呼叫系統滴答函式。

IFX_INTERRUPT(IsrSTM0, 0, OS_CFG_TICK_TASK_PRIO);
IfxStm_CompareConfig g_STMConf;
void IsrSTM0(void)
{
    /* Update the compare register value that will trigger the next interrupt and toggle the LED */
    IfxStm_increaseCompare(BSP_DEFAULT_TIMER, g_STMConf.comparator,
                          (CPU_INT32U)((TimeConst_1s)/OSCfg_TickRate_Hz));
//    IfxPort_togglePin(&MODULE_P14, 9);
    OS_CPU_SysTickHandler();
}

系統時鐘初始化函式在main函式中被呼叫,初始化的值=1S/系統時鐘頻率,這個值在os_cfg_app.h中可以配置,這次配置為1000。同樣的,上面的系統中斷函式的暫存器重置的值也要是這個值。

    OS_CPU_SysTickInit((CPU_INT32U)(TimeConst_1s)/OSCfg_TickRate_Hz);                                   /* Init uC/OS periodic time src (SysTick).              */

至此,系統時鐘就配置完成了。

5. 補充內容

1. 開啟和關閉中斷:

μC/OS在程式中比較關鍵的位置,會設定臨界區,也就是關閉所有中斷,保證程式在這段執行時間內不被打斷。關閉和開啟中斷的介面在cpu.h中,如下圖所示,直接呼叫的iLLD中的介面:

#define  CPU_INT_DIS()         do { cpu_sr = disableInterrupts(); } while (0) /* Save    CPU status word & disable interrupts.*/
#define  CPU_INT_EN()          do { restoreInterrupts((boolean)cpu_sr);} while (0) /* Restore CPU status word.                     */

2. CPU_CntLeadZeros (CPU_DATA val)函式:

這個函式的作用是計算出一個32位數中第一個為1的位之前所有0的個數,比如:

輸入:0x00008010,返回結果:16,即在第一個1前有16個0.

這個函式是μC/OS-III在計算優先順序最高的任務時使用的,具體邏輯就不展開講了,反正它使用頻率比較高,每次任務排程都會用到,如果能提高這個函式的執行效率,將顯著提高任務排程的速度。

這個函式可以用C語言實現,但比較麻煩,所以一般的微控制器都會設定一個彙編指令來實現這個功能,能節省不少時間,Tricore的實現方法如下,Tasking編譯器支援在C語言中內嵌組合語言,且支援輸入和輸出資料,詳細的內容可以參考Tasking使用手冊。

CPU_DATA  CPU_CntLeadZeros (CPU_DATA  val)
{
    CPU_DATA ret;
    __asm ("clz %0, %1" : "=d" (ret) : "d" (val));
    return ret;
}

到此為止,在Tricore上移植μC/OS-III的方法就介紹完了。這個專案花了我近一個月的時間,寫這三篇文章的過程,是一個對其認識不斷加深的過程,也是對程式碼不斷優化的過程。最大的受益者還是我自己,如果同時也能對諸位有一點幫助,也是我莫大的榮幸了。下個專案見。

在這裡插入圖片描述