手把手,嘴對嘴,講解UCOSII嵌入式操做系統的任務(一)

 

  作過軟件的同窗都知道,任何一個程序都必需要有初始化的過程,在初始化過程當中,咱們會對外圍硬件以及CPU的奔跑環境進行初步的設置,以便接下來的使用和調度。node

  之前在寫單片機邏輯程序之時,系統初始化過程大概分爲兩種:算法

  ①外圍硬件的初始化(好比MCU寄存器,時鐘,看門狗,串口,IO口,SPI等等)數組

  ②代碼內參數的初始化(好比堆棧,變量,結構體等等)數據結構

  UCOSII操做系統想要跑起來,固然也須要一系列的初始化,好比中斷模式、延時模塊、外圍硬件等等,但本文不講硬件相關,只對操做系統自己的初始化進行一些講解。app

  首先請看一段熟悉的代碼:函數

 1 #include "sys.h"
 2 #include "delay.h"
 3 #include "led.h"
 4 #include "includes.h"
 5 
 6 
 7 /////////////////////////UCOSII任務設置///////////////////////////////////
 8 //START 任務
 9 //設置任務優先級
10 #define START_TASK_PRIO                 10 //開始任務的優先級設置爲最低
11 //設置任務堆棧大小
12 #define START_STK_SIZE                  64
13 //任務堆棧
14 OS_STK START_TASK_STK[START_STK_SIZE];
15 //任務函數
16 void start_task(void *pdata);
17 
18 //LED0任務
19 //設置任務優先級
20 #define LED0_TASK_PRIO                  7
21 //設置任務堆棧大小
22 #define LED0_STK_SIZE                   64
23 //任務堆棧
24 OS_STK LED0_TASK_STK[LED0_STK_SIZE];
25 //任務函數
26 void led0_task(void *pdata);
27 
28 
29 //LED1任務
30 //設置任務優先級
31 #define LED1_TASK_PRIO                  6
32 //設置任務堆棧大小
33 #define LED1_STK_SIZE                   64
34 //任務堆棧
35 OS_STK LED1_TASK_STK[LED1_STK_SIZE];
36 //任務函數
37 void led1_task(void *pdata);
38 
39 int main(void)
40 {
41     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設置中斷優先級分組爲組2:2位搶佔優先級,2位響應優先級
42     delay_init();       //延時函數初始化
43     LED_Init();         //初始化與LED鏈接的硬件接口
44     OSInit();
45     OSTaskCreate(start_task,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );//建立起始任務
46     OSStart();
47 }
48 
49 //開始任務
50 void start_task(void *pdata)
51 {
52     OS_CPU_SR cpu_sr=0;
53     pdata = pdata;
54     OS_ENTER_CRITICAL();            //進入臨界區(沒法被中斷打斷)
55     OSTaskCreate(led0_task,(void *)0,(OS_STK*)&LED0_TASK_STK[LED0_STK_SIZE-1],LED0_TASK_PRIO);
56     OSTaskCreate(led1_task,(void *)0,(OS_STK*)&LED1_TASK_STK[LED1_STK_SIZE-1],LED1_TASK_PRIO);
57     OSTaskSuspend(START_TASK_PRIO); //掛起起始任務.
58     OS_EXIT_CRITICAL();             //退出臨界區(能夠被中斷打斷)
59 }
60 
61 //LED0任務
62 void led0_task(void *pdata)
63 {
64     while(1)
65     {
66         LED0=0;
67         delay_ms(80);
68         LED0=1;
69         delay_ms(920);
70     };
71 }
72 
73 //LED1任務
74 void led1_task(void *pdata)
75 {
76     while(1)
77     {
78         LED1=0;
79         delay_ms(300);
80         LED1=1;
81         delay_ms(300);
82     };
83 }

  以上的代碼你們都不陌生,這幾乎即是UCOSII系統初始化的標準格式,首先是定義任務的基本信息,優先級,堆棧大小,堆棧空間,任務函數等等。spa

  而後由main函數開始執行具體的初始化過程,分別是中斷模式設定,延時功能設定,以及外圍硬件設定,等這些東西都設定完成之後,便進入了操做系統的設定,最後起始任務執行完畢,程序會跳進應用任務中執行。操作系統

  而main函數中的OSInit()這個函數即是咱們本次講解的重點。指針

  UCOSII操做系統在初始化的過程當中,到底作了一些什麼?或者說函數OSInit()中到底有些什麼處理?code

  廢話很少說,直接進入這個函數的定義:

 1 void  OSInit (void)
 2 {
 3     OSInitHookBegin();                                           /* Call port specific initialization code   */
 4 
 5     OS_InitMisc();                                               /* Initialize miscellaneous variables       */
 6 
 7     OS_InitRdyList();                                            /* Initialize the Ready List                */
 8 
 9     OS_InitTCBList();                                            /* Initialize the free list of OS_TCBs      */
10 
11     OS_InitEventList();                                          /* Initialize the free list of OS_EVENTs    */
12 
13 #if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)
14     OS_FlagInit();                                               /* Initialize the event flag structures     */
15 #endif
16 
17 #if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)
18     OS_MemInit();                                                /* Initialize the memory manager            */
19 #endif
20 
21 #if (OS_Q_EN > 0u) && (OS_MAX_QS > 0u)
22     OS_QInit();                                                  /* Initialize the message queue structures  */
23 #endif
24 
25     OS_InitTaskIdle();                                           /* Create the Idle Task                     */
26 #if OS_TASK_STAT_EN > 0u
27     OS_InitTaskStat();                                           /* Create the Statistic Task                */
28 #endif
29 
30 #if OS_TMR_EN > 0u
31     OSTmr_Init();                                                /* Initialize the Timer Manager             */
32 #endif
33 
34     OSInitHookEnd();                                             /* Call port specific init. code            */
35 
36 #if OS_DEBUG_EN > 0u
37     OSDebugInit();
38 #endif
39 }

  以上即是系統初始化函數中的處理,看得出來,它只是一個接口,一個容器,真正的處理還在它內部調用的那些函數裏,接下來咱們開始一句一句的理解。(簡單的函數用黑色,複雜的函數用紅色)

 

    一:函數OSInitHookBegin(); 

  其定義以下:

1 #if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203
2 void  OSInitHookBegin (void)
3 {
4 #if OS_TMR_EN > 0
5     OSTmrCtr = 0;
6 #endif
7 }
8 #endif

 

     這個函數俗稱鉤子函數,它裏面自己不帶任何處理,是專門留給用戶擴展的,當用戶須要在初始化裏執行某些處理的時候,能夠在這裏把本身的代碼添加進去。

     好比我想在初始化時點亮一個LED小燈或者讓喇叭叫起來,那麼就能夠在裏面寫個點燈的代碼(注意這個函數不能被意外打斷)。

     咱們能夠看到在它的頭上有兩個宏開關,只有都知足纔會執行,第一個開關OS_CPU_HOOKS_EN是使能位,若是咱們不須要在這裏執行任何處理,能夠直接把OS_CPU_HOOKS_EN宏定位爲0(不建議定義爲0,由於編譯會出問題,須要修改的地方很多)。

1 #define OS_CPU_HOOKS_EN              0u                /* uC/OS-II hooks are found in the processor port files         */

  第二個宏開關是系統版本,只有系統在203版本以上才能用這個功能。

1 #define  OS_VERSION                 291u                /* Version of uC/OS-II (Vx.yy mult. by 100)    */

我如今的版本是291,因此固然沒問題啦!

 

  二:函數OS_InitMisc()  

  其定義以下:

 1 static  void  OS_InitMisc (void)
 2 {
 3 #if OS_TIME_GET_SET_EN > 0u
 4     OSTime                    = 0uL;                       /* Clear the 32-bit system clock            */
 5 #endif
 6 
 7     OSIntNesting              = 0u;                        /* Clear the interrupt nesting counter      */
 8     OSLockNesting             = 0u;                        /* Clear the scheduling lock counter        */
 9 
10     OSTaskCtr                 = 0u;                        /* Clear the number of tasks                */
11 
12     OSRunning                 = OS_FALSE;                  /* Indicate that multitasking not started   */
13 
14     OSCtxSwCtr                = 0u;                        /* Clear the context switch counter         */
15     OSIdleCtr                 = 0uL;                       /* Clear the 32-bit idle counter            */
16 
17 #if OS_TASK_STAT_EN > 0u
18     OSIdleCtrRun              = 0uL;
19     OSIdleCtrMax              = 0uL;
20     OSStatRdy                 = OS_FALSE;                  /* Statistic task is not ready              */
21 #endif
22 
23 #ifdef OS_SAFETY_CRITICAL_IEC61508
24     OSSafetyCriticalStartFlag = OS_FALSE;                  /* Still allow creation of objects          */
25 #endif
26 }

  這個函數的做用,是對系統中的某些全局變量進行初始化:

  OSTime是系統中滴答時鐘的存儲變量,若是想要使用這個變量的話,那麼宏開關OS_TIME_GET_SET_EN必需要設置爲1。

  ※這個變量仍是挺有用的,好比當我須要判斷系統時間是否通過了50ms,那麼就能夠調用OSTimeGet()函數讀一下這個變量,而後過一下子再讀一下,只要兩次讀數相差達到50,那就是通過了50ms,這樣就不用專門再開一個定時器了。

  OSIntNesting是中斷嵌套計數變量,進中斷時加1,出中斷時減1,當變量爲0的時候表示沒有進入過中斷,當等於1的時候表示進入了1重中斷,當等於2的時候表示進入了2重中斷……以此類推。

  OSLockNesting是調度器上鎖次數變量,當調用函數OSSchedLock()便會對這個變量進行加1處理,當調用函數OSSchedUnlock()便會對它減1處理,只有在變量OSLockNesting等於0時,系統纔會進行任務調度(當執行某些不能被別的任務打斷的處理時,能夠用這個功能)。

  OSTaskCtr是記錄系統中一共有多少個任務,好比我上面的那段代碼本身建立了3個任務,那等任務創建完畢之後,這個變量至少是大於3了,因爲UCOSII系統還保留了一些系統任務(空閒任務,統計任務等),因此這個變量確定比3大。

       OSRunning是記錄操做系統當前的狀態,操做系統跑起來是TRUE,沒跑起來是FALSE,如今還在初始化,確定是FALSE。

  OSCtxSwCtr是記錄任務切換的次數,當從優先級0的任務切換到優先級1的任務之時,它就會加1,在切換回來,它又會加1,等加到極限之後,從新變成0。

  OSIdleCtr是記錄空閒任務執行的次數,每執行一次空閒任務,它就加1,這個變量能夠配合統計任務來計算CPU的使用率。

 

