通过一个led点灯的demo来熟悉openharmony驱动编写的过程(附带hdf详细调用过程)
概述
本应用程序(led_rgb)是在上实现直接通过消息机制与内核驱动进行交互,设置RGB三色灯的亮灯行为。我从网上随便找了个demo测试了一下,坑了三天…,整个状态如下图,同时也迫使我深度梳理了一下整个流程框架。直到绝望的时候,翻书找思路的时候找到了答案。。。最绝的是想分享的时候从码云上找了下原作者的demo,发现人家有相关问题的说明。实际经验分享完毕,下面开始说细节~~

源码目录
- 需要详细了解的建议大家从这个目录下载
https://gitee.com/liangkzgitee/led_rgb
./led_rgb/
├── apps #应用代码
│ └── led_rgb
│ ├── BUILD.gn
│ └── led_rgb.c
├── config #配置文件
│ ├── device_info
│ │ └── device_info.hcs
│ └── led
│ ├── led_config_hi3516.hcs
│ └── led_config_rk3568.hcs
├── drv #驱动代码
│ ├── build_linux
│ │ └── Makefile
│ ├── build_liteos
│ │ ├── BUILD.gn
│ │ └── Makefile
│ └── led_drv.c
└── README.md #部署说明
APP部署说明
打开 //build/lite/components/applications.json 文件,找到camera_sample_app组件的信息,
在它的"dirs"和"targets"下,分别添加下面两句语句即可:
"dirs": ["drivers/framework/sample/led_rgb/apps/led_rgb",......],"targets": ["//drivers/framework/sample/led_rgb/apps/led_rgb:led_rgb",......],"adapted_kernel": [ "liteos_a", "linux" ],
应用部署在 app/std/hap, 打开 //applications/standard/hap/ohos.build 文件,在其module_list上增加下面一句语句即可:
"//drivers/framework/sample/led_rgb/apps/led_rgb:led_rgb"

配置文件设置
分别将led设备的hcs配置文件加入到对应系统的设备配置树中,一起编译进内核并被使用。
在//vendor/hihope/rk3568/hdf_config/khdf/hdf.hcs文件的 include 部分增加两行代码即可:
#include "../../../../../drivers/framework/sample/led_rgb/config/led/led_config_rk3568.hcs"
#include "../../../../../drivers/framework/sample/led_rgb/config/device_info/device_info.hcs"

驱动部署说明
在//drivers/adapter/khdf/linux/Makefile文件的末尾,添加如下一句语句即可:
obj-$(CONFIG_DRIVERS_HDF) += ../../../framework/sample/led_rgb/drv/build_linux/
把这个Makefile纳入khdf/linux的编译体系中。

