基于OpenHarmony系统HDF Sensor驱动开发

1 概述

在产品开发时就需要对不同厂家或者同一厂家的不同型号进行适配开发,就会增加开发者的开发难度。为了快速开发或者移植传感器驱动,基于HDF(Hardware Driver Foundation)驱动框架开发了Sensor(传感器)驱动模型。Sensor驱动模型主要为上层提供稳定接口能力,对驱动开发者提供开放的接口实现和抽象的配置接口能力。Sensor设备作为外接设备重要组成模块,Sensor驱动模型为上层Sensor服务系统提供稳定的Sensor基础能力接口,包括Sensor列表查询、Sensor启停、Sensor订阅及去订阅,Sensor参数配置等功能。传感器驱动模型总体框架如下图所示。 

Sensor驱动抽象模型主要位于OpenHarmony软件的HAL层,其核心包括三个子模块:

1)Sensor HDI子模块:提供Sensor南向的标准接口定义和实现。

2)Sensor设备管理和通用配置子模块:提供Sensor设备管理,Sensor通用配置能力,Sensor通用数据解析能力。

3)Sensor器件驱动子模块:提供Sensor器件通用驱动和差异化驱动实现。

Sensor设备驱动的开发是基于HDF驱动框架基础上,结合操作系统适配层(OSAL)和平台驱动接口(比如I2C/SPI/UART总线等平台资源)能力,屏蔽不同操作系统和平台总线资源差异,实现Sensor驱动“一次开发,多系统部署”的目标。传感器设备驱动模型框图如下图所示。

Sensor驱动模型作为HDF框架中一个Device Host(驱动宿主),完成对Sensor设备管理,包括Sensor驱动加载,注册,卸载,绑定,配置管理,接口发布。Sensor驱动模型主要包括Sensor HDI子模块,Sensor设备管理和通用配置子模块和Sensor器件驱动子模块。Sensor HDI子模块抽象出Sensor设备的基本能力Sensor列表查询、Sensor启停、Sensor订阅及取消订阅,Sensor参数配置接口,接口和类型定义参考sensor_if.h和sensor_type.h

Sensor设备管理和通用配置子模块,其中,Sensor设备管理完成Sensor设备的注册、管理能力,数据报告能力,接口定义参考sensor_device_if.h;通用配置子模块完成寄存器配置操作接口抽象,Sensor HCS通用配置解析能力,接口定义参考sensor_config_parser.h、sensor_config_controller.h。Sensor器件驱动子模块完成每类Sensor类型驱动的抽象和器件差异化驱动实现。

2 HDF 配置管理

HCS(HDF Configuration Source)是HDF驱动框架的配置描述源码,内容以Key-Value为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。

HC-GEN(HDF Configuration Generator)是HCS配置转换工具,可以将HDF配置文件转换为软件可读取的文件格式:

  • 在弱性能环境中,转换为配置树源码或配置树宏定义,驱动可直接调用C代码或宏式APIs获取配置。

  • 在高性能环境中,转换为HCB(HDF Configuration Binary)二进制文件,驱动可使用HDF框架提供的配置解析接口获取配置。

以下是使用HCB模式的典型应用场景:

HCS经过HC-GEN编译生成HCB文件,HDF驱动框架中的HCS Parser模块会从HCB文件中重建配置树,HDF驱动模块使用HCS Parser提供的配置读取接口获取配置内容。

hc-gen参数说明:

Usage: hc-gen [Options] [File]
options:
  -o <file>   output file name, default same as input
  -a          hcb align with four bytes
  -b          output binary output, default enable
  -t          output config in C language source file style
  -m          output config in macro source file style
  -i          output binary hex dump in C language source file style
  -p <prefix> prefix of generated symbol name
  -d          decompile hcb to hcs
  -V          show verbose info
  -v          show version
  -h          show this help message

生成.c/.h配置文件方法:

hc-gen -o [OutputCFileName] -t [SourceHcsFileName]

生成HCB配置文件方法:

hc-gen -o [OutputHcbFileName] -b [SourceHcsFileName]

生成宏定义配置文件方法:

hc-gen -o [OutputMacroFileName] -m [SourceHcsFileName]

反编译HCB文件为HCS方法:

hc-gen -o [OutputHcsFileName] -d [SourceHcbFileName]

3 传感器驱动模型工作流程

通过介绍Sensor驱动模型的加载以及运行流程,对模型内部关键组件以及关联组件之间的关系进行了划分,整体加载流程如下图所示:

