玩嗨 OpenHarmony:基于 OpenHarmony 的智能门铃(下篇)

玩嗨 OpenHarmony:基于 OpenHarmony 的智能门铃(下篇)

原文引自OpenAtom OpenHarmony公众号的文章《我是如何在智能门铃样例中使用HDF框架的》

五 基于HDF的GPIO使用流

在本样例中,人体红外传感器、LED 灯均使用了基于 HDF 的 GPIO 控制。其中 GPIO11 用于读取人体红外传感器的监测状态。GPIO14 用于控制开发板蓝色 LED 灯亮灭。

GPIO驱动配置文件修改

BES2600WM 开发板的驱动配置文件路径为:device/board/fnlink/shields/v200zr-evb-t1/v200zr-evb-t1.hcs,其中关于 GPIO 配置数据如下:


gpio_config {
  match_attr = "gpio_config";
  pin = [0, 1];       // 驱动接口层gpio号
  // touch_ztw523: TSP_RST - GPIO12, TSP_INT-GPIO27
  // touch_fts: TSP_RST - GPIO05, TSP_INT-GPIO27
  realPin = [5, 27];  // 对应硬件实际的gpio号
  config = [5, 2];    // gpio初始状态(上拉、下拉等),取值范围0~7
  pinNum = 2;         // 驱动接口层可用gpio个数
        }

相关配置的具体含义可以通过 drivers/adapter/platform/gpio/gpio_bes.h 文件中找到定义。

在本样例中,GPIO 配置数据为:


gpio_config 
{
  match_attr = "gpio_config";
  pin = [0, 1, 2, 3, 4, 5, 6, 7];
  // touch_ztw523: TSP_RST - GPIO12, TSP_INT-GPIO27
  // touch_fts: TSP_RST - GPIO05, TSP_INT-GPIO27
  realPin = [5, 27, 1, 11, 15, 10, 14, 13];
  config = [5, 2, 2, 4, 5, 5, 5, 2];
  pinNum = 8;
}

可以看到,共定义了 0~7 共 8 个 GPIO 口,配置情况如下表:

HDI层GPIO号硬件GPIO号初始状态本样例功能
0I005OUTPUT_PUSH_PULL
1I027INPUT_PULL_UP
2I001INPUT_PULL_UP
3I011INPUT_HIGH_IMPEDANCE红外人感传感器状态获取
4I015OUTPUT_PUSH_PULL
5I010OUTPUT_PUSH_PULL
6I014OUTPUT_PUSH_PULL蓝色LED灯控制
7I013INPUT_PULL_UP门铃按键监测
GPIO驱动接口的使用

● 包含驱动路径

在硬件外设处理模块文件夹的 BUILD.gn 文件中,需要包含 GPIO 驱动接口的头文件:


 include_dirs = [
    ......
    "//drivers/framework/include/platform",
    ......
  ]

● 作为输入 IO 口使用

在处理人体感应的源文件中,可以看到对应 GPIO 号 3 的 IO 口被用于判断人体感应模块的高低电平。其初始状态为高阻态输入,当人体红外传感器检测到有人靠近时,拉高 GPIO3 的电平,此时调用 ExtDevGetIrMonSts 函数通过驱动接口 GpioRead 读取 GPIO3 的输入电平,则此时函数的返回值为 EXT_DEV_IR_HAVE_MAN。


#include "gpio_if.h"
#define IR_GPIO 3
......
/// 人体红外传感器监测结果获取
ExtDevIrMontStsDef ExtDevGetIrMonSts(void)
{
    uint16_t val;
    if (0 != GpioRead(IR_GPIO, &val)) {
        LOG_E("GpioRead failed.");
    }
    return (val == GPIO_VAL_HIGH) ? EXT_DEV_IR_HAVE_MAN : EXT_DEV_IR_NO_MAN;
}

● 作为输出 IO 使用

在 LED 灯的控制逻辑处理源文件中,可以看到对应 GPIO 号为 6 的 IO 口被用于控制开发板蓝色 LED 灯。其初始状态为推挽输出,当调用 ExtDevInterSetLedOff 函数关闭蓝色 LED 灯时,函数会调用驱动接口 GpioWrite 将低电平写入 GPIO6,从而达到控制蓝色 LED 灯熄灭。


