随着 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 内核源码
/target
目录下生成工程。printf
。添加用户自定义任务。https://gitee.com/openharmony/kernel_liteos_m
,获取Git仓库地址。https://gitee.com/openharmony/kernel_liteos_m.git
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 内核源码就获取完毕了。
进入/targets
目录,使用 STM32CubeMX 生成工程 STM32F429ZI_Harmony_LiteOS_M
与FreeRTOS类似,由于LiteOS会占用SysTick定时器,因此需要修改HAL库延时的基础时钟,改为其他非SysTick的定时器,避免HAL库延时的定时器和系统运行的定时器冲突。
Code Generator
中一定要选择 Copy only necessary library files
! 如果选择所有库文件都添加的话,那么就会生成很多模板文件。由于我们需要在 Makefile 中添加文件,如果目录中有模板文件的话,我们就无法直接使用筛选功能将所有源文件快速添加到工程中了。
至此工程配置已结束,点击 Generate 即可生成工程。
targets
下的工程目录,新建liteos_file_path.mk
用于将内核源码文件添加到工程Makefile中,该文件相当于C语言中的头文件,主 Makefile 文件可以直接包含该文件。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,而不是空格。否则编译会报错。
# 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 $@
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 */
STM32F407ZGTx_FLASH.ld
_sstack = 0x20000000; /* start of RAM */
_stext = .;
Core\Src\stm32f4xx_it.c
#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 */
}
/*
.........
*/
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, 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 */
Core\Src\main.c
#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 */
/*
.........
*/
}
make -j12
2. 使用STM32Cubeprogrammer下载位于/build
中的固件STM32F429ZI_Harmony_LiteOS_M.hex
3. 观察到LED交替闪烁,串口助手打印出了调试信息。
总的来说,移植的难点还是在于对 Makefile 相关工具链的理解与应用。由于有CMSIS_OS的封装,轻度使用时,与FreeRTOS感受相差不大。对新手来说使用FreeRTOS进行入门还是不错的选择,建议基本了解 FreeRTOS 之后再深入学习 LiteOS-M 并掌握二者之间的差别。