一、外接环境光传感器驱动程序

1.目标

   基于HDF驱动框架开发RK3568-外接环境光传感器驱动程序,为上层应用提供环境光数据获取接口。

2.开发环境

   硬件平台:润和DAYU200开发板

   软件版本:OpenHarmony v3.1 Release

   外接传感器:ROHM BH1750FVI

3.准备工作

   根据扩展IO原理图,使用杜邦线将BH1750FVI环境光传感器连接上开发板。

   扩展IO原理图如下:

   连接示意图:

   

4.目录结构

OpenHarmony
    ├──drivers/framework/model/misc/als/driver/src
    │     │                                     └──als_driver.c
    │     │
    │     └── adapter/khdf/linux/model/misc/als
    │                         │              ├──Kconfig
    │                         │              └──Makefile
    │                         ├──Kconfig
    │                         └──Makefile
    │
    └──vendor/hihope/rk3568/hdf_config/khdf
                                        ├──device_info
                                        │   └──device_info.hcs
                                        ├──als
                                        │   └──als_config.hcs
                                        └──hdf.hcs

5.开发步骤

5.1 驱动源文件编写 

5.1.1 新建源文件 

在OpenHarmony/drivers/framework/model/misc/下新建文件夹目录als/driver/src; 

在als/driver/src下新建als_driver.c文件; 

OpenHarmony/drivers/framework/model/misc
                                    └──als/driver/src
                                                    └──als_driver.c

5.1.2 定义并注册驱动入口对象HdfDriverEntry

#include "device_resource_if.h"
#include "hdf_device_desc.h"
#include "hdf_log.h"
#include "gpio_if.h"
#include "i2c_if.h" /* I2C标准接口头文件 */
#include "osal_time.h" /* 标准延迟&睡眠接口头文件 */

#define HDF_LOG_TAG als_drv

/* 定义Als设备结构体,存储i2c相关硬件信息 */
struct AlsI2cDevice {
    uint16_t busNum; /* I2C总线号 */
    uint16_t busAddr; /* I2C设备地址 */
    DevHandle i2cHandle; /* I2C控制器句柄 */
};
struct AlsI2cDevice g_alsI2cDevice = {0};

// 注册驱动入口对象
struct HdfDriverEntry g_alsDriverEntry = {
    .moduleVersion = 1, // 模块版本号
    .moduleName = "als_driver", // 模块名称
    .Bind = HdfAlsDriverBind, // 绑定函数
    .Init = HdfAlsDriverInit, // 初始化函数
    .Release = HdfAlsDriverRelease, // 资源释放函数
};
// 调用HDF_INIT将驱动入口注册到HDF框架中。
// 在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。
// 当Init调用异常时,HDF框架会调用Release函数释放驱动资源并退出
HDF_INIT(g_alsDriverEntry);

5.1.3 实现Bind函数、Init函数、Release函数 

/* 驱动对外提供的服务绑定到HDF框架 */
int32_t HdfAlsDriverBind(struct HdfDeviceObject *deviceObject)
{
    if (deviceObject == NULL) {
        HDF_LOGE("HDF_INIT(g_alsDriverEntry): HdfAlsDriverBind: FAIL!");
        return HDF_ERR_INVALID_OBJECT;
    }

    static struct IDeviceIoService alsDriverServ = {
        .Dispatch = AlsDriverDispatch, // 绑定Dispatch函数
    };

    deviceObject->service = (struct IDeviceIoService *)(&alsDriverServ);
    HDF_LOGI("HDF_INIT(g_alsDriverEntry): HdfAlsDriverBind: OK!");

    return HDF_SUCCESS;
}

/* 驱动初始化 */
int32_t HdfAlsDriverInit(struct HdfDeviceObject *deviceObject)
{
    if (deviceObject == NULL) {
        HDF_LOGE("HDF_INIT(g_alsDriverEntry): HdfAlsDriverInit: FAIL!");
        return HDF_ERR_INVALID_OBJECT;
    }

    struct DeviceResourceIface *CfgOps = NULL;
    CfgOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);

    if(CfgOps == NULL || CfgOps->GetUint16 == NULL) {
        HDF_LOGE("%s: invalid CfgOps fail!", __func__);
        return HDF_FAILURE;
    }
    // 获取驱动配置信息:总线号,器件总线地址
    if(CfgOps->GetUint16(deviceObject->property, "busNum",&g_alsI2cDevice.busNum, 0) != HDF_SUCCESS) {
        HDF_LOGE("%s: read busNum fail!", __func__);
        return HDF_FAILURE;
    }
    if(CfgOps->GetUint16(deviceObject->property, "busAddr",&g_alsI2cDevice.busAddr, 0) != HDF_SUCCESS) {
        HDF_LOGE("%s: read busAddr fail!", __func__);
        return HDF_FAILURE;
    }

    HDF_LOGI("HDF_INIT(g_alsDriverEntry): HdfAlsDriverInit: OK!");
    return HDF_SUCCESS;
}

/* 资源释放 */
void HdfAlsDriverRelease(struct HdfDeviceObject *deviceObject)
{
    if (deviceObject == NULL) {
        HDF_LOGE("HDF_INIT(g_alsDriverEntry): HdfAlsDriverRelease: FAIL!");
        return;
    }

    HDF_LOGI("HDF_INIT(g_alsDriverEntry): HdfAlsDriverRelease: OK!");
    return;
}

5.1.4 实现Dispatch函数、环境光传感器数据获取函数 

环境光传感器数据获取函数:通过I2C通信接口写入传感器指令,并读取传感器数据; 

Dispatch函数:调用环境光传感器数据获取函数获取传感器数据,并返回给上层应用。

/* 环境光传感器数据获取函数 */
int32_t GetLightData()
{
    uint8_t wbuff[2] = { 0x01, 0x10 }; // 待写入指令,0x01-上电指令,0x10-连续测量指令
    uint8_t rbuff[2] = { 0 }; // 存储读取的光照强度数据,长度为2字节
    struct I2cMsg msgs[3]; // I2C消息结构体数组

    msgs[0].buf = &wbuff[0]; /* 写入的数据 */
    msgs[0].len = 1; /* 写入数据长度 */
    msgs[0].addr = g_alsI2cDevice.busAddr; /* 写入设备地址 */
    msgs[0].flags = 0; /* 传输标记为0,默认为写 */
    msgs[1].buf = &wbuff[1];
    msgs[1].len = 1;
    msgs[1].addr = g_alsI2cDevice.busAddr;
    msgs[1].flags = 0;
    msgs[2].buf = rbuff; /* 要读取的数据 */
    msgs[2].len = 2; /* 读取数据长度为2 */
    msgs[2].addr = g_alsI2cDevice.busAddr; /* 读取设备地址 */
    msgs[2].flags = I2C_FLAG_READ ; /* 标记为读 */

    // 打开总线号对应的I2C控制器
    g_alsI2cDevice.i2cHandle = I2cOpen(g_alsI2cDevice.busNum);
    if (g_alsI2cDevice.i2cHandle == NULL) {
        HDF_LOGE("I2cOpen: failed\n");
        return -1;
    }

    // 写入指令至传感器
    int32_t ret = -1;
    ret = I2cTransfer(g_alsI2cDevice.i2cHandle, &msgs[0], 2);
    if (ret != 2) {
        HDF_LOGE("I2cTransfer1: failed, ret %d\n", ret);
        HDF_LOGE("I2cTransfer1: failed, busNum %d\n", g_alsI2cDevice.busNum);
        HDF_LOGE("I2cTransfer1: failed, addr %x\n", msgs[0].addr);
        return -1;
    }

    // 阻塞180ms,等待传感器获取光照强度数据
    OsalMSleep(180);

    // 读取传感器光照强度数据
    ret = I2cTransfer(g_alsI2cDevice.i2cHandle, &msgs[2], 1);
    if (ret != 1) {
        HDF_LOGE("I2cTransfer2: failed, ret %d\n", ret);
        HDF_LOGE("I2cTransfer2: failed, busNum %d\n", g_alsI2cDevice.busNum);
        HDF_LOGE("I2cTransfer2: failed, addr %x\n", msgs[2].addr);
        return -1;
    }

    // 关闭I2C控制器
    I2cClose(g_alsI2cDevice.i2cHandle);

    // 数据转换
    uint16_t temp = (rbuff[0]<<8) | rbuff[1];
    int32_t res = temp / 1.2;
    return res;
}

/* 驱动对外提供的服务 */
int32_t AlsDriverDispatch(struct HdfDeviceIoClient *client, int32_t cmdId,
struct HdfSBuf *dataBuf, struct HdfSBuf *replyBuf)
{
    if (client == NULL || client->device == NULL) {
        HDF_LOGE("Als driver device is NULL");
        return HDF_ERR_INVALID_OBJECT;
    }

    int32_t result = HDF_FAILURE;
    int32_t lightData = GetLightData(); // 获取环境光传感器数据
    if(replyBuf == NULL){
        HDF_LOGE("AlsDriverDispatch/replyBuf is NULL\n");
        return result;
    }

    result = HdfSbufWriteInt32(replyBuf, lightData); // 将环境光数据写入replyBuf,以返回给上层应用
    if (!result) {
        HDF_LOGE("AlsDriverDispatch/WriteLightData: FAIL!\n");
    }

    return result;
}

5.2 驱动编译文件编写 

5.2.1 新建驱动编译文件 

在OpenHarmony/drivers/adapter/khdf/linux/model/misc/下新建文件夹als; 

在als文件夹下新建Kconfig文件、Makefile文件;

OpenHarmony/drivers/adapter/khdf/linux/model/misc/
                                                └──als
                                                    ├──Kconfig
                                                    └──Makefile

5.2.2 写Kconfig文件、Makefile文件 

OpenHarmony/drivers/adapter/khdf/linux/model/misc/als/Kconfig 

定义模块控制宏,默认为开启。

config DRIVERS_HDF_ALS
    bool "Enable HDF ALS driver"
    default y
    depends on DRIVERS_HDF
    help
        Answer Y to enable HDF als driver.

OpenHarmony/drivers/adapter/khdf/linux/model/misc/als/Makefile 

ALS_ROOT_DIR = ../../../../../../framework/model/misc/als/driver

obj-$(CONFIG_DRIVERS_HDF_ALS) += \
                $(ALS_ROOT_DIR)/src/als_driver.o\

ccflags-y +=-I$(srctree)/drivers/hdf/framework/include/core \
            -I$(srctree)/drivers/hdf/framework/core/common/include/host \
            -I$(srctree)/drivers/hdf/framework/include/utils \
            -I$(srctree)/drivers/hdf/framework/include/osal \
            -I$(srctree)/drivers/hdf/framework/include/platform \
            -I$(srctree)/drivers/hdf/framework/include/config \
            -I$(srctree)/drivers/hdf/framework/core/host/include \
            -I$(srctree)/drivers/hdf/framework/core/shared/include \
            -I$(srctree)/drivers/hdf/framework/utils/include \
            -I$(srctree)/drivers/hdf/khdf/osal/include \
            -I$(srctree)/bounds_checking_function/include

5.2.3 将新建的Kconfig文件、Makefile文件纳入khdf/linux编译体系 

将新建的Kconfig文件、Makefile文件的路径添加到外层Kconfig文件、Makefile文件中,外层Kconfig文件、Makefile文件路径如下: 

OpenHarmony/drivers/adapter/khdf/linux
                                    ├──Kconfig
                                    └──Makefile

在OpenHarmony/drivers/adapter/khdf/linux/Kconfig文件中添加以下语句: 

source "drivers/hdf/khdf/model/misc/als/Kconfig"

在OpenHarmony/drivers/adapter/khdf/linux/Makefile文件中添加以下语句: 

obj-$(CONFIG_DRIVERS_HDF_ALS) += model/misc/als/

5.3 驱动配置文件编写 
相关配置文件的路径如下所示: 