1 #if OS_TASK_STAT_EN > 0u
2     OSIdleCtrRun              = 0uL;
3     OSIdleCtrMax              = 0uL;
4     OSStatRdy                 = OS_FALSE;                  /* Statistic task is not ready              */
5 #endif

  這三句代碼有宏開關,和統計任務相關,專門用來計算CPU的使用率,若是不須要這個數據,直接把宏開關設0即可。

1 #ifdef OS_SAFETY_CRITICAL_IEC61508
2     OSSafetyCriticalStartFlag = OS_FALSE;                  /* Still allow creation of objects          */
3 #endif

  這段代碼我沒有找到是用來作什麼的,若是有哪位同窗瞭解,還但願不吝賜教。

  三:函數OS_InitRdyList()

  其定義以下:

 1 static  void  OS_InitRdyList (void)
 2 {
 3     INT8U  i;
 4 
 5 
 6     OSRdyGrp      = 0u;                                    /* Clear the ready list                     */
 7     for (i = 0u; i < OS_RDY_TBL_SIZE; i++) {
 8         OSRdyTbl[i] = 0u;
 9     }
10 
11     OSPrioCur     = 0u;
12     OSPrioHighRdy = 0u;
13 
14     OSTCBHighRdy  = (OS_TCB *)0;
15     OSTCBCur      = (OS_TCB *)0;
16 }

  該函數的做用是用來初始化任務、以及任務優先級相關的變量。

  OSRdyGrp 是記錄當前全部任務組的就緒狀態,是任務調度算法的重要變量。

  OSRdyTbl[]是記錄當前全部任務的就緒狀態,是任務調度算法的重要變量。

  OSPrioCur是記錄當前正在執行的任務。

  OSPrioHighRdy是記錄當前就緒任務中,優先級最高的那個任務的優先級。

  OSTCBHighRdy這是一個結構體,裏面記錄優先級最高的那個任務的信息,初始化時沒有任務執行,其值爲0。

  OSTCBCur也是一個結構體,裏面記錄當前正在執行的那個任務的信息,初始化時沒有任務執行,其值爲0。

  

  四:函數OS_InitTCBList()

  定義以下:

 1 static  void  OS_InitTCBList (void)
 2 {
 3     INT8U    ix;
 4     INT8U    ix_next;
 5     OS_TCB  *ptcb1;
 6     OS_TCB  *ptcb2;
 7 
 8 
 9     OS_MemClr((INT8U *)&OSTCBTbl[0],     sizeof(OSTCBTbl));      /* Clear all the TCBs                 */
10     OS_MemClr((INT8U *)&OSTCBPrioTbl[0], sizeof(OSTCBPrioTbl));  /* Clear the priority table           */
11     for (ix = 0u; ix < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1u); ix++) {    /* Init. list of free TCBs     */
12         ix_next =  ix + 1u;
13         ptcb1   = &OSTCBTbl[ix];
14         ptcb2   = &OSTCBTbl[ix_next];
15         ptcb1->OSTCBNext = ptcb2;
16 #if OS_TASK_NAME_EN > 0u
17         ptcb1->OSTCBTaskName = (INT8U *)(void *)"?";             /* Unknown name                       */
18 #endif
19     }
20     ptcb1                   = &OSTCBTbl[ix];
21     ptcb1->OSTCBNext        = (OS_TCB *)0;                       /* Last OS_TCB                        */
22 #if OS_TASK_NAME_EN > 0u
23     ptcb1->OSTCBTaskName    = (INT8U *)(void *)"?";              /* Unknown name                       */
24 #endif
25     OSTCBList               = (OS_TCB *)0;                       /* TCB lists initializations          */
26     OSTCBFreeList           = &OSTCBTbl[0];
27 }

 

