RTU 模式:
控制器以RTU模式在Modbus总线上进行通讯时,信息中的数据按十六进制传输,该模式的主要优点是在相同波特率下其传输的字符的密度高于ASCI模式,每个信息必须连续传输。
RTU 模式中每个字节的格式:
编码系统:所有数据均为十六进制。.
数据位:1 起始位
8 位数据,低位先送。
奇/偶校验:有奇/偶校验时占1位;无奇偶校验时不发送。(项目中一般都是无校验)。
停止位:有奇/偶校验则用1位停止位;无奇/偶校验则用2位停止位。(项目中一般使用1个停止位)。.
校验域:循环冗余校验(CRC16).
结束符:3.5个字符(字节)时间空闲。
RTU 帧:
RTU模式中,信息开始至少需要有3.5个字符的静止(空闲)时间(或者主机发送一帧结束后至少要间隔3.5 个字符的时间才发送下一帧),依据使用的波特率,很容易计算这个静止(空闲)的时间下面用实际的命令来说明,命令中所有数据均为十六进制。整个信息必须连续发送,如果在发送帧信息期间出现大于1.5个字符的静止时间时,则接收设备有可能丢弃不完整的信息。.
偏移 0:此字节为从机设备地址,用于指定本次接收处理的从机地址,取值为0x00-0xff。从机设备地址是唯一的,同一条总线上不能有相同地址的连个从机。
一般0x00用于广播(比如发送同步帧),从机不返回信息。上例从机设备地址为0x01。
偏移 1:此字节为功能码,表明本次命令的操作功能,这个可以查询 MODBUS 支持的各种功能,实际项目一般用其中的一部分工嗯呢码,上例的功能码0x10就是“写多寄存器”。.
偏移 2:此 2 字节为要写入的寄存器首地址 0x0100,MODBUS 协议对数据的都是基于寄存器访问的,每个寄存器2个字节(一个WORD)。高字节在前,低字节在后。
偏移 4:此 2 字节为要写入的寄存器个数,0x0008 表示要写入 8 个奇存器。偏移 6:此字节为要写入的字节数,0x10 表示要写入 16个字节(8个寄存器)。
CRC校验电脑是小端模式
51是大端模式:
算出来是12CE传出去的是CD12
偏移 6:此字节为要写入的字节数,0x10 表示要写入 16 个字节(8 个寄存器)。.
偏移7:此16个字节为要写入的数据。.
最后两个字节:CRC16 校验值 0x12CE,低字节在前,高字节在后(算法与电脑通用,小端模式)。
各个从机接收到信息后,有多种方式去判断是否是自己的数据,我一般习惯先校验,校验通过才做数据解
析,然后做出应答。如果校验通过,从机地址正确,则本机一定要做应答(即使是错误的命令)。
偏移0:此字节为从机设备地址,同上述描述。
偏移1:此字节为功能码,同上述描述
偏移 2:此 2 字节为要写入的寄存器首地址,同上述描述。
偏移 4:此 2 字节为已经写入的寄存器个数,0x0004 表示已经写入 4个奇存器。
最后两个字节:CRC16 校验值 0x33C0,低字节在前,高字节在后(算法与电脑通用,小端模式)。,
接着实际操作写多寄存器,读多寄存器:
好好研究STC的CDC串口助手
可以发送CRC校验:
双绞线传输,把干扰减低到最大,和网线一样
但是有寄生电容产生,就用9600!
我们接收方是STC link1D :
读多寄存器也一样:
程序执行的一般流程:
校验结果=0为正常
除了自己的地址,还要检查广播地址0
各种错误代码的处理:
while (1)
{
if(B_RX1_OK && !B_TX1_Busy) //收到数据, 进行MODBUS-RTU协议解析
{
if(MODBUS_CRC16(RX1_Buffer, RX1_cnt) == 0) //首先判断CRC16是否正确, 不正确则忽略, 不处理也不返回信息
{
if((RX1_Buffer == 0x00) || (RX1_Buffer == SL_ADDR)) //然后判断站号地址是否正确, 或者是否广播地址(不返回信息)
{
if(RX1_cnt > 2) RX1_cnt -= 2; //去掉CRC16校验字节
i = MODBUS_RTU(); //MODBUS-RTU协议解析
if(i != 0) //错误处理
{
TX1_Buffer = SL_ADDR; //站号地址
TX1_Buffer = i; //错误代码
crc = MODBUS_CRC16(TX1_Buffer, 2);
TX1_Buffer = (u8)crc; //CRC是小端模式, 先发低字节,后发高字节。
TX1_Buffer = (u8)(crc>>8);
B_TX1_Busy = 1; //标志发送忙
TX1_cnt = 0; //发送字节计数
TX1_number = 4; //要发送的字节数
TI = 1; //启动发送
}
}
}
RX1_cnt = 0;
B_RX1_OK = 0;
}
}