作者介绍
刘飞虎(kevin),担任openharmony社区sig_driverframework组committer,主要负责传感器驱动模型驱动开发工作,贡献传感器驱动模型驱动和加速度传感器驱动。
概述
随着物联网,移动互联网的快速发展,在数字时代,传感器在智能交通,智能工业,智能穿戴等领域有着广阔的应用空间。传感器是检测到被测量信息,将非电量信息转换成电信号的检测装置。就像眼睛是人类心灵的窗户,传感器则是计算机感知世界万物的眼睛。
近年来,传感器技术和制造工艺的快速发展,目前市场可供开发者选择的传感器越来越多,比如:加速度传感器,陀螺仪传感器,磁力传感器,温度传感器等类型。每种传感器厂家都有各自的传感器驱动。
在产品开发时就需要对不同厂家或者同一厂家的不同型号进行适配开发,就会增加开发者的开发难度。为了快速开发或者移植传感器驱动,基于hdf(hardware driver foundation)驱动框架开发了sensor(传感器)驱动模型。sensor驱动模型主要为上层提供稳定接口能力,对驱动开发者提供开放的接口实现和抽象的配置接口能力。
传感器模型框架介绍
sensor设备作为外接设备重要组成模块,sensor驱动模型为上层sensor服务系统提供稳定的sensor基础能力接口,包括sensor列表查询、sensor启停、sensor订阅及去订阅,sensor参数配置等功能。传感器驱动模型总体框架如图1所示。
图1 传感器驱动模型总体框架图
sensor驱动抽象模型主要位于openharmony软件的hal层,其核心包括三个子模块:
1)sensor hdi子模块:提供sensor南向的标准接口定义和实现。
2)sensor设备管理和通用配置子模块:提供sensor设备管理,sensor通用配置能力,sensor通用数据解析能力。
3)sensor器件驱动子模块:提供sensor器件通用驱动和差异化驱动实现。
传感器设备驱动模型介绍
sensor设备作为外接设备重要组成模块,通过sensor驱动模型屏蔽硬件器件差异,为上层sensor服务系统提供稳定的sensor基础能力接口,包括sensor列表查询、sensor启停、sensor订阅及取消订阅,sensor参数配置等功能;
sensor设备驱动的开发是基于hdf驱动框架基础上,结合操作系统适配层(osal)和平台驱动接口(比如i2c/spi/uart总线等平台资源)能力,屏蔽不同操作系统和平台总线资源差异,实现sensor驱动“一次开发,多系统部署”的目标。传感器设备驱动模型框图如图2。
图2 传感器驱动模型框图
sensor驱动模型作为hdf框架中一个device host(驱动宿主),完成对sensor设备管理,包括sensor驱动加载,注册,卸载,绑定,配置管理,接口发布。
sensor驱动模型主要包括sensor hdi子模块,sensor设备管理和通用配置子模块和sensor器件驱动子模块。sensor hdi子模块抽象出sensor设备的基本能力sensor列表查询、sensor启停、sensor订阅及取消订阅,sensor参数配置接口,接口和类型定义参考sensor_if.h和sensor_type.h。
sensor设备管理和通用配置子模块,其中,sensor设备管理完成sensor设备的注册、管理能力,数据报告能力,接口定义参考sensor_device_if.h;通用配置子模块完成寄存器配置操作接口抽象,sensor hcs通用配置解析能力,接口定义参考sensor_config_parser.h、sensor_config_controller.h。sensor器件驱动子模块完成每类sensor类型驱动的抽象和器件差异化驱动实现。
传感器驱动模型工作流程解析
通过介绍sensor驱动模型的加载以及运行流程,对模型内部关键组件以及关联组件之间的关系进行了划分,整体加载流程如图3。
图3 sensor驱动模型运行图
sensor驱动模型以标准系统hi3516dv300产品中的加速度计驱动为例,介绍整个驱动加载及运行流程为:
1)从device info hcs 的sensor host里读取sensor设备管理配置信息。
2)hdf配置框架从hcb数据库解析sensor设备管理配置信息,并关联到对应设备驱动。
3)加载并初始化sensor设备管理驱动。
4)sensor设备管理驱动向hdi发布sensor基础能力接口。
5)从device info hcs 的sensor host里读取加速度计驱动配置信息。
6)加载加速度抽象驱动,调用初始化接口,完成sensor器件驱动资源分配和数据处理队列创建。
7)从accel_xxx_config hcs里读取加速度器件差异化驱动配置和私有化配置信息。
8)加速度计差异化驱动,调用通用配置解析接口,完成器件属性信息解析,器件寄存器解析。
9)加速度计差异化驱动完成器件探测,并分配加速度传感器配置资源,完成加速度计差异化接口注册。
10)加速度器件探测成功之后,加速度差异化驱动通知加速度抽象驱动,注册加速度设备到sensor设备管理中。
为了让开发者更清晰的了解sensor驱动模型工作流程,本节将对sensor驱动模型加载的关键流程代码进行说明。
sensor设备管理驱动实现
hdf驱动框架从device info hcs 的sensor host里读取sensor设备管理配置信息,加载并初始化sensor设备管理驱动。
步骤1-步骤4实现关键代码介绍如下,参考完整代码实现路径driversframeworkmodelsensordrivercommonsrcsensor_device_manager.c。
定义sensor设备管理驱动对应的hdfdriverentry对象,其中driver entry入口函数定义如下:
struct hdfdriverentry g_sensordevmanagerentry = { .moduleversion = 1, .modulename = “hdf_sensor_mgr_ap”, // 值与设备信息hcs中的modulename一样 .bind = bindsensordevmanager, .init = initsensordevmanager, .release = releasesensordevmanager,};
sensor设备管理模块负责系统中所有sensor器件接口发布,在系统启动过程中,hdf框架机制通过sensor host里设备hcs配置信息,加载设备管理驱动。
// sensor host配置包含所有sensor器件设备信息sensor :: host { hostname = “sensor_host”; // host 名字 device_sensor_manager :: device { // sensor管理设备信息 device0 :: devicenode { policy = 1; // 发布策略,1表示对内核态发布,2表示对用户态和内核态发布 priority = 100; // device的加载优先级,同一host内有效,值越小优先级越高 preload = 0; // 设备驱动是否加载标志,0表示加载,2表示不加载 permission = 0664; // 驱动设备节点权限 modulename = “hdf_sensor_mgr_ap”; // 驱动名称,该字段的值必须和驱动入口结构的modulename值一致 servicename = “hdf_sensor_manager_ap”;// 驱动对外发布服务的名称,必须唯一 } }}
sensor设备管理驱动dispatchsensor接口完成sensor设备对外能力的发布,dispatchsensor接口实现如下:
static int32_t dispatchsensor(struct hdfdeviceioclient *client, int32_t cmd, struct hdfsbuf *data, struct hdfsbuf *reply){…… dlist_for_each_entry(pos, &manager-》sensordevinfohead, struct sensordevinfonode, node) { if (sensorid == pos-》devinfo.sensorinfo.sensorid) { // dispatch函数处理sensor对外发布的接口能力 ret = dispatchcmdhandle(&pos-》devinfo, data, reply); (void)osalmutexunlock(&manager-》mutex); return ret; } }…… return hdf_failure;}int32_t bindsensordevmanager(struct hdfdeviceobject *device){ check_null_ptr_return_value(device, hdf_err_invalid_param);……// 通过ioservice.dispatch注册sensor对外接口能力 manager-》ioservice.dispatch = dispatchsensor; manager-》device = device; device-》service = &manager-》ioservice; g_sensordevicemanager = manager; return hdf_success;}
加速度计抽象驱动实现hdf驱动框架从device info hcs 的sensor host里读取加速度计抽象驱动配置信息,加载并初始化加速度计抽象驱动,完成sensor器件驱动资源分配和数据处理队列创建。
步骤5-步骤6实现关键代码介绍如下,参考完整代码实现路径driversframeworkmodelsensordriveraccelsensor_accel_driver.c。
定义加速度计抽象驱动对应的hdfdriverentry对象,其中,driver entry入口函数定义如下:
struct hdfdriverentry g_sensoracceldeventry = { .moduleversion = 1, .modulename = “hdf_sensor_accel”, // 值与设备信息hcs中的modulename一样 .bind = accelbinddriver, .init = accelinitdriver, .release = accelreleasedriver,};
加速度计抽象驱动的配置信息在sensor host定义如下:
// sensor host配置包含所有sensor器件设备信息sensor :: host { hostname = “sensor_host”; // host 名字 device_sensor_accel :: device { // sensor管理设备信息 device0 :: devicenode { policy = 1; // 发布策略,1表示对内核态发布服务,2表示对用户态和内核态发布 priority = 110; // device的加载优先级,同一host内有效,值越小优先级越高 preload = 0; // 设备驱动是否加载标志,0表示加载,2表示不加载 permission = 0664; // 驱动设备节点权限 modulename = “hdf_sensor_accel”; // 驱动名称,该字段的值必须和驱动入口结构的modulename值一致 servicename = “hdf_sensor_accel”;// 驱动对外发布服务的名称,必须唯一 } }}
不同型号的加速度器件在初始化时,会进行器件探测,探测器件是否在位,如果在位,会对加速度器件分配资源,用于存放器件hcs配置信息。加速度计抽象驱动提供初始化过程中器件探测,器件属性配置,寄存器配置资源分配和释放接口。
1、创建加速度配置数据接口
此接口在差异化器件驱动初始化时调用,解析器件hcs私有配置,读取sensor的基本信息,总线方式,器件探测寄存器,并校验器件是否在位。如果器件探测成功,完成加速度器件资源分配,并返回传感器配置数据结构体地址。
struct sensorcfgdata *accelcreatecfgdata(const struct deviceresourcenode *node){ struct acceldrvdata *drvdata = accelgetdrvdata();…… // 器件是否已经探测成功 if (drvdata-》detectflag) { hdf_loge(“%s: accel sensor have detected”, __func__); return null; }…… // 解析器件hcs私有配置,读取sensor的基本信息,总线方式,器件探测寄存器。 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; } // 器件在位,继续解析剩余hcs寄存器配置。 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;……}
2、释放加速度配置数据接口
此接口在差异化器件驱动初始化失败,或者加速度抽象驱动卸载时调用,释放已分配的资源。
void accelreleasecfgdata(struct sensorcfgdata *accelcfg){ check_null_ptr_return(accelcfg); (void)deletesensordevice(&accelcfg-》sensorinfo); releasesensorallregconfig(accelcfg); (void)releasesensorbushandle(&accelcfg-》buscfg); accelcfg-》root = null; (void)memset_s(&accelcfg-》sensorinfo, sizeof(struct sensorbasicinfo), 0, sizeof(struct sensorbasicinfo)); (void)memset_s(&accelcfg-》buscfg, sizeof(struct sensorbuscfg), 0, sizeof(struct sensorbuscfg)); (void)memset_s(&accelcfg-》sensorattr, sizeof(struct sensorattr), 0, sizeof(struct sensorattr));}
3、注册加速度差异化接口
此接口在差异化器件驱动初始化成功时,注册差异实现接口,方便实现器件差异的驱动接口,此接口支持扩展。
int32_t accelregisterchipops(const struct accelopscall *ops){ struct acceldrvdata *drvdata = accelgetdrvdata();…… drvdata-》ops.init = null; drvdata-》ops.readdata = ops-》readdata; return hdf_success;}
加速度计差异化驱动实现
加速度计差异化驱动主要实现因为器件差异无法通过加速度计hcs差异化配置文件实现的能力接口。hdf驱动框架从device info hcs 的sensor host里读取加速度计差异化驱动配置信息,加载并初始化加速度计抽象驱动,完成器件探测,并分配加速度传感器配置资源,完成加速度计差异化接口注册。另外,初始化过程中会从accel_xxx_config hcs里读取私有化配置信息。
步骤7-步骤9实现关键代码介绍如下,参考完整代码实现路径driversframeworkmodelsensordriveraccelsensor_accel_driver.c。
定义加速度计差异化驱动对应的hdfdriverentry对象,其中,driver entry入口函数定义如下:
struct hdfdriverentry g_accelbmi160deventry = { .moduleversion = 1, .modulename = “hdf_sensor_accel_bmi160”, // 值与设备信息hcs中的modulename一样 .bind = bmi160binddriver, .init = bmi160initdriver, .release = bmi160releasedriver,
加速度计差异化驱动的配置信息在sensor host定义如下:
// sensor host配置包含所有sensor器件设备信息sensor :: host { hostname = “sensor_host”; // host 名字 device_sensor_accel :: device { // sensor管理设备信息 device0 :: devicenode { policy = 1; // 发布策略,1表示对内核态发布 priority = 120; // device的加载优先级,同一host内有效,值越小优先级越高 preload = 0; // 设备驱动是否加载标志,0表示加载,2表示不加载 permission = 0664; // 驱动设备节点权限 modulename = “hdf_sensor_accel_bmi160”; // 驱动名称,该字段的值必须和驱动入口结构的modulename值一致 servicename = “hdf_accel_bmi160”;// 驱动对外发布服务的名称,必须唯一 devicematchattr = “hdf_sensor_accel_bmi160_driver”; } }}
加速度计私有化配置信息主要包括总线信息,sensor信息,sensor寄存器等资源信息。加速度计私有化配置信息在如下路径vendorhisiliconhi3516dv300hdf_configkhdfsensoraccelaccel_bmi160_config.hcs。
为了方面开发者使用传感器hcs私有配置,在sensor_common.hcs里面定义通用的传感器配置模板,其他器件直接引用模板修改对应的属性值,如果需要新增功能,可以在私有的配置文件里扩展新的节点即可。
// accel sensor common config templateroot { sensorconfig { template sensorinfo { //在注册设备时需要解析传感器配置信息 sensorname = “accelerometer”; // 器件名字 vendorname = “vendor_xxx”; // 器件厂商名字 firmwareversion = “1.0”;// 固件版本 hardwareversion = “1.0”;// 硬件版本 sensortypeid = 1; // 传感器设备注册时定义的类型,必须是系统定义类型enum sensortypetag sensorid = 1; // 用户自定义传感器id,兼容不同厂商定义,id值要求全局唯一,无特殊要求默认和sensortypeid保持一致,设备访问时采用sensorid作为系统中器件唯一标识 maxrange = 0; // 最大量程,根据器件需要定义 accuracy = 0; // 精度值,根据器件需要定义 power = 0; // 功耗(ua),根据器件需要定义 } template sensorbusconfig { // 器件支持的总线信息 bustype = 0; // 0:i2c 1:spi busnum = 6; // 总线号 busaddr = 0; // 总线地址 regwidth = 1;// 数据宽度 regbigendian = 0;// 字节序 } template sensorattr { chipname = “”;// 器件名字 chipidregister = 0xf;// 器件探测地址 chipidvalue = 0xd1;// 器件探测校验值 } }}
每个sensor器件都要根据业务需求增加或者修改对应的sensor 寄存器分组以满足器件业务需求,当前基本业务分组如下:
enum sensorregopstype { sensor_init_group = 0, // 初始寄存器组 sensor_enable_group, // 使能寄存器组 sensor_disable_group, // 去使能寄存器组 sensor_group_max, };
通用配置子模块提供寄存器配置操作和hcs通用配置解析接口。驱动开发者基于通用sensor hcs配置模板实现的hcs,无需实现解析接口,直接调用如下抽象接口解析即可。
1、传感器设备hcs抽象接口
sensor器件驱动模型提供设备资源通用接口,解析配置文件中的通用节点信息。配置接口会在驱动加载过程中初始化的两个阶段调用,第一个阶段调用getsensorbaseconfigdata接口,解析设备资源hcs中的节点(sensorinfo,sensorbusconfig,sensorattr)信息。
第二个阶段在sensor器件探测成功之后,调用parsesensorregconfig接口,解析sensor寄存器配置信息。最后器件驱动卸载时调用releasesensorallregconfig接口释放所有配置资源。接口定义在sensor_config_parser.h文件。
传感器基本配置数据解析接口函数定义int32_t getsensorbaseconfigdata(const struct deviceresourcenode *node, struct sensorcfgdata *config);传感器寄存器配置数据解析接口函数定义int32_t parsesensorregconfig(struct sensorcfgdata *config);释放传感器所有配置数据接口函数定义void releasesensorallregconfig(struct sensorcfgdata *config);传感器器件探测在位接口int32_t detectsensordevice(struct sensorcfgdata *config);
2、传感器读写寄存器接口
器件驱动开发时,需要用到不同总线接口,配置器件。由于不同操作系统或者平台,总线接口总是有差异,导致开发过程中,需要不断地进行适配修改。传感器驱动模型依赖hdf框架提供的平台接口能力,封装了传感器器读写设备的接口,支持的总线有i2c,spi接口。
读传感器寄存器接口函数定义int32_t readsensor(struct sensorbuscfg *buscfg, uint16_t regaddr, uint8_t *data, uint16_t datalen);读传感器寄存器接口函数定义int32_t writesensor(struct sensorbuscfg *buscfg, uint8_t *writedata, uint16_t len);
sensor设备管理能力接口
加速度器件探测成功之后,加速度差异化驱动通知加速度抽象驱动,注册加速度设备到sensor设备管理中,对应步骤10。sensor设备管理模块还提供如下接口能力:
1、注册设备
设备注册接口addsensordevice把加速度计设备信息注册到sensor设备管理模块。调用注册设备函数时,系统要求sensortpyeid和sensorid全局唯一,并实现sensor信息和sensor接口,才能确保sensor设备注册成功。
struct sensorops { int32_t (*enable)(void); // 使能传感器 int32_t (*disable)(void);// 使能传感器 int32_t (*setbatch)(int64_t samplinginterval, int64_t reportinterval);// 配置传感器采样率和上报时延 int32_t (*setmode)(int32_t mode);// 配置传感器模式 int32_t (*setoption)(uint32_t option);// 配置传感器可选项参数};struct sensordeviceinfo { struct sensorbasicinfo sensorinfo; // 传感器信息,包括传感器名字,厂商名,传感类型id,传感id等信息
注册设备接口函数定义如下:
int32_t addsensordevice(const struct sensordeviceinfo *deviceinfo);
2、删除设备
设备注册失败或者驱动需要卸载时,需要删除注册到sensor设备管理里的设备信息。调用deletesensordevice接口完成设备信息的删除。
int32_t deletesensordevice(const struct sensorbasicinfo *sensorbaseinfo);
3、设备数据报告
sensor设备管理模块提供了抽象的数据上报接口,器件驱动产生数据事件后,调用reportsensorevent接口,把数据事件上报给sensor服务端
上报数据事件格式定义如下:
// 传感器数据上报事件struct sensorreportevent { int32_t sensorid; // 用户自定义传感器id,兼容不同厂商定义,id值要求全局唯一,无特殊要求默认和sensortypeid保持一致,设备访问时采用sensorid作为系统中器件唯一标识 int32_t version; // 传感器版本号,有hal层定义 int64_t timestamp; // 采样数据产生时的时间戳 uint32_t option; // 传感器可选配置,如量程,精度 int32_t mode; // 传感器数据上报模式 uint8_t *data; // 采样数据,根据器件特性定义传感器数据格式和单位,此数据可以是一组采样数据或者多组采样数据 uint32_t datalen; // 采样数据存储长度
数据事件上报接口函数定义如下:
int32_t reportsensorevent(const struct sensorreportevent *events);
对于sensor驱动模型来说,已经实现了sensor hdi接口定义,设备管理驱动和通用配置解析接口,及一些平台无关的读写寄存的接口能力,驱动开发者新增一款传感器器件,只需要实现文件里struct sensorops结构体定义接口,并调用addsensordevice接口添加。
传感器驱动开发指导
本示例介绍新开发一款加速度bmi160器件传感器驱动的实现步骤为例。设备的通讯方式采用i2c总线方式。
加速计传感器驱动开发主要包括两个部分:加速度抽象驱动和加速度差异化驱动。
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)新增文件脚本适配。
加速度抽象驱动实现示例
定义加速度抽象驱动对应的hdfdriverentry对象,其中driver entry入口函数定义如下:
struct hdfdriverentry g_sensoracceldeventry = { .moduleversion = 1, .modulename = “hdf_sensor_accel”, .bind = bindacceldriver, .init = initacceldriver, .release = releaseacceldriver,};hdf_init(g_sensoracceldeventry);
bind接口实现驱动接口实例化,实现示例:
int32_t accelbinddriver(struct hdfdeviceobject *device){ check_null_ptr_return_value(device, hdf_err_invalid_param); // 私有数据分配资源 struct acceldrvdata *drvdata = (struct acceldrvdata *)osalmemcalloc(sizeof(*drvdata)); …… // 需要发布接口函数 drvdata-》ioservice.dispatch = dispatchaccel; drvdata-》device = device; device-》service = &drvdata-》ioservice; g_acceldrvdata = drvdata; return hdf_success;}
init接口实现驱动接口实例化,实现示例:
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;}
release接口在驱动卸载或者init执行失败时,会调用此接口释放资源:
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);}
加速度抽象驱动配置示例
加速度计设备配置信息在device_info.hcs文件sensor_host里面,配置hcs文件在源码仓位置为
// accel器件设备信息device_sensor_accel :: device { device0 :: devicenode { policy = 1; // 加速计直接注册到sensor管理模块,无需对用户态发布 priority = 110; // 优先级105低于sensor管理模块优先级100 preload = 0; permission = 0664; modulename = “hdf_sensor_accel”;// 加速计驱动名称 servicename = “sensor_accel”;// 加速计驱动对外发布的服务名称 devicematchattr = “hdf_sensor_accel_driver”; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等 }}
vendorhisiliconhi3516dv300hdf_configkhdfdevice_infodevice_info.hcs:
加速度抽象驱动内部接口开发实现示例
提供给差异化驱动的初始化接口,完成加速度器件基本配置信息解析(加速度信息,加速度总线配置,加速度器件探测寄存器配置),器件探测,器件寄存器解析。
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; ……}
加速度计差异化驱动实现示例
定义加速度差异化驱动对应的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);}
加速度计差异化驱动私有hcs配置实现示例
加速度计器件私有hcs配置在如下路径vendorhisiliconhi3516dv300hdf_configkhdfsensoraccelaccel_bmi160_config.hcs。为了方面开发者使用传感器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 ]; } }}
加速度计差异化函数接口实现示例
需要开发者实现的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;}
适配编译入口示例
传感器驱动实现在内核态,代码参与编译是通过适配makefile实现,并通过内核模块宏定义,控制加速度设备驱动是否参与编译。
标准系统linux内核加速度模块配置宏定义config_drivers_hdf_sensor_accel、config_drivers_hdf_sensor_accel_bmi160在kernel/linux/config/linux-4.19/arch/arm/configs/hi3516dv300_standard_defconfig文件,若开启模块则config_drivers_hdf_sensor_accel=y,
sensor_root_dir = 。。/。。/。。/。。/。。/framework/model/sensor/driverobj-$(config_drivers_hdf_sensor) += $(sensor_root_dir)/common/src/sensor_config_controller.o $(sensor_root_dir)/common/src/sensor_config_parser.o $(sensor_root_dir)/common/src/sensor_device_manager.o $(sensor_root_dir)/common/src/sensor_platform_if.o obj-$(config_drivers_hdf_sensor_accel) += $(sensor_root_dir)/accel/sensor_accel_driver.oobj-$(config_drivers_hdf_sensor_accel_bmi160) += $(sensor_root_dir)/chipset/accel/accel_bmi160.occflags-y += -idrivers/hdf/framework/model/sensor/driver/include -idrivers/hdf/framework/model/sensor/driver/common/include -idrivers/hdf/framework/model/sensor/driver/chipset/accel -idrivers/hdf/framework/model/sensor/driver/accel -idrivers/hdf/framework/include/core -idrivers/hdf/framework/core/common/include/host -idrivers/hdf/framework/include/utils -idrivers/hdf/framework/include/osal -idrivers/hdf/framework/ability/sbuf/include -idrivers/hdf/framework/include/platform -idrivers/hdf/framework/include/config -idrivers/hdf/khdf/osal/include -i$(project_root)/third_party/bounds_checking_function/include
config_drivers_hdf_sensor_accel_bmi160=y,若关闭模块则删除宏即可。
makefile脚本入口在drivers/adapter/khdf路径下,根据不同操作系统选择不同目录,以标准系统为例说明脚本适配步骤,脚本路径如下/drivers/adapter/khdf/linux/model/sensor/makefile。
总结
本文主要和大家分享传感器驱动模型,重点分析传感驱动模型框架原理和传感器抽象驱动适配开发过程。以开源板hi3516dv300标准系统版本加速度计为例进行了详细的代码说明,希望通过本文档您能初步掌握基于hdf框架的传感器设备的开发步骤与流程。关于传感器驱动框架的更多分析,请关注后续文章。
代码参考原文链接:传送门
嵌入式C语言的指针、函数概念、结构体递归三大挑战
PIC单片机显示程序分享
防水插头连接器使用需要事项
PCB布线技巧去耦电容的摆放
海信与亚马逊达成合作 首次在4K智能电视上搭载Alexa
OpenHarmony HDF传感器模型框架介绍与传感器驱动开发指导
数字科技是世界互联网大会的重要板块之一
英特尔:从手握方向盘到丢掉方向盘 安全始终为先
创盈芯TK11\-A0__容纳了我想象中的所有东西
腾讯云和华为云的ingress路径匹配规则把我绕晕了
一组动图展示,现在的机器人到底多牛逼了?害怕未来机器人统治地球吗?
鑫谷GP700P白金版600W电源拆解 做工如何
ZigBee组网技术在电力SCADA中有怎样的应用
可用于10W以内的离线式开关电源芯片CY3783A
科技巨头和互联网大厂的监管大幕缓缓落下
京东服务接口的高可用设计方案分享
高集成度、低功耗CMMB解决方案及典型应用
多媒体视讯技术发展历程
关于电源布局布线
苹果也撒狗粮:iOS11新图标脱单了