OpenHarmony/vendor/hihope/rk3568/hdf_config/khdf
                                             ├──device_info
                                             │   └──device_info.hcs
                                             ├──als
                                             │   └──als_config.hcs
                                             └──hdf.hcs

5.3.1 驱动设备描述 
在OpenHarmony/vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs文件中配置 ALS驱动设备描述信息:

root {
    device_info {
        ......
        als :: host {
            hostName = "als_host";
            device_als :: device { // als类设备
                device0 :: deviceNode { // als类设备下的具体某个设备节点的配置
                    policy = 2; // 驱动服务发布策略
                    priority = 100; // 驱动启动优先级
                    preload = 0; // 驱动按需加载字段
                    permission = 0666; // 驱动创建设备节点权限
                    moduleName = "als_driver"; // 驱动名称,必须和驱动入口结构的moduleName值一致
                    serviceName = "als_service"; // 驱动对外发布服务的名称,必须唯一
                    deviceMatchAttr = "als_config"; // 驱动私有数据匹配关键字,必须和驱动私有数据配置节点的match_attr匹配
                }
            }
        }
        ......
    }
}

5.3.2 驱动私有配置信息 

在OpenHarmony/vendor/hihope/rk3568/hdf_config/khdf下新建als文件夹; 

在als文件夹下新建als_config.hcs文件; 

在als_config.hcs文件中配置驱动私有信息: 

root {
    als_config {
        match_attr = "als_config"; //该字段的值必须和device_info.hcs中的deviceMatchAttr值一致
        busNum = 3; // 总线号
        busAddr = 0x23; // als设备总线地址
    }
}

5.3.3 将配置文件添加到板级配置入口文件hdf.hcs 

在OpenHarmony/vendor/hihope/rk3568/hdf_config/khdf/hdf.hcs文件中添加如下语句:

#include "device_info/device_info.hcs"
#include "als/als_config.hcs"

6.编译验证

6.1 编译

进入vendor/hihope/rk3568/hdf_config/khdf/hdf_test目录,删除编译hdf.hcs所产生的相关文件:

cd vendor/hihope/rk3568/hdf_config/khdf/hdf_test
rm .*.cmd .*.d *.a *.o *.hcb *.order

具体删除文件如下:

.built-in.a.cmd
.hcs_macro_cases.o.d
.hdf_hcs_hex.o.d
.modules.order.cmd
built-in.a
hcs_macro_cases.o
hdf_hcs.hcb
hdf_hcs_hex.o
modules.order

然后进入out/kernel/src_tmp/linux-5.10目录,执行命令重新编译内核:

cd out/kernel/src_tmp/linux-5.10
./make-ohos.sh TB-RK3568X0 enable_ramdisk

最后回到源码根目录,执行如下命令进行版本编译:

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

6.2 验证

通过工具RKDevTool将镜像烧录到单板;

烧录完成后通过串口调试查看是否有als相关的设备节点:

