OpenHarmony LiteOS-M移植
前言
- OpenHarmony近两非常火,开放原子基金会更新也很频繁,可见目前的火爆程度。作为学校开放原子协会成员,也简单了解了一下,发觉OpenHarmony Lite-M版本移植到STM32资料还比较少,有基本也都是一两年前的资料移植的是3.0版本的,最新版本的几乎没有,官方给的移植的方法又是有一定门槛的。我也尝试用官方的方法去移植最新版本,但碍于本人对Makefile和GCC编译接触实属太少,试了很多次仍旧没能成功。后面就把目标放在了Keil+低版本后续再逐步优化最新版本的方案上,利用之前的开源资料成功移植了3.0版本。
- 此处我们需要理解几个概念
- HarmonyOS就是“鸿蒙操作系统”,或者简称为“鸿蒙OS”是基于 OpenHarmony、AOSP等开源项目的商用版本。OpenHarmony是一个开源项目,由开放原子开源基金会进行管理。因此,OpenHarmony是“鸿蒙操作系统”的底座。
- 华为LiteOS是华为2012年提出的适用于物联网嵌入式设备的操作系统,后面也交由开放原子基金会管理,后面就叫OpenHarmony LiteOS。LiteOS的内核其实也并非只有一个,它被分成Liteos-m和Liteos-a两种,目前OpenHarmony将两种内核都纳入了支持,Liteos-a和Liteos-m的主要区别就是分别为了支持Cortex-A和Cortex-M设备而设计,Liteos-m支持MCU设备,Liteos-a支持MMU设备,将内核内存与应用内存分离,使得应用崩溃不会影响到内核运行进而产生系统崩溃。
- 我们现在的目标就是将OpenHarmony LiteOS-M移植到STM32F407当中,如果对其他的像RT-Thread,FreeRTOS,UCOS等嵌入式操作系统有过一定了解的话,对它的移植应该还是很轻松的的。
准备工作
-
由于不同版本编译情况可能存在差异,如果大家移植出现问题可以看一下版本是否一致
Keil版本 V526 keil编译版本 UV5 STM32CubeMX Version 6.12.0 主控芯片 STM32F407VT6 OpenHarmony LiteOS-M 3.0 LTS Made By JCML -
下载官方源码如果大家发现版本不是3.0LTS可以按下面的方法找到下载ZIP即可
-
此处直接给出链接,直接下载即可 https://gitee.com/openharmony/kernel_liteos_m/tree/OpenHarmony-v3.0-LTS/

- 我们主要需要Kernel内核里面的文件,其他的根目录里面的是一些其他芯片的适配和测试文件,主要的文件结构如下。

移植
建立基础工程
在选好芯片后我们先配置好时钟输入、时钟树、调式方式均按默认的配置方式即可,需要注意的是LiteOS需要使用的SysTick,STM32默认是把SysTick给到HAL(STM32的HAL底层库),故需要把在SYS内的Timebase Source要选其他的定时器作为HAL库的时间基准。此处我们选择TIM7。大家按实际使用情况选择即可。

- 另外建议单片机资源至少打开一个还口用于系统状态的输出,这里用的串口1(USART1),我这里用的是自己的开发板(学生电子爱好者协会2023级新生开发板),方便演示另外开了两个灯用于指示。工具链ToolChain/IDE选择MDK-ARM即然后生成代码。


- 我们先测试一下串口是否能正常输出。在main.c中的任意位置(放在begin和end中间)用于printf的重定向输出,由于用到了stdio.h需要在魔法棒里面配置勾选Use MicroLIB同时配置好调试器。编译下载查看串口能否正常输出,验证工程是否可用。此处我就不做过多阐述。串口重定向代码如下
#include "stdio.h"
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);
USART1->DR = (uint8_t) ch;//此处使用的USART作为输出
return ch;
}
测试
printf("SYS Init Ok\n");
迁移LiteOS-M内核文件
- 由下面的内核框架图易知我们要构成一个最小功能的操作系统主要需要kernel里面的资源和头文件,Kernel里面的arch内核支持,以及必要的通用公共目录utils

