From aa0c1ef3e40c4299aebc7d91fc8de2365949f096 Mon Sep 17 00:00:00 2001 From: sunxuejiao Date: Tue, 30 Nov 2021 11:37:34 +0800 Subject: [PATCH 1/6] refresh sensor file Signed-off-by: sunxuejiao --- .../driver/driver-peripherals-sensor-des.md | 1109 ++++++++--------- 1 file changed, 534 insertions(+), 575 deletions(-) diff --git a/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md b/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md index a730125bec3..08212df25e0 100644 --- a/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md +++ b/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md @@ -1,8 +1,11 @@ # SENSOR - [概述](#section3634112111) -- [接口说明](#section20930112117478) -- [开发步骤](#section1140943382) + - [接口说明](#section188213414114) + +- [开发指导](#section1140943382) + - [开发步骤](#section7893102915819) + - [开发实例](#section257750691) - [测试指导](#section106021256121219) @@ -11,7 +14,7 @@ Sensor(传感器)驱动模块为上层Sensor服务系统提供稳定的Sensor基础能力API,包括Sensor列表查询、Sensor启停、Sensor订阅及去订阅,Sensor参数配置等功能;基于HDF(**H**ardware **D**river **F**oundation)驱动框架开发的Sensor驱动模型,实现跨操作系统迁移,器件差异配置等功能。Sensor驱动模型如下图1所示: **图 1** Sensor驱动模型图 -![](figures/Sensor驱动模型图.png "Sensor驱动模型图") +![](figure/Sensor驱动模型图.png "Sensor驱动模型图") Sensor驱动模型对外开放的API接口能力如下: @@ -19,13 +22,14 @@ Sensor驱动模型对外开放的API接口能力如下: - 提供Sensor驱动模型能力接口:依赖HDF驱动框架实现Sensor器件驱动的注册,加载,去注册,器件探测等能力,提供同一类型Sensor器件驱动归一接口, 寄存器配置解析操作接口,总线访问抽象接口,平台抽象接口。 - 提供开发者实现的能力接口:依赖HDF驱动框架的HCS\(**H**DF **C**onfiguration **S**ource\)配置管理,根据同类型Sensor差异化配置,实现Sensor器件参数序列化配置和器件部分操作接口,简化Sensor器件驱动开发。 -## 接口说明 +### 接口说明 Sensor驱动模型对HDI开放的API接口功能,参考表1。 **表 1** Sensor驱动模型对外API接口功能介绍 + - @@ -70,7 +74,7 @@ Sensor驱动模型对HDI开放的API接口功能,参考表1。 - @@ -95,11 +99,13 @@ Sensor驱动模型对HDI开放的API接口功能,参考表1。

功能分类

接口名

@@ -58,7 +62,7 @@ Sensor驱动模型对HDI开放的API接口功能,参考表1。

设置一种传感器的数据采样间隔和数据上报间隔。

int32_t SetMode(int32_t sensorTypeId, SensorUser *user, int32_t mode)

+

int32_t SetMode(int32_t sensorId, int32_t mode)

设置一种传感器的工作模式,不同的工作模式,上报数据方式不同。

数据订阅操作

int32_t Register(RecordDataCallback cb)

+

int32_t Register(RecordDataCallback cb);

订阅者注册传感器数据回调函数,系统会将获取到的传感器数据上报给订阅者。

+ Sensor驱动模型对驱动开发者开放的功能接口,驱动开发者无需实现,直接使用,参考表2: **表 2** Sensor驱动模型对驱动开发者开放的功能接口列表 + - @@ -125,7 +131,7 @@ Sensor驱动模型对驱动开发者开放的功能接口,驱动开发者无 - @@ -137,16 +143,6 @@ Sensor驱动模型对驱动开发者开放的功能接口,驱动开发者无 - - - - - -

功能分类

接口名

@@ -115,7 +121,7 @@ Sensor驱动模型对驱动开发者开放的功能接口,驱动开发者无

添加当前类型的传感器设备到传感器设备管理。

int32_t DeleteSensorDevice(int32_t sensorId)

+

int32_t DeleteSensorDevice(const struct SensorBasicInfo *sensorBaseInfo)

删除传感器设备管理里指定的传感器设备。

上报指定类型传感器的数据到用户侧。

Sensor抽象总线和平台操作接口

+

Sensor抽象总线

int32_t ReadSensor(struct SensorBusCfg *busCfg, uint16_t regAddr, uint8_t *data, uint16_t dataLen)

按照配置的总线方式,传感器配置数据写入寄存器。

int32_t CreateSensorThread(struct OsalThread *thread, OsalThreadEntry threadEntry, char *name, void *entryPara)

-

创建指定传感器的定时线程,用于传感器数据上报处理。

-

void DestroySensorThread(struct OsalThread *thread, uint8_t *status);

-

销毁传感器创建的定时线程。

-

通用配置操作接口

int32_t SetSensorRegCfgArray(struct SensorBusCfg *busCfg, const struct SensorRegCfgGroupNode *group);

@@ -185,11 +181,12 @@ Sensor驱动模型对驱动开发者开放的功能接口,驱动开发者无
-Sensor驱动模型要求驱动开发者实现的接口功能,参考表3。 +Sensor驱动模型要求驱动开发者实现的接口功能,参考表3 **表 3** Sensor驱动模型要求驱动开发者实现的接口列表 + - - - - - -

功能分类

接口名

@@ -202,22 +199,17 @@ Sensor驱动模型要求驱动开发者实现的接口功能,参考表3。

int32_t init(void)

传感器设备探测成功后,需要对传感器器设备初始化配置。

-

int32_t GetInfo(struct SensorBasicInfo *info)

-

从传感器设备的HCS配置里,获取当前传感器设备的基本信息。

+

传感器器设备探测成功后,需要对传感器器设备初始化配置。

int32_t Enable(void)

根据当前传感器设备的HCS配置,下发传感器设备使能操作组的寄存器配置。

+

根据当前传感器器设备的HCS配置,下发传感器设备使能操作组的寄存器配置。

int32_t Disable(void)

根据当前传感器设备的HCS配置,下发传感器设备去使能操作组的寄存器配置。

+

根据当前传感器器设备的HCS配置,下发传感器设备去使能操作组的寄存器配置。

int32_t SetBatch(int64_t samplingInterval, int64_t reportInterval)