ls dev/*als*
//输出 dev/als_service

7.补充

BH1750FVI是一款用于I2C总线接口的数字环境光传感器,获取的光照强度数据长度为2字节,常用指令如下:


BH1750FV I2C器件地址如下:

  • ADDR引脚接高电平:0X5C
  • ADDR引脚接低电平:0X23

二、外接环境光传感器应用程序

1.目标

   开发实现一个环境光传感器应用程序,通过调用外接环境光传感器驱动对外提供的服务接口来获取光照强度数据。

2.开发环境

   硬件平台:润和DAYU200开发板

   软件版本:OpenHarmony v3.1 Release

3.目录结构

OpenHarmony
    ├──applications/sample/als/src
    │                       │   └──als.c
    │                       │
    │                       ├──BUILD.gn
    │                       └──bundle.json
    │
    ├──build
    │     └──subsystem_config.json
    │
    └──productdefine/common/products
                                └──rk3568.json

4.开发步骤

4.1 应用程序源文件编写

在OpenHarmony/applications/sample/下新建文件夹目录als/src; 

在als/src/下新建als.c文件; 

编写als.c文件,通过调用外接环境光传感器驱动对外提供的服务接口来获取光照强度数据。

#include <stdio.h>
#include "stdlib.h"
#include "hdf_base.h"
#include "hdf_io_service_if.h"
#include "unistd.h" //sleep

#define ALS_SERVICE_NAME "als_service"

int main(int argc, char* argv[])
{
    int ret = HDF_SUCCESS;

    struct HdfIoService *serv = HdfIoServiceBind(ALS_SERVICE_NAME); // 构建驱动服务对象
    struct HdfSBuf *reply = HdfSbufObtainDefaultSize(); // 构建数据对象
    HdfSbufFlush(reply);
    int32_t lightData = 0;

    // 循环获取10S内的光照强度数据
    for(int i = 0; i < 10; i++){
        ret = serv->dispatcher->Dispatch(&serv->object, 0, NULL, reply);
        HdfSbufReadInt32(reply, &lightData);
        printf("\t\tlight data = %d lx\n", lightData);
        sleep(1); // 阻塞1S
    }

    HdfSbufRecycle(reply); // 回收数据对象
    HdfIoServiceRecycle(serv); // 销毁驱动服务对象

    return ret;
}

4.2 编译组织文件编写

在OpenHarmony/applications/sample/als/下新建BUILD.gn文件,内容如下: 

HDF_FRAMEWORKS = "//drivers/framework"

# compile 'als' for ohos_standard(linux)
import("//build/ohos.gni")
import("//drivers/adapter/uhdf2/uhdf.gni")

print("als:: compile 'als' for ohos_standard(linux)")
ohos_executable("als") {
    defines = [ "__USER__" ]

    sources = [
        "src/als.c"
    ]

    include_dirs = [
        "$HDF_FRAMEWORKS/include",
        "$HDF_FRAMEWORKS/include/core",
        "$HDF_FRAMEWORKS/include/osal",
        "$HDF_FRAMEWORKS/include/platform",
        "$HDF_FRAMEWORKS/include/utils",
        "//third_party/bounds_checking_function/include",
        "//drivers/adapter/uhdf2/ipc/include",
        "//drivers/adapter/uhdf2/osal/include",
        "//base/hiviewdfx/hilog/interfaces/native/innerkits/include",
    ]

    deps = [
        "//base/hiviewdfx/hilog/interfaces/native/innerkits:libhilog",
        "//drivers/adapter/uhdf2/utils:libhdf_utils",
     ]

    cflags = [
        "-Wall",
        "-Wextra",
        "-Werror",
        "-Wno-format",
        "-Wno-format-extra-args",
    ]

    subsystem_name = "als"
    part_name = "als"
}

在OpenHarmony/applications/sample/als/下新建bundle.json文件,内容如下:

{
    "name": "@ohos/als",
    "description": "als.",
    "version": "3.1",
    "license": "Apache License 2.0",
    "publishAs": "code-segment",
    "segment": {
        "destPath": "applications/sample/als"
    },
    "dirs": {},
    "scripts": {},
    "component": {
        "name": "als",
        "subsystem": "als",
        "syscap": [],
        "features": [],
        "adapted_system_type": [ "mini", "small", "standard" ],
        "rom": "10KB",
        "ram": "10KB",
        "deps": {
            "components": [],
            "third_party": []
         },
         "build": {
            "sub_component": [
                "//applications/sample/als:als"
             ],
            "inner_kits": [],
            "test": []
         }
    }
}

4.3 相关配置文件修改 

在OpenHarmony/build/subsystem_config.json中添加新建的子系统的配置: 

"als": {
    "path": "applications/sample/als",
    "name": "als"
},

在OpenHarmony/productdefine/common/products/rk3568.json中添加对应的部件:

"als:als":{},

5.编译验证

   进入源码根目录,执行如下命令进行版本编译:

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

   编译完成后,通过工具RKDevTool将镜像烧录到单板;

   烧录完成后,通过串口调试执行als程序,示例如下:

# als
light data = 159 lx
light data = 157 lx
light data = 156 lx
light data = 155 lx
light data = 153 lx
light data = 157 lx
light data = 158 lx
light data = 158 lx
light data = 158 lx
light data = 157 lx


 

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