漫談LiteOS之開發板-LiteOS移植(基於GD32450i-EVAL)
1 為什麼移植?
嵌入式裝置的晶片型號和外設的差異較大,資源有限。而RTOS無法適配整合所有的驅動,因此會先適配部分開發板,然後通過移植使得適配更多的開發板。
可移植性是嵌入式作業系統與普通作業系統的顯著區別之一,而所謂移植就是通過一定的程式碼修改使得該作業系統適配自己的開發板,以使得自己的開發板可以執行一些手頭開發板沒有配套的編譯工程。
2 移植的分類
移植通常分為系統移植和驅動移植,驅動移植需要依賴具體的外設,本文主要介紹作業系統的移植。採用的主要方案是硬中斷接管和不接管中斷中的更加簡便快捷的不接管中斷方式。
3 開發環境
軟體環境:Windows系統、Keil5、J-Link對應驅動;
硬體環境:GD32450i-EVAL開發板、J-Link下載器、串列埠線、資料線;
注意:上述環境也可根據自己需求進行修改,如使用IAR、GCC等。
4 移植流程
4.1 準備工作
(1)下載LiteOS原始碼
在github上下載最新的LiteOS原始碼,地址:https://github.com/LiteOS/LiteOS,下載任意版本皆可,其原始碼核心一致,我這裡使用的是dev-deserted,其工程目錄詳情如圖1所示。
圖1 LiteOS原始碼工程目錄以及對應描述
(2)提取LiteOS核心移植檔案
對LiteOS原始碼做一個簡單的提取便於後續操作的簡潔性,當然也可以不提取,新建資料夾命名為LiteOS,將 LiteOS原始碼中的arch檔案、components/cmsis檔案、kernel檔案、以及targets檔案中的示例工程中拷貝兩個OS_CONFIG檔案,其中一個為接管中斷(如Standard_GD32F103C_START工程中的OS_CONFIG檔案)一個為非接管中斷(如Standard_STM32F103VC_TAIBI工程中的OS_CONFIG檔案)。具體如下表1所示。
表1 LiteOS核心檔案提取
一級目錄 |
一級目錄 |
一級目錄 |
描述 |
arch |
arm |
arm-m |
M 核中斷、排程、tick相關程式碼 |
common |
arm核公用的 cmsis core 介面 |
||
cmsis |
LiteOS 提供的 cmsis os介面實現 |
||
kernel |
base |
core |
LiteOS 基礎核心程式碼檔案,包括佇列、 task 排程、軟 timer、時間片等功能 |
OM |
與錯誤處理相關的檔案 |
||
Include |
LiteOS 核心內部使用的標頭檔案 |
||
ipc |
LiteOS 中 ipc 通訊相關的程式碼檔案,包括事件、訊號量、訊息佇列、互斥鎖等 |
||
mem |
LiteOS 中的核心記憶體管理的相關程式碼 |
||
misc |
記憶體對齊功能以及毫秒級休眠 sleep 功能 |
||
include |
LiteOS 開源核心標頭檔案 |
||
extended |
tickless |
低功耗框架程式碼 |
|
OS_CONFIG |
非接管中斷 |
||
OS_CONFIG_Take |
接管中斷 |
(3)建立裸機工程
使用前面的串列埠示例程式碼作為裸機工程,當然也可以使用keil自己建立裸機工程,自己建立的裸機工程一定要進行簡單的功能測試。將步驟(2)中的LiteOS檔案拷貝到裸機工程根目錄下,具體如圖2所示。
圖2 LiteOS目錄概圖
4.2 新增核心原始碼
在裸機工程中,新增LiteOS相關資料夾,分別命名為LiteOS/cmsis、LiteOS/arch、LiteOS/kernel、LiteOS/config(可有可無,便於後期操作而已),並分別將cmsis os程式碼、kernel程式碼、arch程式碼、OS_CONFIG檔案等新增到對應的資料夾,具體如圖3所示。
圖3 新增工程分組
其中需要新增的原始碼以及對應的檔案所在位置具體如下表所示。新增後的檔案目錄詳情,具體如圖4所示。
圖4 新增Lite OS原始碼
4.3 新增標頭檔案
新增原始碼之後,需要新增對應應用的標頭檔案,具體如圖5所示。
圖5 新增標頭檔案
4.4 修改target.h檔案
由於採用非接管中斷方式移植LiteOS,因此修改檔案較少。首先修改target.h檔案的標頭檔案應用,其標頭檔案如圖6所示。第43行,需要修改為開發板相對應的系列標頭檔案。
圖6 target.h標頭檔案修改
在target.h檔案中的開發板記憶體模組配置的位置,找到下列程式碼,其中一個是引數是起始地址,一個是開發板實際的SRAM的大小,但是該值一般設定略小於實際SRAM大小,因為工程儲存需要佔用一定空間。我們可以使用keil的pack Installer檢視開發板的SRAM大小,具體如圖7所示。可見GD32450i-EVAL(2019)其SRAM大小為256K。
#define BOARD_SRAM_START_ADDR 0x20000000 #define BOARD_SRAM_SIZE_KB 128
圖7 檢視開發板SRAM大小
target.h檔案具體修改方式如圖8所示。注意其中的起始地址設定要與工程配置中的IRAM起始地址保持一致。
圖8 修改記憶體起始地址
4.5 註釋回撥函式程式碼
由於PendSV_Handler以及SysTick_Handler(void)函式在LiteOS原始碼以及裸機工程中的gd32f4xx_it.c檔案中都有定義,因此如果直接編譯會出現重定義的錯誤,如圖9所示。此時我們需要將對應gd32f4xx_it.c中程式碼註釋掉或刪除即可。
圖9 執行錯誤詳情
5 測試
上面已經將移植工作基本完成,後面只需要做一下測試即可,具體包括硬體測試以及LiteOS測試。
5.1 硬體啟動測試
對於測試,首先需要將串列埠除錯好,便於後續的除錯,此處我以串列埠函式以及LED燈進行測試。在使用LiteOS系統之前首先需要將開發板啟動起來,具體如下所示。但是需要注意的是初始化的時候不要呼叫delay函式,因為SysTick_Handler(void)函式被註釋掉了,如果呼叫該函式會導致,程式卡死在delay處。測試的時候一定要注意main函式中要有while迴圈使其卡停在此處。
#include "gd32f4xx.h" #include "gd32f450i_eval.h" #include "systick.h" #include <stdio.h> void Hardware_Init(void); void led_init(void); int main() { Hardware_Init(); printf("\r\n USART printf example: please press the Tamper key \r\n"); while(1){ gd_eval_led_on(LED1); } } void Hardware_Init(void) { led_init(); systick_config(); gd_eval_com_init(EVAL_COM1); } void led_init(void) { gd_eval_led_init(LED1); } int fputc(int ch, FILE *f) { usart_data_transmit(EVAL_COM1, (uint8_t)ch); while(RESET == usart_flag_get(EVAL_COM1, USART_FLAG_TBE)); return ch; }
5.2 移植功能測試
接下來只需要對Lite OS移植進行測試,對主函式進行修改實現任務的創建於呼叫即可,主函式具體如下所示。
#include "gd32f4xx.h" #include "gd32f450i_eval.h" #include "systick.h" #include <stdio.h> #include "los_sys.h " #include "los_typedef.h" #include "los_task.ph" UINT32 g_TskHandle; void Hardware_Init(void); void led_init(void); UINT32 creat_task1(); UINT32 creat_task2(); int main(){ Hardware_Init(); printf("\r\n USART printf example: please press the Tamper key \r\n"); UINT32 uwRet = 0; uwRet = LOS_KernelInit(); if (uwRet != LOS_OK){ return LOS_NOK; } uwRet = creat_task1(); if (uwRet != LOS_OK){ return LOS_NOK; } uwRet = creat_task2(); if (uwRet != LOS_OK){ return LOS_NOK; } LOS_Start(); while(1){ gd_eval_led_on(LED1); } } void Hardware_Init(void){ led_init(); systick_config(); gd_eval_com_init(EVAL_COM1); } void led_init(void){ gd_eval_led_init(LED1); } int fputc(int ch, FILE *f){ usart_data_transmit(EVAL_COM1, (uint8_t)ch); while(RESET == usart_flag_get(EVAL_COM1, USART_FLAG_TBE)); return ch; } void task1(void){ int count = 1; while(1){ printf("This is task1, count is %d\r\n", count++); LOS_TaskDelay(1000); } } UINT32 creat_task1(){ UINT32 uwRet = LOS_OK; TSK_INIT_PARAM_S task_init_param; task_init_param.usTaskPrio = 0; task_init_param.pcName = "task1"; task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)task1; task_init_param.uwStackSize = 0x200; uwRet = LOS_TaskCreate(&g_TskHandle, &task_init_param); if(LOS_OK != uwRet){ return uwRet; } return uwRet; } void task2(void){ int count = 1; while (1){ printf("This is task2,count is %d\r\n",count++); LOS_TaskDelay(2000); } } UINT32 creat_task2(){ UINT32 uwRet = LOS_OK; TSK_INIT_PARAM_S task_init_param; task_init_param.usTaskPrio = 1; task_init_param.pcName = "task2"; task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)task2; task_init_param.uwStackSize = 0x200; uwRet = LOS_TaskCreate(&g_TskHandle, &task_init_param); if(LOS_OK != uwRet){ return uwRet; } return uwRet; }
執行結果如圖10所示,可以看見兩個任務在交替列印。移植完成,本例程使用的是非接管中斷的方式,如果使用接管中斷方式的,需要拷貝結果中斷的OS_CONFIG檔案,同時對於target.h檔案需要額外的修改,同時修改.sct檔案(在keil工程下的Objects資料夾下),而不是使用自動生成的.sct檔案.
圖10 Lite OS移植成功執行結果
新增華為IoT小助手(微訊號:huawei-iot,回覆“部落格園”)獲取更多LiteOS課程。