Sensor驱动模型以标准系统中的加速度计驱动为例,介绍整个驱动加载及运行流程为:

1)从device info HCS 的Sensor Host里读取Sensor设备管理配置信息。

  • 驱动设备描述(必选)

HDF框架加载驱动所需要的信息来源于HDF框架定义的驱动设备描述,因此基于HDF框架开发的驱动必须要在HDF框架定义的device_info.hcs配置文件中添加对应的设备描述,驱动的设备描述填写如下所示:

	sensor :: host {
            hostName = "sensor_host";                             // host名称,host节点是用来存放某一类驱动的容器
            device_sensor_mxc6655xa :: device {                      // sample设备节点
              device0 :: deviceNode {                         // sample驱动的DeviceNode节点
              policy = 1;												  // policy字段是驱动服务发布的策略,在驱动服务管理章节有详细介绍
                    priority = 120;                              //驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序
                    preload = 0;                                // 驱动按需加载字段,在本章节最后的说明有详细介绍
                    permission = 0664;										  // 驱动创建设备节点权限
                    moduleName = "HDF_SENSOR_ACCEL_MXC6655XA";   			 // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致
                    serviceName = "hdf_accel_mxc6655xa";					  // 驱动对外发布服务的名称,必须唯一
                    deviceMatchAttr = "hdf_sensor_accel_mxc6655xa_driver"; 	  // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等              
		  		}
            }
         ...
        }
  • 驱动加载策略

支持按需加载和按序加载两种策略,具体设备的加载策略由配置文件(参考驱动开发)中的preload字段来控制,配置值参考如下:

typedef enum {
    DEVICE_PRELOAD_ENABLE = 0,
    DEVICE_PRELOAD_ENABLE_STEP2 = 1,
    DEVICE_PRELOAD_DISABLE = 2,
    DEVICE_PRELOAD_INVALID
} DevicePreload;

按需加载:

preload字段配置为0(DEVICE_PRELOAD_ENABLE),则系统启动过程中默认加载。

preload字段配置为1(DEVICE_PRELOAD_ENABLE_STEP2),当系统支持快速启动的时候,则在系统完成之后再加载这一类驱动,否则和DEVICE_PRELOAD_ENABLE含义相同。

preload字段配置为2(DEVICE_PRELOAD_DISABLE),则系统启动过程中默认不加载,支持后续动态加载,当用户态获取驱动服务(参考消息机制)时,如果驱动服务不存在,HDF框架会尝试动态加载该驱动。

按序加载(系统默认加载策略):

配置文件中的priority(取值范围为整数0到200)是用来表示host(驱动容器)和驱动的优先级。不同的host内的驱动,host的priority值越小,驱动加载优先级越高;同一个host内驱动的priority值越小,加载优先级越高。

  • 驱动服务管理

驱动服务是HDF驱动设备对外提供能力的对象,由HDF框架统一管理。驱动服务管理主要包含驱动服务的发布和获取。HDF框架定义了驱动对外发布服务的策略,由配置文件中的policy字段来控制,policy字段的取值范围以及含义如下:

typedef enum {
    /* 驱动不提供服务 */
    SERVICE_POLICY_NONE = 0,
    /* 驱动对内核态发布服务 */
    SERVICE_POLICY_PUBLIC = 1,
    /* 驱动对内核态和用户态都发布服务 */
    SERVICE_POLICY_CAPACITY = 2,
    /* 驱动服务不对外发布服务,但可以被订阅 */
    SERVICE_POLICY_FRIENDLY = 3,
    /* 驱动私有服务不对外发布服务,也不能被订阅 */
    SERVICE_POLICY_PRIVATE = 4,
    /* 错误的服务策略 */
    SERVICE_POLICY_INVALID
} ServicePolicy;

2)HDF配置框架从HCB数据库解析Sensor设备管理配置信息,并关联到对应设备驱动。

  • 加速度传感器驱动入口函数

基于HDF驱动框架,按照驱动Driver Entry程序,完成加速度抽象驱动开发,主要由Bind、Init、Release、Dispatch函数接口实现。

/* 注册加速度计传感器入口数据结构体对象 */
struct HdfDriverEntry g_sensorAccelDevEntry = {
    .moduleVersion = 1, //加速度计传感器模块版本号
    .moduleName = "HDF_SENSOR_ACCEL", //加速度计传感器模块名,要与device_info.hcs文件里的加速度计moduleName字段值一样
    .Bind = BindAccelDriver, // 加速度计传感器绑定函数
    .Init = InitAccelDriver, // 加速度计传感器初始化函数
    .Release = ReleaseAccelDriver, // 加速度计传感器资源释放函数
};

