1. 程式人生 > >LiteOS-任務篇-原始碼分析-系統啟動函式

LiteOS-任務篇-原始碼分析-系統啟動函式

[toc] --- ## 前言 * 20201009 * 移植好核心後,開始實戰核心。 * **原始碼分析一般都在程式碼註釋中**。 * 本文LiteOS採用非接管中斷方式。 * 本文原始碼基於 **LiteOS 2018** 原始碼,**最新官方原始碼中都有註釋,可參考**。 * 本文原始碼與**最新官方原始碼**區別 * 原理一樣,原始碼稍有不同,且**最新官方原始碼**中帶有註釋。 ### 連結 * [LiteOS原始碼連結](https://gitee.com/LiteOS/LiteOS) * [常見問題](https://liteos.github.io/porting/faq.html) * [華為開發者社群](https://developer.huaweicloud.com/) * [華為LiteOS官方教程](https://support.huaweicloud.com/LiteOS/index.html) * [我的原始碼](https://gitee.com/lidreaming/LiteOS-mcu) * 包含 裸機原始碼 * LiteOS 工程模板 * 其它關於 LiteOS 的 demo 及 note ### 參考 * *上面連結* ## 開啟排程 ```c /* 開啟排程 */ LOS_Start(); ``` ### `LOS_Start` 函式原始碼 * 具體的原始碼分析可看原始碼篇 * 主要內容 * 配置RTOS的節拍定時器 * 啟動排程 ```c /***************************************************************************** Function : LOS_Start Description : Task start function Input : None Output : None Return : LOS_OK on success or error code on failure *****************************************************************************/ LITE_OS_SEC_TEXT_INIT UINT32 LOS_Start(VOID) { UINT32 uwRet; /* 判斷是否使用專用定時器 */ #if (LOSCFG_BASE_CORE_TICK_HW_TIME == NO) // 不使用專門的定時器 uwRet = osTickStart(); // 開啟排程 if (uwRet != LOS_OK) { PRINT_ERR("osTickStart error\n"); return uwRet; } #else // 使用專門的定時器 extern int os_timer_init(void); uwRet = os_timer_init(); // RTOS 配置的專用定時器 if (uwRet != LOS_OK) { PRINT_ERR("os_timer_init error\n"); return uwRet; } #endif LOS_StartToRun(); // 啟動排程,彙編 return uwRet; } ``` #### `osTickStart` 函式原始碼 * 巨集說明 * `OS_SYS_CLOCK` : 系統時鐘頻率,單位:Hz (*硬系統時鐘頻率,即是CPU頻率*) * `LOSCFG_BASE_CORE_TICK_PER_SECOND` : 每秒心跳次數 (*軟系統時鐘頻率,即是RTOS頻率*) * 主要內容為: * 檢查引數 * 配置RTOS系統時鐘滴答定時器 ```c /***************************************************************************** Function : osTickStart Description: Configure Tick Interrupt Start Input : none output : none return : LOS_OK - Success , or LOS_ERRNO_TICK_CFG_INVALID - failed *****************************************************************************/ LITE_OS_SEC_TEXT_INIT UINT32 osTickStart(VOID) { UINT32 uwRet; if ((0 == OS_SYS_CLOCK) || (0 == LOSCFG_BASE_CORE_TICK_PER_SECOND) || (LOSCFG_BASE_CORE_TICK_PER_SECOND > OS_SYS_CLOCK))/*lint !e506*/ /* 如果每秒心跳次數設定大於系統時鐘頻率的設定,則,ERROR */ { return LOS_ERRNO_TICK_CFG_INVALID; } #if (LOSCFG_PLATFORM_HWI == YES) // 開啟中斷接管 #if (OS_HWI_WITH_ARG == YES) // 引數配置項 osSetVector(SysTick_IRQn, (HWI_PROC_FUNC)osTickHandler, NULL); // 設定中斷向量表 #else osSetVector(SysTick_IRQn, osTickHandler); // 設定中斷向量表 #endif #endif g_uwCyclesPerTick = OS_SYS_CLOCK / LOSCFG_BASE_CORE_TICK_PER_SECOND; // 算出每個心跳的週期 g_ullTickCount = 0; uwRet = SysTick_Config(OS_SYS_CLOCK/LOSCFG_BASE_CORE_TICK_PER_SECOND); // 配置滴答定時器。引數為:兩個中斷之間的節拍數 if (uwRet == 1) { return LOS_ERRNO_TICK_PER_SEC_TOO_SMALL; } return LOS_OK; } ``` #### `LOS_StartToRun` 函式原始碼 * 分析再原始碼註釋中 * 簡略步驟 1. 將 **SysTick** 和 **PendSVd** 優先順序設定為最低 2. 將 **g_bTaskScheduled** 至為 1 3. 設定控制暫存器為 **CONTROL** 4. 更新當前執行任務 `Set g_stLosTask.pstRunTask = g_stLosTask.pstNewTask;` 5. 更新當前執行任務的任務狀態 `Set g_stLosTask.pstRunTask->usTaskStatus |= OS_TASK_STATUS_RUNNING;` 6. 手動更新 **PSP** 值,恢復到棧頂 7. 更新 **LR** 暫存器 8. 開啟中斷 9. 跳轉到當前任務的 **PC** ,教繼續執行任務。 ```S LOS_StartToRun ;系統啟動函式 ;; C:*OS_NVIC_SYSPRI2 = OS_NVIC_PENDSV_PRI; // 配置 SysTick 與 PendSVd 的優先順序(看圖Priority config) LDR R4, =OS_NVIC_SYSPRI2 ;OS_NVIC_SYSPRI2這個值給 R4 LDR R5, =OS_NVIC_PENDSV_PRI ;OS_NVIC_PENDSV_PRI這個值給 R5 STR R5, [R4] ;把 R5 的值存到 R4指定的地址中 ;; C:g_bTaskScheduled = 1; LDR R0, =g_bTaskScheduled ;; 把變數 g_bTaskScheduled 的地址賦給 R0 MOV R1, #1 ;把 1 賦值給 R1 暫存器 STR R1, [R0] ;; 把 2 賦給 程式狀態暫存器 CONTROL MOV R0, #2 MSR CONTROL, R0 ;; C:g_stLosTask.pstRunTask = g_stLosTask.pstNewTask; LDR R0, =g_stLosTask ;; R2 = g_stLosTask.pstNewTask; LDR R2, [R0, #4] ; LDR R0, =g_stLosTask ;; g_stLosTask.pstRunTask = g_stLosTask.pstNewTask; STR R2, [R0] ;; C:g_stLosTask.pstRunTask->usTaskStatus = g_stLosTask.pstRunTask->usTaskStatus | OS_TASK_STATUS_RUNNING; // 把當前任務狀態更新為 OS_TASK_STATUS_RUNNING LDR R3, =g_stLosTask ;; R0 = g_stLosTask.pstRunTask; LDR R0, [R3] LDRH R7, [R0 , #4] ;; R7 = g_stLosTask.pstRunTask->usTaskStatus;; MOV R8, #OS_TASK_STATUS_RUNNING ORR R7, R7, R8 ;; R7 = g_stLosTask.pstRunTask->usTaskStatus | OS_TASK_STATUS_RUNNING STRH R7, [R0 , #4] ;; g_stLosTask.pstRunTask->usTaskStatus = g_stLosTask.pstRunTask->usTaskStatus | OS_TASK_STATUS_RUNNING; ;; C: R12 = *(g_stLosTask.pstRunTask->pStackPointer) + 36; // 先偏移棧指標(手動出棧) (R4-R11, PRIMASK) LDR R12, [R0] ;; R12 = *(g_stLosTask.pstRunTask->pStackPointer); ADD R12, R12, #36 ;; R12 = R12 + 36; // 跳過任務中原本屬於暫存器 (R4-R11, PRIMASK)的值 ;; 把 R12 作為基地址,出棧。LDMFD:先出棧後遞增 ;; R12 出棧到 R0,R12 遞增 4;R12 出棧到 R1,R12 遞增 4;...... LDMFD R12!, {R0-R7} ;; 把任務中原本屬於暫存器 (R0-R3, R12, LR, PC, xPSR) 中的值分別複製到 (R0-R7) MSR PSP, R12 ;; 更新 PSP 指標 MOV LR, R5 ;; 任務中的 LR 指標值賦給 LR 暫存器 ;MSR xPSR, R7 ;; 任務中的 xPSR 指標值賦給 xPSR 暫存器(遮蔽了) CPSIE I ;; 開中斷 BX R6 ;; 跳轉到 當前任務的 PC 指標執行,並切換指令集 ``` * **圖-Priority config** ![](https://img2020.cnblogs.com/blog/2085252/202010/2085252-20201009173820270-792750169.png) * 該函式部分變數原始碼程式碼(**參考**) * 檔案 **los_dispatch_keil.S** 中 * `OS_NVIC_SYSPRI2 EQU 0xE000ED20` * `OS_NVIC_PENDSV_PRI EQU 0xF0F00000` * 檔案 **los_task.c** 中 * `LITE_OS_SEC_BSS BOOL g_bTaskScheduled;` * `LITE_OS_SEC_BSS ST_LOS_TASK g_stLosTask;` * 檔案 **los_task.ph** 中 ```c /** * @ingroup los_task * Define the task control block structure. */ typedef struct tagTaskCB { VOID *pStackPointer; /**< Task stack pointer */ UINT16 usTaskStatus; UINT16 usPriority; UINT32 uwStackSize; /**< Task stack size */ UINT32 uwTopOfStack; /**< Task stack top */ UINT32 uwTaskID; /**< Task ID */ TSK_ENTRY_FUNC pfnTaskEntry; /**< Task entrance function */ VOID *pTaskSem; /**< Task-held semaphore */ VOID *pTaskMux; /**< Task-held mutex */ UINT32 uwArg; /**< Parameter */ CHAR *pcTaskName; /**< Task name */ LOS_DL_LIST stPendList; LOS_DL_LIST stTimerList; UINT32 uwIdxRollNum; EVENT_CB_S uwEvent; UINT32 uwEventMask; /**< Event mask */ UINT32 uwEventMode; /**< Event mode */ VOID *puwMsg; /**< Memory allocated to queues */ } LOS_TASK_