type
status
date
slug
summary
tags
category
icon
password
在做Modbus传输项目的过程中,用到了Modbus的超时重传机制,涉及两个超时时间,但是这个时间并不能随意设置,过大过小都不行,都可能会导致传输出错。下面用实际案例结合自己的思考和调试过程,说明这两个超时时间该如何选取。
 
主机里任务一控制从机1,任务2控制从机2,共用一个串口,分为两个数据包
主机里任务一控制从机1,任务2控制从机2,共用一个串口,分为两个数据包

1、传输出错导致拼接

notion image
如果主机发来数据包时间间隔比较长,那如果任务1接受出错了,两个数据包之间的间隔很长超过了modbus超时重传时间,那么就会重新传输task3的数据包。
但是现在两个数据包时间间隔很短,而超时时间很长,第一个数据包传输一半出错没有重传,切换到传输第二个数据包,两个数据包就会拼接导致传输无法解析的错误数据包。 _RESPONSE_TIMEOUT是“等待回应的时间”:client 给 server 发出请求后,client 要等待 server 发出的回应,这个等待时间要设置得比较大,以便 server 有足够的时间处理数据并发出回应。 _BYTE_TIMEOUT是“等待后续数据的时间”:一旦得到回应的第一个数据后,后续的数据应该是连续不断地来到,所以后续的超时时间可以设置得比较小(比如设置为modbus 协议规定的 3.5 个字节时长)。
传输10个位(开始,停止,数据),波特率115200,10/115200*3.5=0.3ms,3.5个字节需要0.3ms, 所以改成_BYTE_TIMEOUT10ms没必要50ms
 

2、DMA传输要特别考虑

在使用DMA时把_BYTE_TIMEOUT设置为上述的10ms会出现问题,因为DMA是先存起来再通知应用程序。具体地:
对于 DMA 传输要特殊考虑。当主机使用 DMA 方式接收从机数据时,DMA 可以及时地把串口接收到的数据存入内存,但是它无法及时通知应用程序:假设启动 DMA 传输来读取 256 字节(modbus rtu 最大数据包)数据,当读取到一半数据即 128 字节的数据时才会触发“Half Transfer”的回调,这时才会通知应用程序,也就是说从机发来0-128个字节的时候都只是存起来了。
_RESPONSE_TIMEOUT=500ms不会出现问题,这是等待从机回应的时间,怎么接收到从机的回应?只有产生中断回调时写入队列CPU才能读到这个回应,而中断回调只会在在三种情况下产生,知道从机回我了,500ms时间很长,传输一半的时候产生中断或者IDLE中断去通知应用程序,不会出现timeout,所以这部分数据是安全的,不会丢包。
但是后续_BYTE_TIMEOUT只有10ms了,假设 client 等待回应后得到了一半的数据共 128 字节,它需要等待多久才可以得到第 129个字节数据?DMA 很快得到第 129 个数据,但是要等待剩余数据全部接收完毕,这时才会发生中断,应用程序才被通知到。得到回应之后(接收到一半的数据之后),DMA 传输完成还需要一半数据的时间,以波特率 115200 为例,需要128x10/115200=11.1ms,但是在10ms的时候就超时重传出现丢包。
所以,使用 libmosbus 等待回应时,超时时间要设置得比较大,不仅要大于接收完 128 字节数据的时间,也要考虑:留更多的时间给对方,以便它有足够的时间处理数据然后回复。当应用程序读取到第一个回应的数据后,以后再读取后续数据时,超时时间如何设置?
所以,使用libmosbus 传输比较大的回应数据时,_BYTE_TIMEOUT 超时时间设置为 10ms 是不够的,为了保险,我们可以设置为 20ms。
 
How to use Vision Mamba(Vim) easily从零手写Bootloader实现IAP
Loading...