北京迅为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、云终端、车载中控等行业。

 

 

RK3568

 

【本文摘自】【北京迅为】iTOP-RK3568OpenHarmony系统南向驱动开发

 

【相关视频】OpenHarmony学习开发系列教程(第1期 北向基础篇一)

 

            OpenHarmony学习开发系列教程(第2期 南向基础篇一)

 

 

 

第3章 实操-HDF驱动配置LED

从本章节开始,我们来实操一下,配置HDF驱动控制LED。

 

3.1 查看原理图

首先打开底板原理图,如下图所示:

RK3568

 

 

由上图可以看出,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";

 

       }

 }

 

RK3568

接下来解释一下上面的节点配置

 

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中,如下图所示:

 

 

RK3568

 

3.4 新增topeet子系统

 

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

 

 

RK3568

接下来依次解释一下每个文件的作用。

 

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”

}

RK3568

 

在vendor/hihope/rk3568/config.json文件中增加topeet子系统的引入,如下所示: 

 

{

      "subsystem": "topeet",

      "components": [

        {

          "component": "demos",

          "features": [

          ]

        }

      ]

 

    }

 

RK3568

修改完成之后,保存修改。

 

3.6 编译源码

重新编译Openharmony4.1源码,如下所示:

 

./build.sh --product-name rk3568 --ccache

 

或者单独编译部件

 

./build.sh --product-name rk3568 --build-target demos --ccache

 

编译之后,在源码out/rk3568/topeet目录下生成编译产物,如下图所示:

RK3568

 

3.7 LED测试

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

RK3568

 

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

 

 

RK3568

 

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

 

 

RK3568

 

RK3568

 

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

RK3568

 

RK3568
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分