5.Modbus RTU协议下的异常处理
线圈
1.0x07D0→线圈的单次请求长度 2000个线圈/8=250个字节
寄存器
1.0x007D→寄存器单次请求长度→125个寄存器
异常功能码:
判断异常则判断异常功能码高位是否为0x80
List<byte> respBytes = new List<byte>();
while (respBytes.Count < len * 2 + 5)
{
// 超时处理
respBytes.Add((byte)serialPort.ReadByte());
if (respBytes.Count == 5 && respBytes[1] > 0x80)
{
break;
}
Console.WriteLine(respBytes.Count);
}
serialPort.DiscardInBuffer();// 清空接收缓冲区
注:写法所占内存空间区别
1.ushort value=100;
byte[] datas=BitConvert.GetBytes(value);//这种写法是ushort类型,占2个字节
2.byte[] datas=BitConvert.GetBytes(value);//这种写法是int类型,占4个字节
6.ModbusAscii
6.1格式变换
起始(: → 3A)+RTU基础报文(包含从站地址,除CRC校验部分,PDU)+LRC+结束字符(换行回车 →0D 0A)
获取ASCII编码数组,对应byte[],所有字符必须大写
6.2 ASCII码和byte之间转换
//byte->ASCII 重点!!!!
//转换每个ASCII码 x:16进制 2:2位
var bytestr =bytes.Select(x => x.ToString("X2")).ToList();
//分别加头+尾 : → 3A 换行回车 "\r\n" →0D 0A
string str=":"+string.Join("",bytestr) + "\r\n";
byte[] bytes1=Encoding.ASCII.GetBytes(str);
SerialPort serialPort3 = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
serialPort3.Open();
serialPort3.Write(bytes1.ToArray(), 0, bytes1.Length);
//ASCII->byte 重点!!!!!
// 响应 头尾(3个字节)+数据头((6个字节)从站+功能码+长度)+数据(4*4)+LRC校验码(2个字节)
byte[] datas = new byte[len * 4 + 11];
serialPort3.Read(datas, 0, datas.Length);
//获取ASCII码形式
string asciiStr= Encoding.ASCII.GetString(datas, 0, datas.Length);
List<byte> bytes2= new List<byte>();
for (int i = 1; i < asciiStr.Length-2; i+=2)
{
//输出字符串 “01” “03”
string temp = asciiStr[i].ToString() + asciiStr[i + 1].ToString();
//转换为16进制
bytes2.Add(Convert.ToByte(temp,16));
}
//LRC校验
LRC(bytes2);
for (int i = 3; i < bytes2.Count-2; i+=2)
{
byte[] vb = new byte[2] { bytes2[i + 1], bytes2[i] };
ushort v = BitConverter.ToUInt16(vb);// 解析出无符号短整型数据
Console.WriteLine(v);
}
7.ModbusTcp
结构:
TransactionID(2个字节 65535)、ProtocolID(2个字节 0x00 0x00)、Length(2个字节 0x00 0x06)、Unit ID(单元标识符 1 字节)
PDU(可变长度):
-
功能码(Function Code):1 字节(表示操作类型,如读寄存器、写寄存器)。
-
数据域(Data Field):可变长度,取决于功能码。
MODBUS TCP 响应帧总长度计算公式:n*2 + 3 + 6
其中 n
是寄存器数量,3是从站+功能码+长度,6是 MBAP 前导