配置文件分析
- device_info
创建config/device_info.hcs,用于驱动设备描述,具体内容如下:
root {device_info {platform :: host { // led设备节点归类于platform这个hosthostName = "platform_host";priority = 50;device_led :: device { // led类设备device0 :: deviceNode { // led类设备下的具体某个设备节点的配置policy = 2; // 驱动服务发布策略priority = 100; // 驱动启动优先级preload = 0; // 驱动按需加载字段permission = 0666; // 驱动创建设备节点权限moduleName = "led_driver"; // 驱动名称,必须和驱动入口结构的moduleName值一致serviceName = "led_service"; // 驱动对外发布服务的名称,必须唯一deviceMatchAttr = "led_config"; // 驱动私有数据匹配关键字,必须和驱动私有数据配置节点的match_attr匹配}}}}
}
- deviceMatchAttr:关键字必须与led_config_rk3568.hcs的match_attr匹配。
config.hcs
创建config/led_config_rk3568.hcs,用于定义私有变量,具体内容如下:
root {platform {led_config {match_attr = "led_config"; //该字段的值必须和device_info.hcs中的deviceMatchAttr值一致led_version = 2;led_R = 147;led_G = 146;led_B = 149;}}
}
驱动代码分析
由驱动代码的注册函数中可得出
struct HdfDriverEntry g_ledDriverEntry = {.moduleVersion = 1,.moduleName = "led_driver",.Bind = HdfLedDriverBind,.Init = HdfLedDriverInit,.Release = HdfLedDriverRelease,
};
HDF_INIT(g_ledDriverEntry);
由hdf驱动框架中可知驱动框架会调用init和bind函数具体可参考这篇博文
下面分别分析这两个函数,首先看下init函数
int32_t HdfLedDriverInit(struct HdfDeviceObject *deviceObject)|-->struct DeviceResourceIface *CfgOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE)//获取设备资源接口对象指针,可以获取到一个设备资源接口的实例,进而使用该接口提供的功能|--> HcsIfaceConstruct(instance)//构造函数|-->instance->GetRootNode = HcsGetRootNode;|-->instance->GetUint16 = HcsGetUint16;|-->....|-->if(CfgOps->GetUint32(deviceObject->property, "led_version", &g_ledcfg.led_ver, 0)//将配置文件中的版本信息赋值到驱动的全局变量g_ledcfg.led_ver中|-->if(CfgOps->GetUint32(deviceObject->property, "led_R", &g_ledcfg.led_R, 0) != HDF_SUCCESS)//将配置文件中led_R的配置信息赋值到驱动的全局变量g_ledcfg.led_R中|-->....同上会将led_G和led_B赋值到全局变量g_ledcfg中
由以上分析可知此函数主要是将配置文件led_config_rk3568.hcs中的信息配置到驱动缓存中。
- bind函数
一个驱动绑定函数,它的作用是将传入的设备对象(
deviceObject)与一个设备 I/O 服务接口(IDeviceIoService)进行绑定。绑定完成后,设备对象就可以通过这个接口提供 I/O 操作服务。
int32_t HdfLedDriverBind(struct HdfDeviceObject *deviceObject)|-->static struct IDeviceIoService ledDriverServ = {.Dispatch = LedDriverDispatch,};//指向具体的 I/O 操作分发函数。这个函数负责处理设备的 I/O 请求。|-->deviceObject->service = (struct IDeviceIoService *)(&ledDriverServ);//将设备对象与设备 I/O 服务接口绑定在一起
通过对应用代码的分析可知通过调用Dispatch函数实现对led的控制。
应用代码分析
从应用代码中可以看出此应用的主要功能是将传入的参数传入到驱动代码中,首先是通过HdfIoServiceBind获取驱动的服务即绑定RGB三色灯HDF服务,获取HDF空间缓冲区,然后通过服务中的Dispatch方法向驱动发送消息,实现向缓冲区写入需要控制的RGB三色灯低三位数据。
HdfIoServiceBind函数
//drivers\hdf_core\interfaces\inner_api\core\hdf_io_service_if.h
//drivers\hdf_core\framework\core\shared\src\hdf_io_service.c
//serviceName为led_service
struct HdfIoService *HdfIoServiceBind(const char *serviceName)|-->return HdfIoServiceAdapterObtain(serviceName)|-->nodePath = OsalMemCalloc(PATH_MAX);realPath = OsalMemCalloc(PATH_MAX);//分配节点路径内存|-->if (sprintf_s(nodePath, PATH_MAX - 1, "%s%s", , serviceName) < 0)// "/dev/hdf/led_service"|-->TrytoLoadIoService(serviceName, nodePath, realPath);//|-->if (HdfLoadDriverByServiceName(serviceName) != HDF_SUCCESS) {//加载对应的驱动程序|-->if (serviceName == NULL || strcmp(serviceName, DEV_MGR_NODE) == 0) return ret;//注意当设备节点名称为DEV_MGR_NODE即dev_mgr时会直接返回失败。|-->data = HdfSbufObtainDefaultSize();//创建一个默认大小的 HDF 数据缓冲区(HdfSBuf)|-->if (!HdfSbufWriteString(data, serviceName)) {//将服务名称传递给设备管理服务|-->ret = ioService->dispatcher->Dispatch(&ioService->object, DEVMGR_LOAD_SERVICE, data, NULL);//调用设备管理服务的 Dispatch 方法,发送一个请求,加载指定的服务(serviceName),即后文的HdfSyscallAdapterDispatch函数|-->while (realpath(devNodePath, realPath) == NULL && waitCount > 0) {//一个标准库函数,用于将 devNodePath 转换为绝对路径,并存储到 realPath 中|-->adapter = (struct HdfSyscallAdapter *)OsalMemCalloc(sizeof(struct HdfSyscallAdapter))//分配系统适配器|-->DListHeadInit(&adapter->listenerList);//初始化适配器的监听器列表|-->adapter->fd = open(realPath, O_RDWR);//使用 open 函数以读写模式(O_RDWR)打开设备文件(realPath)|-->static struct HdfIoDispatcher dispatch = {//设置服务调度器.Dispatch = HdfSyscallAdapterDispatch,};ioService->dispatcher = &dispatch;
- HdfSyscallAdapterDispatch函数说明
/*** @brief 系统调用适配器的静态分发函数,用于在某种硬件驱动框架(可能是 OpenHarmony 或类似的系统)中通过 ioctl 系统调用与设备驱动程序进行通信* @param object 一个 HdfObject 类型的指针,表示调用该函数的对象。HdfObject 是一个通用的对象结构体* @param code 要执行的操作代码,用于指定具体的设备操作或服务请求* @param data 指向一个 HdfSBuf 类型的指针,表示发送给设备的数据缓冲区* @param reply 指向一个 HdfSBuf 类型的指针,表示从设备接收的响应数据缓冲区* @return int32_t 一个静态函数,返回值类型为 int32_t,表示函数执行的结果状态*/
static int32_t HdfSyscallAdapterDispatch(struct HdfObject *object, int32_t code, struct HdfSBuf *data, struct HdfSBuf *reply)
{if (object == NULL) {HDF_LOGE("Input object is null");return HDF_FAILURE;}struct HdfSyscallAdapter *ioService = (struct HdfSyscallAdapter *)object;//将 object 转换为 HdfSyscallAdapter 类型struct HdfWriteReadBuf wrBuf;//用于存储写入和读取缓冲区的信息if (reply != NULL) {wrBuf.readBuffer = (uintptr_t)HdfSbufGetData(reply);//如果 reply 不为空,则从 reply 中获取读取缓冲区的地址和容量,并将其赋值给 wrBuf.readBufferwrBuf.readSize = HdfSbufGetCapacity(reply);//如果 reply 不为空,则从 reply 中获取读取缓冲区的地址和容量,并将其赋值给wrBuf.readSize} else {wrBuf.readBuffer = 0;wrBuf.readSize = 0;}if (data != NULL) {wrBuf.writeBuffer = (uintptr_t)HdfSbufGetData(data);//如果 data 不为空,则从 data 中获取写入缓冲区的地址和数据大小,并将其赋值给 wrBuf.writeBuffer wrBuf.writeSize = HdfSbufGetDataSize(data);//如果 data 不为空,则从 data 中获取写入缓冲区的地址和数据大小,并将其赋值给 wrBuf.writeSize} else {wrBuf.writeBuffer = 0;wrBuf.writeSize = 0;}wrBuf.readConsumed = 0;wrBuf.writeConsumed = 0;wrBuf.cmdCode = code;//设置为传入的 code,表示要执行的操作代码int32_t ret = ioctl(ioService->fd, HDF_WRITE_READ, &wrBuf);//使用 ioctl 系统调用,通过设备文件描述符 ioService->fd,发送 HDF_WRITE_READ 命令,并将 wrBuf 作为参数传递给设备驱动程序。if (ret < 0) {HDF_LOGE("Failed to dispatch serv call ioctl %{public}d", -errno);ret = -errno;}if (reply != NULL) {HdfSbufSetDataSize(reply, wrBuf.readConsumed);//如果 reply 不为空,则根据 wrBuf.readConsumed 更新 reply 的数据大小}return ret;
}
Dispatch方法
ret = serv->dispatcher->Dispatch(&serv->object, LED_RGB_WRITE, data, NULL);//向缓冲区写入需要控制的RGB三色灯低三位数据
测试记录

为了节省大家时间,快速展示效果,我在这里加快了闪烁,实际应该是2s左右变换一次颜色。

润和DAYU200开发板
问题记录
- 操作app没有反应,报device led_service not in configed device list

解决方案:
通过各种尝试还是未解决掉,崩溃的边缘翻书玩,发现如下稻草。。。并成功解决!

只按上图说明删除,不删除out下的目录时,重新编译后,不会重新生成hcb,所以怀疑应该是没有重新生成,

将out目录重新删除后,进行编译。

参考文档
- #DAYU200体验官# RK3568三色灯点灯流程
- zh-cn\device-dev\driver\driver-peripherals-light-des.md
- 《沉浸式剖析OpenHarmony源代码》
相关文章:
通过一个led点灯的demo来熟悉openharmony驱动编写的过程(附带hdf详细调用过程)
概述 本应用程序(led_rgb)是在上实现直接通过消息机制与内核驱动进行交互,设置RGB三色灯的亮灯行为。我从网上随便找了个demo测试了一下,坑了三天…,整个状态如下图,同时也迫使我深度梳理了一下整个流程框架。直到绝望的时候&…...
pycharm2024.1.1版本_jihuo
目录 前置: 步骤: step one 下载软件 step two 卸载旧版本 1 卸载软件 2 清除残余 step three 下载补丁 step four 安装2024.1.1版本软件 step five 安装补丁 1 找位置放补丁 2 自动设置环境变量 step six 输入jihuo码 前置: 之…...
目标检测20年(四)——最终章
欢迎各位读者尽情阅读前三篇文献解读。这一篇将会介绍文献的第五部分:目标检测近些年的新技术发展以及第六部分:总结与未来展望。这也是本篇论文解读的最后一篇文章。 目录 五、目标检测最新进展 5.1 不采用滑动窗口的检测 5.2 旋转和尺度变化的鲁棒性…...
PyTorch处理数据--Dataset和DataLoader
在 PyTorch 中,Dataset 和 DataLoader 是处理数据的核心工具。它们的作用是将数据高效地加载到模型中,支持批量处理、多线程加速和数据增强等功能。 一、Dataset:数据集的抽象 Dataset 是一个抽象类,用于表示数据集的接口。你…...
【Linux】POSIX信号量与基于环形队列的生产消费者模型
目录 一、POSIX信号量: 接口: 二、基于环形队列的生产消费者模型 环形队列: 单生产单消费实现代码: RingQueue.hpp: main.cc: 多生产多消费实现代码: RingQueue.hpp: main.…...
Spring Boot 连接 MySQL 配置参数详解
Spring Boot 连接 MySQL 配置参数详解 前言参数及含义常用参数及讲解和示例useUnicode 参数说明: 完整配置示例注意事项 前言 在 Spring Boot 中使用 Druid 连接池配置 MySQL 数据库连接时,URL 中 ? 后面的参数用于指定连接的各种属性。以下是常见参数…...
[linux] linux基本指令 + shell + 文件权限
目录 1. Linux的认识 1.1. Linux的应用场景 1.2. Linux的版本问题 1.3. 操作系统的认识 1.4. 常用快捷键 2. 常用指令介绍 2.1. ADD 2.1.1. touch [file] 2.1.1.1. 文件的属性信息 2.1.2. mkdir [directory] 2.1.3. cp [file/directory] 2.1.4. echo [file] 2.1.4.…...
查看进程文件描述符的限制
查看进程文件描述符限制 rootgb:/home/gb/Monitor-Device-Mgr/Monitor-Device-Mgr/bin# ps -ef |grep Monitor-Device-Mgr root 3976 2380 59 11:10 pts/2 00:00:06 ./Monitor-Device-Mgr root 4010 2395 0 11:10 pts/3 00:00:00 grep --colorauto Monito…...
Python实现小红书app版爬虫
简介:由于数据需求的日益增大,小红书网页版已经不能满足我们日常工作的需求,为此,小编特地开发了小红书手机版算法,方便大家获取更多的数据,提升工作效率。 手机版接口主要包括:搜素࿰…...
【docker】docker-compose安装RabbitMQ
docker-compose安装RabbitMQ 1、配置docker-compose.yml文件(docker容器里面的目录请勿修改)2、启动mq3、访问mq4、查看服务器映射目录5、踩坑5.1、权限不足 1、配置docker-compose.yml文件(docker容器里面的目录请勿修改) versi…...
playwright-go实战:自动化登录测试
1.新建项目 打开Goland新建项目playwright-go-demo 项目初始化完成后打开终端输入命令: #安装项目依赖 go get -u github.com/playwright-community/playwright-go #安装浏览器 go run github.com/playwright-community/playwright-go/cmd/playwrightlatest insta…...
LeetCode hot 100 每日一题(13)——73. 矩阵置零
这是一道难度为中等的题目,让我们来看看题目描述: 给定一个 _m_ x _n_ 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 提示: m matrix.lengthn matrix[0].length1 < m, n …...
CEF 给交互函数, 添加控制台是否显示交互参数log开关
CEF 控制台添加一函数,枚举 注册的供前端使用的CPP交互函数有哪些 CEF 多进程模式时,注入函数,获得交互信息-CSDN博客 这两篇文章,介绍了注入函数,在控制台中显示 各自提供的交互函数信息。 有些场景下,我们还需要更详细的信息,比如想知道 彼此传递的参数, 如果每次调…...
云端存储新纪元:SAN架构驱动的智能网盘解决方案
一、企业存储的"不可能三角"破局 1.1 传统存储架构的困局 性能瓶颈:NAS架构在1000并发访问时延迟飙升300%容量限制:传统RAID扩容需停机维护,PB级存储扩展耗时超48小时成本矛盾:全闪存阵列每TB成本高达$3000࿰…...
PVE 安装黑苹果 MacOS
背景 我需要一台黑苹果,登录我不常用苹果账号。 方法 The Definitive Guide to Running MacOS in ProxmoxRunning a MacOS 15 Sequoia VM in ProxMox VE及视频 按照第二个的视频一步一步配置,第一个链接提供了不同版本OS...
Unity URP自定义Shader支持RenderLayer
前言: 当我们想用一个灯光只对特定的物体造成影响,而不对其余物体造成影响时,我们就需要设置相对应的LightLayer,但是这在URP12.0是存在的,在之后就不存在LightLayer这一功能,URP将其隐藏而改成了RenderLa…...
Axure项目实战:智慧城市APP(完整交互汇总版)
亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! 课程主题:智慧城市APP 主要内容:主功能(社保查询、医疗信息、公交查询等)、活动、消息、我的页面汇总 应用场景ÿ…...
LVS-DR模式配置脚本
LVS-DR模式配置脚本 实验环境,需要4台虚拟机 IP说明172.25.254.101客户端172.25.254.102负载均衡器DS172.25.254.103真实服务器RS172.25.254.104真实服务器RSVIP:172.25.254.255/32 系统必须有ipvsadm和ifconfig命令 dnf install ipvsadm dnf install n…...
树状数组 3 :区间修改,区间查询
【题目描述】 这是一道模板题。 给定数列 a[1],a[2],…,a[n],你需要依次进行q个操作,操作有两类: 1lrx:给定 l,r,x对于所有 i∈[l,r],将a[i]加上x(换言之,将 a[l],a[l1],…a[r] 分别加上 x&a…...
架构思维:预约抢茅子架构设计
文章目录 案例:预约抢茅子复杂度分析商品预约阶段等待抢购阶段商品抢购阶段订单支付阶段 技术方案商品预约阶段一、基于 Redis 单节点的分布式锁方案1. 核心流程2. 关键设计点 二、Redis 单节点方案的局限性1. 单点故障风险2. 主从切换问题 三、多节点 Redis 实现高…...
使用 gone.WrapFunctionProvider 快速接入第三方服务
项目地址:https://github.com/gone-io/gone 本文中源代码: esexamples/es 文章目录 1. gone.WrapFunctionProvider 简介2. 配置注入实现3. 实战示例:Elasticsearch 集成4. 使用方式5. 最佳实践6. 总结 在如何给Gone框架编写Goner组件…...
基于SpringBoot+Vue的在教务管理(课程管理)系统+LW示例
1.项目介绍 系统角色:管理员、学生、教师功能模块:管理员(学院管理、专业管理、班级管理、学生管理、教师管理、课程管理、选课修改)、教师(授课查询、教师课表、成绩录入)、学生(选修课程、学…...
gitee 常用指令
1.拉取代码 // http git clone http.........// https git clone https......... 2. 设置自己账户和密码 ----- 绑定git git config --global user.name "你的用户名"git config --global user.email "你的邮箱" 3. 上传本地代码至git git initgit r…...
etcd性能测试
etcd性能测试 本文参考官方文档完成etcd性能测试,提供etcd官方推荐的性能测试方案。 1. 理解性能:延迟与吞吐量 etcd 提供稳定、持续的高性能。有两个因素决定性能:延迟和吞吐量。延迟是完成一项操作所花费的时间。吞吐量是在某个时间段内…...
JIRA/Xray测试管理工具的最佳实践:从基础到高阶的全场景指南
引言:测试管理的数字化转型与工具价值 在数字化时代,软件质量已成为企业竞争力的核心指标。然而,传统的测试管理方式——如Excel记录用例、邮件沟通缺陷、手动执行回归测试——已无法满足快速迭代的敏捷开发需求。据统计,全球因测…...
ubuntu桌面图标异常——主目录下的所有文件(如文档、下载等)全部显示在桌面
ubuntu桌面图标异常 问题现象问题根源系统级解决方案方法一:全局修改(推荐多用户环境)方法二:单用户修改(推荐个人环境)操作验证与调试避坑指南扩展知识参考文档问题现象 主目录文件异常显示 用户主目录(如/home/user/)下的所有文件(如文档、下载等)全部显示在桌面,…...
AIP-191 文件和目录结构
编号191原文链接https://google.aip.dev/191状态批准创建日期2019-07-25更新日期2019-07-25 统一的文件和目录结构,虽然在技术上差别不大,但可以让用户和审查者更容易阅读API界面定义。 指南 注意 以下指南适合于使用protobuf定义的API,例如…...
sql结尾加刷题
找了一下mysql对extractvalue()、updatexml()函数的官方介绍https://dev.mysql.com/doc/refman/5.7/en/xml-functions.html#function_extractvalue ExtractValue(xml_frag, xpath_expr) 知识点 解释一下这两个参数xml_frag,是xml标记片段,第二个参数…...
Linux学习笔记(应用篇三)
基于I.MX6ULL-MINI开发板 LED学习GPIO应用编程输入设备 开发板中所有的设备(对象)都会在/sys/devices 体现出来,是 sysfs 文件系统中最重要的目录结构 /sys下的子目录说明/sys/devices这是系统中所有设备存放的目录,也就是系统中…...
LLM动态Shape实现原理与核心技术
LLM动态Shape实现原理与核心技术 目录 LLM动态Shape实现原理与核心技术1. **动态Shape核心原理**2. **实现方法与关键技术**3. **示例:vLLM处理动态长度输入**4. **动态Shape vs 静态Shape对比**5. **性能优化案例**总结`SamplingParams` 是什么常见参数及作用使用示例1. 动态…...
