> 文档中心 > 【起航】OpenHarmony远征04小型系统移植

【起航】OpenHarmony远征04小型系统移植


openharmony小型系统内核移植信息表

内核 支持的arch ROM 文件系统 Flash类型
Liteos-A ARMV7 >2M VFAT、JFFS2 SPI、NOR、NAND
Linux-4.19 ARM、ARM64、MIPS、X86 >5M VFAT、JFFS2、EXT2 NOR、NAND、EMMC

编译构建系统介绍

编译框架与编译环境的搭建和之前的轻量系统移植类似

Liteos-A内核移植

Liteos-A支持ARM V7的指令集架构,若第三方芯片本身就是ARMV7-a的架构,就没有必要去修改arch目录下的芯片架构支持,正常都是支持的

Liteos-A的初始化流程

  1. 新增target_config.h文件,编写与内存相关的适配宏DDR_MEM_ADDR和DDR_MEM_SIZE,分别来表示内存的起始地址和内存的长度,预链接脚本board.ld.S会根据这两个宏进行展开,生成连接脚本board.ld
  2. 新增定义MMU映射全局数组(g_archMmuInitMapping),指定各个内存段属性及虚实映射关系,内核启动阶段根据该表建立内存映射关系
  3. 如果是多核,需要新增定义从核操作函数句柄(struct SmpOps),其中SmpOps->SmpCpuOn函数需要实现唤醒从核的功能;接着定义SmpRegFunc函数,调用LOS_SmpOpsSet接口进行句柄注册;最后通过启动框架完成注册过程,即LOS_MODULE_INIT(SmpRegFunc, LOS_INIT_LEVEL_EARLIEST)
  4. 链接阶段根据链接脚本board.ld生成内核镜像
  5. 单核CPU镜像运行入口为汇编文件reset_vector_up.S,多核CPU的入口为reset_vector_mp.S,在汇编文件中进行中断向量表初始化、MMU页表初始化等操作
  6. reset_vector.S汇编代码最终会跳转到C语言的main函数,进行硬件时钟、软件定时器、内存和任务等初始化,这个过程会依赖target_config.h的特性宏配置,最后会创建SystemInit任务,并且开启任务调度OsSchedStart()
    在这里插入图片描述
  7. SystemInit任务在单板代码中实现,其中调用DeviceManagerStart函数进行HDF驱动初始化,这个过程会调用单板代码中的驱动配置文件hdf.hcs以及drivers源码实现
    【起航】OpenHarmony远征04小型系统移植
    整体流程如下:
    在这里插入图片描述
    target_config.h的配置项说明
配置项 说明
OS_SYS_CLOCK 系统cycle频率
DDR_MEM_ADDR 系统内存起始地址
DDR_MEM_SIZE 系统内存大小
OS_HWI_MIN 系统中断最小值
OS_HWI_MAX 系统中断最大值
GIC_BASE_ADDR gic中断寄存器基址
PERIPH_PMM_BASE 外设寄存器起始地址
PERIPH_PMM_SIZE 外设寄存器长度大小

启动框架层级

层级 说明
LOS_INIT_LEVEL_EARLIEST 最早起初始化,不依赖架构的纯软件初始化
LOS_INIT_LEVEL_ARCH_EARLY 架构相关,后续的模块初始化过程会依赖,非必要的建议放在LOS_INIT_LEVEL_ARCH
LOS_INIT_LEVEL_PLATFORM_EARLY 单板相关,驱动相关非必要的功能建议放在LOS_INIT_LEVEL_PLATFORM
LOS_INIT_LEVEL_KMOD_PREVM 内存初始化前需要依赖的内核模块进行初始化
LOS_INIT_LEVEL_VM_COMPLETE 内存初始化
LOS_INIT_LEVEL_ARCH 架构后期初始化
LOS_INIT_LEVEL_PLATFORM 驱动初始化
LOS_INIT_LEVEL_KMOD_BASIC 内核基础模块初始化,基础模块的加载
LOS_INIT_LEVEL_KMOD_EXTENDED 内核扩展模块初始化
LOS_INIT_LEVEL_KMOD_TASK 内核任务创建

进行单板移植时,若arch使用已经适配的架构,可以只需要关注LOS_INIT_LEVEL_PLATFORM到LOS_INIT_LEVEL_KMOD_TASK之间的层级

驱动移植

驱动主要包含两部分,平台驱动和器件驱动。平台驱动主要包括通常在SOC内的GPIO、I2C、SPI等;器件驱动则主要包含通常在SOC外的器件,如 LCD、TP、WLAN
在这里插入图片描述
需要注意HDF驱动框架可以跨OS,为了增加软件的灵活性,驱动开发过程中尽量去使用HDF框架中提供的API,否则会使驱动丧失跨OS使用的特性。
常见的驱动目录名称如下:

device├── vendor_name│   ├── drivers│   │   │   ├── common│   │   │   ├── Kconfig # 厂商驱动内核菜单入口│   │   │   └── lite.mk # 构建的入口│   ├── soc_name│   │   ├── drivers│   │   │   ├── dmac│   │   │   ├── gpio│   │   │   ├── i2c│   │   │   ├── LICENSE│   │   │   ├── mipi_dsi│   │   │   ├── mmc│   │   │   ├── pwm│   │   │   ├── README.md # docs 如果需要的话│   │   │   ├── README_zh.md│   │   │   ├── rtc│   │   │   ├── spi│   │   │   ├── uart│   │   │   └── watchdog│   ├── board_name

