玩嗨 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号 | 初始状态 | 本样例功能 |
0 | I005 | OUTPUT_PUSH_PULL | – |
1 | I027 | INPUT_PULL_UP | – |
2 | I001 | INPUT_PULL_UP | – |
3 | I011 | INPUT_HIGH_IMPEDANCE | 红外人感传感器状态获取 |
4 | I015 | OUTPUT_PUSH_PULL | – |
5 | I010 | OUTPUT_PUSH_PULL | – |
6 | I014 | OUTPUT_PUSH_PULL | 蓝色LED灯控制 |
7 | I013 | INPUT_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/blob/master/docs/co-construct_demos/README_zh.md
写在最后
我们最近正带着大家玩嗨OpenHarmony。如果你有好玩的东东,欢迎投稿,让我们一起嗨起来!有点子,有想法,有Demo,立刻联系我们:
合作邮箱:zzliang@atomsource.org