/* 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出 */
HDF_INIT(g_sensorAccelDevEntry);

3)加载并初始化Sensor设备管理驱动。

4)Sensor设备管理驱动向HDI发布Sensor基础能力接口。

5)从device info HCS 的Sensor Host里读取加速度计驱动配置信息。

6)加载加速度抽象驱动,调用初始化接口,完成Sensor器件驱动资源分配和数据处理队列创建。

7)从accel_xxx_config HCS里读取加速度器件差异化驱动配置和私有化配置信息。

  • 私有化配置描述:
root {
    accel_mxc6655xa_chip_config : sensorConfig {
        match_attr = "hdf_sensor_accel_mxc6655xa_driver";
        sensorInfo :: sensorDeviceInfo {
            sensorName = "accelerometer";
            vendorName = "memsi_mxc6655xa"; // max string length is 16 bytes
            sensorTypeId = 1; // enum SensorTypeTag
            sensorId = 1; // user define sensor id
            power = 230;
        }
        sensorBusConfig :: sensorBusInfo {
            busType = 0; // 0:i2c 1:spi
            busNum = 5;
            busAddr = 0x45;
            regWidth = 1; // 1btye
        }
        sensorIdAttr :: sensorIdInfo {
            chipName = "ina220_1";
            chipIdRegister = 0x0f;
            chipIdValue = 0x00;
        }
	...

8)加速度计差异化驱动,调用通用配置解析接口,完成器件属性信息解析,器件寄存器解析。

9)加速度计差异化驱动完成器件探测,并分配加速度传感器配置资源,完成加速度计差异化接口注册。

  • 加速度传感器差异化驱动中差异化接口ReadData函数

    static int32_t ReadMxc6655xaRawData(struct SensorCfgData *data, struct AccelData *rawData, int64_t *timestamp)
    {
        int32_t status = 0;
        int32_t reg[ACCEL_AXIS_BUTT];
        OsalTimespec time;
        int32_t x;
        int32_t y;
        int32_t z;
     
        (void)memset_s(&time, sizeof(time), 0, sizeof(time));
        (void)memset_s(reg, sizeof(reg), 0, sizeof(reg));
     
        CHECK_NULL_PTR_RETURN_VALUE(data, HDF_ERR_INVALID_PARAM);
     
        if (OsalGetTime(&time) != HDF_SUCCESS) {
            HDF_LOGE("%s: Get time failed", __func__);
            return HDF_FAILURE;
        }
        *timestamp = time.sec * SENSOR_SECOND_CONVERT_NANOSECOND + time.usec * SENSOR_CONVERT_UNIT; /* unit nanosecond */
     
        int32_t ret = ReadSensor(&data->busCfg, MXC6655XA_STATUS_ADDR, &status, sizeof(int32_t));
        if (ret != HDF_SUCCESS) {
            HDF_LOGE("%s: data status [%u] ret [%d]", __func__, status, ret);
            return HDF_FAILURE;
        }
     
        ret = ReadSensor(&data->busCfg, MXC6655XA_ACCEL_X_LSB_ADDR, &reg[ACCEL_X_AXIS_LSB], sizeof(int32_t));
        CHECK_PARSER_RESULT_RETURN_VALUE(ret, "read data");
     
        ret = ReadSensor(&data->busCfg, MXC6655XA_ACCEL_X_MSB_ADDR, &reg[ACCEL_X_AXIS_MSB], sizeof(int32_t));
        CHECK_PARSER_RESULT_RETURN_VALUE(ret, "read data");
     
        ret = ReadSensor(&data->busCfg, MXC6655XA_ACCEL_Y_LSB_ADDR, &reg[ACCEL_Y_AXIS_LSB], sizeof(int32_t));
        CHECK_PARSER_RESULT_RETURN_VALUE(ret, "read data");
     
        ret = ReadSensor(&data->busCfg, MXC6655XA_ACCEL_Y_MSB_ADDR, &reg[ACCEL_Y_AXIS_MSB], sizeof(int32_t));
        CHECK_PARSER_RESULT_RETURN_VALUE(ret, "read data");
     
        ret = ReadSensor(&data->busCfg, MXC6655XA_ACCEL_Z_LSB_ADDR, &reg[ACCEL_Z_AXIS_LSB], sizeof(int32_t));
        CHECK_PARSER_RESULT_RETURN_VALUE(ret, "read data");
     
        ret = ReadSensor(&data->busCfg, MXC6655XA_ACCEL_Z_MSB_ADDR, &reg[ACCEL_Z_AXIS_MSB], sizeof(int32_t));
        CHECK_PARSER_RESULT_RETURN_VALUE(ret, "read data");
     
        x = sensor_convert_data(reg[ACCEL_X_AXIS_MSB], reg[ACCEL_X_AXIS_LSB]);
        y = sensor_convert_data(reg[ACCEL_Y_AXIS_MSB], reg[ACCEL_Y_AXIS_LSB]);
        z = sensor_convert_data(reg[ACCEL_Z_AXIS_MSB], reg[ACCEL_Z_AXIS_LSB]);
        rawData->x = x;
        rawData->y = y;
        rawData->z = z;
     
        HDF_LOGE("MXC6655XA read raw data x:%d,y:%d,z:%d",x,y,z);
     
        return HDF_SUCCESS;
    }
     
    int32_t ReadMxc6655xaData(struct SensorCfgData *cfg, struct SensorReportEvent *event)
    {
        int32_t ret;
        struct AccelData rawData = { 0, 0, 0 };
        static int32_t tmp[ACCEL_AXIS_NUM];
     
        CHECK_NULL_PTR_RETURN_VALUE(cfg, HDF_ERR_INVALID_PARAM);
        CHECK_NULL_PTR_RETURN_VALUE(event, HDF_ERR_INVALID_PARAM);
     
        ret = ReadMxc6655xaRawData(cfg, &rawData, &event->timestamp);
        if (ret != HDF_SUCCESS) {
            HDF_LOGE("%s: MXC6655XA read raw data failed", __func__);
            return HDF_FAILURE;
        }
     
        event->sensorId = SENSOR_TAG_ACCELEROMETER;
        event->option = 0;
        event->mode = SENSOR_WORK_MODE_REALTIME;
     
        rawData.x = rawData.x * MXC6655XA_ACC_SENSITIVITY_2G;
        rawData.y = rawData.y * MXC6655XA_ACC_SENSITIVITY_2G;
        rawData.z = rawData.z * MXC6655XA_ACC_SENSITIVITY_2G;
     
        tmp[ACCEL_X_AXIS] = (rawData.x * SENSOR_CONVERT_UNIT) / SENSOR_CONVERT_UNIT;
        tmp[ACCEL_Y_AXIS] = (rawData.y * SENSOR_CONVERT_UNIT) / SENSOR_CONVERT_UNIT;
        tmp[ACCEL_Z_AXIS] = (rawData.z * SENSOR_CONVERT_UNIT) / SENSOR_CONVERT_UNIT;
     
        ret = SensorRawDataToRemapData(cfg->direction, tmp, sizeof(tmp) / sizeof(tmp[0]));
        if (ret != HDF_SUCCESS) {
            HDF_LOGE("%s: MXC6655XA convert raw data failed", __func__);
            return HDF_FAILURE;
        }
     
        event->dataLen = sizeof(tmp);
        event->data = (uint8_t *)&tmp;
     
        return ret;
    }

