基于C#实现与三菱FX系列PLC串口通信

一、通信协议核心要点

三菱PLC采用ASCII模式的专用协议,关键要素包括:

  1. 帧结构 STX(02H) + 命令码 + 地址 + 数据长度 + 数据 + 校验和 + ETX(03H)

  2. 校验方式 累加和取低2位十六进制(如校验和为0x0A+0x0B=0x15 → 校验码为15)

  3. 地址转换规则 位地址:Y737H→ ASCII码33 37 字地址:D04000H→ ASCII码34 30 30 30

  4. 指令集

    功能 指令码 示例
    写单个位 03H 02 30 37 30 37 03 30 36
    读单个字 04H 02 30 34 30 30 30 03 30 36
    写单个字 06H 02 30 34 30 30 30 00 64 03 30 36

二、C#实现代码(含完整类库)

1. 串口通信基类
csharp 复制代码
public class MelsecPLC
{
    private SerialPort _serialPort;
    private const string STX = "\x02";
    private const string ETX = "\x03";
    
    public bool Connect(string portName, int baudRate = 9600, Parity parity = Parity.Even)
    {
        try
        {
            _serialPort = new SerialPort(portName, baudRate, parity, 7, StopBits.One);
            _serialPort.DataReceived += SerialPort_DataReceived;
            _serialPort.Open();
            return true;
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"连接失败: {ex.Message}");
            return false;
        }
    }

    private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        var buffer = new byte[_serialPort.BytesToRead];
        _serialPort.Read(buffer, 0, buffer.Length);
        ProcessResponse(buffer);
    }
}
2. 读写指令封装
csharp 复制代码
public class MelsecPLC : MelsecPLC
{
    // 写单个位
    public bool WriteBit(string address, bool value)
    {
        string cmd = $"{STX}30{StationNumber}{(value ? "03" : "02")}00{address}{ETX}";
        return SendCommand(cmd, new byte[] { 0x06 }); // 期望ACK响应
    }

    // 读单个字
    public ushort ReadWord(string address)
    {
        string cmd = $"{STX}30{StationNumber}0400{address}{ETX}";
        var response = SendCommand(cmd);
        
        if (response.Length >= 6)
        {
            byte[] data = { response[5], response[6] };
            return (ushort)(data[0] * 256 + data[1]);
        }
        return 0;
    }

    private byte[] SendCommand(string cmd, byte[] expectedAck = null)
    {
        try
        {
            byte[] buffer = Encoding.ASCII.GetBytes(cmd);
            _serialPort.Write(buffer, 0, buffer.Length);
            
            // 等待响应(超时3秒)
            if (WaitForResponse(3000))
            {
                if (expectedAck != null && !_receivedData.Contains(expectedAck))
                    throw new Exception("无效响应");
                return _receivedData;
            }
            return null;
        }
        catch (TimeoutException)
        {
            Debug.WriteLine("通信超时");
            return null;
        }
    }
}

三、关键功能实现

1. 地址转换工具类
csharp 复制代码
public static class AddressConverter
{
    // 位地址转PLC内部格式(如Y7→37H)
    public static string BitAddressToPLC(string address)
    {
        string type = address[0].ToString().ToUpper();
        int num = int.Parse(address.Substring(1));
        return $"{(char)('3' + (type == "Y" ? 7 : 0))}{num:X2}";
    }

    // 十进制数转PLC字地址(如D0→4000H)
    public static string DecimalToWordAddress(int address)
    {
        return (address * 2 + 4096).ToString("X4");
    }
}
2. 数据解析示例
csharp 复制代码
private void ProcessResponse(byte[] data)
{
    if (data.Length < 3) return;
    
    // 校验和验证
    byte checksum = data.Skip(1).Take(data.Length - 3).Sum(b => (byte)b) % 256;
    if (checksum != data[data.Length - 2])
    {
        Debug.WriteLine("校验失败");
        return;
    }

    // 解析数据
    if (data[1] == 0x30 && data[2] == 0x34) // 读字响应
    {
        ushort value = (ushort)(data[5] * 256 + data[6]);
        OnDataReceived?.Invoke(this, new PLCDataEventArgs(value));
    }
}

四、完整使用示例

