【北京迅为】iTOP-RK3568OpenHarmony系统南向驱动开发-第3章 实操-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";

       }
 }

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

  1. device_topeet_led设备节点归类于platform这个host
  2. device_topeet_led :: device表示led类设备
  3. device0::deviceNode表示led类设备下的某个具体设备节点的配置
  4. policy = 2;表示驱动服务发布策略,内核态用户态都可调用
  5. priority = 100;表示驱动启动优先级
  6. preload = 0;表示驱动按需加载字段,启动加载
  7. permission = 0666;表示驱动创建设备节点
  8. moduleName = "topeet_led_driver";表示驱动名称是topeet_led_driver,必须和驱动入口结构中的moduleName值一致。
  9. serviceName = "topeet_led_service";表示驱动对外发布服务的名称,必须唯一
  10. 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"
            ]

        }
    }
}

下面是对各个字段的解释:

  1. name: "@ohos/demos" - 这是组件或项目的名称,这里表示它属于OHOS(OpenHarmony OS)生态系统下的一个名为"demos"的组件。
  2. description: "topeet demos" -它描述了组件的简短说明
  3. version: "4.1" - 组件的版本号。
  4. license: "Apache-2.0" - 组件使用的许可证类型,这里是Apache 2.0许可证。
  5. publishAs: "code-segment" - 表示这个组件或项目是以代码段的形式发布的。
  6. segment:
  • destPath: "topeet/demos" - 代码段的目标路径,即这个组件或项目在系统中的存放位置。
  1. dirs: {} - 一个空对象,可能用于定义与组件相关的目录结构,但在这个配置中未使用。
  2. scripts: {} - 一个空对象,可能用于定义与组件相关的脚本,如构建脚本、测试脚本等,但在这个配置中未使用。
  3. 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:驱动释放函数。

  1. 参数:HdfDeviceObject(设备对象)。

  2. 流程:记录日志,表示驱动释放成功。

    复制代码
    void HdfLedDriverRelease(struct HdfDeviceObject *HdfDeviceObject){
        // 如果传入的HdfDeviceObject为空
        if(HdfDeviceObject == NULL){
            // 打印错误日志
            HDF_LOGE("HdfLedDriverRelease: %s failed",__func__);
            // 返回
            return;
        }
        // 打印成功日志
        HDF_LOGI("HdfLedDriverRelease: %s success", __func__);
        // 返回
        return;
    }

    HdfLedDriverBind:绑定解析函数

  3. 参数:deviceObject(设备对象)。

  4. 流程:将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灯的亮灭。

  1. 参数:client(客户端信息),cmdId(命令ID),dataBuf(输入数据缓冲区),replyBuf(回复数据缓冲区)。

  2. 流程:检查设备对象的有效性,验证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灯的开关。

  1. 参数:gpio(GPIO号),mode(LED模式,开或关)。

  2. 流程:设置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灯熄灭,如下图所示:

输入"led_test",LED灯闪烁。

至此,LED灯实验完结,撒花~

相关推荐
恒锐丰小吕17 小时前
无锡黑锋 HF4004 低噪声电荷泵DC-DC转换器技术解析
嵌入式硬件·硬件工程
此生只爱蛋17 小时前
【Linux】正/反向代理
linux·运维·服务器
星一工作室17 小时前
STM32项目分享:基于单片机的智能宠物玩具的设计
stm32·单片机·嵌入式硬件
qq_54702617917 小时前
Linux 基础
linux·运维·arm开发
zfj32117 小时前
sshd除了远程shell外还有哪些功能
linux·ssh·sftp·shell
我只会发热17 小时前
Ubuntu 20.04.6 根目录扩容(图文详解)
linux·运维·ubuntu
up向上up17 小时前
基于51单片机数字频率计仿真设计
单片机·嵌入式硬件·51单片机
爱潜水的小L17 小时前
自学嵌入式day34,ipc进程间通信
linux·运维·服务器
保持低旋律节奏17 小时前
linux——进程状态
android·linux·php
zhuzewennamoamtf17 小时前
Linux I2C设备驱动
linux·运维·服务器