文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

基于 OpenHarmony 的实时水流量监测管理系统(一)

2024-12-01 17:04

关注

​想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com​

一、前言

学习了一段时间的OpenHarmony开发体系后,和小伙伴们一同完成了一个简单的小项目,基于 OpenHarmony 的实时水流量监测管理系统。

二、准备

1、BearPi-HM Micro开发板

2、配置好开发环境

开发环境搭建

3、熟悉官方提供如何编写一个点亮LED灯程序的步骤。

编写一个点亮LED灯程序

三、实战

1、系统组成及功能说明

(1)整体介绍

物联网的典型架构:

端:传感器模块上,使用的水表传感器不同于常见的速度/容积式水表, 选用的是霍尔传感器作为水流量的测量依据。

管:小熊派板载 wifi 模组使用 UDP 协议发数据上云端。

云:云端使用华为云服务器接收小熊派 UDP 协议上发的数据,同时建立TCP服务器(即利用 socket 库开发的一个 http 服务器)开放 http get 接口给小程序。

用:微信小程序端。用户通过小程序轻松了解到自家用水情况。

(2)各模块介绍

在BearPi-HM Micro开发板上外接一个水流量传感器获取水流信息。

霍尔水流量传感器由塑料阀体、水流转子组件和霍尔传感器组成。它装在进水 端,用于检测进水流量,当水通过水流转子组件时,磁性转子转动并且转速随 着流量变化而变化,霍尔传感器输出相应脉冲信号,反馈给控制器,由控制器 判断水流量的大小,进行调控。

所以只要知道连接传感器引脚上的电平值变化,我们就能知道水流量了。

查找OpenHarmony设备开发文档,了解如何使用GPIO开发。

OpenHarmony设备开发文档-平台驱动使用GPIO

● 设置 GPIO 管脚方向 在进行 GPIO 管脚读写前,需要先通过如下函数设置 GPIO 管脚方向: int32_t GpioSetDir(uint16_t gpio, uint16_t dir)。

● 通过 GpioRead()函数读取一个 GPIO 管脚电平: int32_t GpioRead(uint16_t gpio, uint16_t *val)。

在点亮LED灯demo的基础上,我们是不是就可以直接在LED业务代码下略加修改就可以读取到管脚上的电平值呢?添加包含GPIO的头文件,就可以直接使用头文件中的函数,这样就可以跳过驱动程序的编写了

#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include "hdf_sbuf.h"
#include "hdf_io_service_if.h"

if (!HdfSbufWriteUint8(data, eventData))
{
printf("fail to write sbuf!\r\n");
ret = HDF_FAILURE;
goto out;
}

ret = serv->dispatcher->Dispatch(&serv->object, LED_WRITE_READ, data, reply);
if (ret != HDF_SUCCESS)
{
printf("fail to send service call!\r\n");
goto out;
}
int replyData = 0;

if (!HdfSbufReadInt32(reply, &replyData))
{
printf("fail to get service call reply!\r\n");
ret = HDF_ERR_INVALID_OBJECT;
goto out;
}

struct HdfIoService *serv = HdfIoServiceBind(LED_SERVICE);
if (serv == NULL)
{
printf("fail to get service %s!\r\n", LED_SERVICE);
return HDF_FAILURE;
}
for (i=0; i < argc; i++)
{
printf("\r\nArgument %d is %s.\r\n", i, argv[i]);
}
SendEvent(serv, atoi(argv[1]));
HdfIoServiceRecycle(serv);
printf("exit");
return HDF_SUCCESS;
}

想法异想天开,编译会报错。 细读官网文档的小字提示:

那么就转战到驱动代码处device\st\drivers\led\led.c编写。

#include "hdf_log.h"         
#include "device_resource_if.h"
#include "osal_io.h"
#include "osal.h"
#include "osal_mem.h"
#include "gpio_if.h"
#define HDF_LOG_TAG led_driver // 打印日志所包含的标签,如果不定义则用默认定义的HDF_TAG标签
#define LED_WRITE_READ 1 // 读写操作码1
enum LedOps {
LED_OFF,
LED_ON,
LED_TOGGLE,
};
struct Stm32Mp1ILed {
uint32_t gpioNum;
};
static struct Stm32Mp1ILed g_Stm32Mp1ILed;
uint8_t status = 0;
uint32_t status1 = 0;
uint8_t flag = 0;
uint16_t val;
// Dispatch是用来处理用户态发下来的消息
int32_t LedDriverDispatch(struct HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
{
uint8_t contrl;
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:

HdfSbufReadUint8(data,&contrl);
switch (contrl)
{

case LED_ON:
GpioSetDir(6, GPIO_DIR_IN);
ret = GpioRead(6, &val);
status = val;
if(status!=flag){
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_LOW);
status1++;
flag=!flag;
}else{
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_HIGH);
}
break;

case LED_OFF:
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_HIGH);
status = 0;
break;

