嵌入式技术论坛
直播中

贾熹

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

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

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

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

UI 界面控制改变变量值

1.jpg

PC 界面控制修改变量值

1.jpg

SDK 固件修改

一.安装Modbus软件包

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

1.jpg

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

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

1.jpg

二、对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 的初始化函数:

1.jpg

移植 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 目录的结构如下

1.jpg

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

烧录

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

UI界面JS端

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

界面设计如下:

1.jpg

其中,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 当作从机,结果如下

1.jpg

2.jpg

3.jpg

原作者:xiaorui

更多回帖

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