作過軟件的同窗都知道,任何一個程序都必需要有初始化的過程,在初始化過程當中,咱們會對外圍硬件以及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 }
以上即是系統初始化函數中的處理,看得出來,它只是一個接口,一個容器,真正的處理還在它內部調用的那些函數裏,接下來咱們開始一句一句的理解。(簡單的函數用黑色,複雜的函數用紅色)
其定義以下:
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,因此固然沒問題啦!
其定義以下:
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
這段代碼我沒有找到是用來作什麼的,若是有哪位同窗瞭解,還但願不吝賜教。
其定義以下:
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。
定義以下:
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操做系統裏面有好幾個結構徹底同樣的鏈表,好比任務鏈表,優先級鏈表,空閒鏈表等等,也是由這幾個鏈表組合管理任務的信息。
※至於這幾個鏈表的具體應用,暫時不用糾結,會在後面詳細解釋。
定義以下:
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 }
此函數主要用來對系統的事件機制(郵箱、隊列、信號量等等)作一些初始化,裏面處理和任務初始化差很少,做用也是事先佔領一些內存空間,以備從此使用。
管理各類消息的也是鏈表(定義就不貼出來了),事件最大的個數也是經過宏定義來實現,若是沒有使用的話,把宏定義關掉能夠節約一部分空間(不建議)。
這三個函數就是對具體的消息機制、內存管理功能進行初始化,內部的處理也都大同小異,都是根據系統的配置,而後先佔領一些空間,以備從此使用(每一個事件的鏈表的結構體不一樣)。
函數定義這裏不貼出來,具體能夠查看源代碼,在從此專門會推出講解消息量的文章,到時候再詳細說。
定義以下:
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個參數進去後,那裏面具體又是怎麼實現處理的呢?