经过整理的Modbus通讯协议详解(2)

时间:2022-06-22 14:51:18

经过整理的Modbus通讯协议详解(二)

modbus两种传输方式(ASCII或RTU)
      

控制器能设置为两种传输模式(ASCII或RTU)中的任何一种在标准的Modbus网络通信。用户选择想要的模式,包括串口通信参数(波特率、校验方式等),在配置每个控制器的时候,在一个Modbus网络上的所有设备都必须选择相同的传输模式和串口参数。

 

所选的ASCII或RTU方式仅适用于标准的Modbus网络,它定义了在这些网络上连续传输的消息段的每一位,以及决定怎样将信息打包成消息域和如何解码。

 

在其它网络上(象MAP和Modbus Plus)Modbus消息被转成与串行传输无关的帧。

 

下面简单的介绍一下,对于Modbus的ASCII、RTU和TCP协议来说,其中TCP和RTU协议非常类似,我们只要把RTU协议的两个字节的校验码去掉,然后在RTU协议的开始加上5个0和一个6并通过TCP/IP网络协议发送出去即可。所以在这里我仅介绍一下Modbus的ASCII和RTU协议。

 

下表是ASCII协议和RTU协议进行的比较:

协议
 开始标记
 结束标记
 校验
 传输效率
 程序处理
 
ASCII
 :(冒号)
 CR,LF
 LRC
 低
 直观,简单,易调试
 
RTU
 无
 无
 CRC
 高
 不直观,稍复杂
 

 

通过比较可以看到,ASCII协议和RTU协议相比拥有开始和结束标记,因此在进行程序处理时能更加方便,而且由于传输的都是可见的ASCII字符,所以进行调试时就更加的直观,另外它的LRC校验也比较容易。但是因为它传输的都是可见的ASCII字符,RTU传输的数据每一个字节ASCII都要用两个字节来传输,比如RTU传输一个十六进制数0xF9,ASCII就需要传输’F’’9’的ASCII码0x39和0x46两个字节,这样它的传输的效率就比较低。所以一般来说,如果所需要传输的数据量较小可以考虑使用ASCII协议,如果所需传输的数据量比较大,最好能使用RTU协议。

 

 

ASCII模式

当控制器设为在Modbus网络上以ASCII(美国标准信息交换代码)模式通信,在消息中的每个8Bit字节都作为两个ASCII字符发送。这种方式的主要优点是字符发送的时间间隔可达到1秒而不产生错误。

 

代码系统:
 · 十六进制,ASCII字符0...9,A...F
· 消息中的每个ASCII字符都是一个十六进制字符组成

 

每个字节的位:
· 1个起始位
· 7个数据位,最小的有效位先发送
· 1个奇偶校验位,无校验则无

 

LRC域是一个包含一个8位二进制值的字节。LRC值由传输设备来计算并放到消息帧中,接收设备在接收消息的过程中计算LRC,并将它和接收到消息中LRC域中的值比较,如果两值不等,说明有错误。

 

LRC校验比较简单,它在ASCII协议中使用,检测了消息域中除开始的冒号及结束的回车换行号外的内容。它仅仅是把每一个需要传输的数据按字节叠加后取反加1即可。

 

下面是它的VC代码:

BYTE GetCheckCode(const char * pSendBuf, int nEnd)//获得校验码

{

BYTE byLrc = 0;

char pBuf[4];

int nData = 0;

for(i=1; i<end; i+=2)   //i初始为1,避开“开始标记”冒号

    {

//每两个需要发送的ASCII码转化为一个十六进制数

    pBuf [0] = pSendBuf [i];

    pBuf [1] = pSendBuf [i+1];

    pBuf [2] = '/0';

    sscanf(pBuf,"%x",& nData);

    byLrc += nData;

    }

    byLrc = ~ byLrc;

    byLrc ++;

return byLrc;

}

 

CRC校验
CRC域是两个字节,包含一16位的二进制值。它由传输设备计算后加入到消息中。接收设备重新计算收到消息的CRC,并与接收到的CRC域中的值比较,如果两值不同,则有误。

 

CRC添加到消息中时,低字节先加入,然后高字节。

 

编程方法如下:

①装如一个16位寄存器,所有数位均为1。
②该16位寄存器的高位字节与开始8位字节进行“异或”运算。运算结果放入这个16位寄存器。
③把这个16寄存器向右移一位。
④若向右(标记位)移出的数位是1,则生成多项式1010000000000001和这个寄存器进行“异或”运算;若向右移出的数位是0,则返回③。
⑤重复③和④,直至移出8位。
⑥另外8位与该十六位寄存器进行“异或”运算。
⑦重复③~⑥,直至该报文所有字节均与16位寄存器进行“异或”运算,并移位8次。
⑧这个16位寄存器的内容即2字节CRC错误校验,被加到报文的最高有效位。

 

 

CRC添加到消息中时,低字节先加入,然后高字节。下面是它的VC代码:

WORD GetCheckCode(const char * pSendBuf, int nEnd)//获得校验码

{

WORD wCrc = WORD(0xFFFF);

    for(int i=0; i<nEnd; i++)

    {

    wCrc ^= WORD(BYTE(pSendBuf[i]));

    for(int j=0; j<8; j++)

        {

            if(wCrc & 1)

            {

        wCrc >>= 1;

        wCrc ^= 0xA001;

            }

            else

{

        wCrc >>= 1;

            }

        }

    }

    return wCrc;

}

 

对于一条RTU协议的命令可以简单的通过以下的步骤转化为ASCII协议的命令:

1、  把命令的CRC校验去掉,并且计算出LRC校验取代。

2、  把生成的命令串的每一个字节转化成对应的两个字节的ASCII码,比如0x03转化成0x30,0x33(0的ASCII码和3的ASCII码)。

3、  在命令的开头加上起始标记“:”,它的ASCII码为0x3A。

4、  在命令的尾部加上结束标记CR,LF(0xD,0xA),此处的CR,LF表示回车和换行的

ASCII码。