10)加速度器件探测成功之后,加速度差异化驱动通知加速度抽象驱动,注册加速度设备到Sensor设备管理中。

  • 运行用户态demo 可检测到加速度传感器sensor设备

4 DevEco Device tool工具快速创建驱动模块

1.打开DevEco Device Tool,进入Home页,点击New Project创建新工程。

2.在新工程的配置向导页,配置工程相关信息,包括:

  • OpenHarmony Source Code:选择需要下载的OpenHarmony源码,请选择 OpenHarmony Stable Version下的源码版本,支持OpenHarmony-v1.1.4-LTS、OpenHarmony-v3.0.3-LTS和OpenHarmony-v3.1-Release版本。
  • Project Name:设置工程名称。
  • Project Path:选择工程文件存储路径。
  • SOC:选择支持的芯片。
  • Board:选择支持的开发板。
  • Product:选择产品。

3.工程配置完成后,点击Confirm,DevEco Device Tool会自动启动OpenHarmony源码的下载。由于OpenHarmony稳定版本源码包体积较大,请耐心等待源码下载完成。

4.打开HDF工具,添加驱动模块,南向内核态开发运行模式选择kernel,北向用户态开发选择user

5.添加后,DevEco Device Tool会自动在指定源码位置生成对应的makefile、kconfig、hcs以及.c文件

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山下小僧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值