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

前言

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


一、开发环境

硬件:
- STM32F429I-DISC1 开发板

在这里插入图片描述


软件:
- 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

在这里插入图片描述

  • 使用Git Bush,拉取内核源码到本地。

    在这里插入图片描述

  • 创建 /third_party 目录,用于存放第三方依赖文件(STM32 所需的 CMSIS 等),拉取第三方依赖文件。
cd kernel_liteos_m 
mkdir ./third_party
cd third_party
git clone https://gitee.com/openharmony/third_party_bounds_checking_function.git ./bounds_checking_function
git clone https://gitee.com/openharmony/third_party_cmsis.git ./cmsis
git clone https://gitee.com/openharmony/third_party_musl.git ./musl

在这里插入图片描述


到这里 OpenHarmony LiteOS-M 内核源码就获取完毕了。

3. 生成工程

  1. 进入/targets 目录,使用 STM32CubeMX 生成工程 STM32F429ZI_Harmony_LiteOS_M

    在这里插入图片描述

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

    在这里插入图片描述

  3. 配置:下载调试端口SW、串口USART、LED_GPIO、时钟树。

    在这里插入图片描述

  4. 开发环境选择 Makefile

    在这里插入图片描述

  5. Code Generator 中一定要选择 Copy only necessary library files 如果选择所有库文件都添加的话,那么就会生成很多模板文件。由于我们需要在 Makefile 中添加文件,如果目录中有模板文件的话,我们就无法直接使用筛选功能将所有源文件快速添加到工程中了。

    在这里插入图片描述


    至此工程配置已结束,点击 Generate 即可生成工程。

4. 修改工程文件

  1. 使用 VS Code 打开targets下的工程目录,新建liteos_file_path.mk用于将内核源码文件添加到工程Makefile中,该文件相当于C语言中的头文件,主 Makefile 文件可以直接包含该文件。

    在这里插入图片描述

  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/gcc

CFLAGS        += -nostdinc -nostdlib
ASFLAGS       += -imacros $(LITEOSTOPDIR)/kernel/include/los_config.h -DCLZ=CLZ

# list of ASM .S program objects
OBJECTS += $(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

    在这里插入图片描述

  • 增加*.S文件的编译规则
# list of ASM .S program objects
OBJECTS += $(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 $@

在这里插入图片描述

  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 __cplusplus
extern "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 = .;

    在这里插入图片描述

  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 1
int _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,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

在这里插入图片描述


2. 使用STM32Cubeprogrammer下载位于/build中的固件STM32F429ZI_Harmony_LiteOS_M.hex

在这里插入图片描述


3. 观察到LED交替闪烁,串口助手打印出了调试信息。

  • LED 交替闪烁

    请添加图片描述

  • 串口输出信息

    在这里插入图片描述


总结

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

相关文章

文章浏览阅读1.4k次。被@Observed装饰的类,可以被观察到属性...
文章浏览阅读1k次。Harmony OS_harmonyos创建数据库
文章浏览阅读1.1k次,点赞25次,收藏23次。自定义组件Header...
文章浏览阅读952次,点赞11次,收藏25次。ArkUI是一套构建分...
文章浏览阅读735次。​错误: 找不到符号符号: 变量 Layout_l...
文章浏览阅读941次,点赞23次,收藏21次。harmony ARKTS b...