Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
柿饼_PersimmonUI
柿饼_PersimmonUI
柿饼派示例分享:Modbus 通信示例
发布于 2022-07-13 16:29:30 浏览:2495
订阅该版
[tocm] 本帖主要帮助用户快速熟悉新发布的柿饼M3 SDK 如何通过 `JS` 端与 `C` 端进行数据交互的使用。下面通过利用 `DCM` 模块实现 `UI`界面通过串口`UART0`和PC电脑进行 `Modbus` 通信的示例。 ## 示例开始前:先来动图看看效果 - UI 界面控制改变变量值 ![UI控制变量.gif](https://oss-club.rt-thread.org/uploads/20220713/fed39ebaddf20c1180bbb9d61d3688be.gif "UI控制变量.gif") - PC 界面控制修改变量值 ![PC控制修改变量.gif](https://oss-club.rt-thread.org/uploads/20220713/c385cdbf9e2b11226692929c33aa4605.gif "PC控制修改变量.gif") ## SDK 固件修改 ### 一.安装Modbus软件包 1. 在 `software\firmware` 中打开 `ENV` 软件,使用 `menuconfig` 命令。依次进入 `-> RT-Thread online packages -> IoT - internet of things` 选中 `agile_modbus` 按 `Y` 键进行 `Modbus` 软件包的添加。 ![include_modbus_package.png](https://oss-club.rt-thread.org/uploads/20220713/ce9e7e1d4291aeda8ff8586c9485f299.png.webp "include_modbus_package.png") 2. 在 `ENV` 中执行 `pkgs --update` 命令,将软件包下载到本地,也可以在云端 https://github.com/loogg/agile_modbus 将软件包下载到路径 `software\firmware\packages` 中。完成后如下图所示。 > 在 `ENV` 中执行 `scons --target=vsc` 命令,可以生成 `vscode` 工程,这样可以更方便的使用 `vscode` 进行开发 ![package_path.png](https://oss-club.rt-thread.org/uploads/20220713/947ff13f40e4df3190540f6e2a0e8d57.png "package_path.png") ### 二、对Modbus软件包进行移植配置 1. 在路径 `software\firmware` 中创建目录 `dcm_modbus` ,并创建 `SConscript` 文件。 SConscript ```python 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') ``` 2. 在 `software\drivers\drv_uart.c` 中打开 `uart0` 的初始化函数: ![init_uart0.png](https://oss-club.rt-thread.org/uploads/20220713/c9e356515773c8b62bb6c66be7e7354d.png.webp "init_uart0.png") 2. 移植 `agile_modbus` 的串口接口: serial_port.c ```c #include "serial_port.h" #include
#define DBG_TAG "serialP" #define DBG_LVL DBG_INFO #include
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 ```c #ifndef __SERIAL_PORT_H__ #define __SERIAL_PORT_H__ #include
#include
#include
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数据池 1. 创建dcm相关文件 dcm.c ```c /* * Copyright (c) 2006-2020, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 */ #include
#include
#include
#include
#include
#include "dcm_inc.h" #define DBG_TAG "dcm.demo" #define DBG_LVL DBG_LOG #include
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; /** * @brief 设置int16型数据到DCM,并通知JS层 * @param 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 ```c #ifndef __DCM_INC_H__ #define __DCM_INC_H__ #include
#include
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线程 1. 创建modbus.c ```c #include "agile_modbus.h" #include "dcm_inc.h" #include "serial_port.h" #include
#include
#define DBG_TAG "mb_master" #define DBG_LVL DBG_INFO #include
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); ``` ### 五、编译 1. 最后整个 `dcm_modbus` 目录的结构如下 ![dcm_modbus.png](https://oss-club.rt-thread.org/uploads/20220713/ae4a6f88bd1f69bcb24c80cd129fad79.png "dcm_modbus.png") 2. 在 `ENV` 中执行 `scons -j8` 命令,对项目进行编译。 ### 烧录 在 `ENV` 中执行 ` udb.exe upgrade .\fw_compress.rbl download 0` 命令,对项目进行烧录。 ## UI界面JS端 ### 一、前台界面与后台逻辑设计 1. 界面设计如下: ![JS-GUI.png](https://oss-club.rt-thread.org/uploads/20220713/6b0e6a55f6e45e734a3d8fbf080ba3bd.png "JS-GUI.png") 其中,`data1~4` 为读取值,`data5~8` 为写入值。在从机的数据发生改变时,`data1~4` 也会进行相对应的更新。在拖动滑条对 `data5~8` 的值进行重新设定后,点击发送键,`data5~8` 的值也会被同步到从机中。 2. JS代码如下 ```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` 当作从机,结果如下 ![ui.png](https://oss-club.rt-thread.org/uploads/20220713/1c44b09c3c4b8065fd89986ee6a27743.png.webp "ui.png") ![modbus slave.png](https://oss-club.rt-thread.org/uploads/20220713/3ed787ed4de6175050f5c530e81d2605.png.webp "modbus slave.png") ![msh.png](https://oss-club.rt-thread.org/uploads/20220713/704489172afe3b63657f3387711915d3.png.webp "msh.png")
16
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
xiaorui
小睿手办 https://item.taobao.com/item.htm?id=674889867009
文章
7
回答
347
被采纳
70
关注TA
发私信
相关文章
1
【PersimmonUI柿饼学习营】+ A Byte of China + 环境搭建&20W年薪广告
2
【柿饼学习营】+werrysuzhen+20W年薪作业
3
【PersimmonUI柿饼学习营】+ meetwit + 20W年薪广告&lesson01
4
【PersimmonUI柿饼学习营】+ DaZhou + 20W年薪作业+视频有彩蛋
5
【PersimmonUI柿饼学习营】+ DMY+ lesson01 20W年薪广告
6
【PersimmonUI柿饼学习营】+ Bigmagic+20W年薪广告&day01
7
【PersimmonUI柿饼学习营】+ chowguohua+年薪广告
8
【柿饼学习营】+werrysuzhen+脚本及事件学习
9
【PersimmonUI柿饼学习营】+ DaZhou+ 手把手教你做计算器
10
【PersimmonUI柿饼学习营】+ A Byte of China + 2048游戏
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
cubemx
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
编译报错
Debug
rt_mq_消息队列_msg_queue
SFUD
keil_MDK
msh
ulog
MicroPython
C++_cpp
本月问答贡献
出出啊
1517
个答案
342
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
812
个答案
177
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
148
次被采纳
本月文章贡献
出出啊
1
篇文章
2
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
2
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部