Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
freemodbus
基于 serialX 串口驱动移植 freemodbus
10.00
发布于 2023-02-08 17:31:49 浏览:1474
订阅该版
[tocm] ## 关于 serialX 之前,笔者写过多篇 serialX 的文章,已经把它的原理和理念完完全全明明白白讲了,包括它的优势以及使用它需要注意的方面和可能遇到的问题。 到目前为止,笔者只介绍了 finsh/msh 使用 serialX ,实现了中断接收中断发送模式打开串口设备,这次尝试让笔者坚信了即便使用 DMA 收发也能在 finsh 里应付自如。今天我们尝试一下在 freemodbus 里使用 serialX 。 > 注:笔者写过文章,讨论在控制台或者 finsh 里完全的中断收发或者完全的 DMA 收发会有哪些问题。有兴趣的大家可以找找看。 ### 测试环境 笔者手里有一块儿 NK-980IOT V1.0 的开发板,这是去年 rt-thread 论坛搞的测评活动送的。一方面,它吃灰很久了,另一方面,serialX 的驱动很久没新增了。趁此机会,笔者把 NUC980 的 serialX 驱动加上,然后在此基础上调试出 console 和 finsh,最后移植 freemodbus 试试会遇到什么问题。 ### NUC980 serialX 驱动 之前,笔者介绍 serialX 的时候,曾详细的讲解过 `struct rt_uart_ops` 接口中的每一个函数的功能。完全按照每一个函数功能定义去做,后面的事情就是水到渠成的。 花了小半天的时间从 drv\_uart.c 改成 drv\_uartX.c 。 然后使用 [serialX](https://gitee.com/thewon/serialX) 中提供的 测试程序 serialX\_test.c 简单测试了一下,收发回环没发现严重问题(没有数据丢失,没有数据错误)。 启用控制台和 finsh 。改成“中断收发读写”模式,试了几个命令,打印结果完整。 可以说,验证了这次写的驱动还是很不错的,是成功的。 ### 启用 freemodbus env 环境里启用 freemodbus,打开 modbus master 并添加 master 的测试程序。 使用命令 `pkgs --update` 下载 freemodbus 源码。 打开项目后,我们可以看到 "sample_mb_master.c" "port" 开头的以及几个 "mb" 开头的。"sample_mb_master.c" 文件是测试样例程序,"port" 开头的文件是 freemodbus 在 rt-thread 系统上的接口,剩余的是 freemodbus 的核心程序。 这里面 "sample_mb_master.c" "portserial_m.c" 这两个文件是今天我们最关心的。我们可以打开看看这俩文件都做了啥。 #### "sample_mb_master.c" 这个文件的主要工作是创建了两个线程。 一个用来循环调用 `eMBMasterPoll` 函数,这个函数是 freemodbus 工作引擎。不循环执行这个函数 freemodbus 跑不起来。 另一个模式应用层线程,用来发送多寄存器写请求(在这个样例程序里只用这个来做演示)。 #### "portserial_m.c" 这个文件里是串口设备操作相关的。比如初始化、打开、关闭,写数据到串口设备,从串口设备读数据等等。 ##### `xMBMasterPortSerialInit` 笔者第一次打开这个文件,满眼看到的 `serial->config.` `serial->ops->configure` 等操作把我惊呆了。 初始化过程最后创建了子线程 `serial_soft_trans_irq` ,它的工作就是发送数据。 ##### `serial_soft_trans_irq` 发送子线程入口函数,这个函数只关心 `EVENT_SERIAL_TRANS_START` 事件,接收到事件后调用 `xMBMasterRTUTransmitFSM` 函数,如果数据没发送完,调用 `xMBMasterPortSerialPutByte->xMBMasterPortSerialPutByte` 往串口写 **1 个字节**数据。循环执行直到把所有需要发送的数据写完。 ### 第一次测试 经过简单浏览后,笔者想尽快运行程序,做进一步观察。 计算机端,笔者用一个 Qt5 写的 modbus slave 终端。如果一切顺利,NK-980IOT 开发板上发的多寄存器写请求会对 Qt5 modbus slave 终端里的数据修改,并界面上显示到数据变化。 很幸运,笔者这一步也很顺利。 ### 进一步测试 因为自带的 master 样例程序仅仅测试了一个多寄存器写,而且,我们可以看到原来只对 `eMBMasterReqWriteMultipleHoldingRegister` 返回错误代码计数,并没有区分判断会出现哪种错误,我们把这个错误码处理一下 ``` rt_uint32_t err_no = 0, err_reg = 0, err_arg = 0, err_data = 0, err_tout = 0; switch (error_code) { case MB_MRE_NO_ERR: err_no++; break; case MB_MRE_NO_REG: err_reg++; break; case MB_MRE_ILL_ARG: err_arg++; break; case MB_MRE_REV_DATA: err_data++; break; case MB_MRE_TIMEDOUT: err_tout++; break; default: rt_kprintf("n:%d; r:%d; a:%d; d:%d; t:%d\n", err_no, err_reg, err_arg, err_data, err_tout); break; } ``` 这几种返回码,只有 `MB_MRE_NO_ERR` 是正常和完美的,如果出现其它几个都是有问题的。 笔者经过关闭 slave 端,可以测试出现 `MB_MRE_TIMEDOUT` 。但是,**经常还会出现 `MB_MRE_REV_DATA` 这个错误!!!** ### `MB_MRE_REV_DATA` 错误跟踪 这个错误字面含义是,有接收数据,但是接收的数据有异常!我们上面的测试只有一个“多寄存器写”,master 发写请求了以后,只有可能收到成功响应(`MB_MRE_NO_ERR`),或者错误响应信息(`MB_MRE_NO_REG` `MB_MRE_ILL_ARG`)。不应该会出现 slave 给 master 主动发请求的。 为了观察 slave 端接收和发送数据,笔者计划把 slave 端的调试打开,检查一下它是不是回复响应出现错误了。 因为笔者的 slave 端是 Qt 写的,当前使用的这个 exe 是很久之前用 Qt5.9 版本编译出来的。笔者需要重新编译一下这个 exe 程序(现在安装的版本有 Qt5.12 Qt5.15 两个)。编译出来新程序之后,笔者得到如下调试信息(无论是 Qt5.12 还是 Qt5.15), ``` qt.modbus.lowlevel: (RTU server) Received ADU: "01100000000408000f0049000200003574" qt.modbus: (RTU server) Request PDU: 0x100000000408000f004900020000 qt.modbus: (RTU server) Response PDU: 0x1000000004 qt.modbus.lowlevel: (RTU server) Response ADU: "011000000004c1ca" qt.modbus.lowlevel: (RTU server) Received ADU: "011000000004080013004b000200009175" qt.modbus: (RTU server) Request PDU: 0x1000000004080013004b00020000 qt.modbus: (RTU server) Response PDU: 0x1000000004 qt.modbus.lowlevel: (RTU server) Response ADU: "011000000004c1ca" qt.modbus.lowlevel: (RTU server) Received ADU: "011000000004080027004d000200006cb6" qt.modbus: (RTU server) Request PDU: 0x1000000004080027004d00020000 qt.modbus: (RTU server) Response PDU: 0x1000000004 qt.modbus.lowlevel: (RTU server) Response ADU: "011000000004c1ca" ``` slave 断得到了完整正确的 ADU,响应的 ADU 也是正常的。接下来再回过头看看开发板接收响应 ADU 时得到的数据是什么。 在 `eMBMasterRTUReceive` 函数体内 `if` 条件语句之前添加一句代码 `od_mem((unsigned int)ucMasterRTURcvBuf, (unsigned int)ucMasterRTURcvBuf + usMasterRcvBufferPos);`。这句代码将在控制台打印输出接收到的 ADU 。 笔者看到了很多 "01040004 70F9C1" "01040004 70F9F9" 或者其它数据,但是正确的应该是 "01100000 0004C1CA",很多次才出现一次正确的。为什么从串口发出去的数据是正确的,能被远端正常接收,远端响应回来的数据就出现接收错误了?! 为了确定开发板和驱动工作还是正常的,笔者又切换到 serialX 的测试程序,用收发回环测试一遍没有问题,再测试 freemodbus 接收仍然异常! ### 修改 `xMBMasterPortSerialInit` 前边笔者说过,当看到 `serial->config.` `serial->ops->configure` 等操作时惊呆了。这一步,让上帝的归上帝吧。 ``` serial_dev = rt_device_find(uart_name); if(serial_dev == RT_NULL) { /* can not find uart */ return FALSE; } /* set serial configure parameter */ uart_conf.baud_rate = ulBaudRate; /* set serial configure */ rt_device_control(serial_dev, RT_DEVICE_CTRL_CONFIG, &uart_conf); /* open serial device */ if (!rt_device_open(serial_dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX)) { rt_device_set_rx_indicate(serial_dev, serial_rx_ind); } else { return FALSE; } ``` 全局变量 `static struct rt_serial_device *serial;` 改成 `static rt_device_t serial_dev = RT_NULL;`。所有使用 `serial` 这个变量的地方全换成使用 `serial_dev` 。 这样修改了以后,一切变得明朗了起来。 ## 总结 除了上面提到的那个问题,笔者还遇到一种情况,slave 端接收串口数据经常出现断帧,而且字符接收间隔长达 10+ms 。这种情况,当笔者将 `rt_device_write(serial_dev, 0, pucByte, 1);` 改成 `rt_device_write(serial_dev, 0, pucByte, sz);` 后得到解决。但是,之后再也没复现。 得益于 serialX 的框架理念,我们可以从“一个字节一个字节的写”提升到“写一批字节”。而且放到发送缓存里的数据,无论使用中断发送还是 DMA 发送都不会对应用层带来任何压力。 另一方面,读 modbus ADU 的时候,其实也可以“读一批字节”。不是一个字节中断,read 一个字节,下一个接收字节中断,再 read 一个字节... freemodbus 还有很多可圈可点的地方。但是拿它来验证 serialX 驱动可能是最简单的一个了。 最后,笔者把修改过后的 'portserial_m.c' 文件上传上来,但是请大家注意,不止这一个文件需要修改,但是其它修改都是因为这个文件引起的。 [portserial_m.c](https://club.rt-thread.org/file_download/64c5a0e2c33e2a76)
3
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
出出啊
恃人不如自恃,人之为己者不如己之自为也
文章
43
回答
1519
被采纳
343
关注TA
发私信
相关文章
1
FREEMODBUS的设置问题
2
freemodbus tcp
3
freemodbus,写保持寄存器返回值一直是3,但是正确写入 ?
4
FreeModbus 从机该如何更新数据,菜鸟的不解.
5
RT thread freemobus tcp通讯问题
6
freemodbus主机读取从机保持寄存器命令后多两个0
7
freemodbus接收CRC校验疑问
8
freemodbus主机不通
9
freemodbus的主机任何指令都timeout
10
freemodbus 软件包主机有bug
推荐文章
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
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
rt-smart
FAL
I2C_IIC
UART
ESP8266
cubemx
WIZnet_W5500
ota在线升级
PWM
BSP
flash
freemodbus
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
ulog
SFUD
msh
C++_cpp
MicroPython
本月问答贡献
RTT_逍遥
10
个答案
2
次被采纳
xiaorui
3
个答案
2
次被采纳
winfeng
2
个答案
2
次被采纳
三世执戟
8
个答案
1
次被采纳
KunYi
8
个答案
1
次被采纳
本月文章贡献
lizimu
2
篇文章
7
次点赞
catcatbing
2
篇文章
5
次点赞
swet123
1
篇文章
4
次点赞
Days
1
篇文章
4
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部