在講解函數以前,首先看一下任務信息結構體的定義:

 1 typedef struct os_tcb {
 2     OS_STK          *OSTCBStkPtr;           /* Pointer to current top of stack                         */
 3 
 4 #if OS_TASK_CREATE_EXT_EN > 0u
 5     void            *OSTCBExtPtr;           /* Pointer to user definable data for TCB extension        */
 6     OS_STK          *OSTCBStkBottom;        /* Pointer to bottom of stack                              */
 7     INT32U           OSTCBStkSize;          /* Size of task stack (in number of stack elements)        */
 8     INT16U           OSTCBOpt;              /* Task options as passed by OSTaskCreateExt()             */
 9     INT16U           OSTCBId;               /* Task ID (0..65535)                                      */
10 #endif
11 
12     struct os_tcb   *OSTCBNext;             /* Pointer to next     TCB in the TCB list                 */
13     struct os_tcb   *OSTCBPrev;             /* Pointer to previous TCB in the TCB list                 */
14 
15 #if (OS_EVENT_EN)
16     OS_EVENT        *OSTCBEventPtr;         /* Pointer to          event control block                 */
17 #endif
18 
19 #if (OS_EVENT_EN) && (OS_EVENT_MULTI_EN > 0u)
20     OS_EVENT       **OSTCBEventMultiPtr;    /* Pointer to multiple event control blocks                */
21 #endif
22 
23 #if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) || (OS_MBOX_EN > 0u)
24     void            *OSTCBMsg;              /* Message received from OSMboxPost() or OSQPost()         */
25 #endif
26 
27 #if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)
28 #if OS_TASK_DEL_EN > 0u
29     OS_FLAG_NODE    *OSTCBFlagNode;         /* Pointer to event flag node                              */
30 #endif
31     OS_FLAGS         OSTCBFlagsRdy;         /* Event flags that made task ready to run                 */
32 #endif
33 
34     INT32U           OSTCBDly;              /* Nbr ticks to delay task or, timeout waiting for event   */
35     INT8U            OSTCBStat;             /* Task      status                                        */
36     INT8U            OSTCBStatPend;         /* Task PEND status                                        */
37     INT8U            OSTCBPrio;             /* Task priority (0 == highest)                            */
38 
39     INT8U            OSTCBX;                /* Bit position in group  corresponding to task priority   */
40     INT8U            OSTCBY;                /* Index into ready table corresponding to task priority   */
41     OS_PRIO          OSTCBBitX;             /* Bit mask to access bit position in ready table          */
42     OS_PRIO          OSTCBBitY;             /* Bit mask to access bit position in ready group          */
43 
44 #if OS_TASK_DEL_EN > 0u
45     INT8U            OSTCBDelReq;           /* Indicates whether a task needs to delete itself         */
46 #endif
47 
48 #if OS_TASK_PROFILE_EN > 0u
49     INT32U           OSTCBCtxSwCtr;         /* Number of time the task was switched in                 */
50     INT32U           OSTCBCyclesTot;        /* Total number of clock cycles the task has been running  */
51     INT32U           OSTCBCyclesStart;      /* Snapshot of cycle counter at start of task resumption   */
52     OS_STK          *OSTCBStkBase;          /* Pointer to the beginning of the task stack              */
53     INT32U           OSTCBStkUsed;          /* Number of bytes used from the stack                     */
54 #endif
55 
56 #if OS_TASK_NAME_EN > 0u
57     INT8U           *OSTCBTaskName; 58 #endif 59 60 #if OS_TASK_REG_TBL_SIZE > 0u 61  INT32U OSTCBRegTbl[OS_TASK_REG_TBL_SIZE]; 62 #endif 63 } OS_TCB;

 這個結構體乍一看仍是挺大的,不過咱們如今只關注核心數據,去掉那些被宏開關包圍的成員,加上一些註釋,再看一下:

typedef struct os_tcb {
    OS_STK          *OSTCBStkPtr;           /* 指向任務堆棧的指針                   */
 struct os_tcb *OSTCBNext; /* 指向下一個節點的指針 */ struct os_tcb *OSTCBPrev; /* 指向上一個節點的指針 */ INT32U OSTCBDly; /* 任務的延時參數 */ INT8U OSTCBStat; /* 任務的狀態 */ INT8U OSTCBStatPend; /* 任務的阻塞狀態 */ INT8U OSTCBPrio; /* 任務的優先級 */                           /* 下面4個參數是有關優先級算法的,做用三兩句說不清楚,能夠參考我上一篇講任務調度的文章 */ INT8U OSTCBX; /* Bit position in group corresponding to task priority */ INT8U OSTCBY; /* Index into ready table corresponding to task priority */ OS_PRIO OSTCBBitX; /* Bit mask to access bit position in ready table */ OS_PRIO OSTCBBitY; /* Bit mask to access bit position in ready group */ } OS_TCB;

簡化後明瞭多了,咱們由定義的那兩個成員可知,這是一個雙向鏈表(若是對鏈表這種數據結構還不是很清楚,建議先去百度瞭解一下)。

所以,上面那個函數的做用也就是創建一個鏈表(空閒鏈表),長度根據本身的配置。

#define OS_MAX_TASKS             10u   /* Max. number of tasks in your application, MUST be >= 2       */
#define  OS_N_SYS_TASKS          2u    /* Number of system tasks  

我這裏把宏定義爲10(最大支持10個用戶任務),再加上系統自己的保留任務2個(空閒任何、統計任務),那麼這個鏈表的長度就是12。

這個鏈表有什麼用?

我認爲是爲了預留出未來的空間,先創建一個空表在內存中佔地方,等從此須要創建任務的時候,就直接從裏面拿取空間,以免內存不夠的尷尬狀況。

這個函數的做用,是對任務相關的數據進行初始化,最重要的生成一個名爲空閒鏈表的鏈表(這個空閒鏈表連接事先定義好的任務數組)。

UCOSI操做系統裏面有好幾個結構徹底同樣的鏈表,好比任務鏈表,優先級鏈表,空閒鏈表等等,也是由這幾個鏈表組合管理任務的信息。

※至於這幾個鏈表的具體應用,暫時不用糾結,會在後面詳細解釋。

 

  5、函數OS_InitEventList()

  定義以下:

static  void  OS_InitEventList (void)
{
#if (OS_EVENT_EN) && (OS_MAX_EVENTS > 0u)
#if (OS_MAX_EVENTS > 1u)
    INT16U     ix;
    INT16U     ix_next;
    OS_EVENT  *pevent1;
    OS_EVENT  *pevent2;


    OS_MemClr((INT8U *)&OSEventTbl[0], sizeof(OSEventTbl)); /* Clear the event table                   */
    for (ix = 0u; ix < (OS_MAX_EVENTS - 1u); ix++) {        /* Init. list of free EVENT control blocks */
        ix_next = ix + 1u;
        pevent1 = &OSEventTbl[ix];
        pevent2 = &OSEventTbl[ix_next];
        pevent1->OSEventType    = OS_EVENT_TYPE_UNUSED;
        pevent1->OSEventPtr     = pevent2;
#if OS_EVENT_NAME_EN > 0u
        pevent1->OSEventName    = (INT8U *)(void *)"?";     /* Unknown name                            */
#endif
    }
    pevent1                         = &OSEventTbl[ix];
    pevent1->OSEventType            = OS_EVENT_TYPE_UNUSED;
    pevent1->OSEventPtr             = (OS_EVENT *)0;
#if OS_EVENT_NAME_EN > 0u
    pevent1->OSEventName            = (INT8U *)(void *)"?"; /* Unknown name                            */
#endif
    OSEventFreeList                 = &OSEventTbl[0];
#else
    OSEventFreeList                 = &OSEventTbl[0];       /* Only have ONE event control block       */
    OSEventFreeList->OSEventType    = OS_EVENT_TYPE_UNUSED;
    OSEventFreeList->OSEventPtr     = (OS_EVENT *)0;
#if OS_EVENT_NAME_EN > 0u
    OSEventFreeList->OSEventName    = (INT8U *)"?";         /* Unknown name                            */
#endif
#endif
#endif
}

  此函數主要用來對系統的事件機制(郵箱、隊列、信號量等等)作一些初始化,裏面處理和任務初始化差很少,做用也是事先佔領一些內存空間,以備從此使用。

  管理各類消息的也是鏈表(定義就不貼出來了),事件最大的個數也是經過宏定義來實現,若是沒有使用的話,把宏定義關掉能夠節約一部分空間(不建議)。

 

  6、7、8、函數OS_FlagInit(),OS_MemInit(),OS_QInit()

  這三個函數就是對具體的消息機制、內存管理功能進行初始化,內部的處理也都大同小異,都是根據系統的配置,而後先佔領一些空間,以備從此使用(每一個事件的鏈表的結構體不一樣)。

  函數定義這裏不貼出來,具體能夠查看源代碼,在從此專門會推出講解消息量的文章,到時候再詳細說。

 

  9、函數OS_InitTaskIdle()

  定義以下:

 1 static  void  OS_InitTaskIdle (void)
 2 {
 3 #if OS_TASK_NAME_EN > 0u
 4     INT8U  err;
 5 #endif
 6 
 7 
 8 #if OS_TASK_CREATE_EXT_EN > 0u
 9     #if OS_STK_GROWTH == 1u
10     (void)OSTaskCreateExt(OS_TaskIdle,
11                           (void *)0,                                 /* No arguments passed to OS_TaskIdle() */
12                           &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u],/* Set Top-Of-Stack                     */
13                           OS_TASK_IDLE_PRIO,                         /* Lowest priority level                */
14                           OS_TASK_IDLE_ID,
15                           &OSTaskIdleStk[0],                         /* Set Bottom-Of-Stack                  */
16                           OS_TASK_IDLE_STK_SIZE,
17                           (void *)0,                                 /* No TCB extension                     */
18                           OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack  */
19     #else
20     (void)OSTaskCreateExt(OS_TaskIdle,
21                           (void *)0,                                 /* No arguments passed to OS_TaskIdle() */
22                           &OSTaskIdleStk[0],                         /* Set Top-Of-Stack                     */
23                           OS_TASK_IDLE_PRIO,                         /* Lowest priority level                */
24                           OS_TASK_IDLE_ID,
25                           &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u],/* Set Bottom-Of-Stack                  */
26                           OS_TASK_IDLE_STK_SIZE,
27                           (void *)0,                                 /* No TCB extension                     */
28                           OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack  */
29     #endif
30 #else
31     #if OS_STK_GROWTH == 1u
32     (void)OSTaskCreate(OS_TaskIdle,
33                        (void *)0,
34                        &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u],
35                        OS_TASK_IDLE_PRIO);
36     #else
37     (void)OSTaskCreate(OS_TaskIdle,
38                        (void *)0,
39                        &OSTaskIdleStk[0],
40                        OS_TASK_IDLE_PRIO);
41     #endif
42 #endif
43 
44 #if OS_TASK_NAME_EN > 0u
45     OSTaskNameSet(OS_TASK_IDLE_PRIO, (INT8U *)(void *)"uC/OS-II Idle", &err);
46 #endif
47 }

  這個函數的做用是創建一個空閒任務,所謂的空閒任務就是啥也不幹的任務,爲何必需要有這麼一個吃乾飯的傢伙呢?

  試想一下,等UCOSII操做系統跑起來之後,若是我如今全部任務都處在延時狀態中,也就是未就緒的狀態,那麼系統應該執行什麼代碼呢?

       CPU的動力來源於晶振,只要晶振不停,那CPU也是不會停下來的啊。

  這就是空閒任務的做用,當全部的任務都沒有執行的時候,系統就會執行它,雖然空閒任務啥也不幹,但能夠避免CPU一臉懵逼加茫然無措。

 

  這個函數咱們須要重點分析,由於全部任務的創建原理和過程都是同樣的,只要徹底理解了空閒任務的創建過程後,那麼創建別的任務也就明白了。

 

  由於咱們建立任務使用的是標準的create函數,並未使用拓展的create函數(宏定義OS_TASK_CREATE_EXT_EN == 0),因此咱們只須要關注下面那個部分:

  OSTaskCreateExt()爲OSTaskCreate()拓展版本,功能大同小異,只不過多了一些設置,這些設置基本上不會用到。

 1 #else
 2     #if OS_STK_GROWTH == 1u
 3     (void)OSTaskCreate(OS_TaskIdle,
 4                        (void *)0,
 5                        &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u],
 6                        OS_TASK_IDLE_PRIO);
 7     #else
 8     (void)OSTaskCreate(OS_TaskIdle,
 9                        (void *)0,
10                        &OSTaskIdleStk[0],
11                        OS_TASK_IDLE_PRIO);
12     #endif
13 #endif

 