#include "gpio_if.h"
#define BLUE_LED_GPIO 6
......
static ExtDevCtlRst ExtDevInterSetLedOff(ExtDevLedIdDef extDevLedId)
{
  LOG_I("Set led%d off.", g_extDevLedManage[extDevLedId].extDevInterLedId);
  if (HDF_SUCCESS != GpioWrite(g_extDevLedManage[extDevLedId].extDevInterLedId, GPIO_VAL_LOW)) 
  {
    LOG_E("Set led%d off failed!", g_extDevLedManage[extDevLedId].extDevInterLedId);
    return EXT_DEV_ERROR;
  }
  g_extDevLedManage[extDevLedId].extDevLedAct = EXT_DEV_LED_OFF;
  return EXT_DEV_SUCCESS;
}

六 基于HDF的PWM使用流程

PWM驱动配置文件修改

同 GPIO 配置文件一样,BES2600WM 开发板的驱动配置文件路径为:device/board/fnlink/shields/v200zr-evb-t1/v200zr-evb-t1.hcs。但其中没有关于 PWM 配置数据。这可怎么办呢?没关系,配置文件是有格式的,其规范可查阅官方文档。驱动配置文件有两个,分别用于配置设备信息和设备资源。其中,官方 device_info.hcs 配置参考:


root {
  device_info { 
    platform :: host {
       hostName = "platform_host";
       priority = 50;
       device_pwm :: device 
       {//为每一个 pwm 控制器配置一个HDF设备节点,存在多个时【必须】添加,否则不用
         device0 :: deviceNode 
         {
          policy = 1;       // 等于1,向内核态发布服务
          priority = 80;    // 驱动启动优先级
          permission = 0644;// 驱动创建设备节点权限
           moduleName = "HDF_PLATFORM_PWM";   //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致;
           serviceName = "HDF_PLATFORM_PWM_0";//【必要且唯一】驱动对外发布服务的名称
           deviceMatchAttr = "hisilicon_hi35xx_pwm_0";//【必要】用于配置控制器私有数据,要与 pwm_config.hcs 中对应
           // 控制器保持一致,具体的控制器信息在 pwm_config.hcs 中
         }
        device1 :: deviceNode 
        {
          policy = 1;
          priority = 80;
          permission = 0644;
          moduleName = "HDF_PLATFORM_PWM";
          serviceName = "HDF_PLATFORM_PWM_1";
          deviceMatchAttr = "hisilicon_hi35xx_pwm_1";
        }
      }
    }
  }
}

于是在 SDK 的device/soc/bestechnic/bes2600/liteos_m/components/hdf_config/device_info.hcs 文件中 device_gpio 下面追加:


            device_gpio :: device {
                gpio0 :: deviceNode {
                    policy = 2;
                    priority = 100;
                    moduleName = "BES_GPIO_MODULE_HDF";
                    serviceName = "";
                    deviceMatchAttr = "gpio_config";
                }
            }
            device_pwm :: device {//为每一个 pwm 控制器配置一个HDF设备节点,存在多个时【必须】添加,否则不用
                device0 :: deviceNode {
                    policy = 2;       // 等于1,向内核态发布服务
                    priority = 80;    // 驱动启动优先级
                    moduleName = "BES_PWM_MODULE_HDF";   //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致;
                    serviceName = "HDF_PLATFORM_PWM_0";//【必要且唯一】驱动对外发布服务的名称
                    deviceMatchAttr = "pwm0_config";//【必要】用于配置控制器私有数据,要与 pwm_config.hcs 中对应
                                                     // 控制器保持一致,具体的控制器信息在 pwm_config.hcs 中
               }
            }

官方 pwm_config.hcs 配置参考:


root {
  platform {
    pwm_config {
      template pwm_device {                   //【必要】模板配置,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省
        serviceName = "";
        match_attr = "";
        num = 0;                              //【必要】设备号
        base = 0x12070000;                    //【必要】地址映射需要
      }
      device_0x12070000 :: pwm_device {
        match_attr = "hisilicon_hi35xx_pwm_0";//【必要】需要和device_info.hcs中的deviceMatchAttr值一致
      }
      device_0x12070020 :: pwm_device {      //存在多个设备时【必须】添加,否则不用
        match_attr = "hisilicon_hi35xx_pwm_1";
        num = 1;
        base = 0x12070020;                   //【必要】地址映射需要
      }
    }
  }
}