OpenHarmony LiteOS-M核内核架构图
- 我们先建立一个LiteOS_M的文件夹放在工程目录的根目录,将从Gitee下载下来的源码里面的Kernel里面的文件全部复制进来(不是kernel文件夹,是kernei里面的arch,include,src三个文件夹,不复制kernel是为了后续引用路径太过深入麻烦)然后将源码根目录下的utils文件夹也复制进LiteOS_M文件夹中。同时我们为工程的精简,将arch中的不需要的芯片支持删除(如riscv,ARM的除了cortex-m4其他内核均可删除,但inlcude不要删),以及编译方式GCC。
- 然后还需要一个板级的config文件,这个文件我已经找好了,大家直接用就行。另外还需要几个用于驱动的文件,官方的源码是没有这几个文件的,但在Keil的环境下没有这几个文件是过不了编译的,还需要添加。位置任意在工程目录下即可。(我的是直接覆盖到utils文件夹下)
- 以下是整个工程的文件树此处篇幅有限仅展现文件夹的编排,详细文件分布见 附 1 完整工程目录
├─Core
│ ├─Inc
│ └─Src
├─Drivers
│ ├─CMSIS
│ └─STM32F4xx_HAL_Driver
├─LiteOS_M
│ │ target_config.h
│ ├─arch
│ │ ├─arm
│ │ │ ├─cortex-m4
│ │ │ │ └─iar
│ │ │ └─include
│ │ └─include
│ ├─include
│ ├─src
│ │ └─mm
│ └─utils
│ │ ├─include
│ │ ├─internal
│ │ └─securec
│ └─include
└─MDK-ARM
添加库文件和引入路径
- 打开Keil工程,右键工程目录选择Manage Project Items…打开工程管理窗口,新建一个Groups,LiteOS_M,然后单击Add File添加我们需要的文件,以下是需要添加的文件,有两个不是.c文件需要改文件类型为全部就可看到了。以下三张是分开导入的用不同的group,如果嫌麻烦可以直接导入到一个group,只要文件都导入了即可。记得点击OK保存



- 添加include路径。打开keil魔法棒中的C/C++→include paths,添加以下路径,点击OK保存

- 至此,文件基本都已导入。
修改部分文件以适配Keil
-
其1:完成文件的导入和路径的引用后直接编译我们发现还有4个error,且均是两个.s后缀的文件的统一个地方,由于我也没有接触过IAR和汇编只能是猜测,官方还未适配对于Keil的.S文件我们也只能那IAR的做改动来适配,这里感谢Gitee的大佬开源。 大家按下面的修改即可,也可以把下面的代码复制然后替换掉los_exc.S的33行和los_dispatch.S的51行。
另外在los_dispatch.S的最后缺了一个NOP需要添加系统才能正常跑起来
; SECTION .text:CODE(2) ;将该段注释(在最前面加;)
AREA |.text|, CODE, READONLY ;换成这个
MSR PRIMASK, R12
BX LR
;Tab缩进后加BOP
END
- 其2:在改完这个地方编译还有一个报错,说是不支持,我们直接注释该行即可,具体为啥搜出来也怪怪的,但它不影响具体系统。
error: A1356E: Instruction not supported on targeted CPU
//解决方法
; BLX OsSchedTaskSwitch//注释掉
Keil: warning: A1581W: Added 2 bytes of padding at address
//解决方法,最后几行中加入NOP
MSR PRIMASK, R12
BX LR
NOP;Tab缩进后加BOP
END
- 其三:修改STM32的启动文件(startup_stm32f407xx.s,在项目文件夹的Application/MDK-ARM下)LiteOS不像其他的类似RT-Thread将与单片机的对接放在HAL或者说库之上,而是直接釜底抽薪从最底层的StartUp文件开始,也有可能这样更高效,但如此确实拉高了初学者的上手门槛。主要是下面两个地方,其他不需要修改。
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
;P1:导入两个句柄
IMPORT HalPendSV
IMPORT OsTickHandler
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
;P2:注释掉之前的句柄,替换成我们导入的句柄
; DCD PendSV_Handler ; PendSV Handler
; DCD SysTick_Handler ; SysTick Handler
DCD HalPendSV ; PendSV Handler
DCD OsTickHandler ; SysTick Handler
- 至此,编译可能还有一些warning但都无关紧要,系统可以正常运行起来了。
测试系统
-
此处我们为了显示更加直观我们使用LED+串口的方式来验证系统是否正常跑起来。创建两个任务Task,分别去控制LED1和LED2以不同的频率闪动,同时向串口发送该Task正在运行。
-
两个Task
#include "los_task.h" //task1 static void TaskEntry1(void) { while (1) { HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin); LOS_Msleep(500); printf("Task 1 is working....\n"); } } //task2 static void TaskEntry2(void) { while (1) { HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin); LOS_Msleep(200); printf("Task 2 is working....\n"); } } -
main函数中的测试程序
/* USER CODE BEGIN 2 */
printf("SYS Init Ok\n");
LOS_KernelInit();
//start task 1
unsigned int ret;
unsigned int taskID1;
TSK_INIT_PARAM_S task1 = { 0 };
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)TaskEntry1;
task1.uwStackSize = 0x1000;
task1.pcName = "TaskEntry1";
task1.usTaskPrio = 6;
ret = LOS_TaskCreate(&taskID1, &task1);
if (ret != LOS_OK) {
printf("Task1 init Fail!\n");
while(1) {
}
}
//start task 2
unsigned int taskID2;
TSK_INIT_PARAM_S task2 = { 0 };
task2.pfnTaskEntry = (TSK_ENTRY_FUNC)TaskEntry2;
task2.uwStackSize = 0x1000;
task2.pcName = "TaskEntry2";
task2.usTaskPrio = 6;
ret = LOS_TaskCreate(&taskID2, &task2);
if (ret != LOS_OK) {
printf("Task2 init Fail!\n");
while(1) {
}
}
//start LOS
LOS_Start();
/* USER CODE END 2 */
- 测试结果,串口显示正常两个线程交替工作,LED闪烁。


参考资料
开源资料
https://www.openharmony.cn/mainPlay
https://blog.youkuaiyun.com/shudaoshanQAQ/article/details/135350326
Harmony,OpenHarmony与LiteOS的关系 - 知乎
移植相关
https://blog.youkuaiyun.com/qq_45396672/article/details/123971394
https://ost.51cto.com/posts/10219
https://gitee.com/lanzhoo/openharmony_liteos-m_learning
https://gitee.com/AT32437_OpenHarmony/OpenHarmony_AT32F437
https://gitee.com/AT32437_OpenHarmony/OpenHarmony_AT32F437
https://www.cnblogs.com/ablerry-dream/p/14344603.html
https://www.cnblogs.com/worldsing/p/3402729.html
附
附 1 完整工程目录
- 篇幅有限,不显示CubeMX生成的文件
│ │ target_config.h
├─Core
│ ├─Inc
│ └─Src
├─Drivers
│ ├─CMSIS
│ └─STM32F4xx_HAL_Driver
├─LiteOS_M
│ │ target_config.h
│ ├─arch
│ │ ├─arm
│ │ │ ├─cortex-m4
│ │ │ │ └─iar
│ │ │ │ los_arch_atomic.h
│ │ │ │ los_arch_context.h
│ │ │ │ los_arch_interrupt.h
│ │ │ │ los_arch_timer.h
│ │ │ │ los_context.c
│ │ │ │ los_dispatch.S
│ │ │ │ los_exc.S
│ │ │ │ los_interrupt.c
│ │ │ │ los_mpu.c
│ │ │ │ los_timer.c
│ │ │ │
│ │ │ └─include
│ │ │ arch_elf.h
│ │ │
│ │ └─include
│ │ los_arch.h
│ │ los_atomic.h
│ │ los_context.h
│ │ los_interrupt.h
│ │ los_mpu.h
│ │ los_timer.h
│ │
│ ├─include
│ │ los_config.h
│ │ los_event.h
│ │ los_membox.h
│ │ los_memory.h
│ │ los_mux.h
│ │ los_queue.h
│ │ los_sched.h
│ │ los_sem.h
│ │ los_sortlink.h
│ │ los_swtmr.h
│ │ los_task.h
│ │ los_tick.h
│ │
│ ├─src
│ │ │ los_event.c
│ │ │ los_init.c
│ │ │ los_mux.c
│ │ │ los_queue.c
│ │ │ los_sched.c
│ │ │ los_sem.c
│ │ │ los_sortlink.c
│ │ │ los_swtmr.c
│ │ │ los_task.c
│ │ │ los_tick.c
│ │ │
│ │ └─mm
│ │ los_membox.c
│ │ los_memory.c
│ │
│ └─utils
│ │ BUILD.gn
│ │ los_compiler.h
│ │ los_debug.c
│ │ los_debug.h
│ │ los_error.c
│ │ los_error.h
│ │ los_hook.c
│ │ los_hook.h
│ │ los_list.h
│ │ los_reg.h
│ │
│ ├─include
│ │ securec.h
│ │ securectype.h
│ │
│ ├─internal
│ │ los_hook_types.h
│ │ los_hook_types_parse.h
│ │
│ └─securec
│ memcpy_s.c
│ memset_s.c
│ securecutil.c
│ securecutil.h
│ strncpy_s.c
│
└─MDK-ARM
附2
2124

被折叠的 条评论
为什么被折叠?



