通过一个led点灯的demo来熟悉openharmony驱动编写的过程(附带hdf详细调用过程)

news2025/4/21 10:11:42

概述

本应用程序(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这个host
            hostName = "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.readBuffer
        wrBuf.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源代码》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2322679.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pycharm2024.1.1版本_jihuo

目录 前置&#xff1a; 步骤&#xff1a; step one 下载软件 step two 卸载旧版本 1 卸载软件 2 清除残余 step three 下载补丁 step four 安装2024.1.1版本软件 step five 安装补丁 1 找位置放补丁 2 自动设置环境变量 step six 输入jihuo码 前置&#xff1a; 之…

目标检测20年(四)——最终章

欢迎各位读者尽情阅读前三篇文献解读。这一篇将会介绍文献的第五部分&#xff1a;目标检测近些年的新技术发展以及第六部分&#xff1a;总结与未来展望。这也是本篇论文解读的最后一篇文章。 目录 五、目标检测最新进展 5.1 不采用滑动窗口的检测 5.2 旋转和尺度变化的鲁棒性…

【Linux】POSIX信号量与基于环形队列的生产消费者模型

目录 一、POSIX信号量&#xff1a; 接口&#xff1a; 二、基于环形队列的生产消费者模型 环形队列&#xff1a; 单生产单消费实现代码&#xff1a; RingQueue.hpp&#xff1a; main.cc&#xff1a; 多生产多消费实现代码&#xff1a; RingQueue.hpp&#xff1a; main.…

Spring Boot 连接 MySQL 配置参数详解

Spring Boot 连接 MySQL 配置参数详解 前言参数及含义常用参数及讲解和示例useUnicode 参数说明&#xff1a; 完整配置示例注意事项 前言 在 Spring Boot 中使用 Druid 连接池配置 MySQL 数据库连接时&#xff0c;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.…

Python实现小红书app版爬虫

简介&#xff1a;由于数据需求的日益增大&#xff0c;小红书网页版已经不能满足我们日常工作的需求&#xff0c;为此&#xff0c;小编特地开发了小红书手机版算法&#xff0c;方便大家获取更多的数据&#xff0c;提升工作效率。 手机版接口主要包括&#xff1a;搜素&#xff0…

【docker】docker-compose安装RabbitMQ

docker-compose安装RabbitMQ 1、配置docker-compose.yml文件&#xff08;docker容器里面的目录请勿修改&#xff09;2、启动mq3、访问mq4、查看服务器映射目录5、踩坑5.1、权限不足 1、配置docker-compose.yml文件&#xff08;docker容器里面的目录请勿修改&#xff09; versi…

playwright-go实战:自动化登录测试

1.新建项目 打开Goland新建项目playwright-go-demo 项目初始化完成后打开终端输入命令&#xff1a; #安装项目依赖 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. 矩阵置零

这是一道难度为中等的题目&#xff0c;让我们来看看题目描述&#xff1a; 给定一个 _m_ x _n_ 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 提示&#xff1a; m matrix.lengthn matrix[0].length1 < m, n …

Unity URP自定义Shader支持RenderLayer

前言&#xff1a; 当我们想用一个灯光只对特定的物体造成影响&#xff0c;而不对其余物体造成影响时&#xff0c;我们就需要设置相对应的LightLayer&#xff0c;但是这在URP12.0是存在的&#xff0c;在之后就不存在LightLayer这一功能&#xff0c;URP将其隐藏而改成了RenderLa…

Axure项目实战:智慧城市APP(完整交互汇总版)

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;智慧城市APP 主要内容&#xff1a;主功能&#xff08;社保查询、医疗信息、公交查询等&#xff09;、活动、消息、我的页面汇总 应用场景&#xff…

架构思维:预约抢茅子架构设计

文章目录 案例&#xff1a;预约抢茅子复杂度分析商品预约阶段等待抢购阶段商品抢购阶段订单支付阶段 技术方案商品预约阶段一、基于 Redis 单节点的分布式锁方案1. 核心流程2. 关键设计点 二、Redis 单节点方案的局限性1. 单点故障风险2. 主从切换问题 三、多节点 Redis 实现高…

基于SpringBoot+Vue的在教务管理(课程管理)系统+LW示例

1.项目介绍 系统角色&#xff1a;管理员、学生、教师功能模块&#xff1a;管理员&#xff08;学院管理、专业管理、班级管理、学生管理、教师管理、课程管理、选课修改&#xff09;、教师&#xff08;授课查询、教师课表、成绩录入&#xff09;、学生&#xff08;选修课程、学…

ubuntu桌面图标异常——主目录下的所有文件(如文档、下载等)全部显示在桌面

ubuntu桌面图标异常 问题现象问题根源系统级解决方案方法一:全局修改(推荐多用户环境)方法二:单用户修改(推荐个人环境)操作验证与调试避坑指南扩展知识参考文档问题现象 主目录文件异常显示 用户主目录(如/home/user/)下的所有文件(如文档、下载等)全部显示在桌面,…

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&#xff0c;是xml标记片段&#xff0c;第二个参数…

Linux学习笔记(应用篇三)

基于I.MX6ULL-MINI开发板 LED学习GPIO应用编程输入设备 开发板中所有的设备&#xff08;对象&#xff09;都会在/sys/devices 体现出来&#xff0c;是 sysfs 文件系统中最重要的目录结构 /sys下的子目录说明/sys/devices这是系统中所有设备存放的目录&#xff0c;也就是系统中…

【redis】事务详解,相关命令multi、exec、discard 与 watch 的原理

文章目录 什么是事务原子性一致性持久性隔离性 优势与 MySQL 对比用处 事务相关命令开启事务——MULTI执行事务——EXEC放弃当前事务——DISCARD监控某个 key——WATCH作用场景使用方法实现原理 事务总结 什么是事务 MySQL 事务&#xff1a; 原子性&#xff1a;把多个操作&am…

数据库基础知识点(系列七)

视图和索引相关的语句 1&#xff0e;引入视图的主要目的是什么? 答&#xff1a;数据库的基本表是按照数据库设计人员的观点设计的&#xff0c;并不一定符合用户的需求。SQL Server 2008可以根据用户需求重新定义表的数据结构&#xff0c;这种数据结构就是视图。视图是关系数据…

3.3 Taylor公式

1.定义 1.1 taylor公式 1.2 麦克劳林公式 1.3 推论 1.4 拉格朗日余项和皮亚诺型余项 2. 例题 3.几种特殊函数的麦克劳林展开

2000-2019年各省地方财政行政事业性收费收入数据

2000-2019年各省地方财政行政事业性收费收入数据 1、时间&#xff1a;2000-2019年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区、年份、地方财政行政事业性收费收入 4、范围&#xff1a;31省 5、指标说明&#xff1a;地方财政行政事业…