1. 程式人生 > >【UCOSIII】嵌入式實時作業系統UCOSIII及其任務

【UCOSIII】嵌入式實時作業系統UCOSIII及其任務

UCOSIII的任務簡介

在UCOSIII中任務是以何種面貌存在的呢?

在UCOSIII中任務就是程式實體,UCOSIII能夠管理和排程這些小任務(程式)。UCOSIII中的任務由三部分組成:任務堆疊、任務控制塊和任務函式。

  • 任務堆疊:上下文切換的時候用來儲存任務的工作環境,就是STM32的內部暫存器值;
  • 任務控制塊:任務控制塊用來記錄任務的各個屬性;
  • 任務函式:由使用者編寫的任務處理程式碼,是實實在在幹活的,一般寫法如下:
   void XXX_task(void *p_arg)
   {
       while(1)
       {
            ...    //任務處理過程
        }
    }

可以看出用任務函式通常是一個無限迴圈,當然了,也可以是一個只執行一次的任務。任務的引數是一個void型別的,這麼做的目的是可以可以傳遞不同型別的資料甚至是函式。

可以看出任務函式其實就是一個C語言的函式,但是在使用UCOIII的情況下這個函式不能有使用者自行呼叫,任務函式何時執行執行,何時停止完全有作業系統來控制。

UCOSIII的系統任務

UCOSIII預設有5個系統任務:

  • 空閒任務:UCOSIII建立的第一個任務,UCOSIII必須建立的任務,此任務有UCOSIII自動建立,不需要使用者手動建立;
  • 時鐘節拍任務:此任務也是必須建立的任務;
  • 統計任務:可選任務,用來統計CPU使用率和各個任務的堆疊使用量。此任務是可選任務,由巨集OS_CFG_STAT_TASK_EN控制是否使用此任務;
  • 定時任務:用來向用戶提供定時服務,也是可選任務,由巨集OS_CFG_TMR_EN控制是否使用此任務;
  • 中斷服務管理任務:可選任務,由巨集OS_CFG_ISR_POST_DEFERRED_EN控制是否使用此任務。

前兩個系統任務時必須建立的任務,而後三者不是。控制後三者任務的巨集都是在檔案OS_CFG.h中。

UCOSIII的任務狀態

從使用者的角度看,UCOSIII的任務一共有5種狀態:

  • 休眠態:任務已經在CPU的flash中了,但是還不受UCOSIII管理;
  • 就緒態:系統為任務分配了任務控制塊,並且任務已經在就緒表中登記,這時這個任務就具有了執行的條件,此時任務的狀態就是就緒態;
  • 執行態:任務獲得CPU的使用權,正在執行;
  • 等待態:正在執行的任務需要等待一段時間,或者等待某個事件,這個任務就進入了等待態,此時系統就會把CPU使用權轉交給別的任務;
  • 中斷服務態:當傳送中斷,當前正在執行的任務會被掛起,CPU轉而去執行中斷服務函式,此時任務的任務狀態叫做中斷服務態。

這5種狀態之間相互轉化的關係如下圖:


UCOSIII的任務詳解

上文講到:UCOSIII中的任務由三部分組成:任務堆疊、任務控制塊和任務函式。下面就對這三個部分進行分析:

任務堆疊

任務堆疊是任務的重要部分,堆疊是在RAM中按照“先進先出(FIFO)”的原則組織的一塊連續的儲存空間。為了滿足任務切換和響應中斷時儲存CPU暫存器中的內容及任務呼叫其它函式時的需要,每個任務都應該有自己的堆疊。

任務堆疊建立
#define START_STK_SIZE 		512	//堆疊大小
CPU_STK START_TASK_STK[START_STK_SIZE];	//定義一個數組來作為任務堆疊

任務堆疊的大小是多少呢?

CPU_STK為CPU_INT32U型別,也就是unsigned int型別,為4位元組的,那麼任務堆疊START_TASK_STK的大小就為:512 X 4=2048位元組!

任務堆疊初始化

任務如何才能切換回上一個任務並且還能接著從上次被中斷的地方開始執行?

恢復現場即可,現場就是CPU的內部各個暫存器。因此在建立一個新任務時,必須把系統啟動這個任務時所需的CPU各個暫存器初始值事先存放在任務堆疊中。這樣當任務獲得CPU使用權時,就把任務堆疊的內容複製到CPU的各個暫存器,從而可以任務順利地啟動並執行。

把任務初始資料存放到任務堆疊的工作就叫做任務堆疊的初始化,UCOSIII提供了完成堆疊初始化的函式:OSTaskStkInit()。

CPU_STK  *OSTaskStkInit (OS_TASK_PTR    p_task,
                         void          *p_arg,
                         CPU_STK       *p_stk_base,
                         CPU_STK       *p_stk_limit,
                         CPU_STK_SIZE   stk_size,
                         OS_OPT         opt)
{
    ...                        //函式內容
    return (p_stk);
}

當然,使用者一般不會直接操作堆疊初始化函式,任務堆疊初始化函式由任務建立函式OSTaskCreate()呼叫。不同的CPU對於的暫存器和對堆疊的操作方式不同,因此在移植UCOSIII的時候需要使用者根據各自所選的CPU來編寫任務堆疊初始化函式。

void  OSTaskCreate (OS_TCB        *p_tcb,                    //任務控制塊
                    CPU_CHAR      *p_name,                    //任務名字
                    OS_TASK_PTR    p_task,                    //任務函式
                    void          *p_arg,                    //傳遞給任務函式的引數
                    OS_PRIO        prio,                    //任務優先順序
                    CPU_STK       *p_stk_base,                    //------任務堆疊基地址
                    CPU_STK_SIZE   stk_limit,                    //------任務堆疊深度限位
                    CPU_STK_SIZE   stk_size,                    //------任務堆疊大小
                    OS_MSG_QTY     q_size,
                    OS_TICK        time_quanta,
                    void          *p_ext,                    //使用者補充的儲存區
                    OS_OPT         opt,
                    OS_ERR        *p_err)                    //存放該函式錯誤時的返回值
{
    ...                    //函式內容
}

函式OSTaskCreate()中的引數p_stk_base(任務堆疊基地址)如何確定?

根據堆疊的增長方式,堆疊有兩種增長方式:

  • 向上增長:堆疊的增長方向從低地址向高地址增長;
  • 向下增長:堆疊的增長方向從高地址向低地址增長。

函式OSTaskCreate()中的引數p_stk_base是任務堆疊基地址,那麼如果CPU的堆疊是向上增長的話那麼基地址就&START_TASK_STK[0];如果CPU堆疊是向下增長的話基地址就是&START_TASK_STK[START_STK_SIZE-1]。STM32的堆疊是向下增長的!


任務控制塊

任務控制塊是用來記錄與任務相關的資訊的資料結構,每個任務都要有自己的任務控制塊。我們使用OSTaskCreate()函式來建立任務的時候就會給任務分配一個任務控制塊。任務控制塊由使用者自行建立,如下程式碼為建立一個任務控制塊:

   OS_TCB StartTaskTCB;  //建立一個任務控制塊

OS_TCB為一個結構體,描述了任務控制塊,任務控制塊中的成員變數使用者不能直接訪問,更不可能改變他們。

OS_TCB為一個結構體,其中有些成員採用了條件編譯的方式來確定。

struct os_tcb {
    CPU_STK             *StkPtr;                            /* 指向當前任務堆疊的棧頂 */

    void                *ExtPtr;                            /* 指向使用者可定義的資料區 */

    CPU_STK             *StkLimitPtr;                       /* 可指向任務堆疊中的某個位置 */

    OS_TCB              *NextPtr;                           /* Pointer to next     TCB in the TCB list  */
    OS_TCB              *PrevPtr;                           /* Pointer to previous TCB in the TCB list   */

    OS_TCB              *TickNextPtr;
    OS_TCB              *TickPrevPtr;

    OS_TICK_SPOKE       *TickSpokePtr;                      /* Pointer to tick spoke if task is in the tick list    */

    CPU_CHAR            *NamePtr;                           /* Pointer to task name */

    CPU_STK             *StkBasePtr;                        /* Pointer to base address of stack  */

#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
    OS_TLS               TLS_Tbl[OS_CFG_TLS_TBL_SIZE];
#endif

    OS_TASK_PTR          TaskEntryAddr;                 /* Pointer to task entry point address  */
    void                *TaskEntryArg;                  /* Argument passed to task when it was created */

