移植最新版本OpenHarmony到STM32F407
来源:果果小师弟发布时间:2022-05-091671浏览
询问 AI摘要:最近OpenHarmony3.1已正式发布,我又研究了一下OpenHarmony移植到MCU的过程,之前移植失败有些细节没有注意到,后面研究后发现其实整体框架还是很清晰的,顺便将OpenHarmony 移植到MCU的过程分享给大家,相关教程和代码放在我的Gitee和Github仓库。
STM32F407移植OpenHarmony系统
一、前言
最近OpenHarmony3.1已正式发布,标准系统(LiteOS-A)基础能力增强,小型系统(LiteOS-M)基本没啥变化,趋于稳定,为了弥补这个缺陷,移植最新的LiteOS-M到MCU上,使用最新的系统特性,我又研究了一下OpenHarmony移植到MCU的过程,之前移植失败有些细节没有注意到,后面研究后发现其实整体框架还是很清晰的,顺便将OpenHarmony 移植到MCU的过程分享给大家,相关教程和代码放在我的Gitee和Github仓库。
移植编译的方式使用的Makefile进行编译构建,没有使用官方推荐的najia进行构建,官方对于的najia的构建方式的描写的挺详细,也是主推的方法,有兴趣的可以自行去了解,我个人偏向于使用Make方式。
二、开发环境
硬件平台:
原子开发板软件环境:
VSCode、STM32CubeMX、Git工具
技术基础:
- 会使用STM32CubeMX配置生成Makefile工程
- 会使用Git
- 懂一点makefile语法
三、获取源码、添加第三方依赖
上Gitee拉取代码,选3.1发布的版本:链接[1]
OpenHarmony仓库拉到本地的文件夹:
git clone https://gitee.com/openharmony/kernel_liteos_m.git
拉取:
拉取代码然后在git进入到kernel_liteos_m里面,新建third_party目录并进入
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
拉取完成如下:
拉取完成四、导入STM32 Makefile 工程
用 STM32CubeMX新建一个STM32F407ZGT6的工程,导出到liteos目录下面的 targets目录,STM32CubeMX导出Makefile工程详细教程可以百度自行寻找,我简单的写一下,在CubeMX工程中我们配置好时钟树:
时钟树开启串口,方便进行调试:
开启串口修改HAL库延时的基础时钟,改为其他非SysTick的定时器,避免HAL库延时的定时器和系统运行的定时器冲突:
修改延时时钟配置工程选项:
配置工程信息
设置工程输出将生成的工程放到源码 targets 目录下:
复制代码目录target下提供了几个基于芯来risc-v和sifive risc-v架构的芯片示例,我们不用管他。
五、配置编译文件
在 VSCode 打开源码目录:
源码目录在我们的Target下的工程目录中新建一个My_Path.mk文件,用于辅助进行编译构建,在该文件内依次添加如下的Makefile代码。
定位顶层目录
# 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
个人的文件和路径,位置预留:
# My file
C_SOURCES +=
C_INCLUDES +=
架构相关文件,选择M4相关架构文件的目录
# 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 $@
然后在工程的Makefile中如下位置调用我们的makefile文件:
Makefile 文件六、添加工程配置文件
我们要添加一个工程配置文件,用于配置和裁剪内核,在Targets下的工程目录内,添加一个target_config.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 */
这些配置文件的含义和功能,主要是对着los_config.h来进行修改的,后面我会进行分析!
配置文件完成后对代码进行编译,编译使用的task.json来快速配置的,配置环境那一节我有提到过,修改路径如下:
修改路径然后编译,编译完成如下:
编译结果七、修改链接脚本
在链接脚本targets\OpenHarmony_Demo\STM32F407ZGTx_FLASH.ld 中定义栈的起始地址为 0x20000000:
/* Lowest address of the user mode stack */
_sstack = 0x20000000; /* start of RAM */
添加.text 段的起始地址,这是链接脚本的语法,将当前位置地址赋值给_stext:
_stext = .;
具体位置如下:
代码位置八、修改中断服务函数
虽然编译完成了,但就算下载了程序,也无法正常运行,因为之前的配置代码写的
/*=============================================================================
Hardware interrupt module configuration
=============================================================================*/
#define LOSCFG_PLATFORM_HWI 0
#define LOSCFG_USE_SYSTEM_DEFINED_INTERRUPT 0
#define LOSCFG_PLATFORM_HWI_LIMIT 128
不使用硬件中断接管,所以我们需要在系统运行的两个重要中断服务函数添加内核的接口,让STM32的中断管理可以跳转到内核的处理函数:
进入 targets\OpenHarmony_Demo\Core\Src\stm32f4xx_it.c 文件,在下面两个函数添加如下函数:
在PendSV异常中进入LiteOS HalPendSV 异常处理函数,进行任务切换操作
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 */
}
在SysTick中断服务函数添加OsTickHandler函数,为系统提供时间基准
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 */
}
添加个头文件包含:
#include "los_arch_context.h"
#include "los_tick.h"
九、修改串口映射
之前我们有开启串口,所以我们再把printf的底层映射到串口上,供内核调用,便于调试,在main.c中包含头文件 include "stdio.h",然后添加底层接口,底层接口未启动时使用阻塞方式发生数据,系统启动后则使用DMA 传输,通过信号量防止DMA使用冲突:
#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);
}
十、添加测试任务
然后在main函数中添加一个测试任务和一个信号量:
变量定义:
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,
};
初始化:
osKernelInitialize();
UART1_TX_DMA_SemaphoreHandle = osSemaphoreNew(1, 1, &UART1_TX_DMA_Semaphore_attributes);
uart_taskHandle = osThreadNew(Uart_Task, NULL, &uart_task_attributes);
osKernelStart();
任务实体:
void Uart_Task(void *argument);
void Uart_Task(void *argument)
{
while (1)
{
printf("System Runing!!!\r\n");
osDelay(1000);
}
}
编译代码:
编译代码十一、实验现象
串口打印系统正常运行信息
运行信息十二、工程源码
示例工程的核心代码放到 Gitee 上了,需要自取,点击原文可以直接进入工程仓库,Gitee:仓库链接[2]
参考资料
[1]链接: https://gitee.com/openharmony/kernel_liteos_m/tree/OpenHarmony-3.1-Release/
[2]https://gitee.com/JeckXu666/openharmony-for-mcu: https://gitee.com/JeckXu666/openharmony-for-mcu
新闻来源:果果小师弟,文中所述为作者独立观点,不代表icspec立场。更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj。
