來源:轉(zhuǎn)載自21ic論壇極海半導(dǎo)體專區(qū)
最近了解了uc/os3這個(gè)操作系統(tǒng),上篇介紹了uc/os3如何正確移植到APM32F407開發(fā)板上,根據(jù)我最近學(xué)到的一些知識(shí),這篇文章主要介紹一下uc/os3中的一些簡單的任務(wù)管理API以及如何使用。
1、任務(wù)管理介紹:
uc/os3支持單核cpu,不支持多核cpu,這樣在某一時(shí)刻只有一個(gè)任務(wù)會(huì)獲得CPU使用權(quán)進(jìn)入運(yùn)行態(tài),其他的任務(wù)就會(huì)進(jìn)入其他的狀態(tài),uc/os3中的任務(wù)有多個(gè)狀態(tài),如下所示。
任務(wù)狀態(tài) | 描述 |
休眠態(tài) | 休眠態(tài)就是任務(wù)只是以任務(wù)函數(shù)的方式存在,只是存儲(chǔ)區(qū)中的一段代碼, 并未用 OSTaskCreate()函數(shù)創(chuàng)建這個(gè)任務(wù),不受 UCOSIII 管理的。 |
就緒態(tài) | 任務(wù)在就緒表中已經(jīng)登記,等待獲取 CPU 使用權(quán)。 |
運(yùn)行態(tài) | 正在運(yùn)行的任務(wù)就處于運(yùn)行態(tài)。 |
等待態(tài) | 正在運(yùn)行的任務(wù)需要等待某一個(gè)事件,比如信號(hào)量、消息、事件標(biāo)志組等, 就會(huì)暫時(shí)讓出 CPU 使用權(quán),進(jìn)入等待事件狀態(tài)。 |
中斷服務(wù)態(tài) | 一個(gè)正在執(zhí)行的任務(wù)被中斷打斷,CPU 轉(zhuǎn)而執(zhí)行中斷服務(wù)程序,這時(shí)這個(gè) 任務(wù)就會(huì)被掛起,進(jìn)入中斷服務(wù)態(tài)。 |
在uc/os3中任務(wù)可以在這5個(gè)狀態(tài)中轉(zhuǎn)換,轉(zhuǎn)換關(guān)系如下圖。
2、任務(wù)控制塊介紹
我們需要知道uc/os3有一個(gè)重要的數(shù)據(jù)結(jié)構(gòu):任務(wù)控制塊OS_TCB。任務(wù)控制塊用來保存任務(wù)的信息,我們使用OSTaskCreate() 函數(shù)來創(chuàng)建任務(wù)的時(shí)候就會(huì)給任務(wù)分配一個(gè)任務(wù)控制塊。任務(wù)控制塊是一個(gè)結(jié)構(gòu)體。
struct os_tcb {
CPU_STK *StkPtr; /* Pointer to current top of stack */
void *ExtPtr; /* Pointer to user definable data for TCB extension */
CPU_STK *StkLimitPtr; /* Pointer used to set stack 'watermark' limit */
#if (OS_CFG_DBG_EN > 0u)
CPU_CHAR *NamePtr; /* Pointer to task name */
#endif
OS_TCB *NextPtr; /* Pointer to next TCB in the TCB list */
OS_TCB *PrevPtr; /* Pointer to previous TCB in the TCB list */
#if (OS_CFG_TICK_EN > 0u)
OS_TCB *TickNextPtr;
OS_TCB *TickPrevPtr;
#endif
#if ((OS_CFG_DBG_EN > 0u) || (OS_CFG_STAT_TASK_STK_CHK_EN > 0u) || (OS_CFG_TASK_STK_REDZONE_EN > 0u))
CPU_STK *StkBasePtr; /* Pointer to base address of stack */
#endif
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
OS_TLS TLS_Tbl[OS_CFG_TLS_TBL_SIZE];
#endif
#if (OS_CFG_DBG_EN > 0u)
OS_TASK_PTR TaskEntryAddr; /* Pointer to task entry point address */
void *TaskEntryArg; /* Argument passed to task when it was created */
#endif
OS_TCB *PendNextPtr; /* Pointer to next TCB in pend list. */
OS_TCB *PendPrevPtr; /* Pointer to previous TCB in pend list. */
OS_PEND_OBJ *PendObjPtr; /* Pointer to object pended on. */
OS_STATE PendOn; /* Indicates what task is pending on */
OS_
以下是對給定結(jié)構(gòu)體 os_tcb 中每個(gè)成員的解釋:
1. StkPtr: 指向當(dāng)前堆棧頂部的指針,用于跟蹤任務(wù)的堆棧。
2. ExtPtr: 指向任務(wù)的用戶可定義數(shù)據(jù)的指針,用于擴(kuò)展任務(wù)控制塊(TCB)。
3. StkLimitPtr: 用于設(shè)置堆棧限制的指針。
4. NamePtr (如果啟用了調(diào)試功能): 指向任務(wù)名稱的指針。
5. NextPtr 和 PrevPtr: 分別指向任務(wù)列表中下一個(gè)和前一個(gè)任務(wù)的指針。
6. TickNextPtr 和 TickPrevPtr (如果啟用了時(shí)鐘節(jié)拍功能): 分別指向時(shí)鐘節(jié)拍列表中下一個(gè)和前一個(gè)任務(wù)的指針。
7. StkBasePtr (如果啟用了調(diào)試功能或堆棧檢查功能): 指向任務(wù)堆棧基地址的指針。
8. TLS_Tbl (如果定義了OS_CFG_TLS_TBL_SIZE): 任務(wù)線程局部存儲(chǔ)表。
9. TaskEntryAddr 和 TaskEntryArg(如果啟用了調(diào)試功能): 分別為任務(wù)入口點(diǎn)地址和任務(wù)創(chuàng)建時(shí)傳遞的參數(shù)。
10. PendNextPtr、PendPrevPtr、PendObjPtr、PendOn 和 PendStatus: 用于處理任務(wù)掛起的指針和狀態(tài)信息。
11. TaskState 和 Prio: 任務(wù)狀態(tài)和優(yōu)先級。
12. BasePrio、MutexGrpHeadPtr (如果啟用了互斥鎖功能): 基本優(yōu)先級和擁有的互斥鎖組的頭指針。
13. StkSize (如果啟用了調(diào)試功能、堆棧檢查功能或堆棧紅區(qū)功能):任務(wù)堆棧大小。
14. Opt: 由 OSTaskCreate() 傳遞的任務(wù)選項(xiàng)。
15. TS :(如果啟用了時(shí)間戳功能): 時(shí)間戳。
16. SemID (如果定義了OS_CFG_TRACE_EN): 用于跟蹤和調(diào)試的唯一 ID。
17. SemCtr: 任務(wù)特定的信號(hào)量計(jì)數(shù)器。
18. TickRemain 和 TickCtrPrev (如果啟用了時(shí)鐘節(jié)拍功能): 用于任務(wù)延遲和定時(shí)器的計(jì)數(shù)器。
19. TimeQuanta 和 TimeQuantaCtr (如果啟用了循環(huán)調(diào)度功能): 時(shí)間量和計(jì)數(shù)器。
20. MsgPtr 和 MsgSize (如果啟用了消息隊(duì)列功能): 接收到的消息和消息大小。
21. MsgQ (如果啟用了任務(wù)消息隊(duì)列功能): 與任務(wù)相關(guān)聯(lián)的消息隊(duì)列。
22. RegTbl (如果定義了OS_CFG_TASK_REG_TBL_SIZE): 任務(wù)特定的寄存器。
23. FlagsPend、FlagsRdy 和 FlagsOpt (如果啟用了事件標(biāo)志功能): 分別為等待的事件標(biāo)志、使任務(wù)準(zhǔn)備好的事件標(biāo)志和選項(xiàng)。
24. SuspendCtr (如果啟用了任務(wù)掛起功能): 掛起計(jì)數(shù)器。
25. CPUUsage、CPUUsageMax、CtxSwCtr、CyclesDelta、CyclesStart、CyclesTotal 和 CyclesTotalPrev (如果啟用了任務(wù)性能分析功能):CPU 使用率、上下文切換計(jì)數(shù)器和周期計(jì)數(shù)器等信息。
26. StkUsed 和 StkFree (如果啟用了堆棧檢查功能): 堆棧使用量和剩余量。
27. IntDisTimeMax 和 SchedLockTimeMax (如果啟用了中斷禁止時(shí)間測量功能或調(diào)度鎖定時(shí)間測量功能): 中斷禁止時(shí)間和調(diào)度鎖定時(shí)間的最大值。
28. DbgPrevPtr、DbgNextPtr 和 DbgNamePtr (如果啟用了調(diào)試功能): 調(diào)試相關(guān)的指針和名稱。
29. TaskID (如果定義了OS_CFG_TRACE_EN): 任務(wù)的唯一 ID。
3、任務(wù)堆棧介紹
在uc/os3中,任務(wù)堆棧用來切換和調(diào)用其他函數(shù)的時(shí)候保存現(xiàn)場,因此每個(gè)任務(wù)都應(yīng)該有自己的堆棧。我們可以使用CPU_STK定義一個(gè)任務(wù)堆棧,CPU_STK就是CPU_INT32U,一個(gè)CPU_STK變量為4字節(jié),因此任務(wù)的實(shí)際堆棧大小應(yīng)該為我們定義的4倍。
CPU_STK ledTaskStk[64]; //定義一個(gè)任務(wù)堆棧,堆棧大小為64*4=256字節(jié)
我們使用OSTaskCreate()函數(shù)創(chuàng)建任務(wù)的時(shí)候就可以把創(chuàng)建的堆棧傳遞給任務(wù),將堆棧的及地址傳遞給OSTaskCreate()函數(shù)的參數(shù)p_stk_base,將堆棧深度傳遞給stk_limit,堆棧深度通常為堆棧大小的十分之一,主要是用來檢測堆棧是否為空,將堆棧大小傳遞給參數(shù)stk_size。
OSTaskCreate(&ledTaskTCB,
"LED Task",
ledTask,
NULL,
LED_TASK_PRIO,
ledTaskStk,
0,
STACK_SIZE,
0,
0,
NULL,
(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
&err);
創(chuàng)建任務(wù)的時(shí)候會(huì)初始化任務(wù)的堆棧,我們需要提前將CPU的寄存器保存在任務(wù)堆棧中,完成這個(gè)任務(wù)的是OSTaskStkInit()函數(shù),用戶不能調(diào)用這個(gè)函數(shù),這個(gè)函數(shù)是被OSTaskCreate()函數(shù)在創(chuàng)建任務(wù)的時(shí)候調(diào)用的。
4、任務(wù)就緒表介紹
在 uC/OS-III 中,任務(wù)就緒表(ReadyList)是一個(gè)重要的數(shù)據(jù)結(jié)構(gòu),用于管理處于就緒狀態(tài)的任務(wù)。任務(wù)就緒表是一個(gè)數(shù)組,其中每個(gè)元素都對應(yīng)一個(gè)優(yōu)先級。每個(gè)優(yōu)先級的元素是一個(gè)鏈表,存儲(chǔ)了具有相同優(yōu)先級的就緒任務(wù)的任務(wù)控制塊(TaskControl Block,TCB)。
任務(wù)就緒表的基本結(jié)構(gòu)如下:
Ready List:
Priority 0: -> TCB1 -> TCB2 -> ... -> TCBn -> NULL
Priority 1: -> TCB1 -> TCB2 -> ... -> TCBn -> NULL
...
Priority N: -> TCB1 -> TCB2 -> ... -> TCBn -> NULL
在 uC/OS-III 中,任務(wù)按照它們的優(yōu)先級排列在任務(wù)就緒表中。每個(gè)優(yōu)先級鏈表中的任務(wù)控制塊按照某種順序(例如先進(jìn)先出)存儲(chǔ),以確保公平地分配 CPU 時(shí)間片。任務(wù)就緒表使得任務(wù)調(diào)度器能夠快速地找到下一個(gè)要執(zhí)行的任務(wù)。當(dāng)一個(gè)任務(wù)被創(chuàng)建并準(zhǔn)備好執(zhí)行時(shí),它的任務(wù)控制塊將被添加到適當(dāng)優(yōu)先級的就緒鏈表中。當(dāng)任務(wù)調(diào)度器需要選擇下一個(gè)要執(zhí)行的任務(wù)時(shí),它會(huì)遍歷任務(wù)就緒表,從具有最高優(yōu)先級的就緒鏈表中選擇一個(gè)任務(wù)執(zhí)行。
因此,任務(wù)就緒表是uC/OS-III 中實(shí)現(xiàn)任務(wù)調(diào)度的重要數(shù)據(jù)結(jié)構(gòu)之一,它提供了對任務(wù)就緒狀態(tài)的高效管理。
針對任務(wù)就緒表的操作有以下幾個(gè)函數(shù),這些函數(shù)都是uc/os3內(nèi)部使用的,用戶程序不能使用。
函數(shù) | 描述 |
OS_RdyListInit() | 由 OSInit()調(diào)用用來初始化并清空任務(wù)就緒列表 |
OS_RdyListInsertHead() | 向某一優(yōu)先級下的任務(wù)雙向鏈表頭部添加一個(gè)任務(wù)控制塊 TCB |
OS_RdyListInsertTail() | 向某一優(yōu)先級下的任務(wù)雙向鏈表尾部添加一個(gè)任務(wù)控制塊 TCB |
OS_RdyListRemove() | 將任務(wù)控制塊 TCB 從任務(wù)就緒列表中刪除 |
OS_RdyListInsertTail() | 將一個(gè)任務(wù)控制塊 TCB 從雙向鏈表的頭部移到尾部 |
OS_RdyListInsert() | 在就緒表中添加一個(gè)任務(wù)控制塊 TCB |
5、任務(wù)調(diào)度和切換介紹
我們?nèi)绻酪恍┖唵蔚牟僮飨到y(tǒng)的進(jìn)程調(diào)度的話,這一塊是比較簡單的。在 uC/OS-III 中,任務(wù)調(diào)度是指操作系統(tǒng)決定下一個(gè)要執(zhí)行的任務(wù)的過程。這個(gè)過程通常由一個(gè)專門的部分,即任務(wù)調(diào)度器,來負(fù)責(zé)。任務(wù)調(diào)度器根據(jù)一定的策略從就緒任務(wù)中選擇一個(gè)來執(zhí)行。這個(gè)策略可能是基于任務(wù)的優(yōu)先級,也可能是基于其他的調(diào)度算法。
在uc/os3中,主要分為任務(wù)級調(diào)度器、中斷級調(diào)度器以及時(shí)間片輪轉(zhuǎn)調(diào)度器。
任務(wù)級調(diào)度器:我們在創(chuàng)建一個(gè)任務(wù)時(shí),會(huì)給每個(gè)任務(wù)一個(gè)優(yōu)先級,而任務(wù)執(zhí)行的先后順序就是根據(jù)我們事先指定的任務(wù)優(yōu)先級運(yùn)行的,優(yōu)先級數(shù)值越小的任務(wù)擁有更高的優(yōu)先級。
中斷級調(diào)度器:在 uC/OS-III 中, 中斷級調(diào)度器的作用是確保即使在中斷服務(wù)程序執(zhí)行期間,也能夠及時(shí)地響應(yīng)高優(yōu)先級任務(wù)的需求,從而保證系統(tǒng)的實(shí)時(shí)性和響應(yīng)性。
需要注意的是,中斷級調(diào)度器是針對中斷服務(wù)程序中的任務(wù)切換而設(shè)計(jì)的,而普通任務(wù)的切換則由任務(wù)級調(diào)度器負(fù)責(zé)。
時(shí)間片輪轉(zhuǎn)調(diào)度:時(shí)間片輪轉(zhuǎn)調(diào)度器通過周期性地切換任務(wù)執(zhí)行,確保每個(gè)任務(wù)都有機(jī)會(huì)執(zhí)行,并且可以平衡系統(tǒng)中不同任務(wù)的執(zhí)行時(shí)間,提高系統(tǒng)的響應(yīng)性和公平性。
在 uC/OS-III 中,時(shí)間片輪轉(zhuǎn)調(diào)度器的運(yùn)行過程如下:
初始化設(shè)置:在系統(tǒng)啟動(dòng)時(shí),可以通過配置選項(xiàng)來啟用或禁用時(shí)間片輪轉(zhuǎn)調(diào)度器,并設(shè)置每個(gè)任務(wù)的時(shí)間片大小。
2.任務(wù)就緒隊(duì)列:所有就緒狀態(tài)的任務(wù)都會(huì)被放置在任務(wù)就緒隊(duì)列中,等待調(diào)度器選擇執(zhí)行。
3.選擇任務(wù):調(diào)度器會(huì)選擇一個(gè)就緒狀態(tài)的任務(wù)來執(zhí)行。如果啟用了時(shí)間片輪轉(zhuǎn)調(diào)度器,調(diào)度器會(huì)按照一定的策略選擇一個(gè)任務(wù)。
4.執(zhí)行任務(wù):選定的任務(wù)開始執(zhí)行,并且會(huì)被分配一個(gè)時(shí)間片來執(zhí)行。任務(wù)會(huì)持續(xù)執(zhí)行直到它完成了它的時(shí)間片,或者被其他更高優(yōu)先級的任務(wù)搶占。
5.時(shí)間片用完:當(dāng)任務(wù)的時(shí)間片用完時(shí),調(diào)度器會(huì)暫停當(dāng)前任務(wù)的執(zhí)行,并選擇下一個(gè)就緒狀態(tài)的任務(wù)來執(zhí)行。
6.任務(wù)切換:如果有其他任務(wù)需要執(zhí)行,調(diào)度器會(huì)進(jìn)行任務(wù)切換。這個(gè)過程包括保存當(dāng)前任務(wù)的上下文信息,恢復(fù)下一個(gè)任務(wù)的上下文信息,并開始執(zhí)行下一個(gè)任務(wù)。
7.重復(fù)執(zhí)行:這個(gè)過程會(huì)一直重復(fù)進(jìn)行,直到系統(tǒng)關(guān)閉或者沒有就緒狀態(tài)的任務(wù)為止。
6、函數(shù)介紹
1、OSTaskCreate()函數(shù)
uc/os3是多任務(wù)系統(tǒng),那么肯定要能創(chuàng)建任務(wù),創(chuàng)建任務(wù)就是將任務(wù)控制塊、任務(wù)堆棧、任務(wù)代碼等聯(lián)系在一起,并且初始化控制塊的相應(yīng)字段。在uc/os3中,我們使用OSTaskCreate()來創(chuàng)建任務(wù)。調(diào)用OSCreateTask()創(chuàng)建一個(gè)任務(wù)以后,剛創(chuàng)建的任務(wù)就會(huì)進(jìn)入就緒態(tài),注意,不能在中斷服務(wù)程序中調(diào)用OSTaskCreate()函數(shù)來創(chuàng)建任務(wù)。
void OSTaskCreate (OS_TCB *p_tcb, //指向任務(wù)的控制任務(wù)塊
CPU_CHAR *p_name, //指向任務(wù)的名字
OS_TASK_PTR p_task, //執(zhí)行任務(wù)代碼,即任務(wù)函數(shù)名
void *p_arg, //傳遞給任務(wù)的參數(shù)
OS_PRIO prio, //任務(wù)優(yōu)先級
CPU_STK *p_stk_base, //任務(wù)堆棧的基地址
CPU_STK_SIZE stk_limit, //堆棧深度
CPU_STK_SIZE stk_size, //堆棧大小
OS_MSG_QTY q_size, //任務(wù)的內(nèi)部消息隊(duì)列
OS_TICK time_quanta, //時(shí)間片輪轉(zhuǎn)調(diào)度時(shí),用來設(shè)置任務(wù)的時(shí)間片長度
void *p_ext,//指向用戶補(bǔ)充的存儲(chǔ)區(qū)
OS_OPT opt, //任務(wù)待定選項(xiàng),可選項(xiàng)如下
/*
OS_OPT_TASK_NONE 表示沒有任何選項(xiàng)
OS_OPT_TASK_STK_CHK 指定是否允許檢測該任務(wù)的堆棧
OS_OPT_TASK_STK_CLR 指定是否清除該任務(wù)的堆棧
OS_OPT_TASK_SAVE_FP 指定是否存儲(chǔ)浮點(diǎn)寄存器,CPU 需要有浮點(diǎn)
*/
OS_ERR *p_err) //用來保存調(diào)用該函數(shù)后返回的錯(cuò)誤碼。
為什么不能在中斷服務(wù)程序中調(diào)用OSTaskCreate()函數(shù)來創(chuàng)建任務(wù)?
這是因?yàn)樵?uC/OS-III 中,任務(wù)控制塊(TCB)和堆棧空間是預(yù)先分配的,而 OSTaskCreate() 函數(shù)需要使用系統(tǒng)堆棧來創(chuàng)建任務(wù)。而中斷服務(wù)程序中的堆棧通常較小且固定,不適合用于創(chuàng)建任務(wù)。舉個(gè)例子,假設(shè)我們有一個(gè)需要在外部中斷觸發(fā)時(shí)執(zhí)行的任務(wù),我們希望在中斷處理程序中創(chuàng)建一個(gè)新的任務(wù)來處理該中斷觸發(fā)的事件。
#include "includes.h"
// 外部中斷觸發(fā)時(shí)的中斷處理程序
void ExternalInterruptHandler(void) {
// 在這里調(diào)用 OSTaskCreate() 來創(chuàng)建新任務(wù)
OSTaskCreate(NewTask, /* arguments */);
}
// 新任務(wù)的入口函數(shù)
void NewTask(void *p_arg) {
// 處理中斷觸發(fā)的事件
// ...
}
int main(void) {
// 初始化 uC/OS-III 內(nèi)核
OSInit();
// 創(chuàng)建其他任務(wù)
// ...
// 啟動(dòng) uC/OS-III 內(nèi)核
OSStart();
return 0;
}
上面的代碼試圖在外部中斷觸發(fā)時(shí),通過調(diào)用 OSTaskCreate() 函數(shù)來創(chuàng)建一個(gè)新的任務(wù)來處理中斷觸發(fā)的事件。然而,這種做法是錯(cuò)誤的。原因如下:
1. 中斷服務(wù)程序中的堆棧通常較小且固定,不適合用于創(chuàng)建任務(wù)。OSTaskCreate() 函數(shù)需要使用系統(tǒng)堆棧來創(chuàng)建任務(wù),而不是中斷服務(wù)程序的堆棧。 2. 在中斷服務(wù)程序中創(chuàng)建任務(wù)可能會(huì)引入不可預(yù)測的延遲,影響系統(tǒng)的實(shí)時(shí)性能。中斷服務(wù)程序應(yīng)該盡量保持簡潔和高效,以便盡快退出中斷處理,從而允許系統(tǒng)繼續(xù)響應(yīng)其他中斷或任務(wù)。 正確的做法是,將中斷服務(wù)程序中需要處理的任務(wù)標(biāo)記為標(biāo)志、消息或信號(hào)量等形式,并在任務(wù)上下文中通過檢測這些標(biāo)記來執(zhí)行相應(yīng)的操作。例如,中斷服務(wù)程序可以向任務(wù)發(fā)送消息或釋放信號(hào)量,任務(wù)在接收到這些消息或信號(hào)量后執(zhí)行相應(yīng)的操作。
2、OSTaskDel()函數(shù)
OSTaskDel()函數(shù)用來刪除任務(wù),當(dāng)一個(gè)任務(wù)不需要運(yùn)行時(shí),我們就可以將其刪除,刪除任務(wù)不是說刪除任務(wù)代碼,而是uc/os3不在管理這個(gè)任務(wù),再有些應(yīng)用中我們只需要某個(gè)任務(wù)只運(yùn)行一次,與逆行完成就將其刪除掉,比如初始化任務(wù)。
void OSTaskDel (OS_TCB *p_tcb, // 指向要?jiǎng)h除任務(wù)的TCB
OS_ERR *p_err) //用來保存調(diào)用該函數(shù)后返回的錯(cuò)誤碼。
3、OSTaskSuspend()函數(shù)
OSTaskSuspend()函數(shù)用于任務(wù)掛起,有時(shí)有些任務(wù)因?yàn)槟承┰蛐枰獣和_\(yùn)行,但是以后還要運(yùn)行,因此我們就不能刪除這些任務(wù)。
void OSTaskSuspend (OS_TCB *p_tcb, //需要掛起的任務(wù)TCB
OS_ERR *p_err) //指向一個(gè)變量,用來保存該函數(shù)的錯(cuò)誤碼
4、OSTaskResume()函數(shù)
OSTaskResume()函數(shù)用來恢復(fù)被掛起的任務(wù)。
void OSTaskResume (OS_TCB *p_tcb, //需要恢復(fù)的任務(wù)TCB
OS_ERR *p_err) //指向一個(gè)變量,用來保存該函數(shù)的錯(cuò)誤碼
5、OSSchedRoundRobinCfg()函數(shù)
OSSchedRoundRobinCfg()函數(shù)用來使能或失能UCOSIII 的時(shí)間片輪轉(zhuǎn)調(diào)度功能,如果我們 要使用時(shí)間片輪轉(zhuǎn)調(diào)度功能的話不僅要將宏OS_CFG_SCHED_ROUND_ROBIN_EN 定義為 1, 還需要調(diào)用OSSchedRoundRobinCfg()函數(shù)來使能 UCOSIII。
void OSSchedRoundRobinCfg (CPU_BOOLEAN en, //用于設(shè)置打開或關(guān)閉時(shí)間片輪轉(zhuǎn)調(diào)度機(jī)制,如果為 DEF_ENABLED 表 示打開時(shí)間片輪轉(zhuǎn)調(diào)度,為 DEF_DISABLED 表示關(guān)閉時(shí)間片輪轉(zhuǎn)調(diào)度。
OS_TICK dflt_time_quanta,// 設(shè)置默認(rèn)的時(shí)間片長度,就是系統(tǒng)時(shí)鐘節(jié)拍個(gè)數(shù),默認(rèn)的時(shí)間片長度:OSCfg_TickRate_Hz / 10
OS_ERR *p_err) //保存調(diào)用此函數(shù)后返回的錯(cuò)誤碼
這里提一下怎么計(jì)算得到系統(tǒng)時(shí)鐘節(jié)拍。我們可以在os_cfg_app.h中查找OS_CFG_TICK_RATE_HZ變量,這個(gè)變量就是系統(tǒng)時(shí)鐘頻率。例如我們設(shè)置系統(tǒng)時(shí)鐘頻率OS_CFG_TICK_RATE_HZ為1000Hz,那么每個(gè)時(shí)鐘節(jié)拍就是5ms。當(dāng)我們設(shè)置dflt_time_quanta為n時(shí),時(shí)間片長度就是(n*1)ms,如果我們設(shè)置dflt_time_quanta為0時(shí),uc/os3就會(huì)使用默認(rèn)的時(shí)間片長度OSCfg_TickRate_Hz/ 10,例如我們設(shè)置OS_CFG_TICK_RATE_HZ為1000Hz,那么時(shí)間片長度就是1000/10*1=100ms。
6、OSSchedRoundRobinYield()函數(shù)
當(dāng)一個(gè)任務(wù)想要放棄本次時(shí)間片,把CPU的使用全讓給同優(yōu)先級下的另外一個(gè)任務(wù)就可以使用這個(gè)函數(shù)。
void OSSchedRoundRobinYield (OS_ERR *p_err) //保存調(diào)用此函數(shù)后返回的錯(cuò)誤碼
它有以下幾個(gè)錯(cuò)誤的返回值。
- OS_ERR_NONE調(diào)用成功
- OS_ERR_ROUND_ROBIN_1當(dāng)前優(yōu)先級下沒有其他就緒任務(wù)
- OS_ERR_ROUND_ROBIN_DISABLED未使能時(shí)間片輪轉(zhuǎn)調(diào)度功能
- OS_ERR_SCHED_LOCKED 調(diào)度器已被鎖定
- OS_ERR_YIELD_ISR在中斷調(diào)用了本函數(shù)。
我們在調(diào)用這個(gè)后函數(shù)遇到最多的錯(cuò)誤就是OS_ERR_ROUND_ROBIN_1,也就是當(dāng)前優(yōu)先級下沒有就緒任務(wù)了。
7、相關(guān)示例代碼
/* Includes */
#include "main.h"
#include "Board.h"
#include
#include
#include
#define DEBUG_USART USART1
/** @addtogroup Examples
@{
*/
/** @addtogroup Template
@{
*/
/** @defgroup Template_Functions Functions
@{
*/
#define STACK_SIZE 128 // Stack size for LED task
#define LED2_TASK_PRIO 5 // Priority for LED2 task
#define LED3_TASK_PRIO 5 // Priority for LED2 task
#define SERIAL_TASK_PRIO 6 // Priority for serial print task
OS_TCB startTaskTCB; // Task Control Block for start task
OS_TCB led2TaskTCB; // Task Control Block for LED task
OS_TCB led3TaskTCB; // Task Control Block for LED task
OS_TCB serialTaskTCB; // Task Control Block for serial print task
CPU_STK startTaskStk[STACK_SIZE]; // Stack for START task
CPU_STK led2TaskStk[STACK_SIZE]; // Stack for LED task
CPU_STK led3TaskStk[STACK_SIZE]; // Stack for LED task
CPU_STK serialTaskStk[STACK_SIZE]; // Stack for serial print task
void startTask(void *p_arg);
void led2Task(void *p_arg);
void led3Task(void *p_arg);
void serialPrintTask(void *p_arg);
int num = 0;
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] Main program
*
* @param None
*
* @retval None
*/
int main(void)
{
USART_Config_T usartConfig;
/* Configure USART */
usartConfig.baudRate = 115200;
usartConfig.wordLength = USART_WORD_LEN_8B;
usartConfig.stopBits = USART_STOP_BIT_1;
usartConfig.parity = USART_PARITY_NONE ;
usartConfig.mode = USART_MODE_TX_RX;
usartConfig.hardwareFlow = USART_HARDWARE_FLOW_NONE;
SysTick_Config(RCM_ReadSYSCLKFreq()/1000);
APM_MINI_COMInit(COM1,&usartConfig);
APM_MINI_LEDInit(LED2);
APM_MINI_LEDInit(LED3);
OS_ERR err;
// Initialize uC/OS-III
OSInit(&err);
OSTaskCreate(&startTaskTCB, "START Task", startTask, NULL, LED2_TASK_PRIO, startTaskStk, STACK_S
上面是我編寫的一個(gè)簡單的示例代碼,主要是這些函數(shù)的簡單應(yīng)用,這里為了體現(xiàn)時(shí)間片輪轉(zhuǎn)調(diào)度功能,我把兩個(gè)LEDTask的優(yōu)先級設(shè)置的一樣,當(dāng)分配給LED2Task的時(shí)間片用完后,LED3Task就會(huì)搶占LED2Task對CPU的使用權(quán),然后LED2Task進(jìn)入就緒隊(duì)列,如此反復(fù),直到這兩個(gè)任務(wù)都被執(zhí)行完。這段代碼主要實(shí)現(xiàn)的功能如下。
1. 任務(wù)堆棧和控制塊定義:
- 定義了幾個(gè)任務(wù)所需的堆棧和控制塊,包括啟動(dòng)任務(wù)(startTask)、LED任務(wù)(led2Task和led3Task)以及串口打印任務(wù)(serialPrintTask)。
2. main函數(shù):
- 配置USART串口通信的參數(shù),初始化系統(tǒng)時(shí)鐘。
- 初始化uC/OS-III操作系統(tǒng)。
- 創(chuàng)建啟動(dòng)任務(wù)(startTask)。
- 啟動(dòng)uC/OS-III操作系統(tǒng)。
3. startTask函數(shù):
- 啟動(dòng)任務(wù)中進(jìn)行了時(shí)間片輪轉(zhuǎn)調(diào)度的配置,使用了OSSchedRoundRobinCfg()函數(shù)啟用了時(shí)間片輪轉(zhuǎn)調(diào)度,并設(shè)置了時(shí)間片長度為5ms。
- 創(chuàng)建LED任務(wù)(led2Task和led3Task)和串口打印任務(wù)(serialPrintTask),設(shè)置了led2Task和led3Task的時(shí)間片為5*3=15ms,設(shè)置了serialTask的時(shí)間片為100ms。
- 最后刪除啟動(dòng)任務(wù)自身。
4. LED任務(wù):
- led2Task和led3Task分別控制LED2和LED3的閃爍。
- 每個(gè)LED任務(wù)在循環(huán)中先打印五次特定的消息,然后延時(shí)1秒。
- LED3任務(wù)在執(zhí)行到第3次循環(huán)時(shí),暫停串口打印任務(wù)(serialPrintTask)。
- 當(dāng)LED3任務(wù)執(zhí)行到第6次循環(huán)時(shí),恢復(fù)串口打印任務(wù)。
5. 串口打印任務(wù):
- serialPrintTask任務(wù)每500ms向串口打印一條消息。
由于我接觸這個(gè)操作系統(tǒng)也不是特別久,也是最近開始了解并學(xué)習(xí),這篇文檔更偏向于API應(yīng)用,也就是直接講了一下我們經(jīng)常可能會(huì)使用的API,而有關(guān)uc/os3具體的啟動(dòng)流程目前還沒有去深究,后續(xù)有時(shí)間的話我會(huì)去了解學(xué)習(xí)一下,附件是我編寫的示例代碼工程,有需要的可以下載,同時(shí)歡迎各位大佬指導(dǎo)與交流。
注:文章作者在原帖中提供了例程文件,有需要請至原文21ic論壇下載
原文地址:https://bbs.21ic.com/icview-3369956-1-1.html
-
cpu
+關(guān)注
關(guān)注
68文章
11033瀏覽量
215995 -
操作系統(tǒng)
+關(guān)注
關(guān)注
37文章
7082瀏覽量
124943 -
API
+關(guān)注
關(guān)注
2文章
1562瀏覽量
63523 -
開發(fā)板
+關(guān)注
關(guān)注
25文章
5507瀏覽量
102258
原文標(biāo)題:APM32芯得 EP.53 | APM32F407 uc/os3學(xué)習(xí)(一) 任務(wù)管理API簡單介紹與使用
文章出處:【微信號(hào):geehysemi,微信公眾號(hào):Geehy極海半導(dǎo)體】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
基于APM32F407如何制作I2C EEPROM(AT24C02型號(hào))的MDK-Keil下載算法

國產(chǎn)優(yōu)秀替代_APM32F407替代STM32F407記錄

APM32F407工具鏈?zhǔn)褂媒坛?/a>

如何在APM32F407開發(fā)板上應(yīng)用uC/OS-III實(shí)時(shí)操作系統(tǒng)

效率為本丨極海APM32F407通信電源方案

uC/OS-III學(xué)習(xí)筆記
UCOSIII任務(wù)管理學(xué)習(xí)筆記
求一種APM32F407伺服控制器應(yīng)用方案
使用APM32F407替代STM32F407制作的demo
32位微控制器APM32F405xG/APM32F407xExG
極海正式發(fā)布工業(yè)級高性能APM32F407系列MCU
極海APM32F407工業(yè)HMI應(yīng)用方案助您增強(qiáng)交互體驗(yàn)

極海APM32F407 MCU低壓伺服驅(qū)動(dòng)器應(yīng)用方案

評論