请教Modbus高手makesoft:实现Modbus协议一定需要超时检测吗?
首先声明,我对Modbus不熟悉,尤其是如何实现它,最近才从网上下载了协议研究了一下,特此向高手请教。搞清楚这些问题,才能有效地在芯片中实现相应的功能,满足大家的需要。
此帖的目的是继续另一帖的讨论:
建议STM32的芯片加上串口超时功能,很好用的一个功能
这里是我的实现描述:Modbus的请求数据包都是由下面几部分构成:
一、从机地址;固定为一个字节
二、功能代码;固定为一个字节
三、功能参数;长度依不同功能代码而不同
四、数据域;长度在功能参数中定义
五、校验码;固定为两个字节
下面以功能代码=0x03的数据包交换说明如何使用STM32进行数据传送:
1、主机发送8字节:
从机地址: 1字节
功能代码: 1字节 = 0x03
起始地址: 2字节
读取数目: 2字节 = N
CRC校验码: 2字节
2、从机先接收2字节,当得知功能代码为0x03时,再继续接收6个字节
3、从机发送 N*2+4字节 = (1字节功能码+1字节数据长度+N*2字节数据+2字节校验码)
4、主机接收从机发出的(N*2+4)字节数据。因为主机知道N的数值,所以主机知道从机响应的数据包的长度
这里可以看到,如果没有FIFO,需要在上述第2阶段从机接收主机请求时分为两个步骤进行;而在其他的阶段使用DMA的效率不比使用FIFO差,而且在第4阶段,如果数据包长度大于FIFO深度时,使用DMA的效率更高!
根据我的理解,Modbus协议是主从结构,即主机发送请求给从机,从机收到请求后根据要求返回主机需要的数据或状态。主机的发送和接收都是主动的,它随时知道发送或接收数据的长度;而从机的发送是被动的,只有在接收到主机的请求后才能发送指定的数据或状态。
因此,主机没有必要通过超时检测来判断帧的开始和结束。对于从机讲,在接收第一个请求帧时,它一定知道帧的开始,同时根据我上面描述的步骤,它也可以很容易的知道帧的结束。既然可以判断出第一个帧的开始和结束,那么随后的帧就不难区分了。
所以,我希望makesoft能够对你的注解(
“关键是你没有延时无法判断什么时候是一个帧的开始和结束”)加以解释,“延时判断”真的很关键吗?谢谢!
因为MODBUS RTU方式没有帧头和帧未特定的字节
这样在程序中间无法判断一个帧的开始和结束,一般都是用超时来判断的。
在没有错误的情况下,直接从接收的第一个字节来判定应该是没有问题的,但当字节在传输过程中丢失或者是畸变的时候,程序就吊死在那里了。
RTU帧特性是这样表述的
其实在低速率情况下,字节逐一中断并不特别占用CPU时间
还是可以容忍的
我现在的项目超时是用SYSTICK实现的,感觉问题也不大
找到一个在STM32下实现Modbus要求的超时检测的折衷办法
这个方法是使用UART接收信号去复位一个定时器的计数器,如果UART的RX线上长时间没有电平变化,定时器的计数器将连续计数,最终达到设置在输出比较寄存器中的超时数值并触发中断,从而达到超时检测的目的。
该方法占用一个I/O口和一个定时器,好在STM32的资源很丰富,哈哈。
另一个方法是使用UART自身的IDLE检测中断,即在RX线上超过一个字符传送时间而没有检测到启始位时产生的中断,再进一步判断是否有超时。
是这个东西吗?
以下内容摘自NXP LPC2119/2129/2194/2292/2294的User Manual:
The CTI interrupt (U0IIR3:1=110) is a second level interrupt and is set when the UART0 Rx FIFO contains at least one character and no UART0 Rx FIFO activity has occurred in 3.5 to 4.5 character times. Any UART0 Rx FIFO activity (read or write of UART0 RSR) will clear the interrupt. This interrupt is intended to flush the UART0 RBR after a message has been received that is not a multiple of the trigger level size. For example, if a peripheral wished to send a 105 character message and the trigger level was 10 characters, the CPU would receive 10 RDA interrupts resulting in the transfer of 100 characters and 1 to 5 CTI interrupts
(depending on the service routine) resulting in the transfer of the remaining 5 characters.
我知道
Modbus协议接收的数据组是不固定的,而且串口接收到数据后也要马上返回数据,因此该协议规定如果接到一组数据后,停止1.5个字节时间,不再有数据接收,就认为这组数据接收完毕。注意1.5字节的时间跟波尔特率有关系。
一般的实现方法,一但接收到数据,马上打开一个定时器,而且定时器要开中断,定时的时间是当前波特率下1.5字节所需要的时间。如果一组数据没有接收完毕,直接复位定时器,一旦没有数据传送,定时器就不再复位,一直等到定时器中断,这样就认为一组数据接收完毕。
如果串口判断接收端口没有电平的时间,就可以很轻松实现这个功能。楼主Modbus协议就是这比较难实现
哈哈,没想到我的一块砖头引来这么多美玉,这么多高手
谢谢各位,我知道怎么做了。
上次已经说过了,可以用定时器来实现超时。
ATMEL 的ARM7/9 串口也没有 超时功能,
NXP的ARM7/9 串口就有有 超时功能,
正确是叫 字符超时指示(CTI) 很多人利用此作为判断帧结束的标志!
mark
mark"正确是叫 字符超时指示(CTI) 很多人利用此作为判断帧结束的标志!
"
楼上所说的mark是否就是传输线上一个连续的高电平?
在STM32中,这是一个IDLE条件,又专门的状态标志位指示这个状态,并可以产生中断。这就是我在6楼说的第二种方法中提到的IDLE检测中断。
ATMEL 中是有超时! 谢谢cecoyzy提醒!
USART 中断使能寄存器:
TIMEOUT: 超时中断使能
.....
USART 通道状态寄存器:
TIMEOUT: 接收器超时
MODBUS-RTU模式 必须 使用超时检测
参考一下FreeModbus的源代码,里面有怎么处理的,比较符合MODBUS的协议要求
利用定时器的超时溢出来判断帧的开始以及结束
每接受一个字符,定T35,如果到点了,那么接收一帧完毕,接着进行帧处理
IDLE检测中断+DMA
"另一个方法是使用UART自身的IDLE检测中断,即在RX线上超过一个字符传送时间而没有检测到启始位时产生的中断,再进一步判断是否有超时。"
用这个方法+DMA效果最好
对18楼操作的疑问?
刚没有数据发过来的时候,USART线上是一直IDLE的。所有USART空闲时将不断的引起IDLE中断。
那你什么时候开的IDLE中断??????
开启IDLE中断的时间选择在已经接受到至少一个字节以后