于是在 SDK的 device/board/fnlink/shields/v200zr-evb-t1/v200zr-evb-t1.hcs 文件中 gpio_config 下面追加:


        gpio_config {
           match_attr = "gpio_config";
           pin = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
           // touch_ztw523: TSP_RST - GPIO12, TSP_INT-GPIO27
           // touch_fts: TSP_RST - GPIO05, TSP_INT-GPIO27
           realPin = [5, 27, 1, 11, 15, 10, 14, 13, 31, 35];
           config = [5, 2, 2, 4, 5, 5, 5, 2, 3, 3];
           pinNum = 10;
        }
        pwm0_config{
           match_attr = "pwm0_config";
           pwmId = 0; 
           pwmPin = 20;
        }

最终将硬件 IO20 作为 PWM0 的输出管脚用于控制蜂鸣器的报警。

PWM驱动接口的使用

● 包含驱动路径

首先在 BUILD.gn 文件中依然要包含 drivers/framework/include/platform 路径。

● 使用接口

在处理报警蜂鸣器的源文件中,调用 PwmOpen 函数打开 PWM0,调用 PwmSetConfig 配置 PWM0,调用 PwmSetPeriod 设置 PWM0 周期,调用 PwmSetDuty 设置 PWM0 占空比。


#include "pwm_if.h"
......
struct PwmConfig g_beepPwmConfig = {
    .duty = 30000,
    .period= 60000,
    .number= 0,
    .polarity = PWM_NORMAL_POLARITY,
    .status = PWM_DISABLE_STATUS,
};
......
ExtDevCtlRst ExtDevBeepSetFreq(uint32_t ns)
{
    PwmDisable(g_beepPwmHandle);
    if (0 != PwmSetPeriod(g_beepPwmHandle, ns)) {
        LOG_E("Set beep pwm duty failed! ");
        return EXT_DEV_ERROR;
    }
    if (0 != PwmSetDuty(g_beepPwmHandle, ns / 2)) {
        LOG_E("Set beep pwm duty failed! ");
        return EXT_DEV_ERROR;
    }
    PwmEnable(g_beepPwmHandle);

    return EXT_DEV_SUCCESS;
}

static ExtDevCtlRst ExtDevInterSetBeepOff(void)
{
    PwmClose(g_beepPwmHandle);
    return EXT_DEV_SUCCESS;
}
......
/// 蜂鸣器灯初始化
ExtDevCtlRst ExtDevBeepInit(void)
{
    LOG_I("Beep init.");
    g_beepPwmHandle = PwmOpen(0);
    if(NULL == g_beepPwmHandle){
        LOG_E("open pwm failed!");
    }
    if(0 != PwmSetConfig(g_beepPwmHandle, &g_beepPwmConfig)){
        LOG_E("config pwm failed!");
    }
    return EXT_DEV_SUCCESS;
}

● 注意:由于 3.1 Beta 版本 SDK 未将 pwm_if.c 加入编译体系,完成以上步骤后发现编译报错,提示找不到 pwm 相关接口定义。通过查询编译 log 发现 pwm_if.c 文件没有被编译出来。但是 pwm_core.c 却被编译出来了。最后通过全局查找 pwm_core.c 关键字发现在 drivers/adapter/khdf/liteos_m/platform/BUILD.gn 文件中,确实只是将 pwm_core.c 加入了编译,但却未将 pwm_if.c 纳入编译。修改之后果然顺利编译出了固件。


 if (defined(LOSCFG_DRIVERS_HDF_PLATFORM_PWM)) {
    sources += [ 
    "$HDF_FRAMEWORKS_PATH/support/platform/src/pwm/pwm_core.c",
    "$HDF_FRAMEWORKS_PATH/support/platform/src/pwm/pwm_if.c",
    ]
  }

七 总结

本文简述了在智能门铃项目中,我是如何在欧智通 BES2600WM 开发板上使用 OpenHarmony 的 HDF 框架的 GPIO 和 PWM 能力对 LED 灯、人体红外传感器、报警蜂鸣器进行控制。如果对于门铃样例感兴趣,欢迎开发者们参考智能门铃样例的文档进行复刻。如果有更好的想法,也欢迎参与到 OpenHarmony 的样例共建中来,让更多的小伙伴们看到你的创意。

门铃样例

https://gitee.com/openharmony-sig/knowledge_demo_smart_home/blob/master/docs/smart_door_bell/README.md

共建开发样例:

https://gitee.com/openharmony-sig/knowledge/blob/master/docs/co-construct_demos/README_zh.md

写在最后

我们最近正带着大家玩嗨OpenHarmony。如果你有好玩的东东,欢迎投稿,让我们一起嗨起来!有点子,有想法,有Demo,立刻联系我们:

合作邮箱:zzliang@atomsource.org