文章目錄
- 前言
- 一、Modbus概念
- 二、使用步驟
- 1.使用Modbus準備
- 2.使用步驟
- 三、Modbus RTU 與 Modbus ASCII對比
前言
Modbus通信協議!
一、Modbus概念
從站設備編碼(從站地址、單元ID),一主多從。
存儲區:0-線圈狀態、1-輸入線圈(只讀)、3-輸入寄存器(只讀)、4-保持型寄存器
協議公開的,設備可以支持 1-65535
功能碼:
線圈狀態(01-讀取,05-單寫,15-多寫)
輸入線圈(02-讀取)
輸入寄存器(04-讀取)
保持型寄存器(03-讀取,06-單寫,16-多寫)
協議分類:ModbusRTU、ModbusASCII、ModbusTCP
二、使用步驟
1.使用Modbus準備
1、下載ModbusSalve和ModbusPoll軟件
ModbusPoll是主站
ModbusSalve是從站
Modbus是一主多從的形式,所以ModbusPoll只能開一個,ModbusSalve可以開多個。
2、安裝Modbus4庫
2.使用步驟
ModbusRTU和ModbusAscii是基于串口進行通信的所以在使用這兩個協議時需要先創建串口。
ModbusRTU
SerialPort port = new SerialPort("COM3",9600,Parity.None,8,StopBits.One);
port.Open();
// 創建ModbusRTU通信協議對象
IModbusSerialMaster master = ModbusSerialMaster.CreateRtu(port);
master.Transport.ReadTimeout = 2000;// 設置超時時間
master.Transport.Retries = 3;// 設置重試次數
// 讀取從站地址1的保存型寄存器0~9
ushort[] registers = master.ReadHoldingRegisters(slaveAddress: 1,// 從站地址(1-247)startAddress: 0,// 起始寄存器地址numberOfPoints: 10// 讀取寄存器數量);
// 異步讀取從站地址1的保存型寄存器0~9
//Task<ushort[]> taskRegisters = master.ReadHoldingRegistersAsync(1, 0, 10);
// 第一種處理異步方法的返回值,所在方法不需要變更異步方法
//ushort[] registersAsync = taskRegisters.GetAwaiter().GetResult();
// 第二種處理異步方法的返回值,所在方法需要變更為異步方法
//ushort[] registersAsync = await master.ReadHoldingRegistersAsync(1, 0, 10);
Debug.WriteLine($"保持寄存器值:{string.Join(",", registers)}");
// 讀取從站地址1的輸入型寄存器0~9
ushort[] inputRegisters = master.ReadInputRegisters(slaveAddress: 1,startAddress: 0,numberOfPoints: 6);
// 異步讀取從站地址1的輸入型寄存器0~9
//ushort[] inputRegistersAsync = await master.ReadInputRegistersAsync(1, 0, 6);
Debug.WriteLine($"輸入寄存器值:{string.Join(",", inputRegisters)}");
// 讀取從站地址1的線圈狀態0~7
bool[] coils = master.ReadCoils(slaveAddress: 1,startAddress: 0,numberOfPoints: 8);
// 異步讀取從站地址1的線圈狀態0~7
//bool[] coilsAsync = await master.ReadCoilsAsync(1, 0, 8);
Debug.WriteLine($"讀取線圈狀態:{string.Join(",", coils.Select(c=>c.ToString()))}");
// 讀取從站地址1的輸入線圈0~7
bool[] inputCoils = master.ReadInputs(slaveAddress: 1,startAddress: 0,numberOfPoints: 8);
// 異步讀取從站地址1的輸入線圈0~7
//bool[] inputCoilsAsycn = await master.ReadInputsAsync(1, 0, 8);
Debug.WriteLine($"讀取輸入線圈:{string.Join(",", inputCoils.Select(c => c.ToString()))}");
// 寫入單個寄存器,向從站1的寄存器6號地址寫入數據
//master.WriteSingleRegister(1,6,11111);
ushort[] writeValue = { 1, 2, 3 };
// 寫入多個寄存器,向從站1的寄存器0號地址、1號地址、2號地址中依次寫入三個數據寫入數據
//master.WriteMultipleRegisters(1, 0, writeValue);// 大小端字節序問題
//例如 300的16進制為12C,可以使用兩個字節來表示 0x01 0x2C
// 大端字節序:高位在前低位在后
// 0x01 0x2C 存儲在字節數組中 datas[0] = 0x01 datas[1] = 0x2C
//小端字節序:低位在前高位在后
// 0x2C 0x01 存儲在字節數組中 datas[0] = 0x2C datas[1] = 0x01
// 一個寄存器地址存儲2個byte字節
float v7 = 4.5f; // 浮點型數據占4個字節,但是Modbus協議只能傳輸2個字節的ushort數據
// 首先將浮點數據轉換成字節數組,連續兩次發送數據
// 獲取浮點數據對應的字節信息,C#中的字節轉換后的字節數據為小端字節序,然而Modbus協議要求的是大端字節序,所以后面還需處.單最終還得以設備方要求為準.
byte[] v7_bytes = BitConverter.GetBytes(v7);
// 將上面大端數據轉換成小端順序,也就是反轉一下數組
Array.Reverse( v7_bytes );// 轉換成大端順序
// 將處理好的浮點型的字節數組轉換成ushort數據類型
// 在轉換后有4個字節兩兩一組,一組字節中前面那個字節為高位字節,后面的字節為低位字節,如果需要將字節轉換成ushort類型
// ushort類型為16為其中前8位為高位,后8位為低位,如果需要將兩個字節轉換成16位,則高位字節需要左移8為也就是乘以256,將左移后的
// 數據與低位相加也就得到一個轉換后的ushort數據.
ushort s1 = (ushort)(v7_bytes[0] * 256 + v7_bytes[1]);
// 使用C#自帶的BitConverter來將字節數據轉換成ushort類型
s1 = BitConverter.ToUInt16(new Byte[] { v7_bytes[1], v7_bytes[0] });// 注意:C#自帶的BitConverter方法轉換是按小端字節序,所以傳入的字節數組得是小端字節序否則數據轉換錯誤
//將浮點型的后兩位轉換成ushort類型
ushort s2 = (ushort)(v7_bytes[2] *256+v7_bytes[3]);
s2 = BitConverter.ToUInt16(new Byte[] { v7_bytes[3], v7_bytes[2] });
ushort[] floatToUshort = new ushort[2];
floatToUshort[0] = s1;
floatToUshort[1] = s2;
master.WriteMultipleRegisters(1,8,floatToUshort);// 讀取從站地址為1的保持型寄存器8~9(讀取浮點型數據)
ushort[] fs = master.ReadHoldingRegisters(1, 8, 2);
// 將讀取到的ushort類型的數據轉換成byte類型
byte[] us_byte_1 = BitConverter.GetBytes(fs[0]);// BitConverter方法得到的是小端字序
byte[] us_byte_2 = BitConverter.GetBytes(fs[1]);
// 得把小端字序改成大端字序
byte[] f_bytes = new byte[4]
{us_byte_1[1],us_byte_1[0],us_byte_2[1],us_byte_2[0]
};
Array.Reverse(f_bytes);
float f2 = BitConverter.ToSingle(f_bytes, 0);// BitConverter需要小端所以需要使用Array.Reverse將大端轉換成小端// 向從站1的0號線圈寫入True狀態
master.WriteSingleCoil(1, 0, true);
// 向從站1的5號線圈連續寫入5個狀態
master.WriteMultipleCoils(1, 5, new bool[] { true, true, true, true, true });//異步執行寫入操作
//master.WriteSingleCoilAsync(1, 0, true);//master.WriteMultipleCoilsAsync(1, 5, new bool[] { true, true, true, true, true });//master.WriteSingleRegisterAsync(1, 6, 11111);//master.WriteMultipleRegistersAsync(1, 0, writeValue);
ModbusASCII
修改上面代碼
將 IModbusSerialMaster master = ModbusSerialMaster.CreateRtu(port);中的CreateRtu替換成CreateAscii
三、Modbus RTU 與 Modbus ASCII對比
特性 | Modbus RTU | Modbus ASCII |
---|---|---|
數據表示方式 | 二進制格式,每個字節包含 8 位數據 | ASCII 碼格式,每個字節用兩個 ASCII 字符表示 |
傳輸效率 | 高效,數據緊湊,適合大量數據傳輸 | 較低,數據量是 RTU 模式的兩倍左右 |
校驗方式 | CRC(循環冗余校驗),檢測能力強 | LRC(縱向冗余校驗),檢測能力相對較弱 |
通信線路要求 | 要求較高,對數據準確性敏感 | 要求相對較低,對噪聲和干擾有一定容忍度 |
適用場景 | 工業自動化設備間的高速可靠通信 | 與舊設備集成、調試階段或通信速率要求不高的系統 |
抗干擾能力 | 較弱,需采取抗干擾措施 | 較強,有一定容錯能力 |
實現復雜度 | 較復雜,需處理二進制編碼和 CRC 校驗 | 較簡單,易于實現 |
工業場景一般使用ModbusRTU