北京迅为iTOP-RK3568开发板OpenHarmony系统南向驱动开发实操-HDF驱动配置LED
瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工智能应用。RK3568 支持安卓 11 和 linux 系统,主要面向物联网网关、NVR 存储、工控平板、工业检测、工控盒、卡拉 OK、云终端、车载中控等行业。

【本文摘自】【北京迅为】iTOP-RK3568OpenHarmony系统南向驱动开发
【相关视频】OpenHarmony学习开发系列教程(第1期 北向基础篇一)
OpenHarmony学习开发系列教程(第2期 南向基础篇一)
第3章 实操-HDF驱动配置LED
从本章节开始,我们来实操一下,配置HDF驱动控制LED。
3.1 查看原理图
首先打开底板原理图,如下图所示:

由上图可以看出,LED灯是由GPIO0_B7控制的。当GPIO0_B7为高电平时,三极管Q16导通,LED9点亮。当GPIO0_B7为低电平时,三极管Q16截止,LED9不亮。由1.2小节可以计算出GPIO的引脚编号是15。
3.2 修改HCS硬件配置
驱动的设备描述修改/vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs文件,添加如下代码,如下所示:
device_topeet_led :: device {
device0::deviceNode {
policy = 2;
priority = 100;
preload = 0;
permission = 0666;
moduleName = "topeet_led_driver";
serviceName = "topeet_led_service";
deviceMatchAttr = "topeet_led_config";
}

接下来解释一下上面的节点配置
device_topeet_led设备节点归类于platform这个host
device_topeet_led :: device表示led类设备
device0::deviceNode表示led类设备下的某个具体设备节点的配置
policy = 2;表示驱动服务发布策略,内核态用户态都可调用
priority = 100;表示驱动启动优先级
preload = 0;表示驱动按需加载字段,启动加载
permission = 0666;表示驱动创建设备节点
moduleName = "topeet_led_driver";表示驱动名称是topeet_led_driver,必须和驱动入口结构中的moduleName值一致。
serviceName = "topeet_led_service";表示驱动对外发布服务的名称,必须唯一
deviceMatchAttr = "topeet_led_config";表示驱动私有数据匹配关键词,必须和驱动私有数据配置节点的match_attr匹配
3.3 创建私有配置文件
接下来新建vendor/hihope/rk3568/hdf_config/khdf/topeet/topeet_config.hcs文件,topeet_config.hcs为驱动私有配置文件,用来填写一些驱动的默认配置信息。HDF 框架在加载驱动时,会获取相应的配置信息并将其保存在 HdfDeviceObject 的 property 中。这些配置信息通过 Bind 和 Init 方法传递给驱动。
topeet_config.hcs具体内容如下所示:
root {
platform{
topeet_led_config {
//该字段的值必须和device_info.hcs中的deviceMatchAttr一致
match_attr = "topeet_led_config";
led_version = 1;//版本号
led_number = 15;//GPIO引脚号
}
}
驱动私有配置文件写完之后,我们需要将该配置文件添加到板级配置入口文件vendor/hihope/rk3568/hdf_config/khdf/hdf.hcs中,如下图所示:

3.4 新增topeet子系统
在Openharmony源码根目录下新建topeet文件夹及其文件夹下的文件。目录如下所示:

接下来依次解释一下每个文件的作用。
bundle.json:
demos:组件目录
hdf_led:子组件目录
app:led应用层目录
├── BUILD.gn:应用APP的GN文件
└── led_test.c:应用层LED测试程序
driver:内核HDF驱动程序目录
├── led_driver.c:内核LED HDF驱动程序
└── Makefile:内核LED HDF驱动编译脚本
3.4.1 编写bundle.json文件
bundle.json文件内容如下所示:
"name":"@ohos/demos",
"description":"topeet demos",
"version":"4.1",
"license":"Apache-2.0",
"publishAs":"code-segment",
"segment":{
"destPath":"topeet/demos"
},
"dirs":{},
"scripts":{},
"component":{
"name":"demos",
"subsystem":"topeet",
"features":[],
"syscap":[],
"adapted_system_type":["standard"],
"rom":"675KB",
"ram":"7400KB",
"deps":{
"components":[
"c_utils",
"hilog",
"hdf_core",
"napi"
],
"third_party":[]
},
"build":{
"sub_component":[
"//topeet/demos/hdf_led/app:led_test"
]
}
}
下面是对各个字段的解释:
name: "@ohos/demos" - 这是组件或项目的名称,这里表示它属于OHOS(OpenHarmony OS)生态系统下的一个名为"demos"的组件。
description: "topeet demos" -它描述了组件的简短说明
version: "4.1" - 组件的版本号。
license: "Apache-2.0" - 组件使用的许可证类型,这里是Apache 2.0许可证。
publishAs: "code-segment" - 表示这个组件或项目是以代码段的形式发布的。
segment:
destPath: "topeet/demos" - 代码段的目标路径,即这个组件或项目在系统中的存放位置。
dirs: {} - 一个空对象,可能用于定义与组件相关的目录结构,但在这个配置中未使用。
scripts: {} - 一个空对象,可能用于定义与组件相关的脚本,如构建脚本、测试脚本等,但在这个配置中未使用。
component:
name: "demos" - 组件的名称。
subsystem: "topeet" - 组件所属的子系统名称。
features: [] - 组件的功能列表,这里为空,表示没有列出特定功能。
syscap: [] - 系统能力列表,这里为空,表示没有列出特定的系统能力。
adapted_system_type: ["standard"] - 适配的系统类型,这里表示适用于标准系统。
rom: "675KB" - 组件所需的ROM大小。
ram: "7400KB" - 组件所需的RAM大小。
deps:
components: ["c_utils", "hilog", "hdf_core", "napi"] - 组件依赖的其他组件列表。
third_party: [] - 第三方依赖列表,这里为空。
build:
sub_component: ["//topeet/demos/hdf_led/app:led_test"] - 构建时包含的子组件路径,这里指定了一个具体的构建目标。
这个JSON配置文件提供了关于如何构建、部署和管理这个名为"demos"的组件的详细信息。它定义了组件的基本属性、依赖关系、构建信息以及目标系统类型等。
3.4.2 编写内核LED HDF驱动程序
接下来编译LED驱动,该驱动用于在基于华为设备框架(HDF)的系统中控制LED灯的开关,完整代码如下所示:
#include "device_resource_if.h"
#include "hdf_device_desc.h"
#include "hdf_log.h"
#include "gpio_if.h"
#define HDF_LOG_TAG led_driver
#define LED_WRITE 1
#define LED_VERSION 1
#define LED_ON 1
#define LED_OFF 0
struct Led_config{
uint32_t led_version;
uint32_t led_number;
};
struct Led_config g_LedCfg = {0};
/**
* @brief 控制LED的GPIO引脚
* 根据传入的GPIO引脚号和模式,控制LED的开关状态。
* @param gpio GPIO引脚号
* @param mode 控制模式,LED_ON表示打开LED,LED_OFF表示关闭LED
* @return 成功返回HDF_SUCCESS,失败返回HDF_FAILURE
*/
static int32_t LedGpioCtl(uint16_t gpio, uint32_t mode){
// 设置GPIO电平为高电平
uint16_t level = GPIO_VAL_HIGH;
// 设置GPIO为输出方向
if(HDF_SUCCESS != GpioSetDir(gpio, GPIO_DIR_OUT)){
// 设置GPIO方向失败
HDF_LOGE("GpioSetDir fail");
return HDF_FAILURE;
}
// 根据mode设置GPIO电平
if(mode == LED_ON){
level = GPIO_VAL_HIGH;
}else if(mode==LED_OFF){
level = GPIO_VAL_LOW;
}
// 日志记录GPIO操作
HDF_LOGE("%s:Write gpio %d:%d",__func__,gpio,mode);
// 向GPIO写入电平
if(HDF_SUCCESS != GpioWrite(gpio, level)){
// 写入GPIO电平失败
HDF_LOGE("GpioWrite fail",__func__);
}
return HDF_SUCCESS;
/**
* @brief 驱动LED设备
* 根据传入的命令ID和数据,控制LED设备的状态。
* @param client HDF设备客户端指针
* @param cmdId 命令ID,用于指示执行的操作类型
* @param dataBuf 输入数据缓冲区指针,包含需要传递给设备的数据
* @param replyBuf 输出数据缓冲区指针,用于存储设备返回的数据
* @return 返回操作结果,成功返回HDF_SUCCESS,失败返回相应的错误码
*/
int32_t LedDriverDispatch(struct HdfDeviceIoClient *client, int32_t cmdId, struct HdfSBuf *dataBuf, struct HdfSBuf *replyBuf){
int32_t result = HDF_FAILURE;
int32_t LedMode = 0;
// 检查客户端和设备是否为空
if(client == NULL || client->device == NULL){
HDF_LOGE("driver device is NULL");
return HDF_ERR_INVALID_OBJECT;
}
// 检查LED配置版本是否支持
if(g_LedCfg.led_version != LED_VERSION){
HDF_LOGE("led version is not support");
return HDF_FAILURE;
}
switch(cmdId){
case LED_WRITE:
// 从数据缓冲区读取LED模式
result = HdfSbufReadInt32(dataBuf,&LedMode);
if(result ){
// 根据LED模式控制GPIO
LedGpioCtl(g_LedCfg.led_number, (LedMode == LED_ON) ? LED_ON: LED_OFF);
}
break;
default:
// 不支持的命令ID
HDF_LOGE("cmdId is not support");
break;
}
return result;
/**
* @brief 绑定LED设备驱动
* 将LED设备驱动绑定到HDF设备对象上。
* @param deviceObject HDF设备对象指针
* @return 返回HDF状态码,成功返回HDF_SUCCESS,失败返回相应的错误码
*/
int32_t HdfLedDriverBind(struct HdfDeviceObject *deviceObject){
// 检查deviceObject是否为空
if(deviceObject == NULL){
// 如果为空,则记录错误日志并返回错误码
HDF_LOGE("HdfLedDriverBind: %s failed",__func__);
return HDF_ERR_INVALID_OBJECT;
}
// 定义一个静态的IDeviceIoService结构体变量ledDriverServ
static struct IDeviceIoService ledDriverServ = {
.Dispatch = LedDriverDispatch,
};
// 将ledDriverServ的地址赋值给deviceObject的service成员
deviceObject->service =(struct IDeviceIoService *)(&ledDriverServ);
// 记录绑定成功的日志,包括设备名称
HDF_LOGI("g_LedDriverEntry: %s success NodeName[%s]", __func__, deviceObject->property->name);
// 返回成功码
return HDF_SUCCESS;
/**
* @brief 初始化HDF LED驱动
* 该函数用于初始化HDF LED驱动,从HCS配置文件中读取硬件相关配置属性。
* @param deviceObject 设备对象指针
* @return 初始化结果
* - HDF_SUCCESS: 初始化成功
* - HDF_ERR_INVALID_OBJECT: 设备对象无效
* - HDF_FAILURE: 初始化失败
*/
int32_t HdfLedDriverInit(struct HdfDeviceObject *deviceObject){
// 检查deviceObject是否为空
if(deviceObject == NULL){
HDF_LOGE("g_LedDriverEntry: %s failed",__func__);
return HDF_ERR_INVALID_OBJECT;
}
// 获取DeviceResourceIface实例
struct DeviceResourceIface *cfgops= NULL;
cfgops = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
// 检查cfgops及其方法GetUint32是否有效
if (cfgops == NULL || cfgops->GetUint32 == NULL) {
HDF_LOGE("%s:DeviceResourceGetIfaceInstance failed", __func__);
return HDF_FAILURE;
}
// 读取hcs配置中的硬件相关配置属性:led_version
// 读取led_version
if(cfgops->GetUint32(deviceObject->property,"led_version",&g_LedCfg.led_version,0)!= HDF_SUCCESS){
HDF_LOGE("%s: read led_version failed", __func__);
return HDF_FAILURE;
}
// 读取引脚号:led_number
// 读取led_number
if(cfgops->GetUint32(deviceObject->property,"led_number",&g_LedCfg.led_number,0)!= HDF_SUCCESS){
HDF_LOGE("%s:Gread led_number failed", __func__);
return HDF_FAILURE;
}
// 打印初始化成功日志
HDF_LOGI("g_LedDriverEntry: %s success", __func__);
return HDF_SUCCESS;
/**
* @brief 释放HDF LED驱动资源
* 该函数用于释放HDF LED驱动的资源。如果传入的HdfDeviceObject指针为空,则打印错误日志并直接返回。
* 如果HdfDeviceObject指针不为空,则打印成功日志并返回。
* @param HdfDeviceObject HDF设备对象指针
*/
void HdfLedDriverRelease(struct HdfDeviceObject *HdfDeviceObject){
// 如果传入的HdfDeviceObject为空
if(HdfDeviceObject == NULL){
// 打印错误日志
HDF_LOGE("HdfLedDriverRelease: %s failed",__func__);
// 返回
return;
}
// 打印成功日志
HDF_LOGI("HdfLedDriverRelease: %s success", __func__);
// 返回
return;
//定义了一个结构体HdfDriverEntry的实例g_LedDriverEntry,并初始化了它的成员变量
struct HdfDriverEntry g_LedDriverEntry = {
.moduleVersion = 1,
.moduleName = "topeet_led_driver",
.Bind = HdfLedDriverBind,
.Init = HdfLedDriverInit,
.Release = HdfLedDriverRelease,
};
//使用HDF_INIT宏来注册或初始化这个结构体实例g_LedDriverEntry
HDF_INIT(g_LedDriverEntry);
3.4.3 接口函数
在一小节的代码中实现了一个简单的LED驱动,下面是对代码的详细解释:
包含的头文件如下所示:
#include "device_resource_if.h":提供设备资源接口,用于从配置文件中读取设备信息
#include "hdf_device_desc.h":包含HDF设备描述相关的定义
#include "hdf_log.h":提供日志记录功能
#include "gpio_if.h:提供GPIO接口,用于控制LED灯的开关
宏定义如下所示:
#define HDF_LOG_TAG led_driver :定义日志标签,用于区分不同模块的日志
#define LED_WRITE 1:定义LED控制命令的ID
#define LED_VERSION 1: 定义LED驱动的版本号
#define LED_ON 1 :定义LED灯打开的状态
#define LED_OFF 0:定义LED灯关闭的状态
数据结构如下所示:
struct Led_config{ //led_config结构体用于存储LED配置信息,包括LED驱动版本号和LED GPIO编号
uint32_t led_version;
uint32_t led_number;
};
struct Led_config g_LedCfg = {0}; //全局变量,用于存储LED配置
g_LedDriverEntry结构体是驱动入口结构体,如下所示,包含了驱动的版本号、模块名、绑定、初始化和释放函数。
struct HdfDriverEntry g_LedDriverEntry = {
.moduleVersion = 1,
.moduleName = "topeet_led_driver",
.Bind = HdfLedDriverBind,
.Init = HdfLedDriverInit,
.Release = HdfLedDriverRelease,
};
HDF_INIT(g_LedDriverEntry);
HdfLedDriverInit函数是驱动初始化函数。
参数:deviceObject(设备对象)。
流程:获取设备资源接口,读取设备配置中的led_version和led_number(GPIO号),并保存到全局配置变量中。
int32_t HdfLedDriverInit(struct HdfDeviceObject *deviceObject){
// 检查deviceObject是否为空
if(deviceObject == NULL){
HDF_LOGE("g_LedDriverEntry: %s failed",__func__);
return HDF_ERR_INVALID_OBJECT;
}
// 获取DeviceResourceIface实例
struct DeviceResourceIface *cfgops= NULL;
cfgops = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
// 检查cfgops及其方法GetUint32是否有效
if (cfgops == NULL || cfgops->GetUint32 == NULL) {
HDF_LOGE("%s:DeviceResourceGetIfaceInstance failed", __func__);
return HDF_FAILURE;
}
// 读取hcs配置中的硬件相关配置属性:led_version
// 读取led_version
if(cfgops->GetUint32(deviceObject->property,"led_version",&g_LedCfg.led_version,0)!= HDF_SUCCESS){
HDF_LOGE("%s: read led_version failed", __func__);
return HDF_FAILURE;
}
// 读取引脚号:led_number
// 读取led_number
if(cfgops->GetUint32(deviceObject->property,"led_number",&g_LedCfg.led_number,0)!= HDF_SUCCESS){
HDF_LOGE("%s:Gread led_number failed", __func__);
return HDF_FAILURE;
}
// 打印初始化成功日志
HDF_LOGI("g_LedDriverEntry: %s success", __func__);
return HDF_SUCCESS;
HdfLedDriverRelease:驱动释放函数。
参数:HdfDeviceObject(设备对象)。
流程:记录日志,表示驱动释放成功。
void HdfLedDriverRelease(struct HdfDeviceObject *HdfDeviceObject){
// 如果传入的HdfDeviceObject为空
if(HdfDeviceObject == NULL){
// 打印错误日志
HDF_LOGE("HdfLedDriverRelease: %s failed",__func__);
// 返回
return;
}
// 打印成功日志
HDF_LOGI("HdfLedDriverRelease: %s success", __func__);
// 返回
return;
HdfLedDriverBind:绑定解析函数
参数:deviceObject(设备对象)。
流程:将LED驱动的服务对象赋值给设备对象的服务成员。
int32_t HdfLedDriverBind(struct HdfDeviceObject *deviceObject){
// 检查deviceObject是否为空
if(deviceObject == NULL){
// 如果为空,则记录错误日志并返回错误码
HDF_LOGE("HdfLedDriverBind: %s failed",__func__);
return HDF_ERR_INVALID_OBJECT;
}
// 定义一个静态的IDeviceIoService结构体变量ledDriverServ
static struct IDeviceIoService ledDriverServ = {
.Dispatch = LedDriverDispatch,
};
// 将ledDriverServ的地址赋值给deviceObject的service成员
deviceObject->service =(struct IDeviceIoService *)(&ledDriverServ);
// 记录绑定成功的日志,包括设备名称
HDF_LOGI("g_LedDriverEntry: %s success NodeName[%s]", __func__, deviceObject->property->name);
// 返回成功码
return HDF_SUCCESS;
LedDriverDispatch:解析函数,解析应用层下发的命令,执行命令对应的操作,控制led灯的亮灭。
参数:client(客户端信息),cmdId(命令ID),dataBuf(输入数据缓冲区),replyBuf(回复数据缓冲区)。
流程:检查设备对象的有效性,验证LED版本,根据命令ID读取数据并调用LedGpioCtl控制LED。
int32_t LedDriverDispatch(struct HdfDeviceIoClient *client, int32_t cmdId, struct HdfSBuf *dataBuf, struct HdfSBuf *replyBuf){
int32_t result = HDF_FAILURE;
int32_t LedMode = 0;
// 检查客户端和设备是否为空
if(client == NULL || client->device == NULL){
HDF_LOGE("driver device is NULL");
return HDF_ERR_INVALID_OBJECT;
}
// 检查LED配置版本是否支持
if(g_LedCfg.led_version != LED_VERSION){
HDF_LOGE("led version is not support");
return HDF_FAILURE;
}
switch(cmdId){
case LED_WRITE:
// 从数据缓冲区读取LED模式
result = HdfSbufReadInt32(dataBuf,&LedMode);
if(result ){
// 根据LED模式控制GPIO
LedGpioCtl(g_LedCfg.led_number, (LedMode == LED_ON) ? LED_ON: LED_OFF);
}
break;
default:
// 不支持的命令ID
HDF_LOGE("cmdId is not support");
break;
}
return result;
LedGpioCtl:控制指定GPIO(LED)的高低电平,从而控制LED灯的开关。
参数:gpio(GPIO号),mode(LED模式,开或关)。
流程:设置GPIO为输出方向,根据mode设置GPIO的电平,最后记录日志。
static int32_t LedGpioCtl(uint16_t gpio, uint32_t mode){
// 设置GPIO电平为高电平
uint16_t level = GPIO_VAL_HIGH;
// 设置GPIO为输出方向
if(HDF_SUCCESS != GpioSetDir(gpio, GPIO_DIR_OUT)){
// 设置GPIO方向失败
HDF_LOGE("GpioSetDir fail");
return HDF_FAILURE;
}
// 根据mode设置GPIO电平
if(mode == LED_ON){
level = GPIO_VAL_HIGH;
}else if(mode==LED_OFF){
level = GPIO_VAL_LOW;
}
// 日志记录GPIO操作
HDF_LOGE("%s:Write gpio %d:%d",__func__,gpio,mode);
// 向GPIO写入电平
if(HDF_SUCCESS != GpioWrite(gpio, level)){
// 写入GPIO电平失败
HDF_LOGE("GpioWrite fail",__func__);
}
return HDF_SUCCESS;
3.4.4 添加内核编译
编译内核时将该HDF驱动编译到镜像中,接下来编写驱动编译脚本Makefile,代码如下所示:
include drivers/hdf/khdf/platform/platform.mk
obj-y += led_driver.o
加入编译体系,填加模块目录到drivers/hdf_core/adapter/khdf/linux/Makefile 文件
obj-$(CONFIG_DRIVERS_HDF) += ../../../../../topeet/demos/hdf_led/driver/
3.4.5 编写应用APP
在应用代码中我们实现如下功能:
当应用程序启动后会获取命令行参数。如果命令行没有参数,LED灯将循环闪烁;如果命令行带有参数,则根据传输的参数控制LED灯的开启或关闭。通过HdfIoServiceBind 绑定LED灯的HDF服务,获取HDF空间缓存区,并向该缓冲区写入控制数据。然后,通过 LED_WRITE 命令将数据发送到 HDF 驱动,从而控制 LED 灯的亮灭。在程序结束时,会回收 HDF 空间缓冲区和 HDF 服务。
接下来编写应用测试文件led_test.c,完整代码如下所示。
#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "hdf_base.h"
#include "hdf_io_service.h"
#include "hilog/log.h"
#undef LOG_TAG
#undef LOG_DOMAIN
#define LOG_TAG "led_test"
#define LOG_DOMAIN 0xD0020240
#define ARGS_NUM 2
#define LED_SERVICE_NAME "topeet_led_service"
#define LED_WRITE 1
/**
* @brief 主函数,用于控制LED灯的开关状态
* 根据传入的参数控制LED灯的开关状态,如果没有传入参数,则进入主循环,不断切换LED灯的开关状态。
* @param argc 命令行参数的数量
* @param argv 命令行参数的数组
* @return 返回HDF_SUCCESS表示成功,否则返回错误码
*/
int main(int argc, char *argv[]){
int ret = HDF_SUCCESS;
int32_t mode = -1;
// 判断命令行参数数量
if (argc == ARGS_NUM) {
// 将命令行参数转换为整数并赋值给 mode
mode = atoi(argv[1]);
// 打印 mode 的状态
printf("mode[%s][0x%x]\n",(mode==1)?"On":"Off",mode);
} else {
// 命令行参数数量不正确,打印提示信息
printf("led main loop. \n");
}
// 绑定 LED 服务
struct HdfIoService *serv = HdfIoServiceBind(LED_SERVICE_NAME);
if(serv == NULL){
// 绑定服务失败,打印错误信息并返回 -1
HILOG_ERROR(LOG_APP, "get service %s failed!", LED_SERVICE_NAME);
return -1;
}
// 打印绑定服务成功的日志
HILOG_ERROR(LOG_APP, "get service %s succeed", LED_SERVICE_NAME);
// 获取默认大小的 SBuf 对象
struct HdfSBuf *data = HdfSbufObtainDefaultSize();
if(data == NULL){
// 获取 SBuf 对象失败,打印错误信息并返回 -1
HILOG_ERROR(LOG_APP,"obtain data failed\n");
return -1;
}
// 打印获取 SBuf 对象成功的日志
HILOG_ERROR(LOG_APP,"obtain data succeed\n");
// 如果 mode 为 -1,则进入循环
if(mode == -1){
while(1){
// 清空 SBuf 对象
HdfSbufFlush(data);
// 向 SBuf 对象写入整数 1
if(!HdfSbufWriteInt32(data, 1)){
// 写入数据失败,打印错误信息并返回 -1
HILOG_ERROR(LOG_APP,"write data failed");
return -1;
}
// 调用 Dispatch 方法,发送 LED 写入命令
ret = serv->dispatcher->Dispatch(&serv->object, LED_WRITE, data, NULL);
usleep(500 * 1000);
// 清空 SBuf 对象
HdfSbufFlush(data);
// 向 SBuf 对象写入整数 0
if(!HdfSbufWriteInt32(data, 0)){
// 写入数据失败,打印错误信息并返回 -1
HILOG_ERROR(LOG_APP,"write data failed");
return -1;
}
// 调用 Dispatch 方法,发送 LED 写入命令
ret = serv->dispatcher->Dispatch(&serv->object, LED_WRITE, data, NULL);
usleep(500 * 1000);
}
} else {
// 如果 mode 不为 -1,则向 SBuf 对象写入 mode 值
if(!HdfSbufWriteInt32(data, mode)){
// 写入数据失败,打印错误信息并返回 -1
HILOG_ERROR(LOG_APP,"write data failed");
return -1;
}
// 调用 Dispatch 方法,发送 LED 写入命令
ret = serv->dispatcher->Dispatch(&serv->object, LED_WRITE, data, NULL);
// 打印 Dispatch 成功的日志
HILOG_ERROR(LOG_APP,"Dispatch succeed");
}
// 回收 SBuf 对象
HdfSbufRecycle(data);
// 回收服务对象
HdfIoServiceRecycle(serv);
// 打印主程序退出的日志
HILOG_INFO(LOG_APP,"[%s] main exit.",LOG_TAG);
return ret;
接下来编写应用APP的GN文件BUILD.gn,代码内容如下所示:
HDF_FRAMEWORKS = "//drivers/hdf_core/framework"
HDF_ADAPTER = "//drivers/hdf_core/adapter"
import("//build/ohos.gni")
import("$HDF_ADAPTER/uhdf2/uhdf.gni")
print("demos: compile led_test")
ohos_executable("led_test"){
sources = ["led_test.c"]
include_dirs = [
"$HDF_FRAMEWORKS/include",
"$HDF_FRAMEWORKS/include/core",
"$HDF_FRAMEWORKS/include/osal",
"$HDF_FRAMEWORKS/include/platform",
"$HDF_FRAMEWORKS/include/utils",
"$HDF_ADAPTER/uhdf2/ipc/include",
"$HDF_ADAPTER/uhdf2/osal/include",
"//base/hiviewdfx/hilog/interfaces/native/innerkits/include",
"//third_party/bounds_checking_function/include",
]
external_deps = [
"c_utils:utils",
"hdf_core:libhdf_platform",
"hdf_core:libhdf_utils",
"hilog:libhilog",
]
cflags = [
"-Wall",
"-Wextra",
"-Werror",
"-Wno-format",
"-Wno-format-extra-args",
]
part_name = "demos"
install_enable = true
上面的代码用于构建一个“led_test”的可执行文件的构建脚本,它使用了GN(Generate Ninja)构建系统,这是一种元构建系统,用于生成Ninja构建文件。
1-2行定义了两个变量HDF_FRAMEWORKS和HDF_ADAPTER,它们分别指向HDF(Hardware Driver Foundation,硬件驱动框架)核心框架和适配器的路径。这些路径是相对于项目根目录的。
4-5行 使用import语句导入两个GNI(GN Include)文件。GNI文件是GN构建系统用来包含变量定义、函数和模板的文件。这里导入的文件可能包含了一些预定义的变量、函数或构建规则,用于支持构建过程。//build/ohos.gni可能包含了OpenHarmony特有的构建配置,而$HDF_ADAPTER/uhdf2/uhdf.gni可能包含了与uHDF(Unified Hardware Driver Framework,统一硬件驱动框架)相关的配置。
7行 打印一条消息到控制台,表明正在编译led_test示例。
9-40行 定义一个名为led_test的ohos_executable目标,这是一个构建规则,用于生成一个可执行文件。下面是该目标的具体配置:
sources:指定源文件列表,这里只有一个文件led_test.c。
include_dirs:指定头文件搜索路径列表。这些路径用于在编译时查找包含的文件(#include指令引用的文件)。这些路径包括了HDF框架、适配器的多个子目录,以及一些第三方库和内部工具库的头文件路径。
external_deps:指定外部依赖项列表。这些依赖项是在构建过程中需要链接的库。这里列出了几个库,如c_utils:utils、hdf_core:libhdf_platform等,这些库提供了构建led_test所需的功能。
cflags:指定传递给C编译器的标志列表。这里包括了一些常见的编译选项,如-Wall(打开所有警告)、-Wextra(打开额外警告)、-Werror(将所有警告视为错误)、以及两个用于关闭特定警告的选项。
part_name:指定构建产物所属的部件名称,这里是demos。
install_enable:设置为true,表示构建产物应该被安装。这可能意味着在构建成功后,led_test可执行文件会被复制到某个特定的目录,以便于执行或分发。
3.5 在产品中新增子系统
在build/subsystem_config.json文件中增加名为topeet的子系统,在3.4节已经新建了topeet文件夹存放子系统代码。添加topeet子系统进行一个登记,说明路径和子系统名称,如下所示:
“topeet”: {
“path”: “topeet”,
“name”: ”topeet”

在vendor/hihope/rk3568/config.json文件中增加topeet子系统的引入,如下所示:
"subsystem": "topeet",
"components": [
{
"component": "demos",
"features": [
]
}
]
}

修改完成之后,保存修改。
3.6 编译源码
重新编译Openharmony4.1源码,如下所示:
./build.sh --product-name rk3568 --ccache
或者单独编译部件
./build.sh --product-name rk3568 --build-target demos --ccache
编译之后,在源码out/rk3568/topeet目录下生成编译产物,如下图所示:

3.7 LED测试
将编译好的镜像全部进行烧写,镜像在源码根目录out/rk3568/packages/phone/images/目录下。

烧写完成之后,在调试串口查看打印日志,如下图所示:

然后打开hdc工具,运行测试程序,输入“led_test 1”,LED灯点亮,如下图所示:

输入“led_test 0”,LED灯熄灭,如下图所示:


相关文章:
北京迅为iTOP-RK3568开发板OpenHarmony系统南向驱动开发实操-HDF驱动配置LED
瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工…...
C语言函数全解析 | 零基础入门指南
📚 C语言函数全解析 | 零基础入门指南 📑 目录 🌟 什么是函数?🔧 函数的定义与结构⚙️ 函数参数与返回值💡 函数声明与调用🏁 Main函数详解🚀 实战案例演示 1. 什么是函数 功能单…...
驻场运维服务方案书(Word文件)
目 录 第一章 背景分析 1.1. 项目背景 1.2. 项目目标 1.3. 系统现状 1.3.1. 网络系统 1.3.2. 设备清单梳理 1.3.3. 应用系统 第二章 需求分析及理解 2.1. 在重要日期能保障信息系统安全 2.2. 信息系统可长期安全、持续、稳定的运行 2.3. 提升发现安全问题、解决安全…...
【时时三省】(C语言基础)用printf函数输出数据2
山不在高,有仙则名。水不在深,有龙则灵。 ----CSDN 时时三省 格式字符 在输出时,对不同类型的数据要指定不同的格式声明,而格式声明中最重要的内容是格式字符。常用的有以下几种格式字符。 ( 1 ) d格式符。用来输出一个有符号的…...
django框架 [面试篇]
Django 是一个基于 Python 的web框架,遵循"快速开发,不重复造轮子(dont repeat yourself)"的原则,帮助用户构建web应用。 而 Django 它本身提供了一些全栈式的一些组件,包括了 ORM,模板引擎,表单…...
吴恩达机器学习笔记复盘(三)Jupyter NoteBook
Jupyter NoteBook Jupyter是一个开源的交互式计算环境: 特点 交互式编程:支持以单元格为单位编写和运行代码,用户可以实时看到代码的执行结果,便于逐步调试和理解代码逻辑。多语言支持:不仅支持Python,还…...
【Docker项目实战】使用Docker部署serverMmon青蛇探针(详细教程)
【Docker项目实战】使用Docker部署serverMmon青蛇探针 一、serverMmon介绍1.1 serverMmon 简介1.2 主要特点二、本次实践规划2.1 本地环境规划2.2 本次实践介绍三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本四、下载serverMmon镜像五、…...
信息学奥赛一本通 1449:【例题2】魔板
题目 1449:【例题2】魔板 分析 首先注意:输入是按顺时针给出的,但我们处理时需要按正常顺序排,可以用以下代码读入 string s(8, 0); // 初始化全零字符串 cin>>s[0]>>s[1]>>s[2]>>s[3]; cin>>…...
Linly-Talker:开源数字人框架的技术解析与影响
一、引言:AI 数字人的发展趋势 近年来,数字人(Digital Human) 技术迅速发展,从最早的 2D 虚拟主播,到如今能够进行实时交互的 3D 智能助手,AI 在多模态交互领域的应用愈发广泛。各大互联网公司…...
你的完美主义:从缺陷到超能力
所属专栏:《逻辑辨证系列》 前情回顾: 《完美还是完成》(一):完成还是完美—完成大于完美 时间、机会、情绪成本 先完成 … 本期: 《完美还是完成》(二):你的完美主…...
Java 8 + Tomcat 9.0.102 的稳定环境搭建方案,适用于生产环境
一、安装 Java 8 安装 OpenJDK 8 bash sudo apt update sudo apt install openjdk-8-jdk -y 验证安装 bash java -version 应输出类似: openjdk version “1.8.0_412” OpenJDK Runtime Environment (build 1.8.0_412-8u412-ga-1~22.04-b08) OpenJDK 64-Bit Server VM (bui…...
Java基础关键_021_集合(五)
目 录 一、HashMap 1.key 存储自定义类型 2.Hash 表存储原理 3.重写 hashCode 和 equals 方法 4.key 为 null 5.jdk 8 后新特性 (1)初始化时 (2)插入 (3)数据结构 6.容量 二、LinkedHashMap 1.…...
mcp 是一种什么协议,怎么构建mcpserver,怎么实现多智能体的调用
MCP(Message Control Protocol)是一种用于分布式系统中多智能体通信的协议框架,特别适合于构建多智能体系统。下面我将介绍MCP协议的基本原理以及如何构建MCP服务器和实现多智能体调用。 MCP协议概述 MCP协议主要用于定义智能体之间如何交换…...
Nginx的流式响应配置详解
现在大模型场景繁多,项目中涉及nginx转发大模型的流式数据时,需配置nginx的转发策略: location /streaming {proxy_pass http://backend_server;proxy_cache off; # 关闭缓存proxy_buffering off; # 关闭代理缓冲chunked_transfer_encoding …...
windows10 清理 C 盘
系统自带功能清理 磁盘清理:右键单击 C 盘,选择“属性” ,点击“磁盘清理” ,系统会开始扫描可清理的文件,如临时文件、回收站文件、系统更新备份文件等。扫描完成后,勾选想要清理的文件类型,点…...
浅谈Linux中的Shell及其原理
浅谈Linux中的Shell及其原理 Linux中Shell的运行原理github地址前言一、Linux内核与Shell的关系1.1 操作系统核心1.2 用户与内核的隔离 二、Shell的演进与核心机制2.1 发展历程2.2 核心功能解析2.3 shell的工作流程1. 用户输入命令2. 解析器拆分指令3. 扩展器处理动态内容变量替…...
ARM内部寄存器与常用汇编指令(ARM汇编)
1 地址空间&RISC与CISC CISC:复杂指令集计算机 RISC:精简指令集计算机 RISC 与 CISC 的比较 2 ARM内部寄存器 3 ARM汇编概述 RISC精简指令可以大致分为几类: 1.内存读写 2.运算 3.跳转/分支 4.比较 而指令的集合就称之为“指令集”…...
【QT5 Widgets示例】Model/View编程初探
文章目录 Model/ViewModel/View编程的优点常见Model类和View类Model/View应用程序示例只读的表格修改文本外观显示变化的数据设置表格标头可编辑视图示例树结构视图示例获取视图选中项 Model/View Model/View编程的优点 Model/View编程介绍:https://doc.qt.io/qt-…...
一键优化右键菜单,高效又清爽!
打工人们你们好!这里是摸鱼 特供版~ 电脑右键菜单杂乱无章,常用功能被淹没?图标显示异常、打印出错让人手足无措?别担心,Easy Context Menu来帮你!这是一款右键菜单管理工具,能快速清理不必要的…...
排查数据库的正在执行的占用的慢sql语句,可以查看生产的执行sql语句时间
1. 生产报错异常: org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.sql.SQLException: master - Interrupted during connection acquisition2.在数据库执行查询生…...
Java泛型程序设计使用方法
Java泛型程序设计是Java语言中一项强大的特性,它允许你编写更加通用和类型安全的代码。以下是Java泛型程序设计的使用方法和技巧: 1. 基本概念 泛型类:可以定义一个类,其中的某些类型是参数化的。 public class Box<T> {pr…...
成绩排序(结构体排序)
成绩排序 #include<stdio.h> #include<stdlib.h> struct student{char name[50];int score;int order; }; int flag; int cmp(const void *a,const void *b){struct student *s1(struct student*)a;struct student *s2(struct student*)b;if(s1->scores2->…...
【redis】lua脚本
在分布式系统与高并发架构的战场中,开发者们始终在与两个永恒的命题博弈:数据一致性与系统性能。当我们试图用Redis构建高速缓存、实现分布式锁或设计秒杀系统时,往往会陷入这样的困境——如何在保证原子性的同时,避免网络往返带来…...
Oracle中的INHERIT PRIVILEGES权限
Oracle中的INHERIT PRIVILEGES权限 存储过程和用户函数的AUTHID属性调用者权限vs定义者权限一个简单的示例INHERIT PRIVILEGES权限的含义INHERIT PRIVILEGES权限的安全隐患注意到Oracle 19c数据库中有如下权限信息: SQL> select grantor,grantee,table_name,privilege fro…...
Kafka相关的面试题
以下是150道Kafka相关的面试题及简洁回答: Kafka基础概念 1. 什么是Kafka? Kafka是一个分布式、可扩展、容错的发布-订阅消息系统,最初由LinkedIn开发,现为Apache项目。它适用于高吞吐量的场景,如大数据处理和实时数据…...
OpenHarmony-XTS测试
OpenHarmony-XTS测试 OpenHarmony-XTS测试环境搭建测试准备开始运行PS OpenHarmony-XTS测试 针对OpenHarmony版本进行XTS测试使用记录。 windows环境。 以acts套件为例。 环境搭建 获取测试套件,两种方法 1)官网下载:https://www.openharm…...
【物联网-WIFI】
物联网-WIFI ■ ESP32-C3-模块简介■ ESP32-C3-■ ESP32-C3-■ WIFI-模组■ WIFI-■ WIFI- ■ ESP32-C3-模块简介 ■ ESP32-C3- ■ ESP32-C3- ■ WIFI-模组 ■ WIFI- ■ WIFI-...
作业9 (2023-05-05 数组的定义和初始化)
第1题/共11题【单选题】 关于一维数组初始化,下面哪个定义是错误的?( ) A.int arr[10] = {1,2,3,4,5,6}; B.int arr[] = {1,2,3,4,5,6}; C.int arr[] = (1,2,3,4,5,6); D.int arr[10] = {0}; A:正确,10个int的一段连续空间,前6个位置被初始化为1,2,3,4,5,6,其他…...
C语言中的流程控制语句
一.流程控制语句的分类: 1.顺序结构 概念:从上往下依次执行,也是程序默认的执行顺序 2.分支结构 概念:程序在执行的过程中出现了岔路(我们只能选择一条支线进行执行) (1).if语句…...
linux常用基本指令汇总
文章目录 01. ls指令02. pwd指令03. cd指令04. touch指令05. mkdir指令06. rmdir指令07. rm指令08. man指令09. cp指令10. mv指令11. cat指令11. more指令12. less指令13. head指令14. tail指令15. time指令16. cal指令17. find指令18. grep指令19. zip/unzip指令20.tar指令21.…...
