OpenHarmony HDF传感器驱动开发

1 技术背景

近年来,传感器技术和制造工艺的快速发展,目前市场可供开发者选择的传感器越来越多,比如:加速度传感器,陀螺仪传感器,磁力传感器,温度传感器等类型。每种传感器厂家都有各自的传感器驱动,在产品开发时就需要对不同厂家或者同一厂家的不同型号进行适配开发,就会增加开发者的开发难度。为了快速开发或者移植传感器驱动,基于HDF(Hardware Driver Foundation)驱动框架开发了Sensor(传感器)驱动模型。Sensor驱动模型主要为上层提供稳定接口能力,对驱动开发者提供开放的接口实现和抽象的配置接口能力。

2 技术原理

2.1 传感器模型框架介绍

Sensor设备作为外接设备重要组成模块,Sensor驱动模型为上层Sensor服务系统提供稳定的Sensor基础能力接口,包括Sensor列表查询、Sensor使能释放、订阅去订阅、参数设置等。
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/7e89ef0e91c046a38c291de3bf2da24f.png#pic_center

Sensor驱动模型位于OpenHarmony的HAL层,核心有三个子模块:

  1. HDI子模块,提供Sensor南向的标准接口定义和实现,其中包括:Dispatch、Register、Enable等。
  2. Sensor设备管理和通用配置子模块(sensor manager),提供Sensor设备管理,Sensor通用配置能力,Sensor通用数据解析能力。
  3. Sensor器件驱动子模块:提供Sensor通用驱动(sensor temprature)和差异化驱动实现(sht30、sht20)。

2.2 传感器设备驱动模型介绍

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

在这里插入图片描述

传感器驱动模型作为HDF框架中的Device Host,完成对传感器设备管理、驱动加载、注册、卸载、绑定等操作
sensor驱动模型分为:HDI和Driver。

  1. HDI子模块中抽象出了设备的基本操作:sensor列表查询、使能、释放、订阅、取消订阅、参数配置等。接口和类型定义在sensor_if.h和sensor_type.h中。
  2. Driver中包含了sensor设备管理、通用配置子模块和sensor器件驱动子模块:sensor设备管理完成sensor的注册、管理、数据交互能力,接口定义见sensor_device_if.h; 通用配置子模块完成寄存器配置操作接口抽象,驱动设备描述文件配置解析能力,接口定义参考sensor_config_parser.h、sensor_config_controller.h;sensor器件驱动子模块通过解析私有化配置信息文件配置,完成每种传感器驱动的抽象和器件差异化驱动实现。

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

以温度传感器SHT30为例,介绍Sensor驱动模型的文件配置、驱动加载以及运行流程。

在这里插入图片描述

  1. 从device info HCS 的Sensor Host里读取传感器管理配置信息。
  2. HDF框架从HCB数据库解析配置管理信息,并关联到设备驱动。
  3. 加载并初始化设备管理驱动,sensor_device_manager.c。
  4. sensor管理驱动向HDI发布Sensor基础能力接口。
  1. 从device info HCS device_info hcs中读取温度驱动配置信息。
  2. 加载温度传感器抽象驱动,调用初始化接口,完成Sensor器件驱动资源分配和数据处理队列创建。
  1. 从temperaturesht30config.hcs里读取温度传感器器件差异化驱动配置和私有化配置信息。
  2. 温度传感器差异化驱动,调用通用配置解析接口,完成器件属性信息解析,器件寄存器解析。
  3. 温度传感器差异化驱动完成器件探测,并分配温度传感器配置资源,完成温度传感器差异化接口注册。
  4. 温度传感器器件探测成功之后,温度传感器差异化驱动通知温度传感器抽象驱动,注册温度传感器设备到Sensor设备管理中。

3 开发流程

基于HDF的传感器驱动开发流程总体分为两部分:

以oho_executable()模块为编译体系,通过可执行程序,实现用户态调用驱动程序的测试用例。下文将以光感传感器VEML7700模块为例,实现样例测试开发框架搭建。

以ALS抽象驱动实现,以VEML7700光感传感器为驱动差异化实现,实现步骤:

  1. HCS驱动配置
  2. 驱动编译脚本编写
  3. 驱动实现

3.1 用户样例测试开发流程

3.1.1 准备工作

  1. 代码下载
repo init -u ssh://gerrit.openvalley.net:29418/openvalley/zaiohos/manifest -g ohos:standard -b ZaiOHOS-4.1-Master --no-repo-verify

repo sync -c

repo forall -c 'git lfs pull'

./build/prebuilts_download.sh
  1. 编译
./build.sh --product-name rk3568 --target-cpu arm64 --device-type tablet --ccache --no-prebuilt-sdk --gn-args enable_notice_collection=false --disable-package-image --disable-post-build --gn-args skip_generate_module_list_file=true

3.1.2 建立测试框架

  1. 在代码根目录下构建一个samples子系统文件夹,后续所有测试用例都放在该文件夹中。在子系统目录下创建sensors_i2c部件文件夹,sensor_i2c文件目录下创建BUILD.gn、配置文件bundle.json以及src文件夹,在src文件夹下构建一个veml7700.c文件,实现调用veml7700光感传感器HDF框架HDI。
|—— samples                  // 子系统  
		|—— include              // 部件共有配置
		|—— gpio                 // gpio接口的传感器应用层程序归纳 
		|—— uart                 // uart接口的传感器应用层程序归纳
		|—— sensor_i2c           //  i2c接口的传感器应用层程序归纳
				|—— src				       // 存放源文件
						|—— veml7700.c   // veml7700传感器应用层程序
						|—— BUILD.gn     // GN文件
						|—— bundle.json  // 声明文件
  1. 构建budle.json文件,文件包含两个部分,第一部分描述该部件所属子系统的信息,第二部分component则定义该部件构建相关配置。下图是构建budle.json文件的三个关键点,后续修改其他部件测试用例,只用修改这三点即可:①destPath,描述的是改json文件所在的目录;②name,描述的是上级目录名字;subsystem,描述的是子系统文件目录名称;③build,sub_component,描述的是需要编译的组件名称,用于找到BUILD.gn里面的组件。