@@ -243,16 +235,20 @@ Sensor驱动模型要求驱动开发者实现的接口功能,参考表3。
+ 接口实现参考[SENSOR](#section257750691)章节。 -## 开发步骤 +## 开发指导 Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不区分操作系统和芯片平台,为不同Sensor器件提供统一的驱动模型。本篇开发指导以加速度计传感器为例,介绍传感器驱动开发。 -1. 加速度计传感器驱动注册。HDF驱动框架会提供统一的驱动管理模型,通过加速计传感器模块配置信息,识别并加载对应模块驱动。 -2. 加速度计传感器驱动初始化和去初始化。HDF驱动框架通过init入口函数,依次启动传感器设备驱动加载和分配传感器设备数据配置资源。HDF驱动框架通过release函数,释放驱动加载的资源和配置。 -3. 加速度计传感器寄存器组配置解析。不同类型传感器需要在hcs里配置器件对应的HCS配置文件,然后再设备驱动启动过程中探测器件是否在位,然后加载对应的配置文件,生成配置的结构体对象。 -4. 加速度计传感器驱动操作接口实现。实现各个类型传感器归一化驱动接口,如init,GetInfo,Enable,Disable,SetBatch,SetMode,SetOption,ReadSensorData等函数,完成传感器驱动配置下发和数据上报功能。 +### 开发步骤 +1. 基于HDF驱动框架,按照驱动Driver Entry程序,完成加速度抽象驱动开发,主要有Bind,Init,Release,Dispatch函数接口实现。 +2. 完成加速度传感器驱动的设备信息配置。 +3. 完成加速度抽象驱动内部接口开发,包括定时器,工作队列,Enable,Disable,SetBatch,SetMode,SetOption,AccelCreateCfgData,AccelReleaseCfgData,AccelRegisterChipOps接口实现。 +4. 基于HDF驱动框架,按照驱动Driver Entry程序,完成加速度差异化驱动开发,主要有Bind,Init,Release,Dispatch函数接口实现。 +5. 完成加速度传感器差异化驱动中差异化接口ReadData函数实现。 +6. 新增文件脚本适配。 >![](../public_sys-resources/icon-note.gif) **说明:** >传感器驱动模型已经提供一部分能力集,包括驱动设备管理能力,抽象总线和平台操作接口能力,通用配置操作接口能力,配置解析操作接口能力,接口参考[表2](#table1156812588320)。需要开发人员实现部分有:1、传感器部分操作接口([表3](#table1083014911336));2、传感器HCS差异化数据配置;3、驱动基本功能验证。 @@ -261,548 +257,507 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 基于HDF驱动模型,加载启动加速度计传感器驱动,代码形式如下,具体原理可参考[HDF驱动开发指南](driver-hdf-development.md)。加速度传感器选择通讯接口方式为I2C,厂家选择博世BMI160加速度传感器。 -1. 加速度计传感器驱动入口注册 - -- 加速度计传感器驱动入口函数实现 - -``` -/* 注册加速度计传感器入口数据结构体对象 */ -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); -``` - -- 加速度计传感器设备配置描述 - -加速度传感器模型使用HCS作为配置描述源码,HCS配置字段详细介绍参考[配置管理](driver-hdf-manage.md)介绍。 - -``` -/* 加速度计传感器设备HCS配置 */ -device_sensor_accel :: device { - device0 :: deviceNode { - policy = 1; /* policy字段是驱动服务发布的策略 */ - priority = 105; /* 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序 */ - preload = 2; /* 驱动按需加载字段,0表示加载,2表示不加载 */ - permission = 0664; /* 驱动创建设备节点权限 */ - moduleName = "HDF_SENSOR_ACCEL"; /* 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致 */ - serviceName = "sensor_accel"; /* 驱动对外发布服务的名称,必须唯一 */ - deviceMatchAttr = "hdf_sensor_accel_driver"; /* 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等 */ - } -} -``` - -1. 加速度计传感器驱动初始化和去初始化 - -- 初始化入口函数init - -``` -/* 加速度计传感器驱动对外提供的服务绑定到HDF框架 */ -int32_t BindAccelDriver(struct HdfDeviceObject *device) -{ - CHECK_NULL_PTR_RETURN_VALUE(device, HDF_ERR_INVALID_PARAM); - - static struct IDeviceIoService service = { - .object = {0}, - .Dispatch = DispatchAccel, - }; - device->service = &service; - - return HDF_SUCCESS; -} -/*在探测到器件在位后,需要调用RegisterAccelChipOps注册差异化适配函数*/ -int32_t RegisterAccelChipOps(struct AccelOpsCall *ops) -{ - struct AccelDrvData *drvData = NULL; - - CHECK_NULL_PTR_RETURN_VALUE(ops, HDF_ERR_INVALID_PARAM); - - drvData = AccelGetDrvData(); - drvData->ops.Init = ops->Init; - drvData->ops.ReadData = ops->ReadData; - return HDF_SUCCESS; -} -/* 挂载加速度计传感器驱动归一化的接口函数 */ -static int32_t InitAccelOps(struct SensorDeviceInfo *deviceInfo) -{ - struct AccelDrvData *drvData = AccelGetDrvData(); - - (void)memset_s((void *)deviceInfo, sizeof(*deviceInfo), 0, sizeof(*deviceInfo)); - deviceInfo->ops.GetInfo = SetAccelInfo; - deviceInfo->ops.Enable = SetAccelEnable; - deviceInfo->ops.Disable = SetAccelDisable; - deviceInfo->ops.SetBatch = SetAccelBatch; - deviceInfo->ops.SetMode = SetAccelMode; - deviceInfo->ops.SetOption = SetAccelOption; - - if (memcpy_s(&deviceInfo->sensorInfo, sizeof(deviceInfo->sensorInfo), - &drvData->accelCfg->sensorInfo, sizeof(drvData->accelCfg->sensorInfo)) != EOK) { - HDF_LOGE("%s: copy sensor info failed", __func__); - return HDF_FAILURE; - } - /* 传感器类型标识可以在数据HCS配置文件里面配置,也可以在此处 */ - drvData->accelCfg->sensorInfo.sensorTypeId = SENSOR_TAG_ACCELEROMETER; - drvData->accelCfg->sensorInfo.sensorId = SENSOR_TAG_ACCELEROMETER; - - return HDF_SUCCESS; -} -/* 传感器寄存器初始化操作 */ -static int32_t InitAccelAfterConfig(void) -{ - struct SensorDeviceInfo deviceInfo; - - if (InitAccelConfig() != HDF_SUCCESS) { - HDF_LOGE("%s: init accel config failed", __func__); - return HDF_FAILURE; - } - - if (InitAccelOps(&deviceInfo) != HDF_SUCCESS) { - HDF_LOGE("%s: init accel ops failed", __func__); - return HDF_FAILURE; - } - - if (AddSensorDevice(&deviceInfo) != HDF_SUCCESS) { - HDF_LOGE("%s: add accel device failed", __func__); - return HDF_FAILURE; - } - - return HDF_SUCCESS; -} -/*通过器件探测函数,挂载器件差异化函数接口*/ -static int32_t DetectAccelChip(void) -{ - int32_t num; - int32_t ret; - int32_t loop; - struct AccelDrvData *drvData = AccelGetDrvData(); - CHECK_NULL_PTR_RETURN_VALUE(drvData->accelCfg, HDF_ERR_INVALID_PARAM); - - num = sizeof(g_accelDetectIfList) / sizeof(g_accelDetectIfList[0]); - for (loop = 0; loop < num; ++loop) { - if (g_accelDetectIfList[loop].DetectChip != NULL) { - ret = g_accelDetectIfList[loop].DetectChip(drvData->accelCfg); - if (ret == HDF_SUCCESS) { - drvData->detectFlag = true; - break; - } - } - } - - if (loop == num) { - HDF_LOGE("%s: detect accel device failed", __func__); - drvData->detectFlag = false; - return HDF_FAILURE; - } - return HDF_SUCCESS; -} -/* 加速度计传感器驱动初始化入口函数,主要功能为对传感器私有数据的结构体对象进行初始化,传感器HCS数据配置对象空间分配,传感器HCS数据配置初始化入口函数调用,传感器设备探测是否在位功能,传感器数据上报定时器创建,传感器归一化接口挂载,传感器设备注册功能 */ -int32_t InitAccelDriver(struct HdfDeviceObject *device) -{ - /* 获取传感器私有数据结构体对象 */ - struct AccelDrvData *drvData = AccelGetDrvData(); - - /* 同类型传感器不同厂家设备探测时,判断此类型传感器是否已经在位,若已经在位,无需再继续探测,直接返回 */ - if (drvData->detectFlag) { - HDF_LOGE("%s: accel sensor have detected", __func__); - return HDF_SUCCESS; - } - - CHECK_NULL_PTR_RETURN_VALUE(device, HDF_ERR_INVALID_PARAM); - /* 分配存放传感器数据配置的私有结构体数据对象,需要在驱动释放时释放分配的资源空间 */ - drvData->accelCfg = (struct SensorCfgData *)OsalMemCalloc(sizeof(*cfg)); - if (drvData->accelCfg == NULL) { - HDF_LOGE("%s: malloc sensor config data failed", __func__); - return HDF_FAILURE; - } - - drvData->accelCfg->regCfgGroup = &g_regCfgGroup[0]; - /* 初始化传感器配置数据主要是解析传感器通讯总线配置类型信息,传感器基本信息,传感器属性信息,传感器是否在位信息,寄存器分组信息 */ - if (GetSensorBaseConfigData(device->property, drvData->accelCfg) != HDF_SUCCESS) { - HDF_LOGE("%s: get sensor base config failed", __func__); - goto Base_CONFIG_EXIT; - } - - if (DetectAccelChip() != HDF_SUCCESS) { - HDF_LOGE("%s: accel sensor detect device no exist", __func__); - goto DETECT_CHIP_EXIT; - } - drvData->detectFlag = true; - if (ParseSensorRegConfig(drvData->accelCfg) != HDF_SUCCESS) { - HDF_LOGE("%s: detect sensor device failed", __func__); - goto REG_CONFIG_EXIT; - } - - if (InitAccelAfterConfig() != HDF_SUCCESS) { - HDF_LOGE("%s: init accel after config failed", __func__); - goto INIT_EXIT; - } - - HDF_LOGI("%s: init accel driver success", __func__); - return HDF_SUCCESS; - -INIT_EXIT: - DestroySensorThread(&drvData->thread, &drvData->threadStatus); - (void)DeleteSensorDevice(SENSOR_TAG_ACCELEROMETER); -REG_CONFIG_EXIT: - ReleaseSensorAllRegConfig(drvData->accelCfg); - (void)ReleaseSensorBusHandle(&drvData->accelCfg->busCfg); -DETECT_CHIP_EXIT: - drvData->detectFlag = false; -BASE_CONFIG_EXIT: - drvData->accelCfg->root = NULL; - drvData->accelCfg->regCfgGroup = NULL; - OsalMemFree(drvData->accelCfg); - drvData->accelCfg = NULL; - return HDF_FAILURE; -} - -/* 释放驱动初始化时分配的资源 */ -void ReleaseAccelDriver(struct HdfDeviceObject *device) -{ - (void)device; - struct AccelDrvData *drvData = NULL; - - drvData = AccelGetDrvData(); - (void)DestroySensorThread(&drvData->thread, &drvData->threadStatus); - (void)DeleteSensorDevice(SENSOR_TAG_ACCELEROMETER); - drvData->detectFlag = false; - - if (drvData->accelCfg != NULL) { - drvData->accelCfg->root = NULL; - drvData->accelCfg->regCfgGroup = NULL; - ReleaseSensorAllRegConfig(drvData->accelCfg); - (void)ReleaseSensorBusHandle(&drvData->accelCfg->busCfg); - OsalMemFree(drvData->accelCfg); - drvData->accelCfg = NULL; - } - - drvData->initStatus = false; -} -``` - -1. 加速度计传感器寄存器组配置信息 - -加速度计传感器数据配置只需要按照模板配置即可,基于模板配置的解析功能已经在**InitSensorConfigData**函数完成,只需初始化时调用即可。如果有新增配置项,需要同步修改此函数。 - -``` -加速度传感器数据配置模板(accel_config.hcs) -root { - sensorAccelConfig { - accelChipConfig { - /* 传感器设备信息模板 */ - template sensorInfo { - sensorName = "accelerometer"; /* 加速度计名字,字符最大长度16字节 */ - vendorName = "borsh_bmi160"; /* 传感器设备厂商,字符最大长度16字节 */ - firmwareVersion = "1.0"; /* 传感器固件版本号,默认1.0,字符最大长度16字节 */ - hardwareVersion = "1.0"; /* 传感器硬件版本号,默认1.0,字符最大长度16字节 */ - sensorTypeId = 1; /* 传感器类型编号,详见{@link SensorTypeTag} */ - sensorId = 1; /* 传感器的标识号,有传感器驱动开发者定义,推荐用{@link SensorTypeTag}枚举 */ - maxRange = 8; /* 传感器的最大量程,根据开发者需要配置 */ - precision = 0; /* 传感器的精度,与上报数据配合使用,上报数据结构体{@link SensorEvents } */ - power = 230; /* 传感器的功耗 */ - } - /* 传感器使用的总线类型和配置信息模板 */ - template sensorBusConfig { - busType = 0; /* 0:i2c 1:spi */ - busNum = 6; /* 芯片上分配给传感器的器件号 */ - busAddr = 0; /* 芯片上分配给传感器的地址 */ - regWidth = 1; /* 传感器寄存器地址宽度 */ - regBigEndian = 0; /* 传感器寄存器大小端 */ - } - /* 传感器设备属性模板 */ - template sensorAttr { - chipName = ""; /* 传感器芯片名字 */ - chipIdRegister = 0xf; /* 传感器在位检测寄存器地址 */ - chipIdValue = 0xd1; /* 校验传感器在位检测寄存器值 */ - } - } - } -} - -/* 根据不同器件硬件差异,修改模板配置,不修改的就会默认采用模板配置 */ -root { - sensorAccelConfig { - accel_bmi160_chip_config : accelChipConfig { - match_attr = "hdf_sensor_accel_driver"; /* 需要和加速度传感器设备配置match_attr字段保持一致 */ - accelInfo :: sensorInfo { - vendorName = "borsh_bmi160"; - sensorTypeId = 1; - sensorId = 1; - } - accelBusConfig :: sensorBusConfig { - busType = 0; /* i2c通讯方式 */ - busNum = 6; - busAddr = 0x68; - regWidth = 1; /* 1字节位宽 */ - } - accelAttr :: sensorAttr { - chipName = "bmi160"; - chipIdRegister = 0x00; - chipIdValue = 0xd1; - } - accelRegConfig { - /* regAddr: 寄存器地址 - value: 寄存器值 - mask: 寄存器值的掩码 - len: 寄存器值的数据长度(字节) - delay: 配置寄存器延时(ms) - opsType:操作类型 0-无 1-读 2-写 3-读并检查 4-位更新 - calType: 计算类型 0-无 1-写 2-取反 3-异或 4-左移 5-右移 - shiftNum: 移动位数 - debug: 调试开关,0-调试关闭 1-调试打开 - save: 保存数据开关,0-不保存数据 1-保存数据 - */ - /* 传感器寄存器操作分组,按照分组进行有序配置 */ - /* 寄存器地址, 寄存器值, 寄存器值的掩码, 寄存器值的数据长度, 配置寄存器延时, 操作类型, 计算类型, 移动位数, 调试开关, 保存开关 */ - /* 初始化寄存器组 */ - initSeqConfig = [ - 0x7e, 0xb6, 0xff, 1, 5, 2, 0, 0, 0, 0, - 0x7e, 0x10, 0xff, 1, 5, 2, 0, 0, 0, 0 - ]; - /* 使能寄存器组 */ - enableSeqConfig = [ - 0x7e, 0x11, 0xff, 1, 5, 2, 0, 0, 0, 0, - 0x41, 0x03, 0xff, 1, 0, 2, 0, 0, 0, 0, - 0x40, 0x08, 0xff, 1, 0, 2, 0, 0, 0, 0 - ]; - /* 去使能寄存器组 */ - disableSeqConfig = [ - 0x7e, 0x10, 0xff, 1, 5, 2, 0, 0, 0, 0 - ]; - } - } - } -} -``` - -1. 加速度计传感器驱动操作接口实现 - -开发者需要根据每种类型的传感器实现归一化接口。 - -``` -/* 不使用函数暂时置空 */ -static int32_t SetAccelInfo(struct SensorBasicInfo *info) -{ - (void)info; - - return HDF_ERR_NOT_SUPPORT; -} -/* 下发使能寄存器组的配置 */ -static int32_t SetAccelEnable(void) -{ - int32_t ret; - struct AccelDrvData *drvData = AccelGetDrvData(); - - CHECK_NULL_PTR_RETURN_VALUE(drvData->accelCfg, HDF_ERR_INVALID_PARAM); - ret = SetSensorRegCfgArray(&drvData->accelCfg->busCfg, drvData->accelCfg->regCfgGroup[SENSOR_ENABLE_GROUP]); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: accel sensor disable config failed", __func__); - return HDF_FAILURE; - } - - drvData->threadStatus = SENSOR_THREAD_RUNNING; - - return HDF_SUCCESS; -} -/* 下发去使能寄存器组的配置 */ -static int32_t SetAccelDisable(void) -{ - int32_t ret; - struct AccelDrvData *drvData = AccelGetDrvData(); - - CHECK_NULL_PTR_RETURN_VALUE(drvData->accelCfg, HDF_ERR_INVALID_PARAM); - - ret = SetSensorRegCfgArray(&drvData->accelCfg->busCfg, drvData->accelCfg->regCfgGroup[SENSOR_DISABLE_GROUP]); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: accel sensor disable config failed", __func__); - return HDF_FAILURE; - } - - drvData->threadStatus = SENSOR_THREAD_STOPPED; - - return HDF_SUCCESS; -} -/* 配置传感器采样率和数据上报间隔 */ -static int32_t SetAccelBatch(int64_t samplingInterval, int64_t interval) -{ - (void)interval; - - struct AccelDrvData *drvData = AccelGetDrvData(); - drvData->interval = samplingInterval; - - return HDF_SUCCESS; -} -/* 设置传感器工作模式,当前支持实时模式 */ -static int32_t SetAccelMode(int32_t mode) -{ - return (mode == SENSOR_WORK_MODE_REALTIME) ? HDF_SUCCESS : HDF_FAILURE; -} -/* 设置传感器可选配置 */ -static int32_t SetAccelOption(uint32_t option) -{ - (void)option; - return HDF_ERR_NOT_SUPPORT; -} -``` - -- 差异化处理接口 - - ``` - /* 器件探测时,如果探测成功,则注册差异化处理函数到accel驱动模型里 */ - int32_t DetectAccelBim160Chip(struct SensorCfgData *data) - { - int32_t ret; - struct AccelOpsCall ops; - CHECK_NULL_PTR_RETURN_VALUE(data, HDF_ERR_INVALID_PARAM); - - if (strcmp(ACCEL_CHIP_NAME_BMI160, data->sensorAttr.chipName) != 0) { - return HDF_SUCCESS; - } - ret = InitAccelPreConfig(); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: init BMI160 bus mux config", __func__); - return HDF_FAILURE; - } - if (DetectSensorDevice(data) != HDF_SUCCESS) { - return HDF_FAILURE; - } - - /* 差异化处理函数 */ - ops.Init = InitBmi160; - ops.ReadData = ReadBmi160Data; - ret = RegisterAccelChipOps(&ops); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: register BMI160 accel failed", __func__); - (void)ReleaseSensorBusHandle(&data->busCfg); - return HDF_FAILURE; - } - return HDF_SUCCESS; - } - /* 初始化处理函数 */ - static int32_t InitBmi160(struct SensorCfgData *data) - { - int32_t ret; - - CHECK_NULL_PTR_RETURN_VALUE(data, HDF_ERR_INVALID_PARAM); - ret = SetSensorRegCfgArray(&data->busCfg, data->regCfgGroup[SENSOR_INIT_GROUP]); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: bmi160 sensor init config failed", __func__); - return HDF_FAILURE; - } - return HDF_SUCCESS; - } - /* 数据处理函数 */ - int32_t ReadBmi160Data(struct SensorCfgData *data) - { - int32_t ret; - struct AccelData rawData = { 0, 0, 0 }; - int32_t tmp[ACCEL_AXIS_NUM]; - struct SensorReportEvent event; - - (void)memset_s(&event, sizeof(event), 0, sizeof(event)); - - ret = ReadBmi160RawData(data, &rawData, &event.timestamp); - if (ret != HDF_SUCCESS) { - return HDF_FAILURE; - } - - event.sensorId = SENSOR_TAG_ACCELEROMETER; - event.option = 0; - event.mode = SENSOR_WORK_MODE_REALTIME; - - rawData.x = rawData.x * BMI160_ACC_SENSITIVITY_2G; - rawData.y = rawData.y * BMI160_ACC_SENSITIVITY_2G; - rawData.z = rawData.z * BMI160_ACC_SENSITIVITY_2G; - - tmp[ACCEL_X_AXIS] = (rawData.x * SENSOR_1K_UNIT) / SENSOR_CONVERT_UNIT; - tmp[ACCEL_Y_AXIS] = (rawData.y * SENSOR_1K_UNIT) / SENSOR_CONVERT_UNIT; - tmp[ACCEL_Z_AXIS] = (rawData.z * SENSOR_1K_UNIT) / SENSOR_CONVERT_UNIT; - - event.dataLen = sizeof(tmp); - event.data = (uint8_t *)&tmp; - ret = ReportSensorEvent(&event); - return ret; - } - ``` - -- 数据处理函数 - -创建传感器定时器,按照配置的采样率定时采样,并上报给数据订阅者。 - -``` -/* 传感器定时工作线程 */ -static int32_t ReadAccelDataThreadWorker(void *arg) -{ - (void)arg; - int64_t interval; - struct AccelDrvData *drvData = AccelGetDrvData(); - - drvData->threadStatus = SENSOR_THREAD_START; - while (true) { - if (drvData->threadStatus == SENSOR_THREAD_RUNNING) { - if (drvData->ops.ReadData != NULL) { - (void)drvData->ops.ReadData(drvData->accelCfg); - } - interval = OsalDivS64(drvData->interval, (SENSOR_CONVERT_UNIT * SENSOR_CONVERT_UNIT)); - OsalMSleep(interval); - } else if (drvData->threadStatus == SENSOR_THREAD_DESTROY) { - break; - } else { - OsalMSleep(ACC_DEFAULT_SAMPLING_200_MS / SENSOR_CONVERT_UNIT / SENSOR_CONVERT_UNIT); - } - - if ((!drvData->initStatus) || (drvData->interval < 0) || drvData->threadStatus != SENSOR_THREAD_RUNNING) { - continue; - } - } - - return HDF_SUCCESS; -} -/* 创建传感器定时器和器件初始化 */ -static int32_t InitAccelConfig(void) -{ - int32_t ret; - struct AccelDrvData *drvData = AccelGetDrvData(); - - if (drvData->threadStatus != SENSOR_THREAD_NONE && drvData->threadStatus != SENSOR_THREAD_DESTROY) { - HDF_LOGE("%s: accel thread have created", __func__); - return HDF_SUCCESS; - } - - ret = CreateSensorThread(&drvData->thread, ReadAccelDataThreadWorker, "hdf_sensor_accel", drvData); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: accel create thread failed", __func__); - drvData->threadStatus = SENSOR_THREAD_NONE; - return HDF_FAILURE; - } - - CHECK_NULL_PTR_RETURN_VALUE(drvData->ops.Init, HDF_ERR_INVALID_PARAM); - - ret = drvData->ops.Init(drvData->accelCfg); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: accel create thread failed", __func__); - drvData->threadStatus = SENSOR_THREAD_NONE; - return HDF_FAILURE; - } - drvData->initStatus = true; - return HDF_SUCCESS; -} -``` - -- 主要的数据结构 +1. 加速度计传感器驱动入口注册 + + - 加速度计传感器驱动入口函数实现 + + ``` + /* 注册加速度计传感器入口数据结构体对象 */ + 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); + ``` + + - 加速度计传感器设备配置描述 + + 加速度传感器模型使用HCS作为配置描述源码,HCS配置字段详细介绍参考[配置管理](driver-hdf-manage.md)介绍。 + + ``` + /* 加速度计传感器设备HCS配置 */ + device_sensor_accel :: device { + device0 :: deviceNode { + policy = 1; /* policy字段是驱动服务发布的策略 */ + priority = 110; /* 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序 */ + preload = 0; /* 驱动按需加载字段,0表示加载,2表示不加载 */ + permission = 0664; /* 驱动创建设备节点权限 */ + moduleName = "HDF_SENSOR_ACCEL"; /* 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致 */ + serviceName = "sensor_accel"; /* 驱动对外发布服务的名称,必须唯一 */ + deviceMatchAttr = "hdf_sensor_accel_driver"; /* 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等 */ + } + } + ``` + +2. 加速度计传感器驱动操作接口实现 + + - 开发者需要根据每种类型的传感器实现归一化接口。 + + ``` + /* 不使用函数暂时置空 */ + static int32_t SetAccelInfo(struct SensorBasicInfo *info) + { + (void)info; + + return HDF_ERR_NOT_SUPPORT; + } + /* 下发使能寄存器组的配置 */ + static int32_t SetAccelEnable(void) + { + int32_t ret; + struct AccelDrvData *drvData = AccelGetDrvData(); + + CHECK_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_INVALID_PARAM); + CHECK_NULL_PTR_RETURN_VALUE(drvData->accelCfg, HDF_ERR_INVALID_PARAM); + + if (drvData->enable) { + HDF_LOGE("%s: Accel sensor is enabled", __func__); + return HDF_SUCCESS; + } + + ret = SetSensorRegCfgArray(&drvData->accelCfg->busCfg, drvData->accelCfg->regCfgGroup[SENSOR_ENABLE_GROUP]); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: Accel sensor enable config failed", __func__); + return ret; + } + + ret = OsalTimerCreate(&drvData->accelTimer, SENSOR_TIMER_MIN_TIME, AccelTimerEntry, (uintptr_t)drvData); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: Accel create timer failed[%d]", __func__, ret); + return ret; + } + + ret = OsalTimerStartLoop(&drvData->accelTimer); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: Accel start timer failed[%d]", __func__, ret); + return ret; + } + drvData->enable = true; + + return HDF_SUCCESS; + } + /* 下发去使能寄存器组的配置 */ + static int32_t SetAccelDisable(void) + { + int32_t ret; + struct AccelDrvData *drvData = AccelGetDrvData(); + + CHECK_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_INVALID_PARAM); + CHECK_NULL_PTR_RETURN_VALUE(drvData->accelCfg, HDF_ERR_INVALID_PARAM); + + if (!drvData->enable) { + HDF_LOGE("%s: Accel sensor had disable", __func__); + return HDF_SUCCESS; + } + + ret = SetSensorRegCfgArray(&drvData->accelCfg->busCfg, drvData->accelCfg->regCfgGroup[SENSOR_DISABLE_GROUP]); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: Accel sensor disable config failed", __func__); + return ret; + } + + ret = OsalTimerDelete(&drvData->accelTimer); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: Accel delete timer failed", __func__); + return ret; + } + drvData->enable = false; + + return HDF_SUCCESS; + } + /* 配置传感器采样率和数据上报间隔 */ + static int32_t SetAccelBatch(int64_t samplingInterval, int64_t interval) + { + (void)interval; + + struct AccelDrvData *drvData = NULL; + + drvData = AccelGetDrvData(); + CHECK_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_INVALID_PARAM); + + drvData->interval = samplingInterval; + + return HDF_SUCCESS; + } + /* 设置传感器工作模式,当前支持实时模式 */ + static int32_t SetAccelMode(int32_t mode) + { + return (mode == SENSOR_WORK_MODE_REALTIME) ? HDF_SUCCESS : HDF_FAILURE; + } + + static int32_t SetAccelOption(uint32_t option) + { + (void)option; + return HDF_SUCCESS; + } + + /* 设置传感器可选配置 */ + static int32_t SetAccelOption(uint32_t option) + { + (void)option; + return HDF_ERR_NOT_SUPPORT; + } + ``` + +3. 加速度计传感器驱动初始化和去初始化 + + - 初始化入口函数init + + ``` + /* 加速度计传感器驱动对外提供的服务绑定到HDF框架 */ + int32_t AccelBindDriver(struct HdfDeviceObject *device) + { + CHECK_NULL_PTR_RETURN_VALUE(device, HDF_ERR_INVALID_PARAM); + + struct AccelDrvData *drvData = (struct AccelDrvData *)OsalMemCalloc(sizeof(*drvData)); + if (drvData == NULL) { + HDF_LOGE("%s: Malloc accel drv data fail!", __func__); + return HDF_ERR_MALLOC_FAIL; + } + + drvData->ioService.Dispatch = DispatchAccel; + drvData->device = device; + device->service = &drvData->ioService; + g_accelDrvData = drvData; + return HDF_SUCCESS; + } + + /* 挂载加速度计传感器驱动归一化的接口函数 */ + static int32_t InitAccelOps(struct SensorCfgData *config, struct SensorDeviceInfo *deviceInfo) + { + CHECK_NULL_PTR_RETURN_VALUE(config, HDF_ERR_INVALID_PARAM); + + deviceInfo->ops.Enable = SetAccelEnable; + deviceInfo->ops.Disable = SetAccelDisable; + deviceInfo->ops.SetBatch = SetAccelBatch; + deviceInfo->ops.SetMode = SetAccelMode; + deviceInfo->ops.SetOption = SetAccelOption; + + 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; + } + /* 提供给差异化驱动的初始化接口,完成加速度器件基本配置信息解析(加速度信息,加速度总线配置,加速度器件探测寄存器配置),器件探测,器件寄存器解析 */ + static int32_t InitAccelAfterDetected(struct SensorCfgData *config) + { + struct SensorDeviceInfo deviceInfo; + CHECK_NULL_PTR_RETURN_VALUE(config, HDF_ERR_INVALID_PARAM); + // 初始化加速度计接口函数 + if (InitAccelOps(config, &deviceInfo) != HDF_SUCCESS) { + HDF_LOGE("%s: Init accel ops failed", __func__); + return HDF_FAILURE; + } + // 注册加速度计设备到传感器管理模块 + if (AddSensorDevice(&deviceInfo) != HDF_SUCCESS) { + HDF_LOGE("%s: Add accel device failed", __func__); + return HDF_FAILURE; + } + // 器件寄存器解析 + if (ParseSensorRegConfig(config) != HDF_SUCCESS) { + HDF_LOGE("%s: Parse sensor register failed", __func__); + (void)DeleteSensorDevice(&config->sensorInfo); + ReleaseSensorAllRegConfig(config); + return HDF_FAILURE; + } + return HDF_SUCCESS; + } + struct SensorCfgData *AccelCreateCfgData(const struct DeviceResourceNode *node) + { + …… + // 如果探测不到器件在位,返回进行下个器件探测 + if (drvData->detectFlag) { + HDF_LOGE("%s: Accel sensor have detected", __func__); + return NULL; + } + if (drvData->accelCfg == NULL) { + HDF_LOGE("%s: Accel accelCfg pointer NULL", __func__); + return NULL; + } + // 设备基本配置信息解析 + if (GetSensorBaseConfigData(node, drvData->accelCfg) != HDF_SUCCESS) { + HDF_LOGE("%s: Get sensor base config failed", __func__); + goto BASE_CONFIG_EXIT; + } + // 如果探测不到器件在位,返回进行下个器件探测 + if (DetectSensorDevice(drvData->accelCfg) != HDF_SUCCESS) { + HDF_LOGI("%s: Accel sensor detect device no exist", __func__); + drvData->detectFlag = false; + goto BASE_CONFIG_EXIT; + } + drvData->detectFlag = true; + // 器件寄存器解析 + if (InitAccelAfterDetected(drvData->accelCfg) != HDF_SUCCESS) { + HDF_LOGE("%s: Accel sensor detect device no exist", __func__); + goto INIT_EXIT; + } + return drvData->accelCfg; + …… + } + /* 加速度计传感器驱动初始化入口函数,主要功能为对传感器私有数据的结构体对象进行初始化,传感器HCS数据配置对象空间分配,传感器HCS数据配置初始化入口函数调用,传感器设备探测是否在位功能,传感器数据上报定时器创建,传感器归一化接口挂载,传感器设备注册功能 */ + int32_t InitAccelDriver(struct HdfDeviceObject *device) + { + int32_t AccelInitDriver(struct HdfDeviceObject *device) + { + …… + // 工作队列资源初始化 + if (InitAccelData(drvData) != HDF_SUCCESS) { + HDF_LOGE("%s: Init accel config failed", __func__); + return HDF_FAILURE; + } + // 分配加速度配置信息资源 + drvData->accelCfg = (struct SensorCfgData *)OsalMemCalloc(sizeof(*drvData->accelCfg)); + if (drvData->accelCfg == NULL) { + HDF_LOGE("%s: Malloc accel config data failed", __func__); + return HDF_FAILURE; + } + // 挂接寄存器分组信息 + drvData->accelCfg->regCfgGroup = &g_regCfgGroup[0]; + …… + return HDF_SUCCESS; + } + + /* 释放驱动初始化时分配的资源 */ + void AccelReleaseDriver(struct HdfDeviceObject *device) + { + CHECK_NULL_PTR_RETURN(device); + struct AccelDrvData *drvData = (struct AccelDrvData *)device->service; + CHECK_NULL_PTR_RETURN(drvData); + // 器件在位,释放已分配资源 + if (drvData->detectFlag) { + AccelReleaseCfgData(drvData->accelCfg); + } + OsalMemFree(drvData->accelCfg); + drvData->accelCfg = NULL; + // 器件在位,销毁工作队列资源 + HdfWorkDestroy(&drvData->accelWork); + HdfWorkQueueDestroy(&drvData->accelWorkQueue); + OsalMemFree(drvData); + } + ``` + +4. 加速度计差异化驱动私有HCS配置实现示例 + + - 为了方便开发者使用传感器HCS私有配置,在sensor_common.hcs里面定义通用的传感器配置模板,加速度直接引用模板修改对应的属性值即可。 + + ``` + // accel sensor common config template + root { + sensorAccelConfig { + accelChipConfig { + /* 传感器设备信息模板 */ + template sensorInfo { + sensorName = "accelerometer"; /* 加速度计名字,字符最大长度16字节 */ + vendorName = "borsh_bmi160"; /* 传感器设备厂商,字符最大长度16字节 */ + firmwareVersion = "1.0"; /* 传感器固件版本号,默认1.0,字符最大长度16字节 */ + hardwareVersion = "1.0"; /* 传感器硬件版本号,默认1.0,字符最大长度16字节 */ + sensorTypeId = 1; /* 传感器类型编号,详见{@link SensorTypeTag} */ + sensorId = 1; /* 传感器的标识号,有传感器驱动开发者定义,推荐用{@link SensorTypeTag}枚举 */ + maxRange = 8; /* 传感器的最大量程,根据开发者需要配置 */ + accuracy = 0; /* 传感器的精度,与上报数据配合使用,上报数据结构体{@link SensorEvents } */ + power = 230; /* 传感器的功耗 */ + } + /* 传感器使用的总线类型和配置信息模板 */ + template sensorBusConfig { + busType = 0; /* 0:i2c 1:spi */ + busNum = 6; /* 芯片上分配给传感器的器件号 */ + busAddr = 0; /* 芯片上分配给传感器的地址 */ + regWidth = 1; /* 传感器寄存器地址宽度 */ + regBigEndian = 0; /* 传感器寄存器大小端 */ + } + /* 传感器设备属性模板 */ + template sensorAttr { + chipName = ""; /* 传感器芯片名字 */ + chipIdRegister = 0xf; /* 传感器在位检测寄存器地址 */ + chipIdValue = 0xd1; /* 校验传感器在位检测寄存器值 */ + } + } + } + } + ``` + + - 开发者使用传感器HCS配置,在accel_config.hcs里面配置通用的传感器模板,加速度计器件直接引用模板并修改对应的属性值,在此基础上新增寄存器配置,生成accel_bmi160_config.hcs配置文件。 + + ``` + /* 根据不同器件硬件差异,修改模板配置,不修改的就会默认采用模板配置 */ + #include "accel_config.hcs" + root { + accel_bmi160_chip_config : sensorConfig { + match_attr = "hdf_sensor_accel_bmi160_driver"; + sensorInfo :: sensorDeviceInfo { + vendorName = "borsh_bmi160"; // max string length is 16 bytes + sensorTypeId = 1; // enum SensorTypeTag + sensorId = 1; // user define sensor id + } + sensorBusConfig:: sensorBusInfo { + busType = 0; // 0:i2c 1:spi + busNum = 6; + busAddr = 0x68; + regWidth = 1; // 1btye + } + sensorIdAttr :: sensorIdInfo{ + chipName = "bmi160"; + chipIdRegister = 0x00; + chipIdValue = 0xd1; + } + sensorRegConfig { + /* regAddr: register address + value: config register value + len: size of value + mask: mask of value + delay: config register delay time (ms) + opsType: enum SensorOpsType 0-none 1-read 2-write 3-read_check 4-update_bit + calType: enum SensorBitCalType 0-none 1-set 2-revert 3-xor 4-left shift 5-right shift + shiftNum: shift bits + debug: 0-no debug 1-debug + save: 0-no save 1-save + */ + /* regAddr, value, mask, len, delay, opsType, calType, shiftNum, debug, save */ + // 初始化寄存器组 + initSeqConfig = [ + 0x7e, 0xb6, 0xff, 1, 5, 2, 0, 0, 0, 0, + 0x7e, 0x10, 0xff, 1, 5, 2, 0, 0, 0, 0 + ]; + // 使能寄存器组 + enableSeqConfig = [ + 0x7e, 0x11, 0xff, 1, 5, 2, 0, 0, 0, 0, + 0x41, 0x03, 0xff, 1, 0, 2, 0, 0, 0, 0, + 0x40, 0x08, 0xff, 1, 0, 2, 0, 0, 0, 0 + ]; + // 去使能寄存器组 + disableSeqConfig = [ + 0x7e, 0x10, 0xff, 1, 5, 2, 0, 0, 0, 0 + ]; + } + } + } + ``` + +5. 加速度计差异化驱动实现示例 + + - 定义加速度差异化驱动对应的HdfDriverEntry对象,其中Driver Entry入口函数定义如下: + + ``` + struct HdfDriverEntry g_accelBmi160DevEntry = { + .moduleVersion = 1, + .moduleName = "HDF_SENSOR_ACCEL_BMI160", + .Bind = Bmi160BindDriver, + .Init = Bmi160InitDriver, + .Release = Bmi160ReleaseDriver, + }; + HDF_INIT(g_accelBmi160DevEntry); + ``` + + - Bind驱动接口实例化,实现示例: + + ``` + int32_t Bmi160BindDriver(struct HdfDeviceObject *device) + { + CHECK_NULL_PTR_RETURN_VALUE(device, HDF_ERR_INVALID_PARAM); + struct Bmi160DrvData *drvData = (struct Bmi160DrvData *)OsalMemCalloc(sizeof(*drvData)); + if (drvData == NULL) { + HDF_LOGE("%s: Malloc Bmi160 drv data fail", __func__); + return HDF_ERR_MALLOC_FAIL; + } + drvData->ioService.Dispatch = DispatchBMI160; + drvData->device = device; + device->service = &drvData->ioService; + g_bmi160DrvData = drvData; + return HDF_SUCCESS; + } + ``` + + - Init驱动接口实例化,实现示例: + + ``` + int32_t Bmi160InitDriver(struct HdfDeviceObject *device) + { + …… + // 加速度计差异化初始化配置 + ret = InitAccelPreConfig(); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: Init BMI160 bus mux config", __func__); + return HDF_FAILURE; + } + // 创建传感器配置数据接口,完成器件探测,私有数据配置解析 + drvData->sensorCfg = AccelCreateCfgData(device->property); + if (drvData->sensorCfg == NULL) { + return HDF_ERR_NOT_SUPPORT; + } + // 注册差异化接口 + ops.Init = NULL; + ops.ReadData = ReadBmi160Data; + ret = AccelRegisterChipOps(&ops); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: Register BMI160 accel failed", __func__); + return HDF_FAILURE; + } + // 初始化器件配置 + ret = InitBmi160(drvData->sensorCfg); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: Init BMI160 accel failed", __func__); + return HDF_FAILURE; + } + return HDF_SUCCESS; + } + ``` + + - Release驱动接口实例化,实现示例: + + ``` + void Bmi160ReleaseDriver(struct HdfDeviceObject *device) + { + CHECK_NULL_PTR_RETURN(device); + struct Bmi160DrvData *drvData = (struct Bmi160DrvData *)device->service; + CHECK_NULL_PTR_RETURN(drvData); + AccelReleaseCfgData(drvData->sensorCfg); + drvData->sensorCfg = NULL; + OsalMemFree(drvData); + } + ``` + +6. 加速度计差异化函数接口实现示例 + + - 需要开发者实现的ReadBmi160Data接口函数,在Bmi160InitDriver函数里面注册此函数。 + + ``` + int32_t ReadBmi160Data(struct SensorCfgData *data) + { + int32_t ret; + struct AccelData rawData = { 0, 0, 0 }; + int32_t tmp[ACCEL_AXIS_NUM]; + struct SensorReportEvent event; + (void)memset_s(&event, sizeof(event), 0, sizeof(event)); + ret = ReadBmi160RawData(data, &rawData, &event.timestamp); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: BMI160 read raw data failed", __func__); + return HDF_FAILURE; + } + event.sensorId = SENSOR_TAG_ACCELEROMETER; + event.option = 0; + event.mode = SENSOR_WORK_MODE_REALTIME; + …… + ret = ReportSensorEvent(&event); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: BMI160 report data failed", __func__); + } + return ret; + } + ``` + +7. 主要的数据结构 ``` -/* 传感器转换单位*/ -#define SENSOR_CONVERT_UNIT 1000 -#define SENSOR_1K_UNIT 1024 /* 传感器2g对应灵敏度转换值 */ -#define BMI160_ACC_SENSITIVITY_2G 61 +#define BMI160_ACC_SENSITIVITY_2G 61 /* 传感器数据采样寄存器地址 */ #define BMI160_ACCEL_X_LSB_ADDR 0X12 #define BMI160_ACCEL_X_MSB_ADDR 0X13 @@ -810,6 +765,7 @@ static int32_t InitAccelConfig(void) #define BMI160_ACCEL_Y_MSB_ADDR 0X15 #define BMI160_ACCEL_Z_LSB_ADDR 0X16 #define BMI160_ACCEL_Z_MSB_ADDR 0X17 +#define BMI160_STATUS_ADDR 0X1B /* 传感器数据维度 */ enum AccelAxisNum { ACCEL_X_AXIS = 0, @@ -825,12 +781,15 @@ struct AccelData { }; /* 传感器私有数据结构体 */ struct AccelDrvData { + struct IDeviceIoService ioService; + struct HdfDeviceObject *device; + HdfWorkQueue accelWorkQueue; + HdfWork accelWork; + OsalTimer accelTimer; bool detectFlag; - uint8_t threadStatus; - uint8_t initStatus; + bool enable; int64_t interval; struct SensorCfgData *accelCfg; - struct OsalThread thread; struct AccelOpsCall ops; }; /* 差异化适配函数 */ @@ -842,7 +801,7 @@ struct AccelOpsCall { ## 测试指导 -驱动开发完成后,在传感器单元测试里面开发自测试用例,验证驱动基本功能。测试环境采用开发者自测试平台。 +- 驱动开发完成后,在传感器单元测试里面开发自测试用例,验证驱动基本功能。测试环境采用开发者自测试平台。 ``` /* 标识是否上报传感器数据 */ -- Gitee From 02ee0d657dbe1d2ba79926cea90bbf5cf9ee8a2a Mon Sep 17 00:00:00 2001 From: sunxuejiao Date: Tue, 30 Nov 2021 16:38:19 +0800 Subject: [PATCH 2/6] refresh sensor file Signed-off-by: sunxuejiao --- .../driver/driver-peripherals-sensor-des.md | 258 +++++++++--------- 1 file changed, 129 insertions(+), 129 deletions(-) diff --git a/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md b/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md index 08212df25e0..91c09862bbb 100644 --- a/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md +++ b/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md @@ -14,7 +14,7 @@ Sensor(传感器)驱动模块为上层Sensor服务系统提供稳定的Sensor基础能力API,包括Sensor列表查询、Sensor启停、Sensor订阅及去订阅,Sensor参数配置等功能;基于HDF(**H**ardware **D**river **F**oundation)驱动框架开发的Sensor驱动模型,实现跨操作系统迁移,器件差异配置等功能。Sensor驱动模型如下图1所示: **图 1** Sensor驱动模型图 -![](figure/Sensor驱动模型图.png "Sensor驱动模型图") +![Sensor驱动模型图](figures/Sensor%E9%A9%B1%E5%8A%A8%E6%A8%A1%E5%9E%8B%E5%9B%BE.png) Sensor驱动模型对外开放的API接口能力如下: @@ -24,7 +24,7 @@ Sensor驱动模型对外开放的API接口能力如下: ### 接口说明 -Sensor驱动模型对HDI开放的API接口功能,参考表1。 +Sensor驱动模型对HDI开放的API接口功能,参考表1: **表 1** Sensor驱动模型对外API接口功能介绍 @@ -181,7 +181,7 @@ Sensor驱动模型对驱动开发者开放的功能接口,驱动开发者无 -Sensor驱动模型要求驱动开发者实现的接口功能,参考表3 +Sensor驱动模型要求驱动开发者实现的接口功能,参考表3: **表 3** Sensor驱动模型要求驱动开发者实现的接口列表 @@ -236,7 +236,7 @@ Sensor驱动模型要求驱动开发者实现的接口功能,参考表3 -接口实现参考[SENSOR](#section257750691)章节。 +接口实现参考[开发实例章节](#section257750691)章节。 ## 开发指导 @@ -251,7 +251,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 6. 新增文件脚本适配。 >![](../public_sys-resources/icon-note.gif) **说明:** ->传感器驱动模型已经提供一部分能力集,包括驱动设备管理能力,抽象总线和平台操作接口能力,通用配置操作接口能力,配置解析操作接口能力,接口参考[表2](#table1156812588320)。需要开发人员实现部分有:1、传感器部分操作接口([表3](#table1083014911336));2、传感器HCS差异化数据配置;3、驱动基本功能验证。 +>传感器驱动模型已经提供一部分能力集,包括驱动设备管理能力,抽象总线和平台操作接口能力,通用配置操作接口能力,配置解析操作接口能力,接口参考[表2](#table1156812588320)。需要开发人员实现部分有:1、传感器部分操作接口([表3](#table1083014911336));2、传感器HCS差异化数据配置;3、驱动基本功能验证。 ## 开发实例 @@ -753,131 +753,131 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 } ``` -7. 主要的数据结构 - -``` -/* 传感器2g对应灵敏度转换值 */ -#define BMI160_ACC_SENSITIVITY_2G 61 -/* 传感器数据采样寄存器地址 */ -#define BMI160_ACCEL_X_LSB_ADDR 0X12 -#define BMI160_ACCEL_X_MSB_ADDR 0X13 -#define BMI160_ACCEL_Y_LSB_ADDR 0X14 -#define BMI160_ACCEL_Y_MSB_ADDR 0X15 -#define BMI160_ACCEL_Z_LSB_ADDR 0X16 -#define BMI160_ACCEL_Z_MSB_ADDR 0X17 -#define BMI160_STATUS_ADDR 0X1B -/* 传感器数据维度 */ -enum AccelAxisNum { - ACCEL_X_AXIS = 0, - ACCEL_Y_AXIS = 1, - ACCEL_Z_AXIS = 2, - ACCEL_AXIS_NUM = 3, -}; -/* 传感器每个维度值 */ -struct AccelData { - int32_t x; - int32_t y; - int32_t z; -}; -/* 传感器私有数据结构体 */ -struct AccelDrvData { - struct IDeviceIoService ioService; - struct HdfDeviceObject *device; - HdfWorkQueue accelWorkQueue; - HdfWork accelWork; - OsalTimer accelTimer; - bool detectFlag; - bool enable; - int64_t interval; - struct SensorCfgData *accelCfg; - struct AccelOpsCall ops; -}; -/* 差异化适配函数 */ -struct AccelOpsCall { - int32_t (*Init)(struct SensorCfgData *data); - int32_t (*ReadData)(struct SensorCfgData *data); -}; -``` +7. 主要的数据结构 + + ``` + /* 传感器2g对应灵敏度转换值 */ + #define BMI160_ACC_SENSITIVITY_2G 61 + /* 传感器数据采样寄存器地址 */ + #define BMI160_ACCEL_X_LSB_ADDR 0X12 + #define BMI160_ACCEL_X_MSB_ADDR 0X13 + #define BMI160_ACCEL_Y_LSB_ADDR 0X14 + #define BMI160_ACCEL_Y_MSB_ADDR 0X15 + #define BMI160_ACCEL_Z_LSB_ADDR 0X16 + #define BMI160_ACCEL_Z_MSB_ADDR 0X17 + #define BMI160_STATUS_ADDR 0X1B + /* 传感器数据维度 */ + enum AccelAxisNum { + ACCEL_X_AXIS = 0, + ACCEL_Y_AXIS = 1, + ACCEL_Z_AXIS = 2, + ACCEL_AXIS_NUM = 3, + }; + /* 传感器每个维度值 */ + struct AccelData { + int32_t x; + int32_t y; + int32_t z; + }; + /* 传感器私有数据结构体 */ + struct AccelDrvData { + struct IDeviceIoService ioService; + struct HdfDeviceObject *device; + HdfWorkQueue accelWorkQueue; + HdfWork accelWork; + OsalTimer accelTimer; + bool detectFlag; + bool enable; + int64_t interval; + struct SensorCfgData *accelCfg; + struct AccelOpsCall ops; + }; + /* 差异化适配函数 */ + struct AccelOpsCall { + int32_t (*Init)(struct SensorCfgData *data); + int32_t (*ReadData)(struct SensorCfgData *data); + }; + ``` ## 测试指导 -- 驱动开发完成后,在传感器单元测试里面开发自测试用例,验证驱动基本功能。测试环境采用开发者自测试平台。 - -``` -/* 标识是否上报传感器数据 */ -static int32_t g_sensorDataFlag = 0; -/* 保持获取的传感器接口实例地址 */ -static const struct SensorInterface *g_sensorDev = nullptr; - -/* 订阅者注册数据上报函数 */ -static int SensorTestDataCallback(struct SensorEvents *event) -{ - if (event == nullptr) { - return -1; - } - float *data = (float*)event->data; - printf("time [%lld] sensor id [%d] x-[%f] y-[%f] z-[%f]\n\r", event->timestamp, - event->sensorId, (*data), *(data + 1), *(data + g_axisZ)); - if (*data > 1e-5) { - g_sensorDataFlag = 1; - } - return 0; -} -/* 用例执行前,初始化传感器接口实例 */ -void HdfSensorTest::SetUpTestCase() -{ - g_sensorDev = NewSensorInterfaceInstance(); - if (g_sensorDev == nullptr) { - printf("test sensorHdi get Module instace failed\n\r"); - } -} -/* 用例资源释放 */ -void HdfSensorTest::TearDownTestCase() -{ - if (g_sensorDev != nullptr) { - FreeSensorInterfaceInstance(); - g_sensorDev = nullptr; - } -} -/* 传感器驱动测试验证 */ -HWTEST_F(HdfSensorTest,TestAccelDriver_001, TestSize.Level0) -{ - int32_t sensorInterval = 1000000000; /* 数据采样率单位纳秒 */ - int32_t pollTime = 5; /* 数据采样时间单位秒 */ - int32_t accelSensorId = 1; /* 加速度传感器类型标识为1 */ - int32_t count = 0; - int ret; - struct SensorInformation *sensorInfo = nullptr; - - ret = g_sensorDev->Register(SensorTestDataCallback) - EXPECT_EQ(SENSOR_NULL_PTR, ret); - - ret = g_sensorDev->GetAllSensors(&sensorInfo, &count); - EXPECT_EQ(0, ret); - if (sensorInfo == nullptr) { - EXPECT_NE(nullptr, sensorInfo); - return; - } - /* 打印获取的传感器列表 */ - for (int i = 0; i < count; i++) { - printf("get sensoriId[%d], info name[%s]\n\r", sensorInfo[i]->sensorId, sensorInfo[i]->sensorName); - } - ret = g_sensorDev->Enable(accelSensorId); - EXPECT_EQ(0, ret); - g_sensorDataFlag = 0; - - ret = g_sensorDev->SetBatch(accelSensorId, sensorInterval, pollTime); - EXPECT_EQ(0, ret); - /* 在时间pollTime内,观察输出打印数据 */ - OsalSleep(pollTime); - EXPECT_EQ(1, g_sensorDataFlag); - - ret = g_sensorDev->Disable(accelSensorId); - g_sensorDataFlag = 0; - EXPECT_EQ(0, ret); - - ret = g_sensorDev->Unregister(); - EXPECT_EQ(0, ret); -} -``` +- 驱动开发完成后,在传感器单元测试里面开发自测试用例,验证驱动基本功能。测试环境采用开发者自测试平台。 + + ``` + /* 标识是否上报传感器数据 */ + static int32_t g_sensorDataFlag = 0; + /* 保持获取的传感器接口实例地址 */ + static const struct SensorInterface *g_sensorDev = nullptr; + + /* 订阅者注册数据上报函数 */ + static int SensorTestDataCallback(struct SensorEvents *event) + { + if (event == nullptr) { + return -1; + } + float *data = (float*)event->data; + printf("time [%lld] sensor id [%d] x-[%f] y-[%f] z-[%f]\n\r", event->timestamp, + event->sensorId, (*data), *(data + 1), *(data + g_axisZ)); + if (*data > 1e-5) { + g_sensorDataFlag = 1; + } + return 0; + } + /* 用例执行前,初始化传感器接口实例 */ + void HdfSensorTest::SetUpTestCase() + { + g_sensorDev = NewSensorInterfaceInstance(); + if (g_sensorDev == nullptr) { + printf("test sensorHdi get Module instace failed\n\r"); + } + } + /* 用例资源释放 */ + void HdfSensorTest::TearDownTestCase() + { + if (g_sensorDev != nullptr) { + FreeSensorInterfaceInstance(); + g_sensorDev = nullptr; + } + } + /* 传感器驱动测试验证 */ + HWTEST_F(HdfSensorTest,TestAccelDriver_001, TestSize.Level0) + { + int32_t sensorInterval = 1000000000; /* 数据采样率单位纳秒 */ + int32_t pollTime = 5; /* 数据采样时间单位秒 */ + int32_t accelSensorId = 1; /* 加速度传感器类型标识为1 */ + int32_t count = 0; + int ret; + struct SensorInformation *sensorInfo = nullptr; + + ret = g_sensorDev->Register(SensorTestDataCallback) + EXPECT_EQ(SENSOR_NULL_PTR, ret); + + ret = g_sensorDev->GetAllSensors(&sensorInfo, &count); + EXPECT_EQ(0, ret); + if (sensorInfo == nullptr) { + EXPECT_NE(nullptr, sensorInfo); + return; + } + /* 打印获取的传感器列表 */ + for (int i = 0; i < count; i++) { + printf("get sensoriId[%d], info name[%s]\n\r", sensorInfo[i]->sensorId, sensorInfo[i]->sensorName); + } + ret = g_sensorDev->Enable(accelSensorId); + EXPECT_EQ(0, ret); + g_sensorDataFlag = 0; + + ret = g_sensorDev->SetBatch(accelSensorId, sensorInterval, pollTime); + EXPECT_EQ(0, ret); + /* 在时间pollTime内,观察输出打印数据 */ + OsalSleep(pollTime); + EXPECT_EQ(1, g_sensorDataFlag); + + ret = g_sensorDev->Disable(accelSensorId); + g_sensorDataFlag = 0; + EXPECT_EQ(0, ret); + + ret = g_sensorDev->Unregister(); + EXPECT_EQ(0, ret); + } + ``` -- Gitee From 94943eb107fbb25cf58255205336f027a56214b3 Mon Sep 17 00:00:00 2001 From: sunxuejiao Date: Tue, 30 Nov 2021 16:59:41 +0800 Subject: [PATCH 3/6] refresh sensor file Signed-off-by: sunxuejiao --- .../driver/driver-peripherals-sensor-des.md | 153 +++++++++--------- 1 file changed, 76 insertions(+), 77 deletions(-) diff --git a/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md b/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md index 91c09862bbb..2a291c329cc 100644 --- a/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md +++ b/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md @@ -262,16 +262,16 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 - 加速度计传感器驱动入口函数实现 ``` - /* 注册加速度计传感器入口数据结构体对象 */ + // 注册加速度计传感器入口数据结构体对象 struct HdfDriverEntry g_sensorAccelDevEntry = { - .moduleVersion = 1, /* 加速度计传感器模块版本号 */ - .moduleName = "HDF_SENSOR_ACCEL", /* 加速度计传感器模块名,要与device_info.hcs文件里的加速度计moduleName字段值一样*/ - .Bind = BindAccelDriver, /* 加速度计传感器绑定函数 */ - .Init = InitAccelDriver, /* 加速度计传感器初始化函数 */ - .Release = ReleaseAccelDriver, /* 加速度计传感器资源释放函数 */ + .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将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出 HDF_INIT(g_sensorAccelDevEntry); ``` @@ -280,16 +280,16 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 加速度传感器模型使用HCS作为配置描述源码,HCS配置字段详细介绍参考[配置管理](driver-hdf-manage.md)介绍。 ``` - /* 加速度计传感器设备HCS配置 */ + // 加速度计传感器设备HCS配置 device_sensor_accel :: device { device0 :: deviceNode { - policy = 1; /* policy字段是驱动服务发布的策略 */ - priority = 110; /* 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序 */ - preload = 0; /* 驱动按需加载字段,0表示加载,2表示不加载 */ - permission = 0664; /* 驱动创建设备节点权限 */ - moduleName = "HDF_SENSOR_ACCEL"; /* 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致 */ - serviceName = "sensor_accel"; /* 驱动对外发布服务的名称,必须唯一 */ - deviceMatchAttr = "hdf_sensor_accel_driver"; /* 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等 */ + policy = 1; // policy字段是驱动服务发布的策略 + priority = 110; // 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序 + preload = 0; // 驱动按需加载字段,0表示加载,2表示不加载 + permission = 0664; // 驱动创建设备节点权限 + moduleName = "HDF_SENSOR_ACCEL"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致 + serviceName = "sensor_accel"; // 驱动对外发布服务的名称,必须唯一 + deviceMatchAttr = "hdf_sensor_accel_driver"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等 } } ``` @@ -299,14 +299,14 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 - 开发者需要根据每种类型的传感器实现归一化接口。 ``` - /* 不使用函数暂时置空 */ + // 不使用函数暂时置空 static int32_t SetAccelInfo(struct SensorBasicInfo *info) { (void)info; return HDF_ERR_NOT_SUPPORT; } - /* 下发使能寄存器组的配置 */ + // 下发使能寄存器组的配置 static int32_t SetAccelEnable(void) { int32_t ret; @@ -341,7 +341,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 return HDF_SUCCESS; } - /* 下发去使能寄存器组的配置 */ + // 下发去使能寄存器组的配置 static int32_t SetAccelDisable(void) { int32_t ret; @@ -370,7 +370,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 return HDF_SUCCESS; } - /* 配置传感器采样率和数据上报间隔 */ + // 配置传感器采样率和数据上报间隔 static int32_t SetAccelBatch(int64_t samplingInterval, int64_t interval) { (void)interval; @@ -384,7 +384,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 return HDF_SUCCESS; } - /* 设置传感器工作模式,当前支持实时模式 */ + // 设置传感器工作模式,当前支持实时模式 static int32_t SetAccelMode(int32_t mode) { return (mode == SENSOR_WORK_MODE_REALTIME) ? HDF_SUCCESS : HDF_FAILURE; @@ -396,7 +396,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 return HDF_SUCCESS; } - /* 设置传感器可选配置 */ + // 设置传感器可选配置 static int32_t SetAccelOption(uint32_t option) { (void)option; @@ -409,7 +409,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 - 初始化入口函数init ``` - /* 加速度计传感器驱动对外提供的服务绑定到HDF框架 */ + // 加速度计传感器驱动对外提供的服务绑定到HDF框架 int32_t AccelBindDriver(struct HdfDeviceObject *device) { CHECK_NULL_PTR_RETURN_VALUE(device, HDF_ERR_INVALID_PARAM); @@ -427,7 +427,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 return HDF_SUCCESS; } - /* 挂载加速度计传感器驱动归一化的接口函数 */ + // 挂载加速度计传感器驱动归一化的接口函数 static int32_t InitAccelOps(struct SensorCfgData *config, struct SensorDeviceInfo *deviceInfo) { CHECK_NULL_PTR_RETURN_VALUE(config, HDF_ERR_INVALID_PARAM); @@ -446,7 +446,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 return HDF_SUCCESS; } - /* 提供给差异化驱动的初始化接口,完成加速度器件基本配置信息解析(加速度信息,加速度总线配置,加速度器件探测寄存器配置),器件探测,器件寄存器解析 */ + // 提供给差异化驱动的初始化接口,完成加速度器件基本配置信息解析(加速度信息,加速度总线配置,加速度器件探测寄存器配置),器件探测,器件寄存器解析 static int32_t InitAccelAfterDetected(struct SensorCfgData *config) { struct SensorDeviceInfo deviceInfo; @@ -502,7 +502,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 return drvData->accelCfg; …… } - /* 加速度计传感器驱动初始化入口函数,主要功能为对传感器私有数据的结构体对象进行初始化,传感器HCS数据配置对象空间分配,传感器HCS数据配置初始化入口函数调用,传感器设备探测是否在位功能,传感器数据上报定时器创建,传感器归一化接口挂载,传感器设备注册功能 */ + // 加速度计传感器驱动初始化入口函数,主要功能为对传感器私有数据的结构体对象进行初始化,传感器HCS数据配置对象空间分配,传感器HCS数据配置初始化入口函数调用,传感器设备探测是否在位功能,传感器数据上报定时器创建,传感器归一化接口挂载,传感器设备注册功能 int32_t InitAccelDriver(struct HdfDeviceObject *device) { int32_t AccelInitDriver(struct HdfDeviceObject *device) @@ -525,7 +525,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 return HDF_SUCCESS; } - /* 释放驱动初始化时分配的资源 */ + // 释放驱动初始化时分配的资源 void AccelReleaseDriver(struct HdfDeviceObject *device) { CHECK_NULL_PTR_RETURN(device); @@ -553,31 +553,31 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 root { sensorAccelConfig { accelChipConfig { - /* 传感器设备信息模板 */ + // 传感器设备信息模板 template sensorInfo { - sensorName = "accelerometer"; /* 加速度计名字,字符最大长度16字节 */ - vendorName = "borsh_bmi160"; /* 传感器设备厂商,字符最大长度16字节 */ - firmwareVersion = "1.0"; /* 传感器固件版本号,默认1.0,字符最大长度16字节 */ - hardwareVersion = "1.0"; /* 传感器硬件版本号,默认1.0,字符最大长度16字节 */ - sensorTypeId = 1; /* 传感器类型编号,详见{@link SensorTypeTag} */ - sensorId = 1; /* 传感器的标识号,有传感器驱动开发者定义,推荐用{@link SensorTypeTag}枚举 */ - maxRange = 8; /* 传感器的最大量程,根据开发者需要配置 */ - accuracy = 0; /* 传感器的精度,与上报数据配合使用,上报数据结构体{@link SensorEvents } */ - power = 230; /* 传感器的功耗 */ + sensorName = "accelerometer"; // 加速度计名字,字符最大长度16字节 + vendorName = "borsh_bmi160"; // 传感器设备厂商,字符最大长度16字节 + firmwareVersion = "1.0"; // 传感器固件版本号,默认1.0,字符最大长度16字节 + hardwareVersion = "1.0"; // 传感器硬件版本号,默认1.0,字符最大长度16字节 + sensorTypeId = 1; // 传感器类型编号,详见{@link SensorTypeTag} + sensorId = 1; // 传感器的标识号,有传感器驱动开发者定义,推荐用{@link SensorTypeTag}枚举 + maxRange = 8; // 传感器的最大量程,根据开发者需要配置 + accuracy = 0; // 传感器的精度,与上报数据配合使用,上报数据结构体{@link SensorEvents } + power = 230; // 传感器的功耗 } - /* 传感器使用的总线类型和配置信息模板 */ + // 传感器使用的总线类型和配置信息模板 template sensorBusConfig { - busType = 0; /* 0:i2c 1:spi */ - busNum = 6; /* 芯片上分配给传感器的器件号 */ - busAddr = 0; /* 芯片上分配给传感器的地址 */ - regWidth = 1; /* 传感器寄存器地址宽度 */ - regBigEndian = 0; /* 传感器寄存器大小端 */ + busType = 0; // 0:i2c 1:spi + busNum = 6; // 芯片上分配给传感器的器件号 + busAddr = 0; // 芯片上分配给传感器的地址 + regWidth = 1; // 传感器寄存器地址宽度 + regBigEndian = 0; // 传感器寄存器大小端 } - /* 传感器设备属性模板 */ + // 传感器设备属性模板 template sensorAttr { - chipName = ""; /* 传感器芯片名字 */ - chipIdRegister = 0xf; /* 传感器在位检测寄存器地址 */ - chipIdValue = 0xd1; /* 校验传感器在位检测寄存器值 */ + chipName = ""; // 传感器芯片名字 + chipIdRegister = 0xf; // 传感器在位检测寄存器地址 + chipIdValue = 0xd1; // 校验传感器在位检测寄存器值 } } } @@ -587,7 +587,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 - 开发者使用传感器HCS配置,在accel_config.hcs里面配置通用的传感器模板,加速度计器件直接引用模板并修改对应的属性值,在此基础上新增寄存器配置,生成accel_bmi160_config.hcs配置文件。 ``` - /* 根据不同器件硬件差异,修改模板配置,不修改的就会默认采用模板配置 */ + // 根据不同器件硬件差异,修改模板配置,不修改的就会默认采用模板配置 #include "accel_config.hcs" root { accel_bmi160_chip_config : sensorConfig { @@ -609,18 +609,17 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 chipIdValue = 0xd1; } sensorRegConfig { - /* regAddr: register address - value: config register value - len: size of value - mask: mask of value - delay: config register delay time (ms) - opsType: enum SensorOpsType 0-none 1-read 2-write 3-read_check 4-update_bit - calType: enum SensorBitCalType 0-none 1-set 2-revert 3-xor 4-left shift 5-right shift - shiftNum: shift bits - debug: 0-no debug 1-debug - save: 0-no save 1-save - */ - /* regAddr, value, mask, len, delay, opsType, calType, shiftNum, debug, save */ + // regAddr: register address + // value: config register value + // len: size of value + // mask: mask of value + // delay: config register delay time (ms) + // opsType: enum SensorOpsType 0-none 1-read 2-write 3-read_check 4-update_bit + // calType: enum SensorBitCalType 0-none 1-set 2-revert 3-xor 4-left shift 5-right shift + // shiftNum: shift bits + // debug: 0-no debug 1-debug + // save: 0-no save 1-save + // regAddr, value, mask, len, delay, opsType, calType, shiftNum, debug, save // 初始化寄存器组 initSeqConfig = [ 0x7e, 0xb6, 0xff, 1, 5, 2, 0, 0, 0, 0, @@ -640,7 +639,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 } } ``` - + 5. 加速度计差异化驱动实现示例 - 定义加速度差异化驱动对应的HdfDriverEntry对象,其中Driver Entry入口函数定义如下: @@ -756,9 +755,9 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 7. 主要的数据结构 ``` - /* 传感器2g对应灵敏度转换值 */ + // 传感器2g对应灵敏度转换值 #define BMI160_ACC_SENSITIVITY_2G 61 - /* 传感器数据采样寄存器地址 */ + // 传感器数据采样寄存器地址 #define BMI160_ACCEL_X_LSB_ADDR 0X12 #define BMI160_ACCEL_X_MSB_ADDR 0X13 #define BMI160_ACCEL_Y_LSB_ADDR 0X14 @@ -766,20 +765,20 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 #define BMI160_ACCEL_Z_LSB_ADDR 0X16 #define BMI160_ACCEL_Z_MSB_ADDR 0X17 #define BMI160_STATUS_ADDR 0X1B - /* 传感器数据维度 */ + // 传感器数据维度 enum AccelAxisNum { ACCEL_X_AXIS = 0, ACCEL_Y_AXIS = 1, ACCEL_Z_AXIS = 2, ACCEL_AXIS_NUM = 3, }; - /* 传感器每个维度值 */ + // 传感器每个维度值 struct AccelData { int32_t x; int32_t y; int32_t z; }; - /* 传感器私有数据结构体 */ + // 传感器私有数据结构体 struct AccelDrvData { struct IDeviceIoService ioService; struct HdfDeviceObject *device; @@ -792,7 +791,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 struct SensorCfgData *accelCfg; struct AccelOpsCall ops; }; - /* 差异化适配函数 */ + // 差异化适配函数 struct AccelOpsCall { int32_t (*Init)(struct SensorCfgData *data); int32_t (*ReadData)(struct SensorCfgData *data); @@ -804,12 +803,12 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 - 驱动开发完成后,在传感器单元测试里面开发自测试用例,验证驱动基本功能。测试环境采用开发者自测试平台。 ``` - /* 标识是否上报传感器数据 */ + // 标识是否上报传感器数据 static int32_t g_sensorDataFlag = 0; - /* 保持获取的传感器接口实例地址 */ + // 保持获取的传感器接口实例地址 static const struct SensorInterface *g_sensorDev = nullptr; - /* 订阅者注册数据上报函数 */ + // 订阅者注册数据上报函数 static int SensorTestDataCallback(struct SensorEvents *event) { if (event == nullptr) { @@ -823,7 +822,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 } return 0; } - /* 用例执行前,初始化传感器接口实例 */ + // 用例执行前,初始化传感器接口实例 void HdfSensorTest::SetUpTestCase() { g_sensorDev = NewSensorInterfaceInstance(); @@ -831,7 +830,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 printf("test sensorHdi get Module instace failed\n\r"); } } - /* 用例资源释放 */ + // 用例资源释放 void HdfSensorTest::TearDownTestCase() { if (g_sensorDev != nullptr) { @@ -839,12 +838,12 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 g_sensorDev = nullptr; } } - /* 传感器驱动测试验证 */ + // 传感器驱动测试验证 HWTEST_F(HdfSensorTest,TestAccelDriver_001, TestSize.Level0) { - int32_t sensorInterval = 1000000000; /* 数据采样率单位纳秒 */ - int32_t pollTime = 5; /* 数据采样时间单位秒 */ - int32_t accelSensorId = 1; /* 加速度传感器类型标识为1 */ + int32_t sensorInterval = 1000000000; // 数据采样率单位纳秒 + int32_t pollTime = 5; // 数据采样时间单位秒 + int32_t accelSensorId = 1; // 加速度传感器类型标识为1 int32_t count = 0; int ret; struct SensorInformation *sensorInfo = nullptr; @@ -858,7 +857,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 EXPECT_NE(nullptr, sensorInfo); return; } - /* 打印获取的传感器列表 */ + // 打印获取的传感器列表 for (int i = 0; i < count; i++) { printf("get sensoriId[%d], info name[%s]\n\r", sensorInfo[i]->sensorId, sensorInfo[i]->sensorName); } @@ -868,7 +867,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 ret = g_sensorDev->SetBatch(accelSensorId, sensorInterval, pollTime); EXPECT_EQ(0, ret); - /* 在时间pollTime内,观察输出打印数据 */ + // 在时间pollTime内,观察输出打印数据 OsalSleep(pollTime); EXPECT_EQ(1, g_sensorDataFlag); -- Gitee From 47041e7781cb1a855fcfa436901d6bf2a6e64dd7 Mon Sep 17 00:00:00 2001 From: sunxuejiao Date: Wed, 1 Dec 2021 09:33:45 +0800 Subject: [PATCH 4/6] file sensor file Signed-off-by: sunxuejiao --- .../driver/driver-peripherals-sensor-des.md | 181 +++++++++--------- 1 file changed, 91 insertions(+), 90 deletions(-) diff --git a/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md b/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md index 2a291c329cc..3b8a1b22c3d 100644 --- a/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md +++ b/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md @@ -609,17 +609,18 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 chipIdValue = 0xd1; } sensorRegConfig { - // regAddr: register address - // value: config register value - // len: size of value - // mask: mask of value - // delay: config register delay time (ms) - // opsType: enum SensorOpsType 0-none 1-read 2-write 3-read_check 4-update_bit - // calType: enum SensorBitCalType 0-none 1-set 2-revert 3-xor 4-left shift 5-right shift - // shiftNum: shift bits - // debug: 0-no debug 1-debug - // save: 0-no save 1-save - // regAddr, value, mask, len, delay, opsType, calType, shiftNum, debug, save + /* regAddr: register address + value: config register value + len: size of value + mask: mask of value + delay: config register delay time (ms) + opsType: enum SensorOpsType 0-none 1-read 2-write 3-read_check 4-update_bit + calType: enum SensorBitCalType 0-none 1-set 2-revert 3-xor 4-left shift 5-right shift + shiftNum: shift bits + debug: 0-no debug 1-debug + save: 0-no save 1-save + */ + // regAddr, value, mask, len, delay, opsType, calType, shiftNum, debug, save // 初始化寄存器组 initSeqConfig = [ 0x7e, 0xb6, 0xff, 1, 5, 2, 0, 0, 0, 0, @@ -800,83 +801,83 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 ## 测试指导 -- 驱动开发完成后,在传感器单元测试里面开发自测试用例,验证驱动基本功能。测试环境采用开发者自测试平台。 - - ``` - // 标识是否上报传感器数据 - static int32_t g_sensorDataFlag = 0; - // 保持获取的传感器接口实例地址 - static const struct SensorInterface *g_sensorDev = nullptr; - - // 订阅者注册数据上报函数 - static int SensorTestDataCallback(struct SensorEvents *event) - { - if (event == nullptr) { - return -1; - } - float *data = (float*)event->data; - printf("time [%lld] sensor id [%d] x-[%f] y-[%f] z-[%f]\n\r", event->timestamp, - event->sensorId, (*data), *(data + 1), *(data + g_axisZ)); - if (*data > 1e-5) { - g_sensorDataFlag = 1; - } - return 0; - } - // 用例执行前,初始化传感器接口实例 - void HdfSensorTest::SetUpTestCase() - { - g_sensorDev = NewSensorInterfaceInstance(); - if (g_sensorDev == nullptr) { - printf("test sensorHdi get Module instace failed\n\r"); - } - } - // 用例资源释放 - void HdfSensorTest::TearDownTestCase() - { - if (g_sensorDev != nullptr) { - FreeSensorInterfaceInstance(); - g_sensorDev = nullptr; - } - } - // 传感器驱动测试验证 - HWTEST_F(HdfSensorTest,TestAccelDriver_001, TestSize.Level0) - { - int32_t sensorInterval = 1000000000; // 数据采样率单位纳秒 - int32_t pollTime = 5; // 数据采样时间单位秒 - int32_t accelSensorId = 1; // 加速度传感器类型标识为1 - int32_t count = 0; - int ret; - struct SensorInformation *sensorInfo = nullptr; - - ret = g_sensorDev->Register(SensorTestDataCallback) - EXPECT_EQ(SENSOR_NULL_PTR, ret); - - ret = g_sensorDev->GetAllSensors(&sensorInfo, &count); - EXPECT_EQ(0, ret); - if (sensorInfo == nullptr) { - EXPECT_NE(nullptr, sensorInfo); - return; - } - // 打印获取的传感器列表 - for (int i = 0; i < count; i++) { - printf("get sensoriId[%d], info name[%s]\n\r", sensorInfo[i]->sensorId, sensorInfo[i]->sensorName); - } - ret = g_sensorDev->Enable(accelSensorId); - EXPECT_EQ(0, ret); - g_sensorDataFlag = 0; - - ret = g_sensorDev->SetBatch(accelSensorId, sensorInterval, pollTime); - EXPECT_EQ(0, ret); - // 在时间pollTime内,观察输出打印数据 - OsalSleep(pollTime); - EXPECT_EQ(1, g_sensorDataFlag); - - ret = g_sensorDev->Disable(accelSensorId); - g_sensorDataFlag = 0; - EXPECT_EQ(0, ret); - - ret = g_sensorDev->Unregister(); - EXPECT_EQ(0, ret); - } - ``` +驱动开发完成后,在传感器单元测试里面开发自测试用例,验证驱动基本功能。测试环境采用开发者自测试平台。 + +``` +// 标识是否上报传感器数据 +static int32_t g_sensorDataFlag = 0; +// 保持获取的传感器接口实例地址 +static const struct SensorInterface *g_sensorDev = nullptr; + +// 订阅者注册数据上报函数 +static int SensorTestDataCallback(struct SensorEvents *event) +{ + if (event == nullptr) { + return -1; + } + float *data = (float*)event->data; + printf("time [%lld] sensor id [%d] x-[%f] y-[%f] z-[%f]\n\r", event->timestamp, + event->sensorId, (*data), *(data + 1), *(data + g_axisZ)); + if (*data > 1e-5) { + g_sensorDataFlag = 1; + } + return 0; +} +// 用例执行前,初始化传感器接口实例 +void HdfSensorTest::SetUpTestCase() +{ + g_sensorDev = NewSensorInterfaceInstance(); + if (g_sensorDev == nullptr) { + printf("test sensorHdi get Module instace failed\n\r"); + } +} +// 用例资源释放 +void HdfSensorTest::TearDownTestCase() +{ + if (g_sensorDev != nullptr) { + FreeSensorInterfaceInstance(); + g_sensorDev = nullptr; + } +} +// 传感器驱动测试验证 +HWTEST_F(HdfSensorTest,TestAccelDriver_001, TestSize.Level0) +{ + int32_t sensorInterval = 1000000000; // 数据采样率单位纳秒 + int32_t pollTime = 5; // 数据采样时间单位秒 + int32_t accelSensorId = 1; // 加速度传感器类型标识为1 + int32_t count = 0; + int ret; + struct SensorInformation *sensorInfo = nullptr; + + ret = g_sensorDev->Register(SensorTestDataCallback) + EXPECT_EQ(SENSOR_NULL_PTR, ret); + + ret = g_sensorDev->GetAllSensors(&sensorInfo, &count); + EXPECT_EQ(0, ret); + if (sensorInfo == nullptr) { + EXPECT_NE(nullptr, sensorInfo); + return; + } + // 打印获取的传感器列表 + for (int i = 0; i < count; i++) { + printf("get sensoriId[%d], info name[%s]\n\r", sensorInfo[i]->sensorId, sensorInfo[i]->sensorName); + } + ret = g_sensorDev->Enable(accelSensorId); + EXPECT_EQ(0, ret); + g_sensorDataFlag = 0; + + ret = g_sensorDev->SetBatch(accelSensorId, sensorInterval, pollTime); + EXPECT_EQ(0, ret); + // 在时间pollTime内,观察输出打印数据 + OsalSleep(pollTime); + EXPECT_EQ(1, g_sensorDataFlag); + + ret = g_sensorDev->Disable(accelSensorId); + g_sensorDataFlag = 0; + EXPECT_EQ(0, ret); + + ret = g_sensorDev->Unregister(); + EXPECT_EQ(0, ret); +} +``` -- Gitee From 863a2916f2638d7710ab7ae99c44edc27b5262b6 Mon Sep 17 00:00:00 2001 From: sunxuejiao Date: Thu, 2 Dec 2021 09:32:57 +0800 Subject: [PATCH 5/6] refresh sensor file Signed-off-by: sunxuejiao --- .../driver/driver-peripherals-sensor-des.md | 108 +++++++++--------- 1 file changed, 53 insertions(+), 55 deletions(-) diff --git a/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md b/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md index 3b8a1b22c3d..93260bd5e1b 100644 --- a/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md +++ b/zh-cn/device-dev/driver/driver-peripherals-sensor-des.md @@ -262,7 +262,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 - 加速度计传感器驱动入口函数实现 ``` - // 注册加速度计传感器入口数据结构体对象 + /* 注册加速度计传感器入口数据结构体对象 */ struct HdfDriverEntry g_sensorAccelDevEntry = { .moduleVersion = 1, //加速度计传感器模块版本号 .moduleName = "HDF_SENSOR_ACCEL", //加速度计传感器模块名,要与device_info.hcs文件里的加速度计moduleName字段值一样 @@ -271,7 +271,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 .Release = ReleaseAccelDriver, // 加速度计传感器资源释放函数 }; - // 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出 + /* 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出 */ HDF_INIT(g_sensorAccelDevEntry); ``` @@ -280,7 +280,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 加速度传感器模型使用HCS作为配置描述源码,HCS配置字段详细介绍参考[配置管理](driver-hdf-manage.md)介绍。 ``` - // 加速度计传感器设备HCS配置 + /* 加速度计传感器设备HCS配置 */ device_sensor_accel :: device { device0 :: deviceNode { policy = 1; // policy字段是驱动服务发布的策略 @@ -299,14 +299,14 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 - 开发者需要根据每种类型的传感器实现归一化接口。 ``` - // 不使用函数暂时置空 + /* 不使用函数暂时置空 */ static int32_t SetAccelInfo(struct SensorBasicInfo *info) { (void)info; return HDF_ERR_NOT_SUPPORT; } - // 下发使能寄存器组的配置 + /* 下发使能寄存器组的配置 */ static int32_t SetAccelEnable(void) { int32_t ret; @@ -341,7 +341,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 return HDF_SUCCESS; } - // 下发去使能寄存器组的配置 + /* 下发去使能寄存器组的配置 */ static int32_t SetAccelDisable(void) { int32_t ret; @@ -370,7 +370,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 return HDF_SUCCESS; } - // 配置传感器采样率和数据上报间隔 + /* 配置传感器采样率和数据上报间隔 */ static int32_t SetAccelBatch(int64_t samplingInterval, int64_t interval) { (void)interval; @@ -384,7 +384,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 return HDF_SUCCESS; } - // 设置传感器工作模式,当前支持实时模式 + /* 设置传感器工作模式,当前支持实时模式 */ static int32_t SetAccelMode(int32_t mode) { return (mode == SENSOR_WORK_MODE_REALTIME) ? HDF_SUCCESS : HDF_FAILURE; @@ -396,7 +396,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 return HDF_SUCCESS; } - // 设置传感器可选配置 + /* 设置传感器可选配置 */ static int32_t SetAccelOption(uint32_t option) { (void)option; @@ -409,7 +409,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 - 初始化入口函数init ``` - // 加速度计传感器驱动对外提供的服务绑定到HDF框架 + /* 加速度计传感器驱动对外提供的服务绑定到HDF框架 */ int32_t AccelBindDriver(struct HdfDeviceObject *device) { CHECK_NULL_PTR_RETURN_VALUE(device, HDF_ERR_INVALID_PARAM); @@ -427,7 +427,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 return HDF_SUCCESS; } - // 挂载加速度计传感器驱动归一化的接口函数 + /* 挂载加速度计传感器驱动归一化的接口函数 */ static int32_t InitAccelOps(struct SensorCfgData *config, struct SensorDeviceInfo *deviceInfo) { CHECK_NULL_PTR_RETURN_VALUE(config, HDF_ERR_INVALID_PARAM); @@ -446,22 +446,22 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 return HDF_SUCCESS; } - // 提供给差异化驱动的初始化接口,完成加速度器件基本配置信息解析(加速度信息,加速度总线配置,加速度器件探测寄存器配置),器件探测,器件寄存器解析 + /* 提供给差异化驱动的初始化接口,完成加速度器件基本配置信息解析(加速度信息,加速度总线配置,加速度器件探测寄存器配置),器件探测,器件寄存器解析 */ static int32_t InitAccelAfterDetected(struct SensorCfgData *config) { struct SensorDeviceInfo deviceInfo; CHECK_NULL_PTR_RETURN_VALUE(config, HDF_ERR_INVALID_PARAM); - // 初始化加速度计接口函数 + /* 初始化加速度计接口函数 */ if (InitAccelOps(config, &deviceInfo) != HDF_SUCCESS) { HDF_LOGE("%s: Init accel ops failed", __func__); return HDF_FAILURE; } - // 注册加速度计设备到传感器管理模块 + /* 注册加速度计设备到传感器管理模块 */ if (AddSensorDevice(&deviceInfo) != HDF_SUCCESS) { HDF_LOGE("%s: Add accel device failed", __func__); return HDF_FAILURE; } - // 器件寄存器解析 + /* 器件寄存器解析 */ if (ParseSensorRegConfig(config) != HDF_SUCCESS) { HDF_LOGE("%s: Parse sensor register failed", __func__); (void)DeleteSensorDevice(&config->sensorInfo); @@ -473,7 +473,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 struct SensorCfgData *AccelCreateCfgData(const struct DeviceResourceNode *node) { …… - // 如果探测不到器件在位,返回进行下个器件探测 + /* 如果探测不到器件在位,返回进行下个器件探测 */ if (drvData->detectFlag) { HDF_LOGE("%s: Accel sensor have detected", __func__); return NULL; @@ -482,19 +482,19 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 HDF_LOGE("%s: Accel accelCfg pointer NULL", __func__); return NULL; } - // 设备基本配置信息解析 + /* 设备基本配置信息解析 */ if (GetSensorBaseConfigData(node, drvData->accelCfg) != HDF_SUCCESS) { HDF_LOGE("%s: Get sensor base config failed", __func__); goto BASE_CONFIG_EXIT; } - // 如果探测不到器件在位,返回进行下个器件探测 + /* 如果探测不到器件在位,返回进行下个器件探测 */ if (DetectSensorDevice(drvData->accelCfg) != HDF_SUCCESS) { HDF_LOGI("%s: Accel sensor detect device no exist", __func__); drvData->detectFlag = false; goto BASE_CONFIG_EXIT; } drvData->detectFlag = true; - // 器件寄存器解析 + /* 器件寄存器解析 */ if (InitAccelAfterDetected(drvData->accelCfg) != HDF_SUCCESS) { HDF_LOGE("%s: Accel sensor detect device no exist", __func__); goto INIT_EXIT; @@ -502,42 +502,42 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 return drvData->accelCfg; …… } - // 加速度计传感器驱动初始化入口函数,主要功能为对传感器私有数据的结构体对象进行初始化,传感器HCS数据配置对象空间分配,传感器HCS数据配置初始化入口函数调用,传感器设备探测是否在位功能,传感器数据上报定时器创建,传感器归一化接口挂载,传感器设备注册功能 + /* 加速度计传感器驱动初始化入口函数,主要功能为对传感器私有数据的结构体对象进行初始化,传感器HCS数据配置对象空间分配,传感器HCS数据配置初始化入口函数调用,传感器设备探测是否在位功能,传感器数据上报定时器创建,传感器归一化接口挂载,传感器设备注册功能 */ int32_t InitAccelDriver(struct HdfDeviceObject *device) { int32_t AccelInitDriver(struct HdfDeviceObject *device) { …… - // 工作队列资源初始化 + /* 工作队列资源初始化 */ if (InitAccelData(drvData) != HDF_SUCCESS) { HDF_LOGE("%s: Init accel config failed", __func__); return HDF_FAILURE; } - // 分配加速度配置信息资源 + /* 分配加速度配置信息资源 */ drvData->accelCfg = (struct SensorCfgData *)OsalMemCalloc(sizeof(*drvData->accelCfg)); if (drvData->accelCfg == NULL) { HDF_LOGE("%s: Malloc accel config data failed", __func__); return HDF_FAILURE; } - // 挂接寄存器分组信息 + /* 挂接寄存器分组信息 */ drvData->accelCfg->regCfgGroup = &g_regCfgGroup[0]; …… return HDF_SUCCESS; } - // 释放驱动初始化时分配的资源 + /* 释放驱动初始化时分配的资源 */ void AccelReleaseDriver(struct HdfDeviceObject *device) { CHECK_NULL_PTR_RETURN(device); struct AccelDrvData *drvData = (struct AccelDrvData *)device->service; CHECK_NULL_PTR_RETURN(drvData); - // 器件在位,释放已分配资源 + /* 器件在位,释放已分配资源 */ if (drvData->detectFlag) { AccelReleaseCfgData(drvData->accelCfg); } OsalMemFree(drvData->accelCfg); drvData->accelCfg = NULL; - // 器件在位,销毁工作队列资源 + /* 器件在位,销毁工作队列资源 */ HdfWorkDestroy(&drvData->accelWork); HdfWorkQueueDestroy(&drvData->accelWorkQueue); OsalMemFree(drvData); @@ -549,11 +549,11 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 - 为了方便开发者使用传感器HCS私有配置,在sensor_common.hcs里面定义通用的传感器配置模板,加速度直接引用模板修改对应的属性值即可。 ``` - // accel sensor common config template + accel sensor common config template root { sensorAccelConfig { accelChipConfig { - // 传感器设备信息模板 + /* 传感器设备信息模板 */ template sensorInfo { sensorName = "accelerometer"; // 加速度计名字,字符最大长度16字节 vendorName = "borsh_bmi160"; // 传感器设备厂商,字符最大长度16字节 @@ -565,7 +565,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 accuracy = 0; // 传感器的精度,与上报数据配合使用,上报数据结构体{@link SensorEvents } power = 230; // 传感器的功耗 } - // 传感器使用的总线类型和配置信息模板 + /* 传感器使用的总线类型和配置信息模板 */ template sensorBusConfig { busType = 0; // 0:i2c 1:spi busNum = 6; // 芯片上分配给传感器的器件号 @@ -573,7 +573,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 regWidth = 1; // 传感器寄存器地址宽度 regBigEndian = 0; // 传感器寄存器大小端 } - // 传感器设备属性模板 + /* 传感器设备属性模板 */ template sensorAttr { chipName = ""; // 传感器芯片名字 chipIdRegister = 0xf; // 传感器在位检测寄存器地址 @@ -587,7 +587,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 - 开发者使用传感器HCS配置,在accel_config.hcs里面配置通用的传感器模板,加速度计器件直接引用模板并修改对应的属性值,在此基础上新增寄存器配置,生成accel_bmi160_config.hcs配置文件。 ``` - // 根据不同器件硬件差异,修改模板配置,不修改的就会默认采用模板配置 + /* 根据不同器件硬件差异,修改模板配置,不修改的就会默认采用模板配置 */ #include "accel_config.hcs" root { accel_bmi160_chip_config : sensorConfig { @@ -620,19 +620,19 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 debug: 0-no debug 1-debug save: 0-no save 1-save */ - // regAddr, value, mask, len, delay, opsType, calType, shiftNum, debug, save - // 初始化寄存器组 + /* regAddr, value, mask, len, delay, opsType, calType, shiftNum, debug, save */ + /* 初始化寄存器组 */ initSeqConfig = [ 0x7e, 0xb6, 0xff, 1, 5, 2, 0, 0, 0, 0, 0x7e, 0x10, 0xff, 1, 5, 2, 0, 0, 0, 0 ]; - // 使能寄存器组 + /* 使能寄存器组 */ enableSeqConfig = [ 0x7e, 0x11, 0xff, 1, 5, 2, 0, 0, 0, 0, 0x41, 0x03, 0xff, 1, 0, 2, 0, 0, 0, 0, 0x40, 0x08, 0xff, 1, 0, 2, 0, 0, 0, 0 ]; - // 去使能寄存器组 + /* 去使能寄存器组 */ disableSeqConfig = [ 0x7e, 0x10, 0xff, 1, 5, 2, 0, 0, 0, 0 ]; @@ -681,18 +681,18 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 int32_t Bmi160InitDriver(struct HdfDeviceObject *device) { …… - // 加速度计差异化初始化配置 + /* 加速度计差异化初始化配置 */ ret = InitAccelPreConfig(); if (ret != HDF_SUCCESS) { HDF_LOGE("%s: Init BMI160 bus mux config", __func__); return HDF_FAILURE; } - // 创建传感器配置数据接口,完成器件探测,私有数据配置解析 + /* 创建传感器配置数据接口,完成器件探测,私有数据配置解析 */ drvData->sensorCfg = AccelCreateCfgData(device->property); if (drvData->sensorCfg == NULL) { return HDF_ERR_NOT_SUPPORT; } - // 注册差异化接口 + /* 注册差异化接口 */ ops.Init = NULL; ops.ReadData = ReadBmi160Data; ret = AccelRegisterChipOps(&ops); @@ -700,7 +700,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 HDF_LOGE("%s: Register BMI160 accel failed", __func__); return HDF_FAILURE; } - // 初始化器件配置 + /* 初始化器件配置 *、 ret = InitBmi160(drvData->sensorCfg); if (ret != HDF_SUCCESS) { HDF_LOGE("%s: Init BMI160 accel failed", __func__); @@ -756,9 +756,9 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 7. 主要的数据结构 ``` - // 传感器2g对应灵敏度转换值 + /* 传感器2g对应灵敏度转换值 */ #define BMI160_ACC_SENSITIVITY_2G 61 - // 传感器数据采样寄存器地址 + /* 传感器数据采样寄存器地址 */ #define BMI160_ACCEL_X_LSB_ADDR 0X12 #define BMI160_ACCEL_X_MSB_ADDR 0X13 #define BMI160_ACCEL_Y_LSB_ADDR 0X14 @@ -766,20 +766,20 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 #define BMI160_ACCEL_Z_LSB_ADDR 0X16 #define BMI160_ACCEL_Z_MSB_ADDR 0X17 #define BMI160_STATUS_ADDR 0X1B - // 传感器数据维度 + /* 传感器数据维度 */ enum AccelAxisNum { ACCEL_X_AXIS = 0, ACCEL_Y_AXIS = 1, ACCEL_Z_AXIS = 2, ACCEL_AXIS_NUM = 3, }; - // 传感器每个维度值 + /* 传感器每个维度值 */ struct AccelData { int32_t x; int32_t y; int32_t z; }; - // 传感器私有数据结构体 + /* 传感器私有数据结构体 */ struct AccelDrvData { struct IDeviceIoService ioService; struct HdfDeviceObject *device; @@ -792,7 +792,7 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 struct SensorCfgData *accelCfg; struct AccelOpsCall ops; }; - // 差异化适配函数 + /* 差异化适配函数 */ struct AccelOpsCall { int32_t (*Init)(struct SensorCfgData *data); int32_t (*ReadData)(struct SensorCfgData *data); @@ -804,12 +804,10 @@ Sensor驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不 驱动开发完成后,在传感器单元测试里面开发自测试用例,验证驱动基本功能。测试环境采用开发者自测试平台。 ``` -// 标识是否上报传感器数据 -static int32_t g_sensorDataFlag = 0; -// 保持获取的传感器接口实例地址 -static const struct SensorInterface *g_sensorDev = nullptr; +static int32_t g_sensorDataFlag = 0; //标识是否上报传感器数据 +static const struct SensorInterface *g_sensorDev = nullptr; //保持获取的传感器接口实例地址 -// 订阅者注册数据上报函数 +/* 订阅者注册数据上报函数 */ static int SensorTestDataCallback(struct SensorEvents *event) { if (event == nullptr) { @@ -823,7 +821,7 @@ static int SensorTestDataCallback(struct SensorEvents *event) } return 0; } -// 用例执行前,初始化传感器接口实例 +/* 用例执行前,初始化传感器接口实例 */ void HdfSensorTest::SetUpTestCase() { g_sensorDev = NewSensorInterfaceInstance(); @@ -831,7 +829,7 @@ void HdfSensorTest::SetUpTestCase() printf("test sensorHdi get Module instace failed\n\r"); } } -// 用例资源释放 +/* 用例资源释放 */ void HdfSensorTest::TearDownTestCase() { if (g_sensorDev != nullptr) { @@ -839,7 +837,7 @@ void HdfSensorTest::TearDownTestCase() g_sensorDev = nullptr; } } -// 传感器驱动测试验证 +/* 传感器驱动测试验证 */ HWTEST_F(HdfSensorTest,TestAccelDriver_001, TestSize.Level0) { int32_t sensorInterval = 1000000000; // 数据采样率单位纳秒 @@ -858,7 +856,7 @@ HWTEST_F(HdfSensorTest,TestAccelDriver_001, TestSize.Level0) EXPECT_NE(nullptr, sensorInfo); return; } - // 打印获取的传感器列表 + /* 打印获取的传感器列表 */ for (int i = 0; i < count; i++) { printf("get sensoriId[%d], info name[%s]\n\r", sensorInfo[i]->sensorId, sensorInfo[i]->sensorName); } @@ -868,7 +866,7 @@ HWTEST_F(HdfSensorTest,TestAccelDriver_001, TestSize.Level0) ret = g_sensorDev->SetBatch(accelSensorId, sensorInterval, pollTime); EXPECT_EQ(0, ret); - // 在时间pollTime内,观察输出打印数据 + /* 在时间pollTime内,观察输出打印数据 */ OsalSleep(pollTime); EXPECT_EQ(1, g_sensorDataFlag); -- Gitee From 8f3574aa8c21c9037a99d588df405a536e66ee72 Mon Sep 17 00:00:00 2001 From: sunxuejiao Date: Fri, 14 Jan 2022 22:20:03 +0800 Subject: [PATCH 6/6] add vibrator file Signed-off-by: sunxuejiao --- .../driver/driver-peripherals-vibrator-des.md | 426 ++++++++++++++++++ ...1\345\212\250\346\250\241\345\236\213.png" | Bin 0 -> 22132 bytes 2 files changed, 426 insertions(+) create mode 100755 zh-cn/device-dev/driver/driver-peripherals-vibrator-des.md create mode 100755 "zh-cn/device-dev/driver/figures/Vibrator\351\251\261\345\212\250\346\250\241\345\236\213.png" diff --git a/zh-cn/device-dev/driver/driver-peripherals-vibrator-des.md b/zh-cn/device-dev/driver/driver-peripherals-vibrator-des.md new file mode 100755 index 00000000000..ae0c87d26c8 --- /dev/null +++ b/zh-cn/device-dev/driver/driver-peripherals-vibrator-des.md @@ -0,0 +1,426 @@ +# vibrator +- [概述](##概述) + + - [接口描述](###接口描述) + +- [开发指导](##开发指导) + + - [开发步骤](###开发步骤) + + - [开发实例](###开发实例) + +- [测试指导](##测试指导) + + +## 概述 + +​ 马达(vibrator)驱动模型为上层马达硬件服务层提供稳定的马达控制能力接口,包括马达一次振动,马达效果配置震动,马达停止的能力。基于HDF(**H**ardware **D**river **F**oundation)驱动框架开发的马达(vibrator)驱动模型,实现跨操作系统迁移,器件差异配置等功能。马达驱动模型如下图1所示: + +**图 1** 马达驱动模型图 + +![Vibrator驱动模型](figures\Vibrator驱动模型.png) + +### 接口描述 + +​ 马达驱动模型支持静态HCS配置的时间序列和动态配置持续时间两种振动效果配置能力。马达硬件服务调用StartOnce接口动态配置持续振动时间;调用StartEffect接口启动静态配置的振动效果。马达驱动模型对HDI开放的API接口能力,参考表1。 + +**表 1** 马达驱动模型对外API接口能力介绍 + +| 接口名 | 功能描述 | +| -------------------------------------- | -------------------------------------------------------- | +| int32_t StartOnce(uint32_t duration) | 按照指定持续时间触发振动马达,duration为振动持续时长。 | +| int32_t Start(const char *effectType) | 按照指定预置效果启动马达,effectType表示预置的预置效果。 | +| int32_t Stop(enum VibratorMode mode) | 按照指定的振动模式停止马达振动。 | + +## 开发指导 + +### 开发步骤 + +​ 为了快速开发或者移植传感器驱动,基于HDF(Hardware Driver Foundation)驱动框架开发了马达驱动模型。马达设备模型抽象,屏蔽设备驱动与系统交互的实现,为硬件服务层提供统一稳定的驱动接口能力,为驱动开发者提供开放的接口实现和抽象的配置接口能力。用于不同操作系统马达设备部件的部署指导和马达设备部件驱动的开发。马达具体的开发步骤如下: + +1. 基于HDF驱动框架,按照驱动Driver Entry程序,完成马达抽象驱动开发,主要有Bind,Init,Release,Dispatch函数接口实现,配置资源和HCS解析。 +2. 创建马达效果模型,解析马达效果HCS配置。 +3. 完成马达振动和停止接口开发,会根据振动效果的模式创建和销毁定时器。 +4. 马达驱动模型提供给开发者马达驱动差异化接口,开发者实现差异化接口。 + +### 开发实例 + +1. 马达驱动的初始化和去初始化 + + - 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出 马达驱动模型使用HCS作为配置描述源码,HCS配置字段详细介绍参考[配置管理](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/driver/driver-hdf-manage.md)介绍。其中Driver Entry入口函数定义如下: + + ``` + /* 注册马达抽象驱动入口数据结构体对象 */ + struct HdfDriverEntry g_vibratorDriverEntry = { + .moduleVersion = 1, //马达模块版本号 + .moduleName = "HDF_VIBRATOR", //马达模块名,要与device_info.hcs文件里的马达moduleName字段值一样 + .Bind = BindVibratorDriver, //马达绑定函数 + .Init = InitVibratorDriver, //马达初始化函数 + .Release = ReleaseVibratorDriver, //马达资源释放函数 + }; + + HDF_INIT(g_vibratorDriverEntry); + ``` + + - 基于HDF驱动框架,按照驱动Driver Entry程序,完成马达抽象驱动开发,主要有Bind,Init,Release,Dispatch函数接口实现。 + + ``` + /* 马达驱动对外发布的能力 */ + static int32_t DispatchVibrator(struct HdfDeviceIoClient *client, + int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply) + { + int32_t loop; + + for (loop = 0; loop < sizeof(g_vibratorCmdHandle) / sizeof(g_vibratorCmdHandle[0]); ++loop) { + if ((cmd == g_vibratorCmdHandle[loop].cmd) && (g_vibratorCmdHandle[loop].func != NULL)) { + return g_vibratorCmdHandle[loop].func(data, reply); + } + } + + return HDF_SUCCESS; + } + + /* 马达驱动对外提供的服务绑定到HDF框架 */ + int32_t BindVibratorDriver(struct HdfDeviceObject *device) + { + struct VibratorDriverData *drvData = NULL; + CHECK_VIBRATOR_NULL_PTR_RETURN_VALUE(device, HDF_FAILURE); + + drvData = (struct VibratorDriverData *)OsalMemCalloc(sizeof(*drvData)); + CHECK_VIBRATOR_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_MALLOC_FAIL); + + drvData->ioService.Dispatch = DispatchVibrator; + drvData->device = device; + device->service = &drvData->ioService; + g_vibratorDrvData = drvData; + + return HDF_SUCCESS; + } + + /* 马达驱动初始化入口函数*/ + int32_t InitVibratorDriver(struct HdfDeviceObject *device) + { + struct VibratorDriverData *drvData = NULL; + + drvData->mode = VIBRATOR_MODE_BUTT; + drvData->state = VIBRATOR_STATE_IDLE; + ...... + if (CreateVibratorHaptic(device) != HDF_SUCCESS) { + HDF_LOGE("%s: init workQueue fail!", __func__); + return HDF_FAILURE; + } + + return HDF_SUCCESS; + } + + + /* 释放马达驱动初始化时分配的资源 */ + void ReleaseVibratorDriver(struct HdfDeviceObject *device) + { + struct VibratorDriverData *drvData = NULL; + ...... + (void)DestroyVibratorHaptic(); + (void)OsalMutexDestroy(&drvData->mutex); + (void)OsalMemFree(drvData); + g_vibratorDrvData = NULL; + } + + ``` + + - 马达设备管理模块负责系统中马达器件接口发布,在系统启动过程中,HDF框架机制通过马达 Host里设备HCS配置信息,加载设备管理驱动。 + + ``` + /* 马达设备HCS配置 */ + vibrator :: host { + hostName = "vibrator_host"; + device_vibrator :: device { + device0 :: deviceNode { + policy = 2; //policy字段是驱动服务发布的策略 + priority = 100; //驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序 + preload = 0; //驱动按需加载字段,0表示加载,2表示不加载 + permission = 0664; //驱动创建设备节点权限 + moduleName = "HDF_VIBRATOR"; //驱动名称,该字段的值必须和驱动入口结构的moduleName值一致 + serviceName = "hdf_misc_vibrator"; //驱动对外发布服务的名称,必须唯一 + deviceMatchAttr = "hdf_vibrator_driver"; //驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等 + } + } + ``` + +2. 创建马达效果模型,解析马达效果hcs配置。 + + - 创建马达效果模型。 + + ``` + /* 创建马达效果模型,分配资源,解析马达HCS配置 */ + int32_t CreateVibratorHaptic(struct HdfDeviceObject *device) + { + struct VibratorHapticData *hapticData = NULL; + CHECK_VIBRATOR_NULL_PTR_RETURN_VALUE(device, HDF_FAILURE); + + hapticData = (struct VibratorHapticData *)OsalMemCalloc(sizeof(*hapticData)); + CHECK_VIBRATOR_NULL_PTR_RETURN_VALUE(hapticData, HDF_ERR_MALLOC_FAIL); + g_vibratorHapticData = hapticData; + hapticData->supportHaptic = false; + + if (OsalMutexInit(&hapticData->mutex) != HDF_SUCCESS) { + HDF_LOGE("%s: fail to init mutex", __func__); + goto EXIT; + } + + DListHeadInit(&hapticData->effectSeqHead); + + /* 解析马达效果HCS配置 */ + if (ParserVibratorHapticConfig(device->property) != HDF_SUCCESS) { + HDF_LOGE("%s: parser haptic config fail!", __func__); + goto EXIT; + } + + return HDF_SUCCESS; + EXIT: + OsalMemFree(hapticData); + return HDF_FAILURE; + } + ``` + + - 马达效果模型使用HCS作为配置描述源码,HCS配置字段详细介绍参考[配置管理](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/driver/driver-hdf-manage.md)介绍。 + + ``` + 马达数据配置模板(vibrator_config.hcs) + root { + vibratorConfig { + boardConfig { + match_attr = "hdf_vibrator_driver"; //需要和马达设备配置match_attr字段保持一致 + vibratorAttr { + /* 0:转子 1:线性 */ + deviceType = 1; //设备类型 + supportPreset = 1; //支持的预设类型 + } + vibratorHapticConfig { + haptic_clock_timer { + effectName = "haptic.clock.timer"; + type = 1; // 0 内置模式, 1 时间序列 + seq = [600, 600, 200, 600]; // 时间序列 + } + haptic_default_effect { + effectName = "haptic.default.effect"; + type = 0; + seq = [0, 3, 800, 1]; + } + } + } + } + } + ``` + +3. 完成马达振动和停止接口开发,会根据振动效果的模式创建和销毁定时器。 + + - 马达硬件服务调用StartOnce接口动态配置持续振动时间;调用StartEffect接口启动静态配置的振动效果,为驱动开发者提供抽象的配置接口能力。 + + ``` + /* 按照指定持续时间触发振动马达,duration为振动持续时长 */ + static int32_t StartOnce(struct HdfSBuf *data, struct HdfSBuf *reply) + { + uint32_t duration; + int32_t ret; + struct VibratorEffectCfg config; + struct VibratorDriverData *drvData = GetVibratorDrvData(); + (void)reply; + ...... + config.cfgMode = VIBRATOR_MODE_ONCE; + config.duration = duration; + config.effect = NULL; + /* 据振动效果的模式创建 */ + ret = StartHaptic(&config); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: start haptic fail!", __func__); + return ret; + } + + return HDF_SUCCESS; + } + + /* 按照预置效果启动马达,effectType表示预置的预置效果 */ + static int32_t StartEffect(struct HdfSBuf *data, struct HdfSBuf *reply) + { + int32_t ret; + const char *effect = NULL; + struct VibratorEffectCfg config; + struct VibratorDriverData *drvData = GetVibratorDrvData(); + (void)reply; + ...... + config.cfgMode = VIBRATOR_MODE_PRESET; + config.duration = 0; + config.effect = effect; + + ret = StartHaptic(&config); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: start haptic fail!", __func__); + return ret; + } + + return HDF_SUCCESS; + } + + /* 按照指定的振动模式停止马达振动 */ + static int32_t Stop(struct HdfSBuf *data, struct HdfSBuf *reply) + { + int32_t ret; + int32_t mode; + struct VibratorDriverData *drvData = GetVibratorDrvData(); + (void)reply; + ...... + /* 停止马达效果振动,销毁马达定时器 */ + ret = StopHaptic(); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: stop haptic fail!", __func__); + return ret; + } + + (void)OsalMutexLock(&drvData->mutex); + drvData->mode = VIBRATOR_MODE_BUTT; + (void)OsalMutexUnlock(&drvData->mutex); + + return HDF_SUCCESS; + } + ``` + +4. 马达驱动模型提供给开发者马达驱动差异化接口,开发者实现差异化接口。 + + - 此接口在差异化器件驱动初始化成功时,注册差异实现接口,方便实现器件差异的驱动接口。 + + ``` + /* 注册马达差异化实现接口 */ + int32_t RegisterVibrator(struct VibratorOps *ops) + { + struct VibratorDriverData *drvData = GetVibratorDrvData(); + + CHECK_VIBRATOR_NULL_PTR_RETURN_VALUE(ops, HDF_FAILURE); + CHECK_VIBRATOR_NULL_PTR_RETURN_VALUE(drvData, HDF_FAILURE); + + (void)OsalMutexLock(&drvData->mutex); + drvData->ops.Start = ops->Start; + drvData->ops.StartEffect = ops->StartEffect; + drvData->ops.Stop = ops->Stop; + (void)OsalMutexUnlock(&drvData->mutex); + + return HDF_SUCCESS; + } + ``` + + - 马达驱动模型提供给开发者马达驱动差异化接口,具体实现如下: + + ``` + /* 按照指定持续时间触发马达线性驱动 */ + static int32_t StartLinearVibrator() + { + int32_t ret; + struct VibratorLinearDriverData *drvData = GetLinearVibratorData(); + CHECK_VIBRATOR_NULL_PTR_RETURN_VALUE(drvData, HDF_FAILURE); + ...... + ret = GpioWrite(drvData->gpioNum, GPIO_VAL_LOW); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: pull gpio%d to %d level failed", __func__, drvData->gpioNum, GPIO_VAL_LOW); + return ret; + } + return HDF_SUCCESS; + } + + /* 按照预置振动效果启动马达线性驱动 */ + static int32_t StartEffectLinearVibrator(uint32_t effectType) + { + (void)effectType; + HDF_LOGE("%s: vibrator set build-in effect no support!", __func__); + return HDF_SUCCESS; + } + + /* 按照指定的振动模式停止马达线性驱动 */ + static int32_t StopLinearVibrator() + { + int32_t ret; + struct VibratorLinearDriverData *drvData = GetLinearVibratorData(); + CHECK_VIBRATOR_NULL_PTR_RETURN_VALUE(drvData, HDF_FAILURE); + ...... + ret = GpioWrite(drvData->gpioNum, GPIO_VAL_HIGH); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: pull gpio%d to %d level failed", __func__, drvData->gpioNum, GPIO_VAL_HIGH); + return ret; + } + return HDF_SUCCESS; + } + ``` + +## 测试指导 + +驱动开发完成后,在马达单元测试里面开发自测试用例,验证驱动基本功能。测试环境采用开发者自测试平台。 + +``` +/* 用例执行前,初始化马达接口实例 */ +void HdfVibratorTest::SetUpTestCase() +{ + g_vibratorDev = NewVibratorInterfaceInstance(); +} + +/* 用例执行后,释放用例资源 */ +void HdfVibratorTest::TearDownTestCase() +{ + if(g_vibratorDev != nullptr){ + FreeVibratorInterfaceInstance(); + g_vibratorDev = nullptr; + } +} + +/* 测试马达在给定时间内的单次震动 */ +HWTEST_F(HdfVibratorTest, PerformOneShotVibratorDuration001, TestSize.Level1) +{ + ASSERT_NE(nullptr, g_vibratorDev); + + int32_t startRet = g_vibratorDev->StartOnce(g_duration); + EXPECT_EQ(startRet, HDF_SUCCESS); + + OsalMSleep(g_sleepTime1); + + int32_t endRet = g_vibratorDev->Stop(VIBRATOR_MODE_ONCE); //停止马达的单次震动。 + EXPECT_EQ(endRet, HDF_SUCCESS); +} + +/* 测试马达在时间序列的震动效果 */ +HWTEST_F(HdfVibratorTest, ExecuteVibratorEffect001, TestSize.Level1) +{ + ASSERT_NE(nullptr, g_vibratorDev); + + int32_t startRet = g_vibratorDev->Start(g_timeSequence); + EXPECT_EQ(startRet, HDF_SUCCESS); + + OsalMSleep(g_sleepTime2); + + int32_t endRet = g_vibratorDev->Stop(VIBRATOR_MODE_PRESET); + EXPECT_EQ(endRet, HDF_SUCCESS); +} + +/* 通过内置震动效果来控制马达的震动*/ +HWTEST_F(HdfVibratorTest, ExecuteVibratorEffect002, TestSize.Level1) +{ + ASSERT_NE(nullptr, g_vibratorDev); + + int32_t startRet = g_vibratorDev->Start(g_builtIn); + EXPECT_EQ(startRet, HDF_SUCCESS); + + OsalMSleep(g_sleepTime1); + + int32_t endRet = g_vibratorDev->Stop(VIBRATOR_MODE_PRESET); + EXPECT_EQ(endRet, HDF_SUCCESS); +} + +/* 测试马达任意字符串的一次性震动 */ +HWTEST_F(HdfVibratorTest, ExecuteVibratorEffect007, TestSize.Level1) +{ + ASSERT_NE(nullptr, g_vibratorDev); + + int32_t startRet = g_vibratorDev->Start(g_arbitraryStr); + EXPECT_EQ(startRet, HDF_FAILURE); + + int32_t endRet = g_vibratorDev->Stop(VIBRATOR_MODE_ONCE); + EXPECT_EQ(endRet, HDF_SUCCESS); +} +``` + diff --git "a/zh-cn/device-dev/driver/figures/Vibrator\351\251\261\345\212\250\346\250\241\345\236\213.png" "b/zh-cn/device-dev/driver/figures/Vibrator\351\251\261\345\212\250\346\250\241\345\236\213.png" new file mode 100755 index 0000000000000000000000000000000000000000..4f7e6e807a880dd80526de5560d93842adb17ef7 GIT binary patch literal 22132 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 literal 0 HcmV?d00001 -- Gitee