摘要:本文詳細講解如何移植 LiteOS 到小熊派。
本文分享自華為雲社群《小熊派移植華為 LiteOS-M(基於MDK)》,作者: JeckXu666。
前言
之前使用小熊派實現了鴻蒙動畫的開機介面,具體使用的技術棧為 STM32 + LiteOS + LVGL + FATFS +DMA 方式實現,重新整理效率非常高,預覽視訊如下:
關於這個的實現過程我會寫一系列的教程分享出來,主要分為下面幾個部分,本節為第一部分,移植 LiteOS 到小熊派上
- [x] 小熊派移植華為 LiteOS-M(基於MDK):連結;
- [ ] 小熊派基於 LiteOS 移植 LVGL 顯示介面:連結;
- [ ] 小熊派基於 LiteOS 移植 LVGL 檔案系統:連結;
- [ ] 小熊派實現鴻蒙開機介面(LiteOS+LVGL):連結;
一、軟硬體介紹
1.1 小熊派開發板
開發板外貌:
小熊派引數如下:
1.2 LiteOS介紹
LiteOS 是華為面向物聯網領域開發的一個基於實時核心的輕量級作業系統。屬於華為物聯網作業系統 Huawei LiteOS 原始碼,鴻蒙體系的核心之一,本質上就是一個 RTOS,現有基礎核心支援任務管理、記憶體管理、時間管理、通訊機制、中斷管理、佇列管理、事件管理、定時器等作業系統基礎元件,更好地支援低功耗場景,支援tickless機制,支援定時器對齊
同時 LiteOS 提供端雲協同能力,整合了 LwM2M、CoAP、mbedtls、LwIP 全套 IoT 互聯協議棧,且在 LwM2M 的基礎上,提供了 AgentTiny 模組,使用者只需關注自身的應用,而不必關注 LwM2M 實現細節,直接使用 AgentTiny 封裝的介面即可簡單快速實現與雲平臺安全可靠的連線
1.3 移植前言
LiteOS 軟體開發框架按照下面的框架來進行,其中 LiteOS排程核心我們移植官方提供的程式碼即可,而 BSP 外設軟體庫我們則通過 STM32CubeMX 來配置生成 HAL 庫,當這兩方面準備好之後,我們只需要在任務層進行開發即可,開發效率極高
LiteOS 的移植一般也分為兩種移植方法,一種移植的時候將晶片的中斷也關聯到 LiteOS 方便呼叫,另外一種則不關聯中斷,使用微控制器自己的中斷管理,這裡 STM32 的 NVIC 中斷管理很棒,我們不需要接管中斷
二、CubeMX 配置
我們通過 STM32CubeMX 來配置小熊派的一些基本介面,比如配置時鐘樹,開啟 SystemTick定時器,方便移植 LiteOS
配置時鐘樹:
設定 HAL庫定時器為TIM1,防止和 LiteOS 使用的 Systemtick 衝突
配置 LED 的 GPIO 口,方便我們點燈操作
配置工程名稱,選擇生成 MDK
配置程式碼生成選項
生成程式碼
程式碼生成後開啟工程,下一步進入移植操作
三、獲取原始碼
LiteOS 的老版本有對 MDK 的支援,新版本暫時還沒有更新,所以這裡我使用老版本的程式碼,倉庫地址如下:
我們把老版本的倉庫 Down 到本地,Git 指令如下:
git clone -b develop https://github.com/LiteOS/LiteOS.git
程式碼拉取後如下:
沒有 Git 工具的話可以上 GitHub 網頁把 ZIP Downloade 下來再解壓也可以
下面就是程式碼移植階段
四、原始碼移植
在 MDK 工程目錄下新建 LiteOS 移植目錄,按如下新建資料夾
這幾個資料夾的作用如下:
- ARCH:編譯器啟動檔案
- CMSIS:存放 CMSIS-RTOS 介面檔案
- Config:存放核心配置檔案,用於配置和裁剪核心
- Kernel:存放 LiteOS 核心原始碼
下面我們從原始碼資料夾拷貝程式碼到移植目錄下面
4.1 ARCH
移植 檔案路徑\LiteOS\arch\arm
架構資料夾到 ARCH
下,arm 下檔案包含了各個 arm 架構的支援檔案
M 架構下面的檔案主要有三個 GCC、IAR、KEIL 三個,適配不同整合開發環境的編譯器,因為小熊派是 M4 核心,所以我們主要關注的就是 cortex-m4 下面的 keil 資料夾下的啟動檔案,這裡我把所有的都複製進去了
至於 include 和 src 資料夾,則是存放了 LiteOS 的一些核心介面程式碼,比如中斷介面、Systemtick 定時器介面函式等等
4.2 CMSIS
移植 檔案路徑\LiteOS\LiteOS-M-Develop\LiteOS\osdepends\liteos\cmsis
CMSIS 系統介面資料夾到 CMSIS
下,CMSIS 資料夾下存放的是 CMSIS-RTOS 介面資料夾,RTOS核心支援的操作就那麼些,CMSIS-RTOS 對這些操作提供了統一的 RTOS 介面,連線系統底層與 RTOS層,方便我們移植不同的 RTOS:
4.3 Config
Config 資料夾下存放系統的配置檔案,這個配置檔案我們從示例工程下面拷過來,示例工程目錄位置 目錄\LiteOS\targets\STM32F103VET6_NB_GCC\OS_CONFIG
,目錄下檔案如下:
其中 los_builddef.h 用於設定構建的定義,los_printf.h 用於設定串列埠列印配置,target_config.h 可以用於裁剪系統核心,設定系統執行引數等等
4.4 Kernel
kernel 資料夾用於存放核心原始碼,原始碼位置在 目錄\LiteOS\kernel
下面
base 是系統的基礎核心原始碼
- core 是核心原始碼,包括佇列、task排程、軟timer、時間片計算等功能,全部複製
- OM 是與錯誤處理相關的檔案,全部複製
- include 是 LiteOS內 核內部使用的標頭檔案,全部複製
- ipc 是 LiteOS 中 task 間通訊的相關介面,包括事件、訊號量、訊息佇列、互斥鎖等,全部複製
- mem 是LiteOS中的核心記憶體管理的相關程式碼,我們只需要複製其中一部分就行,記憶體分配方式選擇適合小記憶體的分配方式(bestfit_little)
- misc 是記憶體對齊功能以及毫秒級休眠sleep功能,全部複製
extended 是系統擴充套件核心的程式碼,目前裡面有個支援低功耗的核心,我們順便也複製進去
include 存放核心的呼叫標頭檔案,我們全部複製進去
kernel 下面還有一個 los_init.c 檔案,存放初始化程式碼,也複製進去
複製完成的資料夾如下:
五、MDK 配置
程式碼移植完成了,下面就是配置一下 MDK
5.1 檔案匯入
進入 MDK 進行檔案管理,配置資料夾,匯入檔案:
- config 匯入 Config 下面 所有 .h 檔案
- kernel 匯入 Kernel 下面 所有 .c 檔案
- cmsis 匯入 CMSIS 下 cmsis_liteos.c 檔案
- arch 匯入 ARCH\arm\arm-m\cortex-m4\keil 下 los_dispatch_keil.S檔案,以及 ARCH\arm\arm-m\src 下 所有 .c 檔案
5.2 路徑匯入
檔案匯入後下一步就是路徑匯入,在 C/C++ 配置中依次新增標頭檔案路徑
新增的路徑如下,這裡我的工程目錄是 BearPi_LiteOS:
BearPi_LiteOS\Middlewares\LiteOS\Config BearPi_LiteOS\Middlewares\LiteOS\Kernel\base\include BearPi_LiteOS\Middlewares\LiteOS\Kernel\extended\include BearPi_LiteOS\Middlewares\LiteOS\Kernel\include BearPi_LiteOS\Middlewares\LiteOS\ARCH\arm\arm-m\include BearPi_LiteOS\Middlewares\LiteOS\CMSIS\cmsis
到這基本差不多了,下面就是編譯工程
5.3 編譯工程
先編譯一下工程,發現報錯,缺少 #include “stm32f1xx.h” 檔案,是因為我的小熊派開發板是 stm32l431 的晶片,修改這個工程標頭檔案為 #include "stm32l4xx.h"
修改完再編譯,當然還會有報錯,因為 STM32CubeMX 生成的程式碼裡面有
void PendSV_Handler(void) void SysTick_Handler(void)
兩個中斷,這兩個中斷在 LiteOS 的核心原始碼裡面有定義,一個用作系統時鐘,一個用作上下文切換,所以我們要把 STM32CubeMX 生成的這部分中斷處理程式碼註釋掉
註釋完後編譯,編譯通過:
到此係統移植基本完成了,下一步寫個程式碼測試系統有沒有移植成功
六、點燈實驗
標頭檔案包含 cmsis 介面
/* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "cmsis_os.h" /* USER CODE END Includes */
建立一個任務控制程式碼,以及任務實體和初始化函式
osThreadId_t led_taskHandle; const osThreadAttr_t led_task_attributes = { .name = "led_task", .stack_size = 512 * 4, .priority = (osPriority_t) osPriorityNormal, }; void Led_Task(void *argument); void Led_Task(void *argument) { while(1) { HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13); osDelay(1000); } }
在系統初始化的程式碼新增系統初始化程式碼,同時建立任務,建立完成後啟動系統
/* USER CODE BEGIN 2 */ osKernelInitialize(); /* creation of uart_task */ led_taskHandle = osThreadNew(Led_Task, NULL, &led_task_attributes); osKernelStart(); /* USER CODE END 2 */
編譯下載程式,觀察現象: