> 文档中心 > 【STM32】STM32 移植鸿蒙操作系统

【STM32】STM32 移植鸿蒙操作系统


前言

随着 OpenHarmony3.1 的正式发布,其功能也在不断完善。OpenHarmony LiteOS-M 内核是面向IoT领域构建的轻量级物联网操作系统内核,具有小体积、低功耗、高性能的特点,其代码结构简单,主要包括内核最小功能集、内核抽象层、可选组件以及工程目录等,分为硬件相关层以及硬件无关层,硬件相关层提供统一的HAL(Hardware Abstraction Layer)接口,提升硬件易适配性,不同编译工具链和芯片架构的组合分类,满足AIoT类型丰富的硬件和编译工具链的拓展。本文主要介绍如何在STM32上移植 OpenHarmony LiteOS-M 内核,及其注意事项。


一、开发环境

硬件:
- STM32F429I-DISC1 开发板
【STM32】STM32 移植鸿蒙操作系统
软件:
- VSCode:用于编辑代码
- STM32CubeMX:用于生成工程
- make、arm-none-eabi-gcc:用于编译工程
- STM32Cubeprogrammer:用于下载工程
- Git:用于获取 OpenHarmony LiteOS-M 内核源码

二、移植内核

1. 操作流程

  1. 拉取仓库代码。
  2. 使用STM32CubeMX在 /target 目录下生成工程。
  3. 修改Makefile文件,工程加入 OpenHarmony LiteOS-M 内核所需的文件。
  4. 添加用户代码以支持printf。添加用户自定义任务。
  5. 编译下载程序

2. 获取源码

  • 进入https://gitee.com/openharmony/kernel_liteos_m,获取Git仓库地址。
https://gitee.com/openharmony/kernel_liteos_m.git

【STM32】STM32 移植鸿蒙操作系统

  • 使用Git Bush,拉取内核源码到本地。
    【STM32】STM32 移植鸿蒙操作系统
  • 创建 /third_party 目录,用于存放第三方依赖文件(STM32 所需的 CMSIS 等),拉取第三方依赖文件。
cd kernel_liteos_m mkdir ./third_partycd third_partygit clone https://gitee.com/openharmony/third_party_bounds_checking_function.git ./bounds_checking_functiongit clone https://gitee.com/openharmony/third_party_cmsis.git ./cmsisgit clone https://gitee.com/openharmony/third_party_musl.git ./musl

【STM32】STM32 移植鸿蒙操作系统
到这里 OpenHarmony LiteOS-M 内核源码就获取完毕了。

3. 生成工程

  1. 进入/targets 目录,使用 STM32CubeMX 生成工程 STM32F429ZI_Harmony_LiteOS_M
    【STM32】STM32 移植鸿蒙操作系统

  2. 与FreeRTOS类似,由于LiteOS会占用SysTick定时器,因此需要修改HAL库延时的基础时钟,改为其他非SysTick的定时器,避免HAL库延时的定时器和系统运行的定时器冲突。【STM32】STM32 移植鸿蒙操作系统

  3. 配置:下载调试端口SW、串口USART、LED_GPIO、时钟树。
    【STM32】STM32 移植鸿蒙操作系统

  4. 开发环境选择 Makefile
    【STM32】STM32 移植鸿蒙操作系统

  5. Code Generator 中一定要选择 Copy only necessary library files 如果选择所有库文件都添加的话,那么就会生成很多模板文件。由于我们需要在 Makefile 中添加文件,如果目录中有模板文件的话,我们就无法直接使用筛选功能将所有源文件快速添加到工程中了。
    【STM32】STM32 移植鸿蒙操作系统
    至此工程配置已结束,点击 Generate 即可生成工程。

4. 修改工程文件

  1. 使用 VS Code 打开targets下的工程目录,新建liteos_file_path.mk用于将内核源码文件添加到工程Makefile中,该文件相当于C语言中的头文件,主 Makefile 文件可以直接包含该文件。
    【STM32】STM32 移植鸿蒙操作系统
  2. 添加内核源文件目录到 liteos_file_path.mk 中。
# Topdir 顶层目录LITEOSTOPDIR := ../../LITEOSTOPDIR := $(realpath $(LITEOSTOPDIR))# Common 内核源文件及头文件目录C_SOURCES   +=  \    $(wildcard $(LITEOSTOPDIR)/kernel/src/*.c) \    $(wildcard $(LITEOSTOPDIR)/kernel/src/mm/*.c) \    $(wildcard $(LITEOSTOPDIR)/components/cpup/*.c) \    $(wildcard $(LITEOSTOPDIR)/components/power/*.c) \    $(wildcard $(LITEOSTOPDIR)/components/backtrace/*.c) \    $(wildcard $(LITEOSTOPDIR)/components/exchook/*.c) \    $(wildcard $(LITEOSTOPDIR)/components/signal/*.c) \    $(wildcard $(LITEOSTOPDIR)/utils/*.c)C_INCLUDES  +=  \    -I$(LITEOSTOPDIR)/utils \    -I$(LITEOSTOPDIR)/kernel/include \    -I$(LITEOSTOPDIR)/components/cpup \    -I$(LITEOSTOPDIR)/components/power \    -I$(LITEOSTOPDIR)/components/backtrace \    -I$(LITEOSTOPDIR)/components/exchook \    -I$(LITEOSTOPDIR)/components/signal# Third party related 第三方依赖文件及头文件目录C_SOURCES    += \$(wildcard $(LITEOSTOPDIR)/third_party/bounds_checking_function/src/*.c)\    $(wildcard $(LITEOSTOPDIR)/kal/cmsis/*.c)\    $(wildcard $(LITEOSTOPDIR)/kal/posix/src/*.c)C_INCLUDES   += \-I$(LITEOSTOPDIR)/third_party/bounds_checking_function/include \    -I$(LITEOSTOPDIR)/third_party/bounds_checking_function/src\    -I$(LITEOSTOPDIR)/third_party/cmsis/CMSIS/RTOS2/Include \    -I$(LITEOSTOPDIR)/third_party/musl/porting/liteos_m/kernel/include\    -I$(LITEOSTOPDIR)/kal/cmsis \    -I$(LITEOSTOPDIR)/kal/posix/include \    -I$(LITEOSTOPDIR)/kal/posix/musl_src/internal# Arch related ASM_SOURCES   += $(wildcard $(LITEOSTOPDIR)/arch/arm/cortex-m4/gcc/*.s)ASMS_SOURCES  += $(wildcard $(LITEOSTOPDIR)/arch/arm/cortex-m4/gcc/*.S)C_SOURCES     += $(wildcard $(LITEOSTOPDIR)/arch/arm/cortex-m4/gcc/*.c)C_INCLUDES    += -I. \   -I$(LITEOSTOPDIR)/arch/include \   -I$(LITEOSTOPDIR)/arch/arm/cortex-m4/gccCFLAGS += -nostdinc -nostdlibASFLAGS+= -imacros $(LITEOSTOPDIR)/kernel/include/los_config.h -DCLZ=CLZ# list of ASM .S program objectsOBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASMS_SOURCES:.S=.o)))vpath %.S $(sort $(dir $(ASMS_SOURCES)))$(BUILD_DIR)/%.o: %.S Makefile | $(BUILD_DIR)$(CC) -c $(CFLAGS) $(ASFLAGS) $< -o $@

这里注意:最后一行前面的缩进必须为tab,而不是空格。否则编译会报错。

  1. 修改项目Makefile
  • 在Makefile中包含liteos_file_path.mk
    【STM32】STM32 移植鸿蒙操作系统
  • 增加*.S文件的编译规则
# list of ASM .S program objectsOBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASMS_SOURCES:.S=.o)))vpath %.S $(sort $(dir $(ASMS_SOURCES)))$(BUILD_DIR)/%.o: %.S Makefile | $(BUILD_DIR)$(CC) -c $(CFLAGS) $(ASFLAGS) $< -o $@

【STM32】STM32 移植鸿蒙操作系统

  1. 添加 target_config.h 用于配置裁剪内核。该文件相当于FreeRTOS的FreeRTOSConfig.h
/@defgroup los_config System configuration items * @ingroup kernel */#ifndef _TARGET_CONFIG_H#define _TARGET_CONFIG_H#include "stm32f4xx.h"#include "stm32f4xx_it.h"#ifdef __cplusplus#if __cplusplusextern "C"{#endif /* __cplusplus */#endif /* __cplusplus *//*=============================================================================     System clock module configuration=============================================================================*/#define OS_SYS_CLOCK SystemCoreClock#define LOSCFG_BASE_CORE_TICK_PER_SECOND (1000UL)#define LOSCFG_BASE_CORE_TICK_HW_TIME 0#define LOSCFG_BASE_CORE_TICK_WTIMER 0#define LOSCFG_BASE_CORE_TICK_RESPONSE_MAX SysTick_LOAD_RELOAD_Msk/*=============================================================================     Hardware interrupt module configuration=============================================================================*/#define LOSCFG_PLATFORM_HWI 0#define LOSCFG_USE_SYSTEM_DEFINED_INTERRUPT 0#define LOSCFG_PLATFORM_HWI_LIMIT 128/*=============================================================================     Openharmony Kernel configuration=============================================================================*//*=============================================================================    Task module configuration=============================================================================*/#define LOSCFG_BASE_CORE_TSK_LIMIT 24#define LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE (0x500U)#define LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE (0x2D0U)#define LOSCFG_BASE_CORE_TSK_MIN_STACK_SIZE (0x130U)#define LOSCFG_BASE_CORE_TIMESLICE 1#define LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT 20000/*=============================================================================    Semaphore module configuration=============================================================================*/#define LOSCFG_BASE_IPC_SEM 1#define LOSCFG_BASE_IPC_SEM_LIMIT 48/*=============================================================================    Mutex module configuration=============================================================================*/#define LOSCFG_BASE_IPC_MUX 1#define LOSCFG_BASE_IPC_MUX_LIMIT 24/*=============================================================================    Queue module configuration=============================================================================*/#define LOSCFG_BASE_IPC_QUEUE 1#define LOSCFG_BASE_IPC_QUEUE_LIMIT 24/*=============================================================================    Software timer module configuration=============================================================================*/#define LOSCFG_BASE_CORE_SWTMR 1#define LOSCFG_BASE_CORE_SWTMR_ALIGN 1#define LOSCFG_BASE_CORE_SWTMR_LIMIT 48/*=============================================================================    Memory module configuration=============================================================================*/#define LOSCFG_MEM_MUL_POOL 1#define OS_SYS_MEM_NUM 20/*=============================================================================    Exception module configuration=============================================================================*/#define LOSCFG_PLATFORM_EXC 1/*=============================================================================     TestSuite configuration=============================================================================*/#define LOSCFG_TEST 0#ifndef LOSCFG_BACKTRACE_TYPE#define LOSCFG_BACKTRACE_TYPE 1#endif/ * @ingroup los_config * Configuration backtrace depth. */#ifndef LOSCFG_BACKTRACE_DEPTH#define LOSCFG_BACKTRACE_DEPTH 15#endif#ifdef __cplusplus#if __cplusplus}#endif /* __cplusplus */#endif /* __cplusplus */#endif /* _TARGET_CONFIG_H */
  1. 修改链接脚本文件 STM32F407ZGTx_FLASH.ld
  • 添加程序起始地址 _sstack = 0x20000000; /* start of RAM */
  • 添加.text 段的起始地址,这是链接脚本的语法,将当前位置地址赋值给_stext。_stext = .;
    【STM32】STM32 移植鸿蒙操作系统
  1. 修改中断服务函数 Core\Src\stm32f4xx_it.c
  • 添加 LiteOS 头文件
  • 在PendSV异常中进入LiteOS HalPendSV 异常处理函数,进行任务切换操作
  • 在SysTick中断服务函数添加OsTickHandler函数,为系统提供时间基准
#include "los_arch_context.h"#include "los_tick.h"/*.........*/void PendSV_Handler(void){    /* USER CODE BEGIN PendSV_IRQn 0 */    HalPendSV();    /* USER CODE END PendSV_IRQn 0 */    /* USER CODE BEGIN PendSV_IRQn 1 */    /* USER CODE END PendSV_IRQn 1 */}/*.........*/void SysTick_Handler(void){    /* USER CODE BEGIN SysTick_IRQn 0 */    OsTickHandler();    /* USER CODE END SysTick_IRQn 0 */    /* USER CODE BEGIN SysTick_IRQn 1 */    /* USER CODE END SysTick_IRQn 1 */}/*.........*/
  1. 修改串口映射 Core\Src\main.c
  • 包含头文件#include "stdio.h"
  • 修改串口映射以支持printf
/* USER CODE BEGIN 0 */#if 1int _write(int fd, char *ptr, int len){    osStatus_t result;    osKernelState_t state;    if (osKernelGetState() == osKernelInactive)    { //系统未启动时不使用DMA HAL_UART_Transmit(&huart1, ptr, len, 0xFFFF); return len;    }    else    { //获取信号,如果上一个DMA传输完成 //信号就能获取到,没有传输完成任务就挂起 //等到传输完成再恢复 result = osSemaphoreAcquire(UART1_TX_DMA_SemaphoreHandle, 0xFFFF); if (result == osOK) {     HAL_UART_Transmit_DMA(&huart1, ptr, len); //获取成功,发送数据     return len; } else {     return -1; //获取失败 }    }}#endif// DMA 传输完成后会调用传输完成回调函数,在该函数中我们释放信号void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){    if (huart->Instance == huart1.Instance) osSemaphoreRelease(UART1_TX_DMA_SemaphoreHandle);}/* USER CODE END 0 */
  1. 添加测试任务 Core\Src\main.c
  • 包含os头文件#include "cmsis_os.h"
  • 增加测试任务
/*.........*/#include "cmsis_os.h"/*.........*//* USER CODE BEGIN PV */osSemaphoreId_t UART1_TX_DMA_SemaphoreHandle;const osSemaphoreAttr_t UART1_TX_DMA_Semaphore_attributes = {    .name = "UART1_TX_DMA_Semaphore",};osThreadId_t uart_taskHandle;const osThreadAttr_t uart_task_attributes = {    .name = "uart_task",    .stack_size = 512 * 2,    .priority = (osPriority_t)osPriorityNormal3,};/* USER CODE END PV *//*.........*//* USER CODE BEGIN 0 *//*.........*/void Uart_Task(void *argument){    HAL_GPIO_WritePin(LED_G_GPIO_Port, LED_G_Pin, GPIO_PIN_SET);    HAL_GPIO_WritePin(LED_R_GPIO_Port, LED_R_Pin, GPIO_PIN_RESET);    while (1)    { HAL_GPIO_TogglePin(LED_G_GPIO_Port, LED_G_Pin); HAL_GPIO_TogglePin(LED_R_GPIO_Port, LED_R_Pin); printf("System Runing!!!\r\n"); osDelay(1000);    }}/* USER CODE END 0 *//*.........*/int main(){/*.........*//* USER CODE BEGIN 2 */    osKernelInitialize();    printf("System Init!\r\n");    UART1_TX_DMA_SemaphoreHandle = osSemaphoreNew(1, 1, &UART1_TX_DMA_Semaphore_attributes);    uart_taskHandle = osThreadNew(Uart_Task, NULL, &uart_task_attributes);    osKernelStart();    /* USER CODE END 2 *//*.........*/}

5. 编译下载

  1. 使用make命令编译工程,这里的-j12表示使用多线程编译,可以提高速度,12表示电脑核心数。
make -j12

【STM32】STM32 移植鸿蒙操作系统
2. 使用STM32Cubeprogrammer下载位于/build中的固件STM32F429ZI_Harmony_LiteOS_M.hex
【STM32】STM32 移植鸿蒙操作系统
3. 观察到LED交替闪烁,串口助手打印出了调试信息。

  • LED 交替闪烁
    【STM32】STM32 移植鸿蒙操作系统

  • 串口输出信息
    【STM32】STM32 移植鸿蒙操作系统


总结

总的来说,移植的难点还是在于对 Makefile 相关工具链的理解与应用。由于有CMSIS_OS的封装,轻度使用时,与FreeRTOS感受相差不大。对新手来说使用FreeRTOS进行入门还是不错的选择,建议基本了解 FreeRTOS 之后再深入学习 LiteOS-M 并掌握二者之间的差别。