1. 程式人生 > >ucosIII 時鐘節拍和時間管理

ucosIII 時鐘節拍和時間管理

一 時間管理

1 API介面

API介面程式碼在OS_TIME.c

API 註釋
OSTimeDly() 延時執行任務n 個時基
OSTimeDlyHMSM() 延時執行任務
OSTimeDlyResume() 恢復處於延時狀態的任務
OSTimeGet() 獲得當前的時基計數值
OSTimeSet() 設定當前的時基計數值
OSTimeTick() 用於標記,表示發生了一個時基中斷

OSTimeDly()

函式結構

void OSTimeDly(OS_TICK dly,//指定延時的長度,單位為時間節拍
OS_OPT opt,//延時模式 OS_ERR *p_err//
  • 如果延時時間dly>0,則會發生任務排程;0表示不延時
  • OSTimeDly()函式延時模式有四種:OS_OPT_TIME_DLY;OS_OPT_TIME_TIMEOUT;OS_OPT_TIME_PERIODIC;OS_OPT_TIME_MATCH;

OS_TimeDlyHMSM()

void OS_TimeDlyHMSM(CPU_INT16U hours,//延時小時
                     CPU_INT16U minutes,//分鐘
                     CPU_INT16U seconds,//秒
CPU_INT32U milli,//毫秒 OS_OPT opt, //延時選項,範圍大小 //OS_OPT_TIME_HMSM_NON_STRICT //OS_OPT_TIME_HMSM_STRICT OS_ERR, *p_err )
  • OSTimeDlyHMSM()有兩種模式:可以指定引數的範圍。

延時任務任務可通過在其他任務中呼叫函式OSTimeDlyResume()取消延時而進入就緒狀態,此函式最後會引發一次任務排程。

二軟體定時器

軟體定時器步驟:
相關檔案:OS_TMR.C
- 使能:巨集OS_CFG_TMR_EN 使能為 1
- 時間解析度:巨集OS_CFG_TMR_TASK_RATE_HZ,表示步長,單位為HZ。
- 定義:OS_TMR xxx
- 建立定時器
- 建立回撥函式
- start或者stop定時器開啟後定時器才有用

1 API介面

API 註釋
OSTmrCreate() 建立和設定定時器
OSTmrDel() 刪除一個定時器
OSTmrRemainGet() 獲得定時器的剩餘期限值
OSTmrStart() 開始定時器執行
OSTmrStateGet() 獲得定時器當前的狀態
OSTmrStop() 暫停定時器

1)建立定時器

OSTmrCreate (OS_TMR *p_tmr, //結構體描述定時器
             CPU_CHAR *p_name,//名字
             OS_TICK dly,   //初始延時的第一個週期
             OS_TICK period,//除了第一個週期,之後的所有周期
             OS_OPT opt,   //選擇單次還是週期定時器
             OS_TMR_CALLBACK_PTR p_callback,//回撥函式
             void *p_callback_arg,//回撥函式的引數
             OS_ERR *p_err)

注:回撥函式名與定時器名字不能是一樣的

2)定時器開始

OSTmrStart (OS_TMR  *p_tmr, OS_ERR  *p_err)

3)定時器停止

OSTmrStop (OS_TMR *p_tmr,//結構體
           OS_OPT opt,  //CALLBACK在沒有計數到0的時候,就執行了stop也可以執行回撥函式
           void *p_callback_arg,//回撥函式新引數
           OS_ERR *p_err);

三 時鐘節拍

1時鐘節拍頻率

  • os_cfg_app.h設定時鐘節拍頻率,10Hz~1000Hz
#define  OS_CFG_TICK_RATE_HZ   1000u
  • OSTickCtr記錄了系統時鐘節拍數,在呼叫OSInit()時被初始化為0,以後每發生1個時鐘節拍,OSTickCtr加1。

2時鐘節拍原始碼分析(可跳過)

1)時間節拍產生

ucos中的時鐘節拍可以基於軟中斷實現或者基於時鐘節拍任務(但是這個任務要給予很高的優先順序),對於STM32來說這個就是SysTick中斷,當中斷髮生時呼叫OS_CPU_SysTickHandler()函式,這樣就提供了系統的時鐘節拍。
注意:產生時鐘節拍的方法有很多,可以是其他中斷源,因此在低功耗應用中可以利用這個特性。

/*時鐘節拍中斷函式函式*/
void  OS_CPU_SysTickHandler (void)
{
    CPU_SR_ALLOC();
    CPU_CRITICAL_ENTER();
    OSIntNestingCtr++;                                      /* Tell uC/OS-III that we are starting an ISR*/
    CPU_CRITICAL_EXIT();
    OSTimeTick();                                           /* Call uC/OS-III's OSTimeTick()*/
    OSIntExit();                                            /* Tell uC/OS-III that we are leaving the ISR*/
}

2)OSTimeTick()函式

上面程式碼可以看到,中斷函式肯定會呼叫OSTimeTick()。中斷函式說明請看ucosIII中斷章節。

