C#上位机使用Modbus RTU协议控制变频器
一、RTU报文格式示例
通信命令码:
03H:读从机参数
06H:写从机参数
10H:写多个参数
上位机写单个寄存器:【从机地址】1字节+【命令码】1字节+【寄存器地址高-低】2字节+【数据值高-低】2字节+【CRC校验值低-高】2字节,总共8字节。
上位机读寄存器:【从机地址】1字节+【命令码】1字节+【寄存器地址高-低】2字节+【读取寄存器数量高-低】2字节+【CRC校验值低-高】2字节,总共8字节。
二、生成CRC校验值的方法
private byte[] CalculateCrc(byte[] data)
{
ushort crc = 0xFFFF;
for (int i = 0; i < data.Length; i++)
{
crc ^= (ushort)data[i];
for (int j = 0; j < 8; j++)
{
if ((crc & 0x0001) != 0)
{
crc >>= 1;
crc ^= 0xA001;
}
else
{
crc >>= 1;
}
}
}
byte[] crcBytes = new byte[2];
//CRC低位
crcBytes[0] = (byte)(crc & 0xFF);
//CRC高位
crcBytes[1] = (byte)(crc >> 8);
return crcBytes;
}
三、对变频器进行读写
这个方法是将传入的不带CRC校验的字节数组计算CRC校验值之后组成完整的报文并下发。
public void WriteMessage(byte[] data)
{
try
{
//计算CRC校验值
byte[] crc = this.CalculateCrc(data);
//把CRC校验值组合进去,组成完整的报文
byte[] sendDataCRC = data.Concat(crc).ToArray();
//将加了CRC校验值的报文转换成16进制的字符串
string str = this.serialPortHelper.AlgorithmHelperObject.BytesTo16(sendDataCRC, Enum16Hex.Blank);
this.SendData(str);
}
catch (Exception ex)
{
MessageBox.Show("主站设备下发报文错误:" + ex.Message);
}
}
当通道有数据传回时,通过串口的DataReceived事件直接触发接收数据方法,并将报文显示到界面上。
this.serialPortHelper.SerialPortObject.DataReceived += new SerialDataReceivedEventHandler(this.SerialPort_DataReceived);
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
string data = string.Empty;
byte[] temp = this.serialPortHelper.ReceiveData();
if (temp.Length != 0)
{
foreach (var item in temp)
{
data += item.ToString("X2") + " ";
}
}
this.txtReceiveData.Invoke(new Action<string>(s =>
{ this.txtReceiveData.Text += "【" + DateTime.Now.ToLongTimeString() + "】" + s + "\r\n"; }), data);
}
catch (Exception ex)
{
MessageBox.Show("接收数据出现错误:" + ex.Message);
}
}
public byte[] ReceiveData()
{
//定义一个接收数组,获取结束缓冲区数据的字节数
byte[] byteData = new byte[this.serialPort.BytesToRead];
//读取数据
this.serialPort.Read(byteData, 0, serialPort.BytesToRead);
return byteData;
}
1、设定变频器运行频率
变频器频率设置地址为1E01H。
private void btnSetHz_Click(object sender, EventArgs e)
{
//从机地址【0X01】
//命令码【0X03】,读从机参数
//起始地址【0X0000】,对应PLC的40001
//寄存器数量【0X0001】
//byte[] sendData = new byte[6] {0X01, 0X03, 0X00, 0X00, 0X00, 0X01};
//将写入的数据转换为字节数组,最大频率设置为5000,即50.00Hz
byte[] value = new byte[2];
if (Convert.ToInt32(this.txtSetHz.Text.Trim()) > 5000)
{
value = BitConverter.GetBytes(5000);
}
else
{
value = BitConverter.GetBytes(Convert.ToInt16(this.txtSetHz.Text.Trim()));
}
//报文格式:从机地址【1字节】——通信命令码【1字节】——参数地址【2字节,高-低位】——参数值【2字节,高-低位】——CRC【2字节,低-高位】
//除了CRC之后的报文,从机地址:0X01,命令码:0X06(写入单个寄存器),寄存器地址:0X1E01,写入的数据:writeData[]。
byte[] sendData = new byte[6] { 0X01, 0X06, 0X1E, 0X01, value[1], value[0] };
WriteMessage(sendData);
}
2、启动/停止变频器运行
变频器操作命令字地址为1E00H。
private void btnOperateCode_Click(object sender, EventArgs e)
{
byte[] value = new byte[2];
value = BitConverter.GetBytes(Convert.ToInt16(this.cboxOperateCode.Text));
byte[] sendData = new byte[6] { 0X01, 0X06, 0X1E, 0X00, value[1], value[0] };
WriteMessage(sendData);
}
3、监控变频器状态
变频器状态地址为1E02H。
private void btnMonitorUuivertorState_Click(object sender, EventArgs e)
{
isMonitor = true;
if (thread == null)
{
thread = new Thread(ReadUuivertorState);
thread.IsBackground = true;
thread.Start();
}
}
private void ReadUuivertorState()
{
while (isMonitor)
{
//byte[] value = new byte[2];
//value = BitConverter.GetBytes(Convert.ToInt16(this.cboxOperateCode.Text));
//报文格式:从机地址【1字节】——通信命令码【1字节】——参数地址【2字节,高-低位】——参数个数【2字节,高-低位】——CRC【2字节,低-高位】
//除了CRC之后的报文,从机地址:0X01,命令码:0X03(读单个或多个寄存器),寄存器地址:0X1E02,参数个数:0X0001。
byte[] sendData = new byte[6] { 0X01, 0X03, 0X1E, 0X02, 0X00, 0X01 };
WriteMessage(sendData);
Thread.Sleep(1000);
}
}