0
点赞
收藏
分享

微信扫一扫

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

柠檬果然酸 2022-04-14 阅读 41
harmonyos

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

内核支持的archROM文件系统Flash类型
Liteos-AARMV7>2MVFAT、JFFS2SPI、NOR、NAND
Linux-4.19ARM、ARM64、MIPS、X86>5MVFAT、JFFS2、EXT2NOR、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源码实现
    在这里插入图片描述
    整体流程如下:
    在这里插入图片描述
    target_config.h的配置项说明
配置项说明
OS_SYS_CLOCK系统cycle频率
DDR_MEM_ADDR系统内存起始地址
DDR_MEM_SIZE系统内存大小
OS_HWI_MIN系统中断最小值
OS_HWI_MAX系统中断最大值
GIC_BASE_ADDRgic中断寄存器基址
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.mk

SOC_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.mk

SOC_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.mk
include $(LITEOSTOPDIR)/../../drivers/adapter/kndf/liteos/lite.mk

#指定输出对象的名称,这里的名称与soc驱动构建里边的BASELIB保持一致
MODULE_NAME := hdf_gpio

#增加hdf框架的include
LOCAL_CFLAGS += $(HDG_INCLUDE)

#需要编译的文件
LOCAL_SRCS += soc_name_gpio.c

#编译参数
LOCAL_CFLAGS += -fstack-protector-strong -Wextera -Wall

include $(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";
                }
        }
    }
}
举报

相关推荐

0 条评论