    OS_PEND_DATA        *PendDataTblPtr;                /* Pointer to list containing objects pended on */
    OS_STATE             PendOn;                        /* Indicates what task is pending on */
    OS_STATUS            PendStatus;                    /* Pend status */

    OS_STATE             TaskState;                     /* See OS_TASK_STATE_xxx */
    OS_PRIO              Prio;                          /* Task priority (0 == highest) */
    CPU_STK_SIZE         StkSize;                       /* Size of task stack (in number of stack elements) */
    OS_OPT               Opt;                           /* Task options as passed by OSTaskCreate()*/

    OS_OBJ_QTY           PendDataTblEntries;            /* Size of array of objects to pend on  */

    CPU_TS               TS;                            /* Timestamp */

    OS_SEM_CTR           SemCtr;                        /* Task specific semaphore counter */

                                                        /* DELAY / TIMEOUT   */
    OS_TICK              TickCtrPrev;                   /* Previous time when task was            ready   */
    OS_TICK              TickCtrMatch;                  /* Absolute time when task is going to be ready    */
    OS_TICK              TickRemain;                    /* Number of ticks remaining for a match  */
                                                        /* ... run-time by OS_StatTask()   */
    OS_TICK              TimeQuanta;
    OS_TICK              TimeQuantaCtr;

#if OS_MSG_EN > 0u
    void                *MsgPtr;                        /* Message received   */
    OS_MSG_SIZE          MsgSize;
#endif

#if OS_CFG_TASK_Q_EN > 0u
    OS_MSG_Q             MsgQ;                          /* Message queue associated with task   */
#if OS_CFG_TASK_PROFILE_EN > 0u
    CPU_TS               MsgQPendTime;                  /* Time it took for signal to be received     */
    CPU_TS               MsgQPendTimeMax;               /* Max amount of time it took for signal to be received */
#endif
#endif

#if OS_CFG_TASK_REG_TBL_SIZE > 0u
    OS_REG               RegTbl[OS_CFG_TASK_REG_TBL_SIZE];  /* Task specific registers   */
#endif

#if OS_CFG_FLAG_EN > 0u
    OS_FLAGS             FlagsPend;                     /* Event flag(s) to wait on      */
    OS_FLAGS             FlagsRdy;                      /* Event flags that made task ready to run    */
    OS_OPT               FlagsOpt;                      /* Options (See OS_OPT_FLAG_xxx)    */
#endif

#if OS_CFG_TASK_SUSPEND_EN > 0u
    OS_NESTING_CTR       SuspendCtr;                    /* Nesting counter for OSTaskSuspend()    */
#endif

#if OS_CFG_TASK_PROFILE_EN > 0u
    OS_CPU_USAGE         CPUUsage;                      /* CPU Usage of task (0.00-100.00%)   */
    OS_CPU_USAGE         CPUUsageMax;                   /* CPU Usage of task (0.00-100.00%) - Peak   */
    OS_CTX_SW_CTR        CtxSwCtr;                      /* Number of time the task was switched in  */
    CPU_TS               CyclesDelta;                   /* value of OS_TS_GET() - .CyclesStart    */
    CPU_TS               CyclesStart;                   /* Snapshot of cycle counter at start of task resumption  */
    OS_CYCLES            CyclesTotal;                   /* Total number of # of cycles the task has been running  */
    OS_CYCLES            CyclesTotalPrev;               /* Snapshot of previous # of cycles                       */

    CPU_TS               SemPendTime;                   /* Time it took for signal to be received    */
    CPU_TS               SemPendTimeMax;                /* Max amount of time it took for signal to be received   */
#endif

#if OS_CFG_STAT_TASK_STK_CHK_EN > 0u
    CPU_STK_SIZE         StkUsed;                       /* Number of stack elements used from the stack   */
    CPU_STK_SIZE         StkFree;                       /* Number of stack elements free on   the stack    */
#endif

#ifdef CPU_CFG_INT_DIS_MEAS_EN
    CPU_TS               IntDisTimeMax;                 /* Maximum interrupt disable time    */
#endif
#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u
    CPU_TS               SchedLockTimeMax;              /* Maximum scheduler lock time      */
#endif

#if OS_CFG_DBG_EN > 0u
    OS_TCB              *DbgPrevPtr;
    OS_TCB              *DbgNextPtr;
    CPU_CHAR            *DbgNamePtr;
#endif
};
任務控制塊初始化

USOCIII提供了用於任務控制塊初始化的函式:OS_TaskInitTCB()。但是,使用者不需要自行初始化任務控制塊。因為和任務堆疊初始化函式一樣,函式OSTaskCreate()在建立任務的時候會對任務的任務控制塊進行初始化。

UCOSIII的任務就緒表

UCOSIII中任務優先順序數由巨集OS_CFG_PRIO_MAX來配置,UCOSIII中數值越小,優先順序越高,最低可用優先順序就是OS_CFG_PRIO_MAX-1。預設OS_CFG_PRIO_MAX的值為64。

UCOSIII中就緒表由2部分組成:

  • 優先順序位對映表OSPrioTbl[]:用來記錄哪個優先順序下有任務就緒;
  • 就緒任務列表OSRdyList[]:用來記錄每一個優先順序下所有就緒的任務。

優先順序位對映表OSPrioTbl[]

OSPrioTbl[]在os_prio.c中有定義:

CPU_DATA   OSPrioTbl[OS_PRIO_TBL_SIZE]; 

在STM32中CPU_DATA為unsigned int,有4個位元組,32位。因此表OSPrioTbl每個引數有32位,其中每個位對應一個優先順序下是否有任務就緒。

OS_PRIO_TBL_SIZE = ((OS_CFG_PRIO_MAX - 1u) / DEF_INT_CPU_NBR_BITS)+ 1)
DEF_INT_CPU_NBR_BITS = CPU_CFG_DATA_SIZE * DEF_OCTET_NBR_BITS

OS_CFG_PRIO_MAX由使用者自行定義,預設為64。而CPU_CFG_DATA_SIZE=CPU_WORD_SIZE_32=4,DEF_OCTET_NBR_BITS=8。

所以,當系統有64個優先順序的時候:OS_PRIO_TBL_SIZE=((64-1)/(4*8)+1)=2。


對比圖就很明確了,由於由64個優先順序,所以用兩個32位的數來儲存。每一位相對應於一個優先順序,該位為1表示該優先順序有任務就緒,該位為0表示該優先順序沒有任務就緒。

如何找到已經就緒了的最高優先順序的任務?

函式OS_PrioGetHighest()用於找到就緒了的最高優先順序的任務。

OS_PRIO  OS_PrioGetHighest (void)
{
    CPU_DATA  *p_tbl;                    
    OS_PRIO    prio;

    prio  = (OS_PRIO)0;
    p_tbl = &OSPrioTbl[0];                                //指向優先順序位對映表的第一個32bit的數
    while (*p_tbl == (CPU_DATA)0) {                         /* 判斷該32位的數為0       */
        prio += DEF_INT_CPU_NBR_BITS;                       /* 將32位的數拿出來               */
        p_tbl++;
    }
    prio += (OS_PRIO)CPU_CntLeadZeros(*p_tbl);              /* Find the position of the first bit set at the entry    */
    return (prio);
}

這段程式的思路:先判斷每一個32位的數是否為0,不為0的話,再通過函式CPU_CntLeadZeros()來計算前導零數量。這個函式用匯編來寫的:

CPU_CntLeadZeros
        CLZ     R0, R0                          ; Count leading zeros
        BX      LR

就緒任務列表OSRdyList[]

通過上一步我們已經知道了哪個優先順序的任務已經就緒了,但是UCOSIII支援時間片輪轉排程,同一個優先順序下可以有多個任務,因此我們還需要在確定是優先順序下的哪個任務就緒了。

先看一下os_rdy_list[]的定義:

struct  os_rdy_list {
    OS_TCB           *HeadPtr	         //用於建立連結串列,指向連結串列頭
    OS_TCB           *TailPtr;           //用於建立連結串列,指向連結串列尾
    OS_OBJ_QTY       NbrEntries;         //此優先順序下的任務數量
};

UCOSIII支援時間片輪轉排程,因此在一個優先順序下會有多個任務,那麼我們就要對這些任務做一個管理,這裡使用OSRdyList[]陣列管理這些任務。OSRdyList[]陣列中的每個元素對應一個優先順序,比如OSRdyList[0]就用來管理優先順序0下的所有任務。OSRdyList[0]為OS_RDY_LIST型別,從上面OS_RDY_LIST結構體可以看到成員變數:HeadPtr和TailPtr分別指向OS_TCB,我們知道OS_TCB是可以用來構造連結串列的,因此同一個優先順序下的所有任務是通過連結串列來管理的,HeadPtr和TailPtr分別指向這個連結串列的頭和尾,NbrEntries用來記錄此優先順序下的任務數量,圖5.5.2表示了優先順序4現在有3個任務時候的就緒任務列表。


這裡記住一句話:同一優先順序下如果有多個任務的話最先執行的永遠是HeadPtr所指向的任務!

相關推薦

UCOSIII嵌入式實時作業系統UCOSIII及其任務

UCOSIII的任務簡介在UCOSIII中任務是以何種面貌存在的呢?在UCOSIII中任務就是程式實體,UCOSIII能夠管理和排程這些小任務(程式)。UCOSIII中的任務由三部分組成:任務堆疊、任務控制塊和任務函式。任務堆疊:上下文切換的時候用來儲存任務的工作環境,就是S

專欄 - 嵌入式實時作業系統UCOS-III

我的學校:東南大學(福建大三本) [注1:如有疑問,歡迎郵件或QQ交流] [注2:CSDN登入頻繁,也可直接在部落格評論處留言] The best preparation for tomorrow is doing your best today!

原創linux實時作業系統xenomai x86平臺基準測試(benchmark)

## 一、前言 benchmark 即基準測試。通常作業系統主要服務於應用程式,其執行也是需要一定cpu資源的,一般來說作業系統提供服務一定要快,否則會影響應用程式的執行效率,尤其是實時作業系統。所以本文針對作業系統來做一些基準測試,看看在低端x86平臺上,xenomai提供我們平時常用的服務所需要的時間,

RTX作業系統教程第2章 嵌入式實時作業系統介紹

第2章      嵌入式實時作業系統介紹 轉載來源: http://forum.armfly.com/forum.php?mod=viewthread&tid=16447&highlight=RTX%B2%D9%D7%F7%CF%

JavaScript設計模式-module模式及其改進

uid ... hello 代碼 first 其中 nbsp amp 初學者 寫在前面 編寫易於維護的代碼,其中最重要的方面就是能夠找到代碼中重復出現的主題並優化他們,這也是設計模式最有價值的地方 說到這裏...... 《head first設計模式》裏有一篇文章,是說使用

嵌入式實時作業系統small RTOS51原理及應用 ----筆記 第三章 一個簡單的例子

嵌入式實時作業系統small RTOS51原理及應用 ----筆記 第三章 一個簡單的例子 keil C51 除錯程式碼: 軟體模擬模擬 執行 原始碼 #define OS_ENTER_CRITICAL() EA = 0,Os_Enter_Sum++

嵌入式實時作業系統small RTOS51原理及應用 ----筆記 前言 緒論

圍繞 DP-51 微控制器模擬實驗儀的硬體電路, 闡述Small RTOS51 作業系統的設計原理。 詳細的介紹了RS232 ,I2C,USB1.1和CANBUS等一系列外圍器件及其功能部件的應用設計。 本書的原理篇,以Small RTOS V1.12.1版本為基礎, 微型嵌入式

嵌入式實時作業系統small RTOS51原理及應用 ----筆記 第五章 如何任務切換

嵌入式實時作業系統small RTOS51原理及應用 ----筆記 第五章 如何任務切換 5.3 何時進行任務切換 參考書籍<MCS-51微控制器原理與應用.pdf> TMOD = (TMOD & 0XF0) | 0X01; TL0 = 0x0;

jenkinsjenkins實時顯示python指令碼輸出

jenkins在構建shell指令碼時可以實時輸出結果,但是在構建python指令碼時,是等到python執行完成以後,才顯示結果,這個對於我們判斷指令碼執行狀態非常不利 這裡介紹一種方法,能夠實時顯示python的輸出。 也就是用python -u python指令碼 使用python --help檢

轉載UML類圖畫法及其之間的幾種關係

【轉載】UML類圖畫法及其之間的幾種關係 原文地址: https://blog.csdn.net/wanmeirongyan100/article/details/51601570 UML類圖畫法及其之間的幾種關係 最近做重構專案,需要畫一下類圖,發現類圖的畫法及其之間的幾種關

6基礎知識類---eval函式及其安全性問題

eval()函式及其安全性問題 1.主要用途 a. 型別轉換:字串轉為列表、字典、元組 b. 做計算器使用 # 舉例1:型別轉換:字串轉為列表、字典、元組 mylist = '[1,2,3,4,[5,6,7,8,9]]' mydict = "{'a':12

嵌入式資料庫系統Berkeley DB

UNIX/LINUX平臺下的資料庫種類非常多,參考資料1中列舉了其中的大部分。通常,我們在設計UNIX/LINUX平臺下的應用軟體時,如果資料種類繁多,資料與資料之間關係比較複雜,就會選用一些大型的企業級資料庫系統,如DB2,ORACLE、SYBASE等,如果軟體規模不大,就傾向選用如MYSQL、POSTGR

總結嵌入式Linux學習中遇到的問題及解決方法

持續更新。。。(1)uboot中執行nfs 32000000  192.168.1.101:/work/nfs_root/uImage_new下載新核心時出現:TTT***ERROR:Cannot um

HTM層級實時記憶腦皮質學習演算法 一、當前進度

躲不過去的畢業,好煩惱。 但是不能再逃避了 第一,先定了論文題目 第二,確定研究思路 第三,出成果,寫論文 HTM是基於大腦學習、預測、記憶的原理的演算法,而非神經網路演算法那種只求結果一樣,不求原理的演算法。 直接學習之前,還是應該先看《人工智慧的未來》,否則對於演算

嵌入式實時作業系統µC/OS-II》學習筆記(一)

這本書,早在兩年前畢業,一位一起進公司的好友就買了,不過一直沒看,翻了翻目錄,似乎工作中根本用不到,抱著一種若不能學以致用,則學了也很難深入的想法,一直也就沒看。直到在上期《程式設計師》上看到推薦,才忽然提起興趣,兩年嵌入式開發以後,再回過頭來看此書,確實還說不好合適不合適,也許隨著瞭解的深入,不保證某天就中

嵌入式實時作業系統µC/OS-II》學習筆記(二)

就緒表:uC/OS-II最多支援64個任務(包括系統任務),使用一個BYTE(OSRdyGrp)和一個最大(與任務數相關)BYTE[8]的陣列(OSRdyTbl[])表示它們的就緒狀態。OSRdyGrp的第n位代表第n組中是(1)否(0)存在就緒任務,OSRdyTbl[n]的第n位表示這個任務是(1)否(0)

嵌入式實時作業系統uc/os-ii 原理及應用 讀書筆記

對任務就緒表的操作理解: 將優先級別為prio的任務置為就緒狀態,可使用如下程式碼 OSRdyGrp |= OSMapTbl[prio >>3];//將prio任務所在的組狀態置為1,表示該組有任務就緒。 OSRdyTbl[prio>>3] |= O

非客觀書評(二)——《嵌入式實時作業系統μCOS-Ⅱ》

作  者: (美)Jean J.Labrosse 著,邵貝貝 等譯 出 版 社: 北京航天航空大學出版社 出版時間: 2003-5-1 字  數: 979000 版  次: 1 頁  數: 582 印刷時間: 2003-5-1 開  本: 16開 紙  張: 膠

什麼是嵌入式實時作業系統ucos-ii?有了解的嗎?

對於嵌入式實時作業系統嵌入式實時作業系統ucos-ii你知道有那哪些嗎?凌陽教育的老師為你解答, 關於嵌入式實時作業系統ucos-ii: uCOS II   是由Labrosse先生編寫的一個開放式核心,最主要的特點就是原始碼公開。這一點對於使用者來說可謂利弊各半,好處在於

嵌入式實時作業系統ucos/ii 原理與應用(七)

第八章 在51微控制器上移植μC/OS-Ⅱ 8.1 μC/OS-Ⅱ移植的一般性問題 8.1.1 可重入函式 能允許同時被多個任務所呼叫,而不會通過函式中變數的耦合引起任務之間的相互干擾的函式叫做可重入函式。 一個可重入函式只使用區域性變數,因為函式的區域性變數儲存