基于C#的三菱PLC串口通信实现方案
一、通信协议选择与配置
推荐采用MELSEC通信协议(MC协议),支持串口/以太网通信,兼容FX3U/Q系列PLC。核心参数配置如下:
// 串口参数配置(需与PLC设置一致)
SerialPort serialPort = new SerialPort();
serialPort.PortName = "COM1"; // 波特率
serialPort.BaudRate = 9600; // 数据位
serialPort.DataBits = 8; // 停止位
serialPort.StopBits = StopBits.One;// 校验位
serialPort.Parity = Parity.None; // 流控
serialPort.Handshake = Handshake.None;
二、核心通信类实现
public class MitsubishiPLC
{
private SerialPort _serialPort;
private const byte STX = 0x02;
private const byte ETX = 0x03;
private const byte ENQ = 0x05;
private const byte ACK = 0x06;
private const byte NAK = 0x15;
public MitsubishiPLC(string portName, int baudRate)
{
_serialPort = new SerialPort(portName, baudRate);
_serialPort.DataReceived += SerialPort_DataReceived;
}
// 打开串口
public bool Open()
{
try
{
if (!_serialPort.IsOpen)
{
_serialPort.Open();
return true;
}
return false;
}
catch (Exception ex)
{
Log($"串口打开失败: {ex.Message}");
return false;
}
}
// 关闭串口
public void Close()
{
if (_serialPort.IsOpen) _serialPort.Close();
}
// 发送指令并接收响应
private byte[] SendCommand(byte[] command)
{
_serialPort.Write(command, 0, command.Length);
Thread.Sleep(100); // 等待响应
if (_serialPort.BytesToRead > 0)
{
byte[] response = new byte[_serialPort.BytesToRead];
_serialPort.Read(response, 0, response.Length);
return response;
}
return null;
}
// 读取保持寄存器(D区)
public ushort[] ReadD(string address, int count)
{
byte[] cmd = BuildReadCommand('D', address, count);
byte[] response = SendCommand(cmd);
return ParseResponse(response);
}
// 写入保持寄存器(D区)
public bool WriteD(string address, ushort[] values)
{
byte[] cmd = BuildWriteCommand('D', address, values);
byte[] response = SendCommand(cmd);
return CheckAck(response);
}
// 构建读取指令
private byte[] BuildReadCommand(char type, string addr, int count)
{
string cmdStr = $"{(char)ENQ}00FF{type}{addr.PadLeft(4,'0')}{count.ToString("X4")}03";
byte[] data = Encoding.ASCII.GetBytes(cmdStr);
byte checksum = CalculateChecksum(data);
return data.Concat(new byte[] { checksum, ETX }).ToArray();
}
// 构建写入指令
private byte[] BuildWriteCommand(char type, string addr, ushort[] values)
{
string valueHex = string.Join("", values.Select(v => v.ToString("X4")));
string cmdStr = $"{(char)ENQ}00FF{type}{addr.PadLeft(4,'0')}{values.Length.ToString("X2")}{valueHex}";
byte[] data = Encoding.ASCII.GetBytes(cmdStr);
byte checksum = CalculateChecksum(data);
return data.Concat(new byte[] { checksum, ETX }).ToArray();
}
// 校验和计算
private byte CalculateChecksum(byte[] data)
{
byte sum = 0;
foreach (byte b in data)
{
sum += b;
}
return (byte)(0 - sum);
}
// 解析响应数据
private ushort[] ParseResponse(byte[] response)
{
if (response[0] != STX || response[1] != 0x30) return null;
int length = response[2] - 0x30;
ushort[] data = new ushort[length / 2];
for (int i = 0; i < length; i += 2)
{
data[i / 2] = (ushort)(response[3 + i] << 8 | response[4 + i]);
}
return data;
}
// 校验ACK响应
private bool CheckAck(byte[] response)
{
return response != null && response[0] == ACK;
}
}
三、关键功能实现示例
1. 读取D0-D9寄存器
MitsubishiPLC plc = new MitsubishiPLC("COM1", 9600);
if (plc.Open())
{
ushort[] data = plc.ReadD("0000", 10);
for(int i=0; i<data.Length; i++)
{
Console.WriteLine($"D{i}: {data[i]}");
}
}
2. 写入单个D寄存器
ushort[] writeData = { 0x1234 };
plc.WriteD("0000", writeData);
3. 批量写入M区位元件
// 写入M0-M15(16位)
byte[] cmd = new byte[] { 0x05, 0x30, 0x30, 0x46, 0x46, 0x42, 0x57, 0x41, 0x4D, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x32 };
_serialPort.Write(cmd, 0, cmd.Length);
四、异常处理与调试
-
超时处理
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { if (DateTime.Now - _lastRecvTime > TimeSpan.FromSeconds(2)) { Log("通信超时"); _serialPort.Close(); } } -
日志记录
private void Log(string message) { File.AppendAllText("plc_log.txt", $"{DateTime.Now}: {message}\r\n"); } -
错误码解析
错误码 含义 解决方案 0x0101 站号错误 检查PLC站号设置 0x0102 指令不支持 确认协议版本兼容性 0x0105 数据校验错误 检查校验和计算逻辑
五、高级功能扩展
-
Modbus RTU从站模式
通过RS指令实现从站功能,需配置PLC的通信参数:
LD M8000 RS D1000 D101F 0A 03 // 从站地址03,接收区D1000-D101F,长度10 RS D2000 D201F 0A 03 // 发送区D2000-D201F,长度10 -
多线程通信
private Thread _commThread; private void StartComm() { _commThread = new Thread(() => { while (true) { ReadDataPeriodically(); Thread.Sleep(1000); } }); _commThread.Start(); }
参考代码 通过串口与三菱PLC进行通讯,能读取、写入各个软元件。 www.youwenfan.com/contentcnp/93723.html
六、调试工具推荐
- GX Works2模拟器 通过GX Works2的仿真功能测试通信逻辑,无需物理PLC设备。
- 串口调试助手 使用SecureCRT或Putty进行原始数据监控,验证报文格式。
- Wireshark抓包 分析串口通信的完整数据帧结构。
七、完整项目结构
PLC_Communication/
├── MitsubishiPLC.cs // 核心通信类
├── SerialPortConfig.cs // 串口配置管理
├── ProtocolParser.cs // 报文解析工具
├── ExceptionHandler.cs // 异常处理模块
└── TestApp/ // 测试程序
├── MainForm.cs // 主界面
└── DeviceManager.cs // 设备管理器
八、注意事项
- 通信参数一致性:确保PLC与上位机的波特率、数据位、停止位、校验位完全一致
- 数据缓冲区管理:批量读写时注意PLC的接收缓冲区大小限制(通常≤256字节)
- 实时性要求:关键控制指令建议采用中断接收方式
- 安全防护:工业环境中需增加光电隔离和防浪涌电路
浙公网安备 33010602011771号