嵌入式技术论坛
直播中

刘伟

7年用户 1552经验值
私信 关注
[经验]

利用DCM模块实现UI界面通过串口UART0和PC电脑进行Modbus通信

本帖主要帮助用户快速熟悉新发布的柿饼M3 SDK 如何通过 JS 端与 C 端进行数据交互的使用。下面通过利用 DCM 模块实现 UI界面通过串口UART0和PC电脑进行 Modbus 通信的示例。

示例开始前:先来动图看看效果

UI 界面控制改变变量值

PC 界面控制修改变量值

SDK 固件修改

一.安装Modbus软件包

在 software\firmware 中打开 ENV 软件,使用 menuconfig 命令。依次进入 -> RT-Thread online packages -> IoT - internet of things 选中 agile_modbus 按 Y 键进行 Modbus 软件包的添加。

在 ENV 中执行 pkgs --update 命令,将软件包下载到本地,也可以在云端将软件包下载到路径 software\firmware\packages 中。完成后如下图所示。

在 ENV 中执行 scons --target=vsc 命令,可以生成 vscode 工程,这样可以更方便的使用 vscode 进行开发

二、对Modbus软件包进行移植配置

在路径 software\firmware 中创建目录 dcm_modbus ,并创建 SConscript 文件。

SConscript

