來源:轉載自21ic論壇極海半導體專區
最近了解了uc/os3這個操作系統,上篇介紹了uc/os3如何正確移植到APM32F407開發板上,根據我最近學到的一些知識,這篇文章主要介紹一下uc/os3中的一些簡單的任務管理API以及如何使用。
1、任務管理介紹:
uc/os3支持單核cpu,不支持多核cpu,這樣在某一時刻只有一個任務會獲得CPU使用權進入運行態,其他的任務就會進入其他的狀態,uc/os3中的任務有多個狀態,如下所示。
任務狀態 | 描述 |
休眠態 | 休眠態就是任務只是以任務函數的方式存在,只是存儲區中的一段代碼, 并未用 OSTaskCreate()函數創建這個任務,不受 UCOSIII 管理的。 |
就緒態 | 任務在就緒表中已經登記,等待獲取 CPU 使用權。 |
運行態 | 正在運行的任務就處于運行態。 |
等待態 | 正在運行的任務需要等待某一個事件,比如信號量、消息、事件標志組等, 就會暫時讓出 CPU 使用權,進入等待事件狀態。 |
中斷服務態 | 一個正在執行的任務被中斷打斷,CPU 轉而執行中斷服務程序,這時這個 任務就會被掛起,進入中斷服務態。 |
在uc/os3中任務可以在這5個狀態中轉換,轉換關系如下圖。
2、任務控制塊介紹
我們需要知道uc/os3有一個重要的數據結構:任務控制塊OS_TCB。任務控制塊用來保存任務的信息,我們使用OSTaskCreate() 函數來創建任務的時候就會給任務分配一個任務控制塊。任務控制塊是一個結構體。
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_
以下是對給定結構體 os_tcb 中每個成員的解釋:
1. StkPtr: 指向當前堆棧頂部的指針,用于跟蹤任務的堆棧。
2. ExtPtr: 指向任務的用戶可定義數據的指針,用于擴展任務控制塊(TCB)。
3. StkLimitPtr: 用于設置堆棧限制的指針。
4. NamePtr (如果啟用了調試功能): 指向任務名稱的指針。
5. NextPtr 和 PrevPtr: 分別指向任務列表中下一個和前一個任務的指針。
6. TickNextPtr 和 TickPrevPtr (如果啟用了時鐘節拍功能): 分別指向時鐘節拍列表中下一個和前一個任務的指針。
7. StkBasePtr (如果啟用了調試功能或堆棧檢查功能): 指向任務堆棧基地址的指針。
8. TLS_Tbl (如果定義了OS_CFG_TLS_TBL_SIZE): 任務線程局部存儲表。
9. TaskEntryAddr 和 TaskEntryArg(如果啟用了調試功能): 分別為任務入口點地址和任務創建時傳遞的參數。
10. PendNextPtr、PendPrevPtr、PendObjPtr、PendOn 和 PendStatus: 用于處理任務掛起的指針和狀態信息。
11. TaskState 和 Prio: 任務狀態和優先級。
12. BasePrio、MutexGrpHeadPtr (如果啟用了互斥鎖功能): 基本優先級和擁有的互斥鎖組的頭指針。
13. StkSize (如果啟用了調試功能、堆棧檢查功能或堆棧紅區功能):任務堆棧大小。
14. Opt: 由 OSTaskCreate() 傳遞的任務選項。
15. TS :(如果啟用了時間戳功能): 時間戳。
16. SemID (如果定義了OS_CFG_TRACE_EN): 用于跟蹤和調試的唯一 ID。
17. SemCtr: 任務特定的信號量計數器。
18. TickRemain 和 TickCtrPrev (如果啟用了時鐘節拍功能): 用于任務延遲和定時器的計數器。
19. TimeQuanta 和 TimeQuantaCtr (如果啟用了循環調度功能): 時間量和計數器。
20. MsgPtr 和 MsgSize (如果啟用了消息隊列功能): 接收到的消息和消息大小。
21. MsgQ (如果啟用了任務消息隊列功能): 與任務相關聯的消息隊列。
22. RegTbl (如果定義了OS_CFG_TASK_REG_TBL_SIZE): 任務特定的寄存器。
23. FlagsPend、FlagsRdy 和 FlagsOpt (如果啟用了事件標志功能): 分別為等待的事件標志、使任務準備好的事件標志和選項。
24. SuspendCtr (如果啟用了任務掛起功能): 掛起計數器。
25. CPUUsage、CPUUsageMax、CtxSwCtr、CyclesDelta、CyclesStart、CyclesTotal 和 CyclesTotalPrev (如果啟用了任務性能分析功能):CPU 使用率、上下文切換計數器和周期計數器等信息。
26. StkUsed 和 StkFree (如果啟用了堆棧檢查功能): 堆棧使用量和剩余量。
27. IntDisTimeMax 和 SchedLockTimeMax (如果啟用了中斷禁止時間測量功能或調度鎖定時間測量功能): 中斷禁止時間和調度鎖定時間的最大值。
28. DbgPrevPtr、DbgNextPtr 和 DbgNamePtr (如果啟用了調試功能): 調試相關的指針和名稱。
29. TaskID (如果定義了OS_CFG_TRACE_EN): 任務的唯一 ID。
3、任務堆棧介紹
在uc/os3中,任務堆棧用來切換和調用其他函數的時候保存現場,因此每個任務都應該有自己的堆棧。我們可以使用CPU_STK定義一個任務堆棧,CPU_STK就是CPU_INT32U,一個CPU_STK變量為4字節,因此任務的實際堆棧大小應該為我們定義的4倍。
CPU_STK ledTaskStk[64]; //定義一個任務堆棧,堆棧大小為64*4=256字節
我們使用OSTaskCreate()函數創建任務的時候就可以把創建的堆棧傳遞給任務,將堆棧的及地址傳遞給OSTaskCreate()函數的參數p_stk_base,將堆棧深度傳遞給stk_limit,堆棧深度通常為堆棧大小的十分之一,主要是用來檢測堆棧是否為空,將堆棧大小傳遞給參數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);
創建任務的時候會初始化任務的堆棧,我們需要提前將CPU的寄存器保存在任務堆棧中,完成這個任務的是OSTaskStkInit()函數,用戶不能調用這個函數,這個函數是被OSTaskCreate()函數在創建任務的時候調用的。
4、任務就緒表介紹
在 uC/OS-III 中,任務就緒表(ReadyList)是一個重要的數據結構,用于管理處于就緒狀態的任務。任務就緒表是一個數組,其中每個元素都對應一個優先級。每個優先級的元素是一個鏈表,存儲了具有相同優先級的就緒任務的任務控制塊(TaskControl Block,TCB)。
任務就緒表的基本結構如下:
Ready List:
Priority 0: -> TCB1 -> TCB2 -> ... -> TCBn -> NULL
Priority 1: -> TCB1 -> TCB2 -> ... -> TCBn -> NULL
...
Priority N: -> TCB1 -> TCB2 -> ... -> TCBn -> NULL
在 uC/OS-III 中,任務按照它們的優先級排列在任務就緒表中。每個優先級鏈表中的任務控制塊按照某種順序(例如先進先出)存儲,以確保公平地分配 CPU 時間片。任務就緒表使得任務調度器能夠快速地找到下一個要執行的任務。當一個任務被創建并準備好執行時,它的任務控制塊將被添加到適當優先級的就緒鏈表中。當任務調度器需要選擇下一個要執行的任務時,它會遍歷任務就緒表,從具有最高優先級的就緒鏈表中選擇一個任務執行。
因此,任務就緒表是uC/OS-III 中實現任務調度的重要數據結構之一,它提供了對任務就緒狀態的高效管理。
針對任務就緒表的操作有以下幾個函數,這些函數都是uc/os3內部使用的,用戶程序不能使用。
函數 | 描述 |
OS_RdyListInit() | 由 OSInit()調用用來初始化并清空任務就緒列表 |
OS_RdyListInsertHead() | 向某一優先級下的任務雙向鏈表頭部添加一個任務控制塊 TCB |
OS_RdyListInsertTail() | 向某一優先級下的任務雙向鏈表尾部添加一個任務控制塊 TCB |
OS_RdyListRemove() | 將任務控制塊 TCB 從任務就緒列表中刪除 |
OS_RdyListInsertTail() | 將一個任務控制塊 TCB 從雙向鏈表的頭部移到尾部 |
OS_RdyListInsert() | 在就緒表中添加一個任務控制塊 TCB |
5、任務調度和切換介紹
我們如果知道一些簡單的操作系統的進程調度的話,這一塊是比較簡單的。在 uC/OS-III 中,任務調度是指操作系統決定下一個要執行的任務的過程。這個過程通常由一個專門的部分,即任務調度器,來負責。任務調度器根據一定的策略從就緒任務中選擇一個來執行。這個策略可能是基于任務的優先級,也可能是基于其他的調度算法。
在uc/os3中,主要分為任務級調度器、中斷級調度器以及時間片輪轉調度器。
任務級調度器:我們在創建一個任務時,會給每個任務一個優先級,而任務執行的先后順序就是根據我們事先指定的任務優先級運行的,優先級數值越小的任務擁有更高的優先級。
中斷級調度器:在 uC/OS-III 中, 中斷級調度器的作用是確保即使在中斷服務程序執行期間,也能夠及時地響應高優先級任務的需求,從而保證系統的實時性和響應性。
需要注意的是,中斷級調度器是針對中斷服務程序中的任務切換而設計的,而普通任務的切換則由任務級調度器負責。
時間片輪轉調度:時間片輪轉調度器通過周期性地切換任務執行,確保每個任務都有機會執行,并且可以平衡系統中不同任務的執行時間,提高系統的響應性和公平性。
在 uC/OS-III 中,時間片輪轉調度器的運行過程如下:
初始化設置:在系統啟動時,可以通過配置選項來啟用或禁用時間片輪轉調度器,并設置每個任務的時間片大小。
2.任務就緒隊列:所有就緒狀態的任務都會被放置在任務就緒隊列中,等待調度器選擇執行。
3.選擇任務:調度器會選擇一個就緒狀態的任務來執行。如果啟用了時間片輪轉調度器,調度器會按照一定的策略選擇一個任務。
4.執行任務:選定的任務開始執行,并且會被分配一個時間片來執行。任務會持續執行直到它完成了它的時間片,或者被其他更高優先級的任務搶占。
5.時間片用完:當任務的時間片用完時,調度器會暫停當前任務的執行,并選擇下一個就緒狀態的任務來執行。
6.任務切換:如果有其他任務需要執行,調度器會進行任務切換。這個過程包括保存當前任務的上下文信息,恢復下一個任務的上下文信息,并開始執行下一個任務。
7.重復執行:這個過程會一直重復進行,直到系統關閉或者沒有就緒狀態的任務為止。
6、函數介紹
1、OSTaskCreate()函數
uc/os3是多任務系統,那么肯定要能創建任務,創建任務就是將任務控制塊、任務堆棧、任務代碼等聯系在一起,并且初始化控制塊的相應字段。在uc/os3中,我們使用OSTaskCreate()來創建任務。調用OSCreateTask()創建一個任務以后,剛創建的任務就會進入就緒態,注意,不能在中斷服務程序中調用OSTaskCreate()函數來創建任務。
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_OPT_TASK_NONE 表示沒有任何選項
OS_OPT_TASK_STK_CHK 指定是否允許檢測該任務的堆棧
OS_OPT_TASK_STK_CLR 指定是否清除該任務的堆棧
OS_OPT_TASK_SAVE_FP 指定是否存儲浮點寄存器,CPU 需要有浮點
*/
OS_ERR *p_err) //用來保存調用該函數后返回的錯誤碼。
為什么不能在中斷服務程序中調用OSTaskCreate()函數來創建任務?
這是因為在 uC/OS-III 中,任務控制塊(TCB)和堆棧空間是預先分配的,而 OSTaskCreate() 函數需要使用系統堆棧來創建任務。而中斷服務程序中的堆棧通常較小且固定,不適合用于創建任務。舉個例子,假設我們有一個需要在外部中斷觸發時執行的任務,我們希望在中斷處理程序中創建一個新的任務來處理該中斷觸發的事件。
#include "includes.h"
// 外部中斷觸發時的中斷處理程序
void ExternalInterruptHandler(void) {
// 在這里調用 OSTaskCreate() 來創建新任務
OSTaskCreate(NewTask, /* arguments */);
}
// 新任務的入口函數
void NewTask(void *p_arg) {
// 處理中斷觸發的事件
// ...
}
int main(void) {
// 初始化 uC/OS-III 內核
OSInit();
// 創建其他任務
// ...
// 啟動 uC/OS-III 內核
OSStart();
return 0;
}
上面的代碼試圖在外部中斷觸發時,通過調用 OSTaskCreate() 函數來創建一個新的任務來處理中斷觸發的事件。然而,這種做法是錯誤的。原因如下:
1. 中斷服務程序中的堆棧通常較小且固定,不適合用于創建任務。OSTaskCreate() 函數需要使用系統堆棧來創建任務,而不是中斷服務程序的堆棧。 2. 在中斷服務程序中創建任務可能會引入不可預測的延遲,影響系統的實時性能。中斷服務程序應該盡量保持簡潔和高效,以便盡快退出中斷處理,從而允許系統繼續響應其他中斷或任務。 正確的做法是,將中斷服務程序中需要處理的任務標記為標志、消息或信號量等形式,并在任務上下文中通過檢測這些標記來執行相應的操作。例如,中斷服務程序可以向任務發送消息或釋放信號量,任務在接收到這些消息或信號量后執行相應的操作。
2、OSTaskDel()函數
OSTaskDel()函數用來刪除任務,當一個任務不需要運行時,我們就可以將其刪除,刪除任務不是說刪除任務代碼,而是uc/os3不在管理這個任務,再有些應用中我們只需要某個任務只運行一次,與逆行完成就將其刪除掉,比如初始化任務。
void OSTaskDel (OS_TCB *p_tcb, // 指向要刪除任務的TCB
OS_ERR *p_err) //用來保存調用該函數后返回的錯誤碼。
3、OSTaskSuspend()函數
OSTaskSuspend()函數用于任務掛起,有時有些任務因為某些原因需要暫停運行,但是以后還要運行,因此我們就不能刪除這些任務。
void OSTaskSuspend (OS_TCB *p_tcb, //需要掛起的任務TCB
OS_ERR *p_err) //指向一個變量,用來保存該函數的錯誤碼
4、OSTaskResume()函數
OSTaskResume()函數用來恢復被掛起的任務。
void OSTaskResume (OS_TCB *p_tcb, //需要恢復的任務TCB
OS_ERR *p_err) //指向一個變量,用來保存該函數的錯誤碼
5、OSSchedRoundRobinCfg()函數
OSSchedRoundRobinCfg()函數用來使能或失能UCOSIII 的時間片輪轉調度功能,如果我們 要使用時間片輪轉調度功能的話不僅要將宏OS_CFG_SCHED_ROUND_ROBIN_EN 定義為 1, 還需要調用OSSchedRoundRobinCfg()函數來使能 UCOSIII。
void OSSchedRoundRobinCfg (CPU_BOOLEAN en, //用于設置打開或關閉時間片輪轉調度機制,如果為 DEF_ENABLED 表 示打開時間片輪轉調度,為 DEF_DISABLED 表示關閉時間片輪轉調度。
OS_TICK dflt_time_quanta,// 設置默認的時間片長度,就是系統時鐘節拍個數,默認的時間片長度:OSCfg_TickRate_Hz / 10
OS_ERR *p_err) //保存調用此函數后返回的錯誤碼
這里提一下怎么計算得到系統時鐘節拍。我們可以在os_cfg_app.h中查找OS_CFG_TICK_RATE_HZ變量,這個變量就是系統時鐘頻率。例如我們設置系統時鐘頻率OS_CFG_TICK_RATE_HZ為1000Hz,那么每個時鐘節拍就是5ms。當我們設置dflt_time_quanta為n時,時間片長度就是(n*1)ms,如果我們設置dflt_time_quanta為0時,uc/os3就會使用默認的時間片長度OSCfg_TickRate_Hz/ 10,例如我們設置OS_CFG_TICK_RATE_HZ為1000Hz,那么時間片長度就是1000/10*1=100ms。
6、OSSchedRoundRobinYield()函數
當一個任務想要放棄本次時間片,把CPU的使用全讓給同優先級下的另外一個任務就可以使用這個函數。
void OSSchedRoundRobinYield (OS_ERR *p_err) //保存調用此函數后返回的錯誤碼
它有以下幾個錯誤的返回值。
- OS_ERR_NONE調用成功
- OS_ERR_ROUND_ROBIN_1當前優先級下沒有其他就緒任務
- OS_ERR_ROUND_ROBIN_DISABLED未使能時間片輪轉調度功能
- OS_ERR_SCHED_LOCKED 調度器已被鎖定
- OS_ERR_YIELD_ISR在中斷調用了本函數。
我們在調用這個后函數遇到最多的錯誤就是OS_ERR_ROUND_ROBIN_1,也就是當前優先級下沒有就緒任務了。
7、相關示例代碼
/* 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
上面是我編寫的一個簡單的示例代碼,主要是這些函數的簡單應用,這里為了體現時間片輪轉調度功能,我把兩個LEDTask的優先級設置的一樣,當分配給LED2Task的時間片用完后,LED3Task就會搶占LED2Task對CPU的使用權,然后LED2Task進入就緒隊列,如此反復,直到這兩個任務都被執行完。這段代碼主要實現的功能如下。
1. 任務堆棧和控制塊定義:
- 定義了幾個任務所需的堆棧和控制塊,包括啟動任務(startTask)、LED任務(led2Task和led3Task)以及串口打印任務(serialPrintTask)。
2. main函數:
- 配置USART串口通信的參數,初始化系統時鐘。
- 初始化uC/OS-III操作系統。
- 創建啟動任務(startTask)。
- 啟動uC/OS-III操作系統。
3. startTask函數:
- 啟動任務中進行了時間片輪轉調度的配置,使用了OSSchedRoundRobinCfg()函數啟用了時間片輪轉調度,并設置了時間片長度為5ms。
- 創建LED任務(led2Task和led3Task)和串口打印任務(serialPrintTask),設置了led2Task和led3Task的時間片為5*3=15ms,設置了serialTask的時間片為100ms。
- 最后刪除啟動任務自身。
4. LED任務:
- led2Task和led3Task分別控制LED2和LED3的閃爍。
- 每個LED任務在循環中先打印五次特定的消息,然后延時1秒。
- LED3任務在執行到第3次循環時,暫停串口打印任務(serialPrintTask)。
- 當LED3任務執行到第6次循環時,恢復串口打印任務。
5. 串口打印任務:
- serialPrintTask任務每500ms向串口打印一條消息。
由于我接觸這個操作系統也不是特別久,也是最近開始了解并學習,這篇文檔更偏向于API應用,也就是直接講了一下我們經常可能會使用的API,而有關uc/os3具體的啟動流程目前還沒有去深究,后續有時間的話我會去了解學習一下,附件是我編寫的示例代碼工程,有需要的可以下載,同時歡迎各位大佬指導與交流。
注:文章作者在原帖中提供了例程文件,有需要請至原文21ic論壇下載
原文地址:https://bbs.21ic.com/icview-3369956-1-1.html
-
cpu
+關注
關注
68文章
11074瀏覽量
216900 -
操作系統
+關注
關注
37文章
7140瀏覽量
125516 -
API
+關注
關注
2文章
1599瀏覽量
63960 -
開發板
+關注
關注
25文章
5661瀏覽量
104408
原文標題:APM32芯得 EP.53 | APM32F407 uc/os3學習(一) 任務管理API簡單介紹與使用
文章出處:【微信號:geehysemi,微信公眾號:Geehy極海半導體】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
基于APM32F407如何制作I2C EEPROM(AT24C02型號)的MDK-Keil下載算法

國產優秀替代_APM32F407替代STM32F407記錄

如何在APM32F407開發板上應用uC/OS-III實時操作系統

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

求一種APM32F407伺服控制器應用方案
32位微控制器APM32F405xG/APM32F407xExG
極海正式發布工業級高性能APM32F407系列MCU
極海APM32F407工業HMI應用方案助您增強交互體驗

極海APM32F407 MCU低壓伺服驅動器應用方案

評論