zoukankan      html  css  js  c++  java
  • #2020征文-开发板# 用鸿蒙开发AI应用(五)HDF 驱动补光灯

    目录:

    前言

    硬件准备

    HDF 驱动开发 

    总结

    前言
    上一篇,我们在鸿蒙上运行了第一个程序,这一篇我们来编写一个驱动开启摄像头的红外补光灯,顺便熟悉一下鸿蒙上的 HDF 驱动开发。
     

    #2020征文-开发板# 用鸿蒙开发AI应用(五)HDF 驱动补光灯

    硬件准备
    先查一下原理图(具体可参考第一篇的硬件资料),找到红外灯的 IO 口编号,GPIO5_1。

    #2020征文-开发板# 用鸿蒙开发AI应用(五)HDF 驱动补光灯

    HDF 驱动开发
    1. 简介
    HDF(OpenHarmony Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理和驱动消息机制。旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。

    HDF框架以组件化的驱动模型作为核心设计思路,为开发者提供更精细化的驱动管理,让驱动开发和部署更加规范。HDF框架将一类设备驱动放在同一个host里面,驱动内部实现开发者也可以将驱动功能分层独立开发和部署,支持一个驱动多个node,HDF框架管理驱动模型如下图所示:

    #2020征文-开发板# 用鸿蒙开发AI应用(五)HDF 驱动补光灯

    2. 驱动框架
    2.1 驱动框架实现
    在 huawei/hdf 目录下新建一个文件夹 led, 然后在其中新建一个源文件 led.c。

    #include "hdf_device_desc.h"  // HDF框架对驱动开放相关能力接口的头文件
    #include "hdf_log.h"          // HDF 框架提供的日志接口头文件
    
    #define HDF_LOG_TAG led_driver   // 打印日志所包含的标签,如果不定义则用默认定义的HDF_TAG标签
    
    //驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架
    int32_t HdfLedDriverBind(struct HdfDeviceObject *deviceObject)
    {
        HDF_LOGD("Led driver bind success");
        return 0;
    }
    
    // 驱动自身业务初始的接口
    int32_t HdfLedDriverInit(struct HdfDeviceObject *deviceObject)
    {
        if (deviceObject == NULL) {
            HDF_LOGE("Led driver Init failed!");
            return HDF_ERR_INVALID_OBJECT;
        }
        HDF_LOGD("Led driver Init success");
        return HDF_SUCCESS;
    }
    
    // 驱动资源释放的接口
    void HdfLedDriverRelease(struct HdfDeviceObject *deviceObject)
    {
        if (deviceObject == NULL) {
            HDF_LOGE("Led driver release failed!");
            return;
        }
    
        HDF_LOGD("Led driver release success");
        return;
    }

    2.2 驱动入口注册到HDF框架

    // 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量
    struct HdfDriverEntry g_ledDriverEntry = {
        .moduleVersion = 1,
        .moduleName = "led_driver",
        .Bind = HdfLedDriverBind,
        .Init = HdfLedDriverInit,
        .Release = HdfLedDriverRelease,
    };
    
    // 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
    HDF_INIT(g_ledDriverEntry);

    3. 驱动编译

    在 huawei/hdf/led 目录下新建编译文件 Makefile

    include $(LITEOSTOPDIR)/../../drivers/hdf/lite/lite.mk  #导入hdf预定义内容,必需
    
    MODULE_NAME := hdf_led_driver  #生成的结果文件
    LOCAL_SRCS += led.c  #本驱动的源代码文件
    LOCAL_INCLUDE := ./include  #本驱动的头文件目录
    LOCAL_CFLAGS += -fstack-protector-strong -Wextra -Wall -Werror  #自定义的编译选项
    include $(HDF_DRIVER)  #导入模板makefile完成编译

    这里的hdf_led_driver为驱动文件名,注意对应关系。

    4. 编译结果链接到内核镜像

    修改 huawei/hdf/hdf_vendor.mk 文件,添加以下代码

    LITEOS_BASELIB += -lhdf_led_driver  #链接生成的静态库
    LIB_SUBDIRS    += $(VENDOR_HDF_DRIVERS_ROOT)/led  #驱动代码Makefile的目录

    填入驱动文件名和源码路径。

    5. 驱动配置

    驱动配置包含两部分,HDF框架定义的驱动设备描述和驱动的私有配置信息。

    5.1 驱动设备描述(必选)
    HDF框架加载驱动所需要的信息来源于HDF框架定义的驱动设备描述。

    修改 vendor/hisi/hi35xx/hi3516dv300/config/device_info/device_info.hcs配置文件,添加驱动的设备描述。

    platform :: host {
        hostName = "platform_host";  // host名称,host节点是用来存放某一类驱动的容器
        priority = 50;  // host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序
    
        device_led :: device {                  // led设备节点
            device0 :: deviceNode {             // led驱动的DeviceNode节点
                policy = 2;                     // policy字段是驱动服务发布的策略,在驱动服务管理章节有详细介绍
                priority = 100;                 // 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序
                preload = 0;                    // 驱动按需加载字段
                permission = 0666;              // 驱动创建设备节点权限
                moduleName = "led_driver";      // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致
                serviceName = "led_service";    // 驱动对外发布服务的名称,必须唯一
                deviceMatchAttr = "led_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等
            }
        } 

    其中,moduleName、serviceName和deviceMatchAttr 都比较重要,分布链接到源码的不同位置,我这里都分开命名,便于理解。

    5.2 驱动私有配置信息(可选)
    如果驱动有私有配置,则可以添加一个驱动的配置文件,用来填写一些驱动的默认配置信息,HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject 中的property里面,通过Bind和Init(参考驱动开发)传递给驱动。

    在 vendor/hisi/hi35xx/hi3516dv300/config/ 目录下新建一个文件夹 led, 然后在其中新建一个源文件 led_config.hcs, 填入以下代码。

    root {
        LedDriverConfig {
            led_version = 1;
            match_attr = "led_config";   //该字段的值必须和device_info.hcs中的deviceMatchAttr值一致
        }
    }

    配置信息定义之后,需要将该配置文件添加到板级配置入口文件hdf.hcs。

    5.3 板级配置(可选)
    修改 vendor/hisi/hi35xx/hi3516dv300/config/hdf.hcs文件,添加代码

    #include "device_info/device_info.hcs"
    #include "led/led_config.hcs"

    6. 驱动消息机制管理

    当用户态应用和内核态驱动需要交互时,可以使用HDF框架的消息机制来实现。用消息管理可以在用户态和内核态之间架起桥梁,这为我们之后的APP提供了操控底层设备功能的能力。

    这里我们在用户态实现一个简单的消息机制,内核态接受到消息后,翻转摄像头两侧的红外补光灯。

    6.1 配置服务策略
    HDF框架定了驱动对外发布服务的策略,是由配置文件中的policy字段来控制。

    typedef enum {
        /* 驱动不提供服务 */
        SERVICE_POLICY_NONE = 0,
        /* 驱动对内核态发布服务 */
        SERVICE_POLICY_PUBLIC = 1,
        /* 驱动对内核态和用户态都发布服务 */
        SERVICE_POLICY_CAPACITY = 2,
        /* 驱动服务不对外发布服务,但可以被订阅 */
        SERVICE_POLICY_FRIENDLY = 3,
        /* 驱动私有服务不对外发布服务,也不能被订阅 */
        SERVICE_POLICY_PRIVATE = 4,
        /* 错误的服务策略 */
        SERVICE_POLICY_INVALID
    } ServicePolicy;

    我们将驱动配置信息中服务策略policy字段设置为2,在之前的设备描述文件device_info.hcs里已经配置好了。

    6.2 实现服务
    在第2章,我们实现了一个空的驱动框架,现在继续实现内核态的消息服务接口。

    编辑 huawei/hdf/led/led.c, 实现服务基类成员IDeviceIoService中的Dispatch方法。收到用户态发来的命令后,操作LED设备,然后将返回值通过reply传回,最后再将收到的命令回传给用户态程序。

    // Dispatch是用来处理用户态发下来的消息
    int32_t LedDriverDispatch(struct HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
    {
        int32_t result = HDF_FAILURE;
        HDF_LOGE("Led driver dispatch");
        if (client == NULL || client->device == NULL)
        {
            HDF_LOGE("Led driver device is NULL");
            return HDF_ERR_INVALID_OBJECT;
        }
    
        switch (cmdCode)
        {
        case LED_WRITE_READ:
            const char *recv = HdfSbufReadString(data);
            if (recv != NULL)
            {
                HDF_LOGI("recv: %s", recv);
                result = CtlLED(-1);  # 操作设备
                // CtlLED(GPIO_VAL_HIGH);
                if (!HdfSbufWriteInt32(reply, result))
                {
                    HDF_LOGE("replay is fail");
                }
                return HdfDeviceSendEvent(client->device, cmdCode, data);
            }
            break;
    
        default:
            break;
        }
        return result;
    }

    修改 HdfLedDriverBind函数,将服务绑定到框架。

    //驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架
    int32_t HdfLedDriverBind(struct HdfDeviceObject *deviceObject)
    {
        if (deviceObject == NULL)
        {
            HDF_LOGE("Led driver bind failed!");
            return HDF_ERR_INVALID_OBJECT;
        }
        static struct IDeviceIoService ledDriver = {
            .Dispatch = LedDriverDispatch,
        };
        deviceObject->service = (struct IDeviceIoService *)(&ledDriver);
        HDF_LOGD("Led driver bind success");
        return HDF_SUCCESS;
    }

    7. 业务代码
    内核态核心功能,就简单实现一个每调用一次,就翻转一下LED状态的CtrlLED函数。这里mode为 -1 时为翻转,也可以直接指定高电平或低电平来开关,方便后续扩展。

    其中Hi3516DV300的控制器管理12组GPIO管脚,每组8个。

    GPIO号 = GPIO组索引(0~11)* 每组GPIO管脚数(8) + 组内偏移。

    那么GPIO5_1的GPIO号 = 5 * 8 +1 = 41。

    static int32_t CtlLED(int mode)
    {
        int32_t ret;
        uint16_t valRead;
        /* LED的GPIO管脚号 */
        uint16_t gpio = 5 * 8 + 1;  // 红外补光灯
        // uint16_t gpio = 2 * 8 + 3;  // 绿色指示灯
        // uint16_t gpio = 3 * 8 + 4;  // 红色指示灯
    
        /* 将GPIO管脚配置为输出 */
        ret = GpioSetDir(gpio, GPIO_DIR_OUT);
        if (ret != 0)
        {
            HDF_LOGE("GpioSerDir: failed, ret %d
    ", ret);
            return ret;
        }
    
        if (mode == -1)
        {
            // 翻转输出口
            (void)GpioRead(gpio, &valRead);
            ret = GpioWrite(gpio, (valRead == GPIO_VAL_LOW) ? GPIO_VAL_HIGH : GPIO_VAL_LOW);
        }
        else
        {
            ret = GpioWrite(gpio, mode);
        }
    
        if (ret != 0)
        {
            HDF_LOGE("GpioWrite: failed, ret %d
    ", ret);
            return ret;
        }
        return ret;
    }

    同理,GPIO2_3、GPIO3_4和蜂鸣器组件等等通用IO设备也能相应控制,可以尽情发挥想象力了。

    8. 配置Kconfig

    在`vendor/huawei/hdf/led/`下,新建一个目录`driver`,再在其下新建`Kconfig`文件。

    config LOSCFG_DRIVERS_HDF_PLATFORM_LED
        bool "Enable HDF LED driver"
        default n
        depends on LOSCFG_DRIVERS_HDF_PLATFORM
        help
          Answer Y to enable HDF LED driver.

    将其链接到板级Kconfig中,在vendor/huawei/hdf/Kconfig增加

    source "../../vendor/huawei/hdf/led/driver/Kconfig"

    好了,内核态的程序基本都搞定了。

    查看更多章节>>>

    作者:bluishfish
    想了解更多内容,请访问: 51CTO和华为官方战略合作共建的鸿蒙技术社区https://harmonyos.51cto.com/

  • 相关阅读:
    AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换
    SPEC CPU——简介和使用
    Python——sklearn 中 Logistics Regression 的 coef_ 和 intercept_ 的具体意义
    伯努利分布的最大似然估计(最小化交叉熵、分类问题)
    Python关于%matplotlib inline
    MySQL——告别慢SQL,如何去写一手好SQL
    HiveSQL——row_number() over() 使用
    Python——Python缓存技术
    solr——Lucene打分公式的数学推导
    solr——影响Lucene对文档打分的四种方式
  • 原文地址:https://www.cnblogs.com/HarmonyOS/p/14283379.html
Copyright © 2011-2022 走看看