在这里插入图片描述

 下图为BUILD.gn模板

在这里插入图片描述

  1. 构建BUILD.gn文件,改文件是用于在 OpenHarmony 的构建系统中定义如何构建模块的脚本文件。它通常包含了模块的名称、源文件、头文件路径等信息,以及模块的编译方式和依赖关系。下图是构建build.json文件的四个要点,后续添加其他部件只需要修改这个四点即可:①ohos_executable添加可执行ohos模块,在终端输入hdc shell,输入led_gpio命令即可运行sources路径下的文件;②实现测试业务代码的路径;③头文件的路径;④所属部件名称。

在这里插入图片描述

  1. 将测试用例框架,加入编译中,修改产品配置文件,在 \\vendor\openvalley\rk3568\config.json 中添加对应子系统和对应需要编译的部件,subsystem配置为json里subsystem名称,component配置为sensors_i2c部件名称。
{
      "subsystem": "samples",
      "components": [
        {
          "component": "gpio",
          "features": []
        },
        {
          "component": "sensors_i2c",
          "features": []
        },
        {
          "component": "uart",
          "features": []
        }
      ]
    }      
  1. 修改子系统配置文件,在 \\build\subsystem_config.json 中添加新建的子系统的配置,本案例path配置samples文件夹所在路径, name与json里subsystem名称匹配即可。
 "samples":{
  "path": "samples",
  "name": "samples"

3.1.3 业务代码

本测试业务代码主要实现调用Sensors HDF框架的HDI接口,实现读取jy901b 10轴传感器数据。

主要实现以下几个功能

  1. 创建传感器接口实例
 		//1.创建传感器接口实例
    const struct SensorInterface *sensorDev = NewSensorInterfaceInstance();
    if (sensorDev == NULL) {
        return;
    }
    printf("NewSensorInterfaceInstance success\n");
  1. 订阅者注册传感器数据回调处理函数,串口打印从HDI接口拿到的数据。
    //2.订阅者注册传感器数据回调处理函数
		ret = sensorDev->Register(TRADITIONAL_SENSOR_TYPE, SensorDataCallback);
    if (ret != 0) {
        return;
    }
    printf("Register success\n");
    
		//创建回调函数
		static int32_t SensorDataCallback(const struct SensorEvents *event)
		{
		    if (event == NULL || event->data == NULL || event->dataLen < sizeof(uint32_t)) {
		        printf("event == NULL\n");
		        return HDF_FAILURE; // Check for NULL event, NULL data, and ensure enough data size
		    }
		    printf("sensor id [%d], data : %d\n", event->sensorId, *(event->data));
		    printf("Sht30Callback\n\r");
		
		    return HDF_SUCCESS;
		}
		
  1. 获取设备支持的Sensor列表。
    ret = sensorDev->GetAllSensors(&sensorInfo, &count);
    if (ret != 0) {
        return;
    }

    printf("GetAllSensors count: %d\n", count);

    for (int i = 0; i < count; i++)
    {
        printf("sensor [%d] : sensorName:%s, vendorName:%s, sensorTypeId:%d, sensorId:%d\n", i,
               sensorInfo[i].sensorName, sensorInfo[i].vendorName, sensorInfo[i].sensorTypeId, sensorInfo[i].sensorId);
    }
  1. 设置传感器采样率。
 		  ret = sensorDev->SetBatch(sensorInfo[i].sensorId, sensorInterval, reportInterval);
      if (ret != 0) {
          printf("SetBatch failed\n ,ret: %d",ret);
          continue;
      }
      printf("SetBatch success\n");
  1. 使能、去使能传感器。
      // 5.使能传感器 
      ret = sensorDev->Enable(sensorInfo[i].sensorId);
      if (ret != 0) {
          continue;
      }
      printf("Enable success\n");
      
      OsalSleep(10);
      
      //6.去使能传感器 
      ret = sensorDev->Disable(sensorInfo[i].sensorId);
      if (ret != 0) {
          continue;
      }
      printf("Disable success\n");
  1. 取消传感器数据订阅函数、释放传感器接口实例。
     //7.取消传感器数据订阅函数 
     ret = sensorDev->Unregister(TRADITIONAL_SENSOR_TYPE, SensorDataCallback);
     if (ret != 0) {
        return;
     }
     printf("Unregister success\n");

     //8.释放传感器接口实例 
     ret = FreeSensorInterfaceInstance();
     if (ret != 0) {
        return;
     }
     printf("FreeSensorInterfaceInstance success\n");

注意事项

  1. 设备树配置I2C、GPIO、UART等接口的时候,要注意是否I2C引脚是否被占用,在终端输入hdc shell,输入

cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pinmux-pins

即可查看引脚复用情况。

3.2 基于HDF的驱动开发流程

3.2.1 添加驱动配置

添加驱动配置共分为两部分,一部分是驱动设备描述,用于驱动管理将抽象驱动和私有化驱动加载,拉起来,如果单板在启动的时候没有拉起来传感器设备,可能是驱动设备描述配置不对。另一部分是驱动私有配置信息,将私有化传感器驱动加载之后,一些传感器配置(总线、寄存器等配置)需要通过这个配置文件加载进来,如果单板在启动的时候,加载了设备,但是在解析配置文件的时候出现了错误,可能是改私有化驱动配置有问题。因为现在标准系统的HCS加载与解析机制是直接将HCS文件打包成HCB文件,传感器管理器会直接调用解析接口来解析HCB文件,并不会编译并报错有错误的地方,所以在添加驱动配置(HCS)文件的时候,要特别注意格式。接下来详细的描述以下以VEML7700传感器如何添加驱动配置。

  1. 驱动设备描述

该文件在 \\vendor\openvalley\rk3568\hdf_config\khdf\device_info\device_info.hcs,需要配置传感器管理器配置、抽象传感器配置、私有化传感器配置。

    // 传感器管理器配置
    sensor :: host {
        hostName = "sensor_host";
        device_sensor_manager :: device {
            device0 :: deviceNode {
                policy = 2;
                priority = 60;
                preload = 0;
                permission = 0664;
                moduleName = "HDF_SENSOR_MGR_AP";
                serviceName = "hdf_sensor_manager_ap";
            }
        }
     // 抽象传感器配置
	   device_sensor_als :: device {
              device0 :: deviceNode {
                  policy = 2;
                  priority = 110;
                  preload = 0;
                  permission = 0664;
                  moduleName = "HDF_SENSOR_ALS";
                  serviceName = "hdf_sensor_als";
                  deviceMatchAttr = "hdf_sensor_als_driver";
                }
            }
     // 私有化传感器配置
	   device_sensor_veml7700 :: device {
              device0 :: deviceNode {
                  policy = 2;
                  priority = 120;
                  preload = 0;
                  permission = 0664;
                  moduleName = "HDF_SENSOR_ALS_VEML7700";
                  serviceName = "hdf_als_veml7700";
                  deviceMatchAttr = "hdf_sensor_als_veml7700_driver";
                }
            }

**注意:**moduleName字段要与HDF框架里的moduleName字段一致,否则不会加载该传感器驱动。

讲一下policy 、preload 和 priority 三个字段的功能

policy:驱动服务管理

  • policy配置为0,驱动不提供服务
  • policy配置为1,驱动对内核态发布服务
  • policy配置为2,驱动对内核态和用户态都发布服务
  • policy配置为3,驱动服务不对外发布,但可以被订阅
  • policy配置为4,驱动私有服务不对外发布,也不能被订阅

preload:驱动加载

  • preload配置为0,则系统启动过程中默认加载
  • preload配置为1,当系统支持快速启动的时候,则在系统完成之后再加载这一类驱动
  • preload配置为2,则系统启动过程中默认不加载,支持后续动态加载,当用户态获取驱动服务消息机制时,如果驱动服务不在,HDF框架会尝试动态加载该驱动
  • 当驱动服务异常退出的时候,preload为0或者1的驱动服务,由启动模块拉起host并重新加载服务。preload为2的驱动服务,需要业务模块注册HDF的服务状态监听器,当收到服务退出消息时,业务模块调用LoadDevice重新加载服务。

priority:按序加载

  • priority取值范围为整数0~200,用来表示host和驱动的优先级,值越小,驱动加载优先级越高。

  1. 驱动私有化配置

VEML7700传感器私有化配置文件在 \\vendor\openvalley\rk3568\hdf_config\khdf\sensor\accel\jy901b_config.hcs,这里配置的东西比较多,会逐一详细解释一下如何配置。

共需要用到三个节点配置,sensorInfo、sensorBusConfig和sensorRegConfig。

  • 配置sensorInfo
#include "../sensor_common.hcs"
root {
    als_veml7700_chip_config : sensorConfig {
        match_attr = "hdf_sensor_als_veml7700_driver";
        sensorInfo :: sensorDeviceInfo {
            sensorName = "als";
            vendorName = "veml7700"; // max string length is 16 bytes
            sensorTypeId = 5; // enum SensorTypeTag
            sensorId = 5; // user define sensor id
            power = 230;
            minDelay = 0;
            maxDelay = 0;
        }
  1. match_attr要与驱动设备描述一致,否则不会加载该驱动私有化配置。
  2. sensorTypeId在HDF驱动中有枚举定义,按照定义设置TypedId,sensorId则不做要求。
  • 配置sensorBusConfig
        sensorBusConfig :: sensorBusInfo {
            busType = 0; // 0:i2c 1:spi
            busNum = 1;
            busAddr = 0x10;
            regWidth = 1; // 1 byte
        }
  1. busType 0 为 i2c,1为spi。
  2. busNum字段则为总线几。
  3. busAddr为从设备地址,需要查看设备数据手册进行确认,如果是i2c设备可以使用i2cdetect 工具进行确认从设备地址。
  4. regWidth为寄存器位数,查看设备数据手册进行确认。
  • 配置sensorIdAttr
        sensorIdAttr :: sensorIdInfo {
            chipName = "veml7700";
            chipIdRegister = 0x07;
            chipIdValue = 0x81;
        }

该配置是根据数据手册,查看chipID寄存器进行配置,如果没有chipID寄存器,可以不配置。

  • 配置sensorRegConfig
sensorRegConfig {
            /* regAddr, value, mask, len, delay, opsType, calType, shiftNum, debug, save */
            // users can configure value in enableSeqConfig to make another gain or time.
            initSeqConfig = [
                0x00,   0x0,  0x0,   0,     5,       2,       0,        0,     0,    0
            ];
            enableSeqConfig = [
                0x00,   0x0,  0x1,   1,     5,       2,       0,        0,     0,    0
            ];
            disableSeqConfig = [
                0x00,   0x1,  0x1,   1,     5,       2,       0,        0,     0,    0
            ];
            
        }
        extendAlsRegConfig {
            /* regAddr, value, mask, len, delay, opsType, calType, shiftNum, debug, save */
            time = [
                0x00,    0x0300, 0x03c0,   2,     5,       0,       1,        0,     0,    0,  //25msec
                0x00,    0x0200, 0x03c0,   2,     5,       0,       1,        0,     0,    0,  //50msec
                0x00,    0x0000, 0x03c0,   2,     5,       0,       1,        0,     0,    0,  //100msec
                0x00,    0x0040, 0x03c0,   2,     5,       0,       1,        0,     0,    0,  //200msec
                0x00,    0x0080, 0x03c0,   2,     5,       0,       1,        0,     0,    0,  //400msec
                0x00,    0x00c0, 0x03c0,   2,     5,       0,       1,        0,     0,    0,  //800msec
            ];
            gain = [
                0x00,    0x0000, 0x1800,   2,     5,       0,       1,        0,     0,    0,  //1X
                0x00,    0x0800, 0x1800,   2,     5,       0,       1,        0,     0,    0,  //2X
                0x00,    0x1000, 0x1800,   2,     5,       0,       1,        0,     0,    0,  //1/8
                0x00,    0x1800, 0x1800,   2,     5,       0,       1,        0,     0,    0   //1/8
            ];
        }
    }
}

寄存器需要配置10个参数

  1. regAddr:寄存器地址,查阅设备数据手册
  2. value:寄存器传入的值,8位和16位的
  3. mask:掩码值,因为一个寄存器可能包括多个命令,一个命令的执行,就需要mask值来进行位运算
  4. len:value的长度,8位长度为1,16位的为2
  5. delay:单位为ms,配置寄存器的延时,一般配置为5ms
  6. opsType:寄存器操作码,0-空,1-读,2-写,3-读_检查,4-更新位
  7. calType:寄存器位操作码,0-空,1-设置,2-归位,4-加,5-左移,5-右移
  8. shiftNum:移位数
  9. debug:0-no,1-yes
  10. save:0-no,1-yes

VEML7700 10轴传感器设置了三种寄存器种类配置

  1. initSeqConfig:初始化寄存器配置,根据数据手册初始化寄存器配置进行设置,reset寄存器
  2. enableSeqConfig:配置功能寄存器,通过该寄存器配置特定的time、gain两个参数
  3. disableSeqConfig:睡眠寄存器配置
  4. extendAlsRegConfig:扩展寄存器配置,光照传感器的值需要根据设置的time、gain进行计算

注意:需要在 \\vendor\openvalley\rk3568\hdf_config\khdf\hdf.hcs 添加 #include "als/veml7700_config.hcs”,才会将该私有化配置文件编译成HCB

3.2.2 驱动编译脚本编写

drivers\hdf_core\adapter\khdf\linux\model\sensor\Makefile 中添加私有传感器驱动和抽象传感器驱动文件编译路径

obj-$(CONFIG_DRIVERS_HDF_SENSOR_ALS) += $(SENSOR_ROOT_DIR)/als/sensor_als_driver.o
obj-$(CONFIG_DRIVERS_HDF_SENSOR_ALS_VEML7700) += $(SENSOR_ROOT_CHIPSET)/chipset/als/als_veml7700.o

ccflags-y += -I$(srctree)/drivers/hdf/framework/model/sensor/driver/als \
             -I$(srctree)/drivers/hdf/peripheral/sensor/chipset/als \

添加drivers\hdf_core\adapter\khdf\linux\model\sensor\Kconfig 宏定义配置文件

config DRIVERS_HDF_SENSOR_ALS_VEML7700
    bool "Enable HDF als veml7700 sensor driver"
    default n
    depends on DRIVERS_HDF_SENSOR_ALS
    help
      Answer Y to enable HDF als veml7700 sensor driver.

\\device\board\openvalley\rk3568\kernel\rockchip_linux_defconfig 打开宏定义配置

CONFIG_DRIVERS_HDF_SENSOR_ALS=y
CONFIG_DRIVERS_HDF_SENSOR_ALS_VEML7700=y

3.2.3驱动实现

驱动共包括两部分,抽象化驱动实现和私有化驱动实现。

抽象化驱动实现流程

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

  • 光照传感器驱动入口函数实现
struct HdfDriverEntry g_sensorAlsDevEntry = {
    .moduleVersion = 1,
    .moduleName = "HDF_SENSOR_ALS",
    .Bind = AlsBindDriver,
    .Init = AlsInitDriver,
    .Release = AlsReleaseDriver,
};
HDF_INIT(g_sensorAlsDevEntry);
  • 光照传感器驱动操作接口实现
// 绑定ALS驱动,主要绑定dispatch接口、服务接口
// 光照传感器驱动对外提供的服务绑定到HDF框架
int32_t AlsBindDriver(struct HdfDeviceObject *device)
{
    HDF_LOGI("%s: AlsBindDriver Enter\n", __func__);
    CHECK_NULL_PTR_RETURN_VALUE(device, HDF_ERR_INVALID_PARAM);

    struct AlsDrvData *drvData = (struct AlsDrvData *)OsalMemCalloc(sizeof(*drvData));
    if (drvData == NULL) {
        HDF_LOGE("%s: Malloc als drv data fail!", __func__);
        return HDF_ERR_MALLOC_FAIL;
    }

    drvData->ioService.Dispatch = DispatchAls;
    drvData->device = device;
    device->service = &drvData->ioService;
    g_alsDrvData = drvData;
    return HDF_SUCCESS;
}
// 光感传感器驱动初始化入口函数,主要功能为对传感器私有数据的结构体对象进行初始化,传感器HCS数据配置对象空间分配,
// 传感器HCS数据配置初始化入口函数调用,传感器设备探测是否在位功能,传感器数据上报定时器创建,传感器归一化接口注册,传感器设备注册功能 
int32_t AlsInitDriver(struct HdfDeviceObject *device)
{
    CHECK_NULL_PTR_RETURN_VALUE(device, HDF_ERR_INVALID_PARAM);
    struct AlsDrvData *drvData = (struct AlsDrvData *)device->service;
    CHECK_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_INVALID_PARAM);
    // 工作队列资源初始化
    if (InitAlsData(drvData) != HDF_SUCCESS) {
        HDF_LOGE("%s: Init als config failed", __func__);
        return HDF_FAILURE;
    }
		// 分配光感传感器配置信息资源
    drvData->alsCfg = (struct SensorCfgData *)OsalMemCalloc(sizeof(*drvData->alsCfg));
    if (drvData->alsCfg == NULL) {
        HDF_LOGE("%s: Malloc als config data failed", __func__);
        return HDF_FAILURE;
    }
		// 注册寄存器分组信息
    drvData->alsCfg->regCfgGroup = &g_regCfgGroup[0];
    drvData->alsCfg->extendedRegCfgGroup = &g_extendAlsRegCfgGroup[0];
    HDF_LOGI("%s: Init Als driver success", __func__);
    return HDF_SUCCESS;
}
// 释放ALS驱动初始化分配的资源
void AlsReleaseDriver(struct HdfDeviceObject *device)
{
    CHECK_NULL_PTR_RETURN(device);

    struct AlsDrvData *drvData = (struct AlsDrvData *)device->service;
    CHECK_NULL_PTR_RETURN(drvData);
		// 器件在位,释放已分配资源
    if (drvData->detectFlag && drvData->alsCfg != NULL) {
        AlsReleaseCfgData(drvData->alsCfg);
    }

    OsalMemFree(drvData->alsCfg);
    drvData->alsCfg = NULL;
    // 器件在位,销毁工作队列资源
#ifndef __LITEOS__
    HdfWorkDestroy(&drvData->alsWork);
    HdfWorkQueueDestroy(&drvData->alsWorkQueue);
#endif
    OsalMemFree(drvData);
}
// 提供给私有化驱动的初始化接口,完成光照传感器器件基本配置信息解析(光照信息、总线配置、光照传感器探测寄存器)
static int32_t InitAlsAfterDetected(struct SensorCfgData *config)
{
    struct SensorDeviceInfo deviceInfo;
    CHECK_NULL_PTR_RETURN_VALUE(config, HDF_ERR_INVALID_PARAM);

    if (InitAlsOps(config, &deviceInfo) != HDF_SUCCESS) {
        HDF_LOGE("%s: Init als ops failed", __func__);
        return HDF_FAILURE;
    }

    if (AddSensorDevice(&deviceInfo) != HDF_SUCCESS) {
        HDF_LOGE("%s: Add als device failed", __func__);
        return HDF_FAILURE;
    }

    if (ParseSensorRegConfig(config) != HDF_SUCCESS) {
        HDF_LOGE("%s: Parse sensor register failed", __func__);
        goto SENSOR_REG_CONFIG_EXIT;
    }

    if (ParseExtendedAlsRegConfig(config) != HDF_SUCCESS) {
        HDF_LOGE("%s: Parse sensor extendedRegCfgGroup register failed", __func__);
        goto EXTENDED_ALS_REG_CONFIG_EXIT;
    }

    return HDF_SUCCESS;

EXTENDED_ALS_REG_CONFIG_EXIT:
    ReleaseSensorAllRegConfig(config);
SENSOR_REG_CONFIG_EXIT:
    (void)DeleteSensorDevice(&config->sensorInfo);

    return HDF_FAILURE;
}
// 注册光感传感器驱动归一化的接口函数
static int32_t InitAlsOps(struct SensorCfgData *config, struct SensorDeviceInfo *deviceInfo)
{
    CHECK_NULL_PTR_RETURN_VALUE(config, HDF_ERR_INVALID_PARAM);

    deviceInfo->ops.Enable = SetAlsEnable;
    deviceInfo->ops.Disable = SetAlsDisable;
    deviceInfo->ops.SetBatch = SetAlsBatch;
    deviceInfo->ops.SetMode = SetAlsMode;
    deviceInfo->ops.SetOption = SetAlsOption;
    deviceInfo->ops.ReadSensorData = ReadAlsData;

    if (memcpy_s(&deviceInfo->sensorInfo, sizeof(deviceInfo->sensorInfo),
        &config->sensorInfo, sizeof(config->sensorInfo)) != EOK) {
        HDF_LOGE("%s: Copy sensor info failed", __func__);
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}
  • 完成光感传感器抽象驱动内部接口开发,包括Enable、Disable、SetBatch、SetMode、SetOption、AlsCreateCfgData、AlsReleaseCfgData、AlsRegiseterChipOps等接口实现
// 下发使能寄存器组的配置
static int32_t SetAlsEnable(void)
{
    int32_t ret;
    struct AlsDrvData *drvData = AlsGetDrvData();

    CHECK_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_INVALID_PARAM);
    CHECK_NULL_PTR_RETURN_VALUE(drvData->alsCfg, HDF_ERR_INVALID_PARAM);

    if (drvData->enable) {
        HDF_LOGE("%s: Als sensor is enabled", __func__);
        return HDF_SUCCESS;
    }

    ret = SetSensorRegCfgArray(&drvData->alsCfg->busCfg, drvData->alsCfg->regCfgGroup[SENSOR_ENABLE_GROUP]);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: Als sensor enable config failed", __func__);
        return ret;
    }
#ifndef __LITEOS__
    ret = OsalTimerCreate(&drvData->alsTimer, SENSOR_TIMER_MIN_TIME, AlsTimerEntry, (uintptr_t)drvData);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: Als create timer failed[%d]", __func__, ret);
        return ret;
    }

    ret = OsalTimerStartLoop(&drvData->alsTimer);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: Als start timer failed[%d]", __func__, ret);
        return ret;
    }