from building import *
import rtconfig
cwd = GetCurrentDir()
src = Glob('.c') + Glob('.cpp')
CPPPATH = [cwd]
group = DefineGroup('dcm_modbus_demo', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
在 software\drivers\drv_uart.c 中打开 uart0 的初始化函数:

移植 agile_modbus 的串口接口:

serial_port.c

#include "serial_port.h"

#include <board.h>

#define DBG_TAG "serialP"

#define DBG_LVL DBG_INFO

#include <rtdbg.h>

static struct rt_serial_device *serial = RT_NULL;

static rt_sem_t _rx_sem = RT_NULL;

static rt_err_t serial_ind_cb(rt_device_t dev, rt_size_t size)

{

rt_sem_release(_rx_sem);

return RT_EOK;

}

int serial_send(uint8_t *buf, int len)

{

rt_device_write(&serial->parent, 0, buf, len);

return len;

}

int serial_receive(uint8_t *buf, int bufsz, int timeout, int bytes_timeout)

{

int len = 0;

while (1)

{

    rt_sem_control(_rx_sem, RT_IPC_CMD_RESET, RT_NULL);

    int rc = rt_device_read(&serial->parent, 0, buf + len, bufsz);

    if (rc > 0)

    {

        timeout = bytes_timeout;

        len += rc;

        bufsz -= rc;

        if (bufsz == 0)

            break;

        continue;

    }

    if (rt_sem_take(_rx_sem, rt_tick_from_millisecond(timeout)) != RT_EOK)

        break;

    timeout = bytes_timeout;

}

return len;

}

int serial_init(void)

{

_rx_sem = rt_sem_create("serial", 0, RT_IPC_FLAG_FIFO);

if (_rx_sem == RT_NULL)

{

    LOG_E("create rx_sem failed.");

    return -RT_ERROR;

}

rt_device_t dev = RT_NULL;

dev = rt_device_find("uart0");

if (dev == RT_NULL)

{

    LOG_E("can't find device uart0.");

    rt_sem_delete(_rx_sem);

    return -RT_ERROR;

}

serial = (struct rt_serial_device *)dev;

serial->config.data_bits = DATA_BITS_8;

serial->config.parity = PARITY_NONE;

serial->config.baud_rate = 9600;

serial->config.stop_bits = STOP_BITS_1;

serial->ops->configure(serial, &(serial->config));

if (!rt_device_open(&serial->parent, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX))

{

    rt_device_set_rx_indicate(&serial->parent, serial_ind_cb);

}

else

{

    return -RT_ERROR;

}

return RT_EOK;

}

serial_port.h

#ifndef SERIAL_PORT_H

#define SERIAL_PORT_H

#include <rtthread.h>

#include <rtdevice.h>

#include <stdint.h>

int serial_send(uint8_t *buf, int len);

int serial_receive(uint8_t *buf, int bufsz, int timeout, int bytes_timeout);

int serial_init(void);

#endif

三、创建DCM数据池

创建dcm相关文件

dcm.c

/*

  • Copyright (c) 2006-2020, RT-Thread Development Team
  • SPDX-License-Identifier: Apache-2.0

*/

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <finsh.h>

#include <rtthread.h>

#include "dcm_inc.h"

#define DBG_TAG "dcm.demo"

#define DBG_LVL DBG_LOG

#include <rtdbg.h>

struct dcm_pool *g_dcm_pool = RT_NULL;

#define DCM_POOL_NAME "sensor"

#define DCM_TEST_CACHE_NUM_MAX 100

rt_int16_t data5 = 0;

rt_int16_t data6 = 0;

rt_int16_t data7 = 0;

rt_int16_t data8 = 0;

rt_bool_t is_need_set = RT_FALSE;

/**

  • [url=home.php?mod=space&uid=2666770]@Brief[/url] 设置int16型数据到DCM,并通知JS层
  • [url=home.php?mod=space&uid=3142012]@param[/url] name 变量名
  • @param value 值

*/

void dcm_tool_set_int16_value(const char *name, rt_int16_t value)

{

if (g_dcm_pool == RT_NULL)

    return;

struct dcm_pool *dcm_pool = g_dcm_pool;

dt_value_t *setptr = RT_NULL;

dt_value_t dt_value;

setptr = dt_value_init(&dt_value);

dt_pack_int32(setptr, value);

dcm_value_set(dcm_pool, name, setptr);

}

static void dcm_data5_change_task(void *user_data)

{

data5 = (rt_int16_t)dt_unpack_int32(dcm_value_get(g_dcm_pool, "data5"));

is_need_set = RT_TRUE;

LOG_D("data5: %d", data5);

}

static void dcm_data6_change_task(void *user_data)

{

data6 = (rt_int16_t)dt_unpack_int32(dcm_value_get(g_dcm_pool, "data6"));

is_need_set = RT_TRUE;

LOG_D("data6: %d", data6);

}

static void dcm_data7_change_task(void *user_data)

{

data7 = (rt_int16_t)dt_unpack_int32(dcm_value_get(g_dcm_pool, "data7"));

is_need_set = RT_TRUE;

LOG_D("data7: %d", data7);

}

static void dcm_data8_change_task(void *user_data)

{

data8 = (rt_int16_t)dt_unpack_int32(dcm_value_get(g_dcm_pool, "data8"));

is_need_set = RT_TRUE;

LOG_D("data8: %d", data8);

}

int dcm_sensor_start(void)

{

if (g_dcm_pool == RT_NULL)

{

    g_dcm_pool = rt_calloc(1, sizeof(struct dcm_pool));

    if (g_dcm_pool == RT_NULL)

    {

        LOG_E("dcm pool(%s) malloc failed.", DCM_POOL_NAME);

        return -RT_ENOMEM;

    }

    g_dcm_pool->name = DCM_POOL_NAME;

    g_dcm_pool->max_num = DCM_TEST_CACHE_NUM_MAX;

    /* 初始化数据缓存池 */

    if (dcm_pool_init(g_dcm_pool, RT_NULL, RT_NULL) < 0)

    {

        LOG_E("dcm pool initialized failed.");

        return -RT_ERROR;

    }

    LOG_D("dcm pool(%s) initialized success.\n", DCM_POOL_NAME);

}

// 将数据进行绑定

dcm_cache_bind(g_dcm_pool, "data5", DCM_CB_TYPE_CHANGE, dcm_data5_change_task, RT_NULL);

dcm_cache_bind(g_dcm_pool, "data6", DCM_CB_TYPE_CHANGE, dcm_data6_change_task, RT_NULL);

dcm_cache_bind(g_dcm_pool, "data7", DCM_CB_TYPE_CHANGE, dcm_data7_change_task, RT_NULL);

dcm_cache_bind(g_dcm_pool, "data8", DCM_CB_TYPE_CHANGE, dcm_data8_change_task, RT_NULL);

return 0;

}

INIT_APP_EXPORT(dcm_sensor_start);

dcm_inc.h

#ifndef DCM_INC_H

#define DCM_INC_H

#include <dcmtype.h>

#include <ubj.h>

extern rt_int16_t data5;

extern rt_int16_t data6;

extern rt_int16_t data7;

extern rt_int16_t data8;

extern rt_bool_t is_need_set;

extern void dcm_tool_set_int16_value(const char *name, rt_int16_t value);

#endif

四、创建Modbus线程

创建modbus.c

#include "agile_modbus.h"

#include "dcm_inc.h"

#include "serial_port.h"

#include <dcmtype.h>

#include <rtthread.h>

#define DBG_TAG "mb_master"

#define DBG_LVL DBG_INFO

#include <rtdbg.h>

static uint8_t ctx_send_buf[AGILE_MODBUS_MAX_ADU_LENGTH];

static uint8_t ctx_read_buf[AGILE_MODBUS_MAX_ADU_LENGTH];

static uint16_t hold_register[100];

static void mb_master_poll(void *parameter)

{

agile_modbus_rtu_t ctx_rtu;

agile_modbus_t *ctx = &ctx_rtu._ctx;

agile_modbus_rtu_init(&ctx_rtu, ctx_send_buf, sizeof(ctx_send_buf), ctx_read_buf, sizeof(ctx_read_buf));

agile_modbus_set_slave(ctx, 1); //设置从机地址为1

LOG_I("Running.");

while (1)

{

    rt_thread_mdelay(100);

    int send_len = 0, read_len = 0, rc = 0;

    if (!is_need_set)

    {

        send_len = agile_modbus_serialize_read_registers(ctx, 0, 4);

        serial_send(ctx->send_buf, send_len);

        read_len = serial_receive(ctx->read_buf, ctx->read_bufsz, 1000, 20);

        if (read_len == 0)

        {

            continue;

        }

        rc = agile_modbus_deserialize_read_registers(ctx, read_len,

                                                     hold_register);

        if (rc < 0)

        {

            continue;

        }

        // 将数据通过DCM层传递给JS

        dcm_tool_set_int16_value("data1", hold_register[0]);

        dcm_tool_set_int16_value("data2", hold_register[1]);

        dcm_tool_set_int16_value("data3", hold_register[2]);

        dcm_tool_set_int16_value("data4", hold_register[3]);

    }

    else

    {

        is_need_set = RT_FALSE;

        rt_int16_t *buf = rt_malloc(4);

        buf[0] = data5;

        buf[1] = data6;

        buf[2] = data7;

        buf[3] = data8;

        send_len = agile_modbus_serialize_write_registers(ctx, 4, 4, (const rt_uint16_t *)buf);

        rt_free(buf);

        serial_send(ctx->send_buf, send_len);

        read_len = serial_receive(ctx->read_buf, ctx->read_bufsz, 1000, 20);

        if (read_len == 0)

        {

            continue;

        }

        LOG_I("write cnt success!");

    }

}

}

int mb_master_start()

{

rt_thread_t tid1 = RT_NULL;

serial_init(); // 初始化串口,配置为9600,无校验

tid1 = rt_thread_create("md_m_poll", mb_master_poll, RT_NULL, 4096, 10, 10);

if (tid1 != RT_NULL)

{

    rt_thread_startup(tid1);

}

else

{

    goto __exit;

}

return RT_EOK;

__exit:

if (tid1)

    rt_thread_delete(tid1);

return -RT_ERROR;

}

MSH_CMD_EXPORT(mb_master_start, mb_master_start);

五、编译

最后整个 dcm_modbus 目录的结构如下

在 ENV 中执行 scons -j8 命令,对项目进行编译。

烧录

​ 在 ENV 中执行 udb.exe upgrade .\fw_compress.rbl download 0 命令,对项目进行烧录。

UI界面JS端

一、前台界面与后台逻辑设计

界面设计如下:

其中,data1~4 为读取值,data5~8 为写入值。在从机的数据发生改变时,data1~4 也会进行相对应的更新。在拖动滑条对 data5~8 的值进行重新设定后,点击发送键,data5~8 的值也会被同步到从机中。
JS代码如下

/*

  • File : app.js
  • COPYRIGHT (C) 2006-2021, Shanghai Real-Thread Technology Co., Ltd
  • Change Logs:
  • Date Author Notes
  • 2020-08-20 realthread the first version
    /
    Page({
    data5: 0,
    data6: 0,
    data7: 0,
    data8: 0,
    /
    页面加载时触发该函数 /
    onLoad: function (event) {
    this.dcm = require("dcm");
    this.dbpool = this.dcm.Open('sensor');
    var that = this;
    /
    对data1的改变事件进行绑定,当Data1的值改变时,程序将其读取出来并对控件进行赋值 /
    that.dbpool.onChange("data1", this.onData1Change = function (event) {
    var data = that.dbpool.getItem("data1");
    that.setData({ label1: { value: data } });
    });
    that.dbpool.onChange("data2", this.onData2Change = function (event) {
    var data = that.dbpool.getItem("data2");
    that.setData({ label2: { value: data } });
    });
    that.dbpool.onChange("data3", this.onData3Change = function (event) {
    var data = that.dbpool.getItem("data3");
    that.setData({ label3: { value: data } });
    });
    that.dbpool.onChange("data4", this.onData4Change = function (event) {
    var data = that.dbpool.getItem("data4");
    that.setData({ label4: { value: data } });
    });
    },
    /
    此方法展示窗体后发生 /
    onResume: function (event) {
    },
    /
    页面显示时触发该函数 /
    onShow: function (event) { },
    /
    页面隐藏时触发该函数 /
    onHide: function (event) { },
    /
    页面退出时触发该函数 */
    onExit: function (event) {
    this.dbpool.offChange("data1", this.onData1Change);
    this.dbpool.offChange("data2", this.onData2Change);
    this.dbpool.offChange("data3", this.onData3Change);
    this.dbpool.offChange("data4", this.onData4Change);
    },
    on_button: function (event) {
    this.dbpool.setItem("data5", this.data5);
    this.dbpool.setItem("data6", this.data6);
    this.dbpool.setItem("data7", this.data7);
    this.dbpool.setItem("data8", this.data8);
    },
    on_slider: function (event) {
    var that = this;
    switch (event.target.id) {
    case 'slider1':
    that.data5 = event.detail.value;
    this.setData({
    label5: {
    value: this.data5
    }
    });
    break;
    case 'slider2':
    that.data6 = event.detail.value;
    this.setData({
    label6: {
    value: this.data6
    }
    });
    break;
    case 'slider3':
    that.data7 = event.detail.value;
    this.setData({
    label7: {
    value: this.data7
    }
    });
    break;
    case 'slider4':
    that.data8 = event.detail.value;
    this.setData({
    label8: {
    value: this.data8
    }
    });
    break;
    }
    }
    });

二、下载UI工程

​ 在 UI 工程的根目录下打开 ENV ,在 ENV 中执行 udb.exe push .\dist\output\original\ /gui 命令即可下载 UI 文件

结果演示
​ 在 MSH 中执行命令 mb_master_start 打开 modbus 主机程序,为了方便演示,这里使用 Modbus Slave 当作从机,结果如下


原作者:xiaorui

更多回帖

发帖
×
20
完善资料,
赚取积分