open harmony官方给出的platform驱动实例为GPIO驱动:

  1. 创建GPIO驱动,在源码目录//device/vendor/soc/drivers/gpio中创建文件soc_name_gpio.c
#官方示例code#include "gpio_core.h"// 定义GPIO结构体,如果需要的话struct SocNameGpioCntlr {    struct GpioCntlr cntlr;  // 这是HDF GPIO驱动框架需要的结构体    int myData; // 以下是当前驱动自身需要的};// Bind 方法在HDF驱动中主要用户对外发布服务,这里我们不需要,直接返回成功即可static int32_t GpioBind(struct HdfDeviceObject *device){    (void)device;    return HDF_SUCCESS;}// Init方法时驱动初始化的入口,我们需要在Init方法中完成模型实例的注册static int32_t GpioInit(struct HdfDeviceObject *device){    SocNameGpioCntlr *impl = CreateGpio(); // 你的创建代码    ret = GpioCntlrAdd(&impl->cntlr);  // 注册GPIO模型实例    if (ret != HDF_SUCCESS) { HDF_LOGE("%s: err add controller:%d", __func__, ret); return ret;    }    return HDF_SUCCESS;}// Release方法会在驱动卸载时被调用,这里主要完成资源回收static void GpioRelease(struct HdfDeviceObject *device){    // GpioCntlrFromDevice 方法能从抽象的设备对象中获得init方法注册进去的模型实例。    struct GpioCntlr *cntlr = GpioCntlrFromDevice(device);    //资源释放...}struct HdfDriverEntry g_gpioDriverEntry = {    .moduleVersion = 1,    .Bind = GpioBind,    .Init = GpioInit,    .Release = GpioRelease,    .moduleName = "SOC_NAME_gpio_driver", // 这个名字我们稍后会在配置文件中用到,用来加载驱动。};HDF_INIT(g_gpioDriverEntry); // 注册一个GPIO的驱动入口
  1. 创建厂商驱动构建入口
    //device/vendor/drivers/lite.mk是厂商驱动的构建入口,从该入口开始进行构建
#文件device/vendor_name/drivers/lite.mkSOC_VENDOR_NAME := $(subst $/",,$(LOSCFG_DEVICE_COMPANY))SOC_NAME := $(subst $/",,$(LOSCFG_PLATFORM))BOARD_NAME := $(subst $/",,$(LOSCFG_PRODUCT_NAME))# 指定SOC进行构建LIB_SUBDIRS += $(LITEOSTOPDIR)/../../device/$(SOC_VENDOR_NAME)/$(SOC_NAME)/drivers/
  1. 创建soc驱动构建入口
#文件device/vendor_name/soc_name/drivers/lite.mkSOC_DRIVER_ROOT := $(LITEOSTOPDIR)/../../device/$(SOC_VENDOR_NAME)/$(SOC_NAME)/drivers/# 判断如果打开了GPIO的内核编译开关ifeq ($(LOSCFG_DRIVERS_HDF_PLATFORM_GPIO), y)    # 构建完成要链接一个叫hdf_gpio的对象    LITEOS_BASELIB += -lhdf_gpio    # 增加构建目录gpio    LIB_SUBDIRS    += $(SOC_DRIVER_ROOT)/gpio endif# 后续其他驱动在此基础上追加
  1. GPIO构建入口
include $(LITEOSTOPDIR)/config.mkinclude $(LITEOSTOPDIR)/../../drivers/adapter/kndf/liteos/lite.mk#指定输出对象的名称,这里的名称与soc驱动构建里边的BASELIB保持一致MODULE_NAME := hdf_gpio#增加hdf框架的includeLOCAL_CFLAGS += $(HDG_INCLUDE)#需要编译的文件LOCAL_SRCS += soc_name_gpio.c#编译参数LOCAL_CFLAGS += -fstack-protector-strong -Wextera -Wallinclude $(HDF_DRIVER)     
  1. 指定对应的产品去加载驱动
    产品的设备信息被定义在源码文件中//vendor/product/config/device_info/device_info.hcs
    还是以gpio驱动为例子,platform的驱动需要加载到platform的host中
root {    ...    platform :: host { device_gpio :: device {  device0 :: deviceNode {      policy = 0;      priority = 10;      permission = 0644;      moduleName = "SOC_NAME_gpio_driver";   } }    }}

open harmony官方给出的器件驱动实例为LCD驱动

器件驱动的相关代码没有放在device目录下,例如LCD的驱动放在源码目录//drivers/framework/model/display/driver/panel中
1.创建Panel驱动
创建HDF驱动,在驱动初始化过程中调用RegisterPanel接口去注册模型实例

int32_t LCDxxEntryInit(struct HdfDeviceObject *object){    struct PanelData *panel = CreateYourPanel();    // 注册模型实例    if (RegisterPanel(panel) != HDF_SUCCESS) { HDF_LOGE("%s: RegisterPanel failed", __func__); return HDF_FAILURE;    }    return HDF_SUCCESS;}struct HdfDriverEntry g_xxxxDevEntry = {    .moduleVersion = 1,    .moduleName = "LCD_XXXX",    .Init = LCDxxEntryInit,};HDF_INIT(g_xxxxDevEntry);
  1. 配置加载Panel驱动
    和platform driver一样,产品相关的设备信息被定义在//vendor/vendor_name/product_name/config/device_info/device_info.hcs,唯一不同的是module定义的位置不同,lcd driver放在display的host中
root {    ...    display :: host { device_lcd :: device {  deviceN :: deviceNode {      policy = 0;      priority = 100;      preload = 2;      moduleName = "LCD_XXXX";  } }    }}