#endif
    drvData->enable = true;

    return HDF_SUCCESS;
}
// 下发去使能寄存器的
static int32_t SetAlsDisable(void)
{
    int32_t ret;
    struct AlsDrvData *drvData = AlsGetDrvData();

    CHECK_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_INVALID_PARAM);
    CHECK_NULL_PTR_RETURN_VALUE(drvData->alsCfg, HDF_ERR_INVALID_PARAM);

    if (!drvData->enable) {
        HDF_LOGE("%s: Als sensor had disable", __func__);
        return HDF_SUCCESS;
    }

    ret = SetSensorRegCfgArray(&drvData->alsCfg->busCfg, drvData->alsCfg->regCfgGroup[SENSOR_DISABLE_GROUP]);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: Als sensor disable config failed", __func__);
        return ret;
    }
#ifndef __LITEOS__
    ret = OsalTimerDelete(&drvData->alsTimer);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: Als delete timer failed", __func__);
        return ret;
    }
#endif
    drvData->enable = false;

    return HDF_SUCCESS;
}
// 配置传感器采样率和数据上报间隔
static int32_t SetAlsBatch(int64_t samplingInterval, int64_t interval)
{
    (void)interval;

    struct AlsDrvData *drvData = NULL;

    drvData = AlsGetDrvData();
    CHECK_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_INVALID_PARAM);

    drvData->interval = samplingInterval;

    return HDF_SUCCESS;
}
// 配置传感器工作模式,当前支持实时模式
static int32_t SetAlsMode(int32_t mode)
{
    if (mode <= SENSOR_WORK_MODE_DEFAULT || mode >= SENSOR_WORK_MODE_MAX) {
        HDF_LOGE("%s: The current mode is not supported", __func__);
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}
// 配置传感器可选配置
static int32_t DispatchAls(struct HdfDeviceIoClient *client,
    int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    (void)client;
    (void)cmd;
    (void)data;
    (void)reply;

    return HDF_SUCCESS;
}
// 创建传感器配置数据接口
struct SensorCfgData *AlsCreateCfgData(const struct DeviceResourceNode *node)
{
    HDF_LOGI("%s: AlsCreateCfgData Enter\n", __func__);
    struct AlsDrvData *drvData = AlsGetDrvData();

    if (drvData == NULL || node == NULL) {
        HDF_LOGE("%s: Als node pointer NULL", __func__);
        return NULL;
    }
		// 如果探测不到器件在位,返回进行下个器件探测
    if (drvData->detectFlag) {
        HDF_LOGE("%s: Als sensor have detected", __func__);
        return NULL;
    }

    if (drvData->alsCfg == NULL) {
        HDF_LOGE("%s: Als alsCfg pointer NULL", __func__);
        return NULL;
    }
		// 解析器件HCS私有配置信息
    if (GetSensorBaseConfigData(node, drvData->alsCfg) != HDF_SUCCESS) {
        HDF_LOGE("%s: Get sensor base config failed", __func__);
        goto BASE_CONFIG_EXIT;
    }
		// 如果探测不到器件在位,返回进行下个器件探测
    if (DetectSensorDevice(drvData->alsCfg) != HDF_SUCCESS) {
        HDF_LOGI("%s: Als sensor detect device no exist", __func__);
        drvData->detectFlag = false;
        goto BASE_CONFIG_EXIT;
    }

    drvData->detectFlag = true;
    // 器件寄存器解析
    if (InitAlsAfterDetected(drvData->alsCfg) != HDF_SUCCESS) {
        HDF_LOGE("%s: Als sensor detect device no exist", __func__);
        goto INIT_EXIT;
    }

    return drvData->alsCfg;

INIT_EXIT:
    (void)ReleaseSensorBusHandle(&drvData->alsCfg->busCfg);
BASE_CONFIG_EXIT:
    drvData->alsCfg->root = NULL;
    (void)memset_s(&drvData->alsCfg->sensorInfo, sizeof(struct SensorBasicInfo), 0, sizeof(struct SensorBasicInfo));
    (void)memset_s(&drvData->alsCfg->busCfg, sizeof(struct SensorBusCfg), 0, sizeof(struct SensorBusCfg));
    (void)memset_s(&drvData->alsCfg->sensorAttr, sizeof(struct SensorAttr), 0, sizeof(struct SensorAttr));
    return drvData->alsCfg;
}
// 释放传感器配置数据接口
void AlsReleaseCfgData(struct SensorCfgData *alsCfg)
{
    CHECK_NULL_PTR_RETURN(alsCfg);

    (void)DeleteSensorDevice(&alsCfg->sensorInfo);
    ReleaseSensorAllRegConfig(alsCfg);
    ReleaseExtendedAlsRegConfig(alsCfg);
    (void)ReleaseSensorBusHandle(&alsCfg->busCfg);

    alsCfg->root = NULL;
    (void)memset_s(&alsCfg->sensorInfo, sizeof(struct SensorBasicInfo), 0, sizeof(struct SensorBasicInfo));
    (void)memset_s(&alsCfg->busCfg, sizeof(struct SensorBusCfg), 0, sizeof(struct SensorBusCfg));
    (void)memset_s(&alsCfg->sensorAttr, sizeof(struct SensorAttr), 0, sizeof(struct SensorAttr));
}
// 注册传感器差异化接口
int32_t AlsRegisterChipOps(const struct AlsOpsCall *ops)
{
    struct AlsDrvData *drvData = AlsGetDrvData();

    CHECK_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_INVALID_PARAM);
    CHECK_NULL_PTR_RETURN_VALUE(ops, HDF_ERR_INVALID_PARAM);

    drvData->ops.Init = ops->Init;
    drvData->ops.ReadData = ops->ReadData;
    return HDF_SUCCESS;
}
  • 读数据接口开发
static int32_t ReadAlsData(struct SensorReportEvent *events)
{
    struct AlsDrvData *drvData = AlsGetDrvData();

    if (drvData->ops.ReadData == NULL) {
        HDF_LOGE("%s: Als ReadSensorData function NULl", __func__);
        return HDF_FAILURE;
    }
    
    // ReadData为私有化传感器实现配置
    if (drvData->ops.ReadData(drvData->alsCfg, events) != HDF_SUCCESS) {
        HDF_LOGE("%s: Als read data failed", __func__);
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}

私有化驱动实现流程

  1. 基于HDF驱动框架,按照驱动Driver Entry程序,完成光感传感器差异化驱动开发,主要由Bind、Init、Release、Dispatch函数接口实现
// 光感传感器私有化驱动消息交互
static int32_t DispatchVEML7700(struct HdfDeviceIoClient *client,
    int cmd, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    (void)client;
    (void)cmd;
    (void)data;
    (void)reply;

    return HDF_SUCCESS;
}
// 光感传感器私有化驱动对外提供的服务绑定到HDF框架
static int32_t Veml7700BindDriver(struct HdfDeviceObject *device)
{
    CHECK_NULL_PTR_RETURN_VALUE(device, HDF_ERR_INVALID_PARAM);

    struct Veml7700DrvData *drvData = (struct Veml7700DrvData *)OsalMemCalloc(sizeof(*drvData));
    if (drvData == NULL) {
        HDF_LOGE("%s: veml7700::Malloc Veml7700 drv data fail", __func__);
        return HDF_ERR_MALLOC_FAIL;
    }

    drvData->ioService.Dispatch = DispatchVEML7700;
    drvData->device = device;
    device->service = &drvData->ioService;
    g_veml7700DrvData = drvData;

    return HDF_SUCCESS;
}
// 光感传感器私有化驱动初始化
static int32_t Veml7700InitDriver(struct HdfDeviceObject *device)
{
    int32_t ret;
    struct AlsOpsCall ops;

    CHECK_NULL_PTR_RETURN_VALUE(device, HDF_ERR_INVALID_PARAM);
    struct Veml7700DrvData *drvData = (struct Veml7700DrvData *)device->service;
    CHECK_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_INVALID_PARAM);

    // 创建配置数据
    drvData->sensorCfg = AlsCreateCfgData(device->property);
    if (drvData->sensorCfg == NULL || drvData->sensorCfg->root == NULL) {
        HDF_LOGD("%s: veml7700::Creating alscfg failed because detection failed", __func__);
        return HDF_ERR_NOT_SUPPORT;
    }

    ops.Init = NULL;
    ops.ReadData = ReadVeml7700Data;
    ret = AlsRegisterChipOps(&ops);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: Register VEML7700 als failed", __func__);
        return HDF_FAILURE;
    }

    ret = InitVeml7700(drvData->sensorCfg);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: Init VEML7700 als failed", __func__);
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}
// 释放驱动初始化时分配的资源
static void Veml7700ReleaseDriver(struct HdfDeviceObject *device)
{
    CHECK_NULL_PTR_RETURN(device);

    struct Veml7700DrvData *drvData = (struct Veml7700DrvData *)device->service;
    CHECK_NULL_PTR_RETURN(drvData);

    if (drvData->sensorCfg != NULL) {
        AlsReleaseCfgData(drvData->sensorCfg);
        drvData->sensorCfg = NULL;
    }
    OsalMemFree(drvData);
}
// 光感传感器差异化驱动对应的HdfDriverEntry对象
struct HdfDriverEntry g_alsVeml7700DevEntry = {
    .moduleVersion = 1,
    .moduleName = "HDF_SENSOR_ALS_VEML7700",
    .Bind = Veml7700BindDriver,
    .Init = Veml7700InitDriver,
    .Release = Veml7700ReleaseDriver,
};

HDF_INIT(g_alsVeml7700DevEntry);
  1. 完成光感传感器私有化驱动中差异化接口ReadData函数实现
// 读原始数据接口,寄存器数据转化
static int32_t ReadVeml7700RawData(struct SensorCfgData *data, struct VEML7700AlsData *rawData, uint64_t *timestamp)
{
    HDF_LOGI("%s: ReadVeml7700RawData enter!", __func__);
    uint8_t reg_high_raw[VEML7700_TEMP_DATA_BUF_LEN] = { 0 };
    uint8_t reg_white_channel[VEML7700_TEMP_DATA_BUF_LEN] = { 0 };
    OsalTimespec time;
    int32_t ret = HDF_SUCCESS;
    uint16_t Value_high_raw;
    uint16_t Value_white_channel;

    (void)memset_s(&time, sizeof(time), 0, sizeof(time));
    (void)memset_s(reg_high_raw, sizeof(reg_high_raw), 0, sizeof(reg_high_raw));
    (void)memset_s(reg_white_channel, sizeof(reg_white_channel), 0, sizeof(reg_white_channel));

    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;

    // 读高精度ALS寄存器值
    ret = ReadSensor(&data->busCfg, VEML7700_ALS_HIGH_RESOLUTION_ADDR, reg_high_raw, sizeof(reg_high_raw));
    CHECK_PARSER_RESULT_RETURN_VALUE(ret, "read data");

    // 读白通道寄存器值,暂未用到
    ret = ReadSensor(&data->busCfg, VEML7700_ALS_WHITE_CHANNEL_ADDR, reg_white_channel, sizeof(reg_white_channel));
    CHECK_PARSER_RESULT_RETURN_VALUE(ret, "read data");

    // 数值转化
    Value_high_raw = reg_high_raw[VEML7700_TEMP_VALUE_IDX_ONE];
    Value_high_raw <<= SENSOR_DATA_WIDTH_8_BIT;
    Value_high_raw |= reg_high_raw[VEML7700_TEMP_VALUE_IDX_ZERO];
    rawData->als = (uint16_t)Value_high_raw;

    return HDF_SUCCESS;
}
// 调用转换接口,实现原始数据转化成流明值
static int32_t ReadVeml7700Data(struct SensorCfgData *data, struct SensorReportEvent *event)
{
    HDF_LOGI("%s: ReadVeml7700RawData enter!", __func__);
    int32_t ret;
    uint32_t tmp[ALS_VEML7700_NUM];
    struct VEML7700AlsData rawData = { 0 };
    static struct AlsReportData reportData;

    ret = ReadVeml7700RawData(data, &rawData, &event->timestamp);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: BH1745 read raw data failed", __func__);
        return HDF_FAILURE;
    }
    tmp[ALS_HIGH_DATA] = rawData.als;
    event->sensorId = SENSOR_TAG_AMBIENT_LIGHT;
    event->option = 0;
    event->mode = SENSOR_WORK_MODE_REALTIME;
    
    // 流明值转化 
    ret = RawDataConvert(data, &reportData, tmp[ALS_HIGH_DATA]);
    CHECK_PARSER_RESULT_RETURN_VALUE(ret, "RawDataConvert");

    event->dataLen = sizeof(reportData.als);
    HDF_LOGI("%s: lux data = [%u]", __func__, reportData.als);
    HDF_LOGI("%s: lux datalen = [%u]", __func__, event->dataLen);
    event->data = (uint8_t *)&reportData.als;

    // 调试信息
    // HDF_LOGI("%s: address = [%p]", __func__, (void *)&reportData.als);
    // uint32_t sensorData = *(uint32_t *)(event->data);
    // HDF_LOGI("%s: sensorData = [%d]", __func__, sensorData);
    return ret;
}
  1. 完成根据寄存器配置实现数据转化
// 配置gain和time参数结构体
static struct TimeRegAddrValueMap g_timeMap[EXTENDED_ALS_TIME_GROUP_INDEX_MAX] = {
    { EXTENDED_ALS_TIME_GROUP_ATTR_VALUE_0,           VEML7700_TIME_25MSEC },
    { EXTENDED_ALS_TIME_GROUP_ATTR_VALUE_1,           VEML7700_TIME_50MSEC },
    { EXTENDED_ALS_TIME_GROUP_ATTR_VALUE_2,           VEML7700_TIME_100MSEC },
    { EXTENDED_ALS_TIME_GROUP_ATTR_VALUE_3,           VEML7700_TIME_200MSEC },
    { EXTENDED_ALS_TIME_GROUP_ATTR_VALUE_4,           VEML7700_TIME_400MSEC },
    { EXTENDED_ALS_TIME_GROUP_ATTR_VALUE_5,           VEML7700_TIME_800MSEC }
};

static struct GainRegAddrValueMap g_gainMap[EXTENDED_ALS_GAIN_GROUP_INDEX_MAX] = {
    { EXTENDED_ALS_GAIN_GROUP_ATTR_VALUE_0,              VEML7700_GAIN_1X },
    { EXTENDED_ALS_GAIN_GROUP_ATTR_VALUE_1,              VEML7700_GAIN_2X },
    { EXTENDED_ALS_GAIN_GROUP_ATTR_VALUE_2,              VEML7700_GAIN_1_8 },
    { EXTENDED_ALS_GAIN_GROUP_ATTR_VALUE_3,              VEML7700_GAIN_1_4 }
};
// 根据寄存器配置来计算流明值转化实现
static int32_t RawDataConvert(struct SensorCfgData *CfgData, struct AlsReportData *reportData, uint32_t als_data) 
{
    HDF_LOGI("%s: RawDataConvert enter!", __func__);
    int ret;
    uint32_t time;
    uint32_t gain;
    uint8_t regValue[2] = { 0 };
    uint16_t regValue_2bit;
    uint32_t index = 1;
    uint32_t luxTemp;
    uint8_t itemNum;
    struct SensorRegCfgGroupNode *groupNode = NULL;
    int32_t timeIndex = EXTENDED_ALS_TIME_GROUP_INDEX_0;
    int32_t gainIndex = EXTENDED_ALS_GAIN_GROUP_INDEX_0;

    groupNode = CfgData->extendedRegCfgGroup[EXTENDED_ALS_TIME_GROUP];
    itemNum = groupNode->itemNum;
    if (itemNum > EXTENDED_ALS_TIME_GROUP_INDEX_MAX) {
        HDF_LOGE("%s: ItemNum out of range ", __func__);
        return HDF_FAILURE;
    }

    ret = ReadSensorRegCfgArray(&CfgData->busCfg, groupNode, timeIndex, regValue, sizeof(regValue));
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: Failed to read sensor register array [timeIndex] ", __func__);
        return HDF_FAILURE;
    }

    regValue_2bit = (regValue[1] << 8) | regValue[0];
    // mask id 
    regValue_2bit &= groupNode->regCfgItem->mask;

    time = GetTimeByRegValue(regValue_2bit, g_timeMap, itemNum);

    memset(regValue, 0, sizeof(regValue));
    groupNode = CfgData->extendedRegCfgGroup[EXTENDED_ALS_GAIN_GROUP];
    itemNum = groupNode->itemNum;

    ret = ReadSensorRegCfgArray(&CfgData->busCfg, groupNode, gainIndex, regValue, sizeof(regValue));
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: Failed to read sensor register array [gainIndex]", __func__);
        return HDF_FAILURE;
    }
    regValue_2bit = (regValue[1] << 8) | regValue[0];
    // mask id 
    regValue_2bit &= groupNode->regCfgItem->mask;

    gain = GetGainByRegValue(regValue_2bit, g_gainMap, itemNum);
    HDF_LOGI("%s: GetTimeByRegValue= [%u]!", __func__, time);
    HDF_LOGI("%s: GetGainByRegValue= [%u]!", __func__, gain);
    if (gain == SENSOR_GAIN_INCREASE || time == SENSOR_TIME_INCREASE) {
        HDF_LOGI("%s: Failed to read sensor register array, gain = 0 or time = 0", __func__);
        return HDF_FAILURE;
    } else {
        // 计算公式:Light level(lx) = ALS OUTPUT DATA * (2/ALS_GAIN) * (800/ALS_IT) * 0.0036
        reportData->als = (als_data * (2 / (gain / 1000)) * (800 / time) * VEML7700_RESPONSIVITY) / 10000;
    }

    return HDF_SUCCESS;

}
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