csharp 复制代码
// 初始化连接
var plc = new MelsecPLC();
if (plc.Connect("COM3"))
{
    // 写入Y7置1
    plc.WriteBit("Y7", true);
    
    // 读取D0值
    ushort d0Value = plc.ReadWord(AddressConverter.DecimalToWordAddress(0));
    
    // 异常处理
    plc.DataError += (s, e) => 
    {
        Debug.WriteLine($"错误代码:{e.ErrorCode},信息:{e.Message}");
    };
}

五、调试技巧与注意事项

  1. 串口调试工具

    使用PuttySecureCRT验证基础通信,设置参数:

    复制代码
    波特率:9600 | 数据位:7 | 停止位:1 | 校验:偶校验
  2. 抓包分析

    通过Wireshark捕获串口数据,验证指令格式是否正确:

    csharp 复制代码
    02 30 37 30 37 03 30 36  → 写Y7置1
    02 30 34 30 30 30 03 30 36 → 读D0
  3. 异常处理 超时重试机制:设置3次重试机会 自动重连:监控DataReceived事件频率

  4. 性能优化

    csharp 复制代码
    // 使用缓冲区减少GC压力
    private byte[] _buffer = new byte[1024];
    _serialPort.Read(_buffer, 0, _buffer.Length);

六、扩展功能实现

1. 批量读写指令
csharp 复制代码
public ushort[] ReadWords(string startAddr, int count)
{
    string cmd = $"{STX}30{StationNumber}0400{startAddr}{count:X2}{ETX}";
    var response = SendCommand(cmd);
    
    ushort[] result = new ushort[count];
    for(int i=0; i<count; i++)
    {
        result[i] = (ushort)(response[5+2*i] * 256 + response[6+2*i]);
    }
    return result;
}
2. 自动重连机制
csharp 复制代码
public async Task AutoReconnectAsync()
{
    while (!_isDisposed)
    {
        if (!_serialPort.IsOpen)
        {
            try
            {
                _serialPort.Open();
                Debug.WriteLine("自动重连成功");
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"重连失败: {ex.Message}");
                await Task.Delay(5000);
            }
        }
        await Task.Delay(1000);
    }
}

参考代码 上位机与三菱plc的串口通讯 www.youwenfan.com/contentcsp/112223.html

七、推荐开发工具

工具名称 用途 下载地址
GX Works2 PLC程序编写与调试 三菱电机官网
HslCommunication 高级PLC通信库 GitHub
Serial Port Monitor 串口数据监控 NirSoft官网

八、项目结构建议

csharp 复制代码
PLC_Communication/
├── MelsecPLC/              # 核心通信类
│   ├── MelsecPLC.cs       # 主类
│   └── AddressConverter.cs # 地址转换工具
├── TestApp/               # 测试程序
│   ├── MainForm.cs        # 主界面
│   └── PLCConfigForm.cs   # 参数配置界面
└── Resources/             # 配置文件
    └── plc_params.json    # PLC参数存储
相关推荐
深邃-2 分钟前
【数据结构与算法】-二叉树(2):实现顺序结构二叉树(堆的实现),向上调整算法,向下调整算法,堆排序,TOP-K问题
数据结构·算法·二叉树·排序算法·堆排序··top-k
咸鱼2.03 分钟前
【java入门到放弃】Dubbo
java·开发语言·dubbo
We་ct3 小时前
LeetCode 5. 最长回文子串:DP + 中心扩展
前端·javascript·算法·leetcode·typescript
JAVA面经实录9176 小时前
Java企业级工程化·终极完整版背诵手册(无遗漏、全覆盖、面试+落地通用)
java·开发语言·面试
王老师青少年编程7 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【哈夫曼贪心】:合并果子
c++·算法·贪心·csp·信奥赛·哈夫曼贪心·合并果子
周杰伦fans7 小时前
AutoCAD .NET 二次开发:深入理解 EntityJig 的工作原理与正确实现
开发语言·.net
叼烟扛炮8 小时前
C++第二讲:类和对象(上)
数据结构·c++·算法·类和对象·struct·实例化
天疆说8 小时前
【哈密顿力学】深入解读航天器交会最优控制中的Hamilton函数
人工智能·算法·机器学习
wuweijianlove8 小时前
关于算法设计中的代价函数优化与约束求解的技术7
算法
leoufung9 小时前
LeetCode 149: Max Points on a Line - 解题思路详解
算法·leetcode·职场和发展