case LED_TOGGLE:
if(status == 0)
{
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_LOW);
status = 1;
}
else
{
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_HIGH);
status = 0;
}
break;
default:
break;
}

if (!HdfSbufWriteInt32(reply, status))
{
HDF_LOGE("replay is fail");
return HDF_FAILURE;
}
break;
default:
break;
}
return HDF_SUCCESS;
}
// 读取驱动私有配置
static int32_t Stm32LedReadDrs(struct Stm32Mp1ILed *led, const struct DeviceResourceNode *node)
{
int32_t ret;
struct DeviceResourceIface *drsOps = NULL;

drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
if (drsOps == NULL || drsOps->GetUint32 == NULL) {
HDF_LOGE("%s: invalid drs ops!", __func__);
return HDF_FAILURE;
}

ret = drsOps->GetUint32(node, "led_gpio_num", &led->gpioNum, 0);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: read led gpio num fail!", __func__);
return ret;
}
return HDF_SUCCESS;
}
//驱动对外提供的服务能力,将相关的服务接口绑定到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;
}
// 驱动自身业务初始的接口
int32_t HdfLedDriverInit(struct HdfDeviceObject *device)
{
struct Stm32Mp1ILed *led = &g_Stm32Mp1ILed;
int32_t ret;
if (device == NULL || device->property == NULL) {
HDF_LOGE("%s: device or property NULL!", __func__);
return HDF_ERR_INVALID_OBJECT;
}

ret = Stm32LedReadDrs(led, device->property);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: get led device resource fail:%d", __func__, ret);
return ret;
}

ret = GpioSetDir(led->gpioNum, GPIO_DIR_OUT);
if (ret != 0)
{
HDF_LOGE("GpioSerDir: failed, ret %d\n", ret);
return ret;
}
HDF_LOGD("Led driver Init success");
return HDF_SUCCESS;
}
// 驱动资源释放的接口
void HdfLedDriverRelease(struct HdfDevijceObect *deviceObject)
{
if (deviceObject == NULL)
{[请添加链接描述](https://docs.openharmony.cn/pages/v3.1/zh-cn/device-dev/driver/driver-hdf-message-management.md/)
}
HDF_LOGD("Led driver release success");
return;
}
// 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量
struct HdfDriverEntry g_ledDriverEntry = {
.moduleVersion = 1,
.moduleName = "HDF_LED",
.Bind = HdfLedDriverBind,
.Init = HdfLedDriverInit,
.Release = HdfLedDriverRelease,
};
// 调用HDF_INIT将驱动入口注册到HDF框架中
HDF_INIT(g_ledDriverEntry);

在此之前要了解OpenHarmony的驱动程序框架HDF(Hardware Driver Foundation),内核态驱动和用户态应用如何互相传递消息。

官方文档:驱动消息机制管理详情解读,可参考这位开发者的文章:【FFH】HDF驱动开发之编写驱动代码

简单概括就是主要靠Dispatch函数。

用户态获取服务接口并发送消息到驱动。

ret = serv->dispatcher->Dispatch(&serv->object, LED_WRITE_READ, data, reply);
if (ret != HDF_SUCCESS)
{
printf("fail to send service call!\r\n");
goto out;
}

驱动接收用户态消息。

// Dispatch是用来处理用户态发下来的消息
int32_t LedDriverDispatch(struct HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)

驱动给用户态发消息。


if (!HdfSbufWriteInt32(reply, status1))
{
HDF_LOGE("replay is fail");
return HDF_FAILURE;
}

用户程序是无法直接访问驱动的,当只有驱动程序向用户态暴露server后,用户程序才能通过Dispatch的方式发送指令到驱动程序,并可以将用户态的数据携带到驱动程序,也可以从驱动程序读出数据,如下图所示为用户态程序与驱动自己数据交互的过程。

就此用户态界面可以获得GPIO的电平变化次数:

根据实际测量得500ml水会引起GPIO电平变化的次数为320次,所以累计水流量可以用公式。

Flow=(T/320)✖500 (ml)表示。

​想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com​​。

来源:鸿蒙社区内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