/*OSTimeTick函式*/
void  OSTimeTick (void)
{
    OS_ERR  err;
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
    CPU_TS  ts;
#endif
    OSTimeTickHook();                                       /* Call user definable hook*/
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
    ts = OS_TS_GET();                                       /* Get timestamp*/
    OS_IntQPost((OS_OBJ_TYPE) OS_OBJ_TYPE_TICK,             /* Post to ISR queue*/
                (void      *)&OSRdyList[OSPrioCur],
                (void      *) 0,
                (OS_MSG_SIZE) 0u,
                (OS_FLAGS   ) 0u,
                (OS_OPT     ) 0u,
                (CPU_TS     ) ts,
                (OS_ERR    *)&err);
#else
   (void)OSTaskSemPost((OS_TCB *)&OSTickTaskTCB,            /* Signal tick task*/
                       (OS_OPT  ) OS_OPT_POST_NONE,
                       (OS_ERR *)&err);


#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
    OS_SchedRoundRobin(&OSRdyList[OSPrioCur]);
#endif

#if OS_CFG_TMR_EN > 0u
    OSTmrUpdateCtr--;
    if (OSTmrUpdateCtr == (OS_CTR)0u) {
        OSTmrUpdateCtr = OSTmrUpdateCnt;
        OSTaskSemPost((OS_TCB *)&OSTmrTaskTCB,              /* Signal timer task*/
                      (OS_OPT  ) OS_OPT_POST_NONE,
                      (OS_ERR *)&err);
    }
#endif
#endif

OS_CFG_ISR_POST_DEFERRED_EN是區分中斷函式是延時釋出還是直接釋出,具體分析請看中斷管理一章。
可以看到,ucos是通過中斷髮送訊息給優先順序較高的節拍任務來處理相關的節拍程式碼,而不直接在中斷處理,這樣好處是中斷佔用時間更少,平常中斷用法應該也是參考這樣使用。程式碼清單30-42行涉及Round-Robin時間片定時器相關暫不展開,先看OSTimeTickHook函式和OS_TickTask節拍任務

3)OSTimeTickHook()函式

一開始執行的是OSTimeTickHook(),這是一個鉤子函式,使用者可以在裡面編寫自己需要比較實時的程式碼,比如一個時鐘節拍任務,如果用延時是不精確的。

void  OSTimeTickHook (void)
{
#if OS_CFG_APP_HOOKS_EN > 0u
    if (OS_AppTimeTickHookPtr != (OS_APP_HOOK_VOID)0) {
        (*OS_AppTimeTickHookPtr)();
    }
#endif
}

OS_CFG_APP_HOOKS_EN 使能hook函式
先判斷OS_AppTimeTickHookPtr指標是否為空,如果不是呼叫則OS_AppTimeTickHookPtr。關於函式指標用法,可以看作者的文章。
hook鉤子函式初始化如下。初始化時應該關閉中斷CPU_CRITICAL_ENTER()。至於後面還會遇到CPU_CRITICAL_ENTER和OS_CRITICAL_ENTER區別,在中斷章節再詳細討論。
OS_AppTimeTickHookPtr = App_OS_TimeTickHook;

4)OS_TickTask節拍任務

os_tick.c中。

void  OS_TickTask (void  *p_arg)
{
    OS_ERR  err;
    CPU_TS  ts_delta;
    CPU_TS  ts_delta_dly;
    CPU_TS  ts_delta_timeout;
    CPU_SR_ALLOC();
    (void)&p_arg;      /* Prevent compiler warning*/
    while (DEF_ON) {
        (void)OSTaskSemPend((OS_TICK  )0,
                            (OS_OPT   )OS_OPT_PEND_BLOCKING,
                            (CPU_TS  *)0,
                            (OS_ERR  *)&err);                   /* Wait for signal from tick interrupt*/
        if (err == OS_ERR_NONE) {
            OS_CRITICAL_ENTER();
            OSTickCtr++;                               /* Keep track of the number of ticks*/
#if (defined(TRACE_CFG_EN) && (TRACE_CFG_EN > 0u))
            TRACE_OS_TICK_INCREMENT(OSTickCtr);           /* Record the event.*/
#endif
            OS_CRITICAL_EXIT();
            ts_delta_dly     = OS_TickListUpdateDly();
            ts_delta_timeout = OS_TickListUpdateTimeout();
            ts_delta         = ts_delta_dly + ts_delta_timeout; /* Compute total execution time of list updates*/
            if (OSTickTaskTimeMax < ts_delta) {
                OSTickTaskTimeMax = ts_delta;
            }
        }
    }
}

死迴圈中等待ISR傳送的訊號量,如果OSTaskSemPend沒有錯誤則呼叫OS_TickListUpdateDly,處理延時超時都在這裡,處理完繼續陷入死迴圈,等待下一次時鐘節拍ISR傳送的訊號量。
(未完持續)

5)OS_TickListUpdateDly(未完持續)

os_tick.c

6)雜湊演算法(未完持續)