#define  OS_STK_GROWTH        1      /* Stack grows from HIGH to LOW memory on ARM    */

 

  因爲咱們使用mcu是m3內核,它堆棧的增加方向是由高到低,因此只須要看第一個宏裏面的代碼。

  看看函數的定義和形參:

INT8U  OSTaskCreate (void   (*task)(void *p_arg),
                     void    *p_arg,
                     OS_STK  *ptos,
                     INT8U    prio)

   看看咱們傳遞進去的實參:

    (void)OSTaskCreate(OS_TaskIdle,
                       (void *)0,
                       &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u],
                       OS_TASK_IDLE_PRIO);

 

void   (*task)(void *p_arg):任務函數名               賦值=OS_TaskIdle(空閒任務函數名,系統已經實現了這個任務,不須要本身添加)

void    *p_arg                    :給任務傳入的參數   賦值= 0

OS_STK  *ptos                 :任務的堆棧空間       賦值= OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u](空閒任務的堆棧空間和大小也是系統定義的)

INT8U    prio                     :任務的優先級          賦值= OS_TASK_IDLE_PRIO(既然都叫空閒任務,那優先級確定是最低的,這個宏的值是63,由系統定義)

你們快來看啊,原來建立一個任務這麼簡單,只須要給4個參數就能夠了。

傳遞了4個參數進去後,那裏面具體又是怎麼實現處理的呢?

 

待續……

相關文章
相關標籤/搜索