C#与三菱PLC串口通信源码实现(基于MC协议)

简介: 基于System.IO.Ports.SerialPort实现三菱FX系列PLC的串口通信,支持D寄存器、M位元件读写操作,包含指令帧构建、校验和计算、响应解析等关键模块。

一、核心代码框架

基于System.IO.Ports.SerialPort实现三菱FX系列PLC的串口通信,支持D寄存器、M位元件读写操作,包含指令帧构建、校验和计算、响应解析等关键模块。

using System;
using System.IO.Ports;
using System.Text;
using System.Threading;

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 = 9600)
    {
   
        _serialPort = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One);
        _serialPort.DataReceived += SerialPort_DataReceived;
    }

    // 打开串口
    public bool Connect()
    {
   
        try
        {
   
            if (!_serialPort.IsOpen)
            {
   
                _serialPort.Open();
                Thread.Sleep(100); // 等待初始化
                return true;
            }
            return false;
        }
        catch (Exception ex)
        {
   
            Log($"串口打开失败: {ex.Message}");
            return false;
        }
    }

    // 关闭串口
    public void Disconnect()
    {
   
        if (_serialPort.IsOpen) _serialPort.Close();
    }

    // 读取D寄存器(示例读取D0-D9)
    public ushort[] ReadDRegisters(string startAddr, int count)
    {
   
        byte[] cmd = BuildReadCommand('D', startAddr, count);
        byte[] response = SendCommand(cmd);
        return ParseResponse(response);
    }

    // 写入单个D寄存器
    public bool WriteDRegister(string address, ushort value)
    {
   
        byte[] cmd = BuildWriteCommand('D', address, new ushort[] {
    value });
        byte[] response = SendCommand(cmd);
        return CheckAck(response);
    }

    // 构建读取指令
    private byte[] BuildReadCommand(char device, string addr, int count)
    {
   
        string cmdStr = $"{ENQ}{0x30}{0x30}{device}{addr.PadLeft(4, '0')}{count:X2}03";
        byte[] data = Encoding.ASCII.GetBytes(cmdStr);
        byte checksum = CalculateChecksum(data);
        return data.Concat(new byte[] {
    checksum, ETX }).ToArray();
    }

    // 构建写入指令
    private byte[] BuildWriteCommand(char device, string addr, ushort[] values)
    {
   
        string valueHex = string.Join("", values.Select(v => v.ToString("X4")));
        string cmdStr = $"{ENQ}{0x30}{0x30}{device}{addr.PadLeft(4, '0')}{values.Length:X2}{valueHex}";
        byte[] data = Encoding.ASCII.GetBytes(cmdStr);
        byte checksum = CalculateChecksum(data);
        return data.Concat(new byte[] {
    checksum, ETX }).ToArray();
    }

    // 发送指令并接收响应
    private byte[] SendCommand(byte[] command)
    {
   
        try
        {
   
            _serialPort.DiscardInBuffer();
            _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;
        }
        catch (TimeoutException)
        {
   
            Log("通信超时");
            return null;
        }
    }

    // 校验和计算
    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;
    }

    // 日志记录
    private void Log(string message)
    {
   
        File.AppendAllText("plc_log.txt", $"{DateTime.Now}: {message}\r\n");
    }

    // 数据接收事件
    private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
   
        // 处理实时数据流(如心跳检测)
    }
}

// 使用示例
var plc = new MitsubishiPLC("COM1");
if (plc.Connect())
{
   
    ushort[] data = plc.ReadDRegisters("0000", 10);
    plc.WriteDRegister("0010", 1234);
}

二、关键功能解析

  1. 指令帧结构

    • 起始符0x02 (STX)

    • 站号:PLC地址(ASCII码,如0x30表示站号0)

    • 指令码0x30表示写操作,0x31表示读操作

    • 数据地址:4位ASCII码(如D0000表示D寄存器起始地址)

    • 数据长度:2位十六进制(如0A表示读取10个字)

    • 校验和:所有字节累加取补码

    • 结束符0x03 (ETX)

  2. 通信参数配置

    _serialPort.BaudRate = 9600;    // 波特率
    _serialPort.DataBits = 8;       // 数据位
    _serialPort.StopBits = StopBits.One; // 停止位
    _serialPort.Parity = Parity.None; // 校验位
    
  3. 异常处理策略

    • 超时重试:发送指令后等待100ms,未收到响应则重试3次

    • 校验失败:丢弃错误帧并记录日志

    • 连接状态检测:定期发送ENQ指令检测PLC在线状态


三、扩展功能实现

  1. 批量读写优化

    public ushort[] ReadDBlock(string startAddr, int count)
    {
         
        string cmd = $"{ENQ}00FF{startAddr.PadLeft(4, '0')}{count:X4}03";
        byte[] data = Encoding.ASCII.GetBytes(cmd);
        byte checksum = CalculateChecksum(data);
        _serialPort.Write(data.Concat(new byte[] {
          checksum, ETX }).ToArray());
    
        // 等待完整响应(最大3秒)
        byte[] response = ReadCompleteResponse();
        return ParseResponse(response);
    }
    
  2. M位元件操作

    public bool WriteM(string address, bool[] bits)
    {
         
        string hexStr = "";
        foreach (bool bit in bits)
        {
         
            hexStr += bit ? "1" : "0";
        }
        byte[] cmd = Encoding.ASCII.GetBytes($"{ENQ}00FFM{address.PadLeft(4, '0')}{hexStr.Length}00{hexStr}");
        byte checksum = CalculateChecksum(cmd);
        _serialPort.Write(cmd.Concat(new byte[] {
          checksum, ETX }).ToArray());
        return CheckAck(_serialPort.ReadExisting().ToByteArray());
    }
    

四、调试与优化建议

  1. 串口调试工具

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

       波特率: 9600 | 数据位:8 | 停止位:1 | 校验:
  2. 性能监控

    public class PerformanceMetrics
    {
         
        public long MessagesSent {
          get; set; }
        public long MessagesReceived {
          get; set; }
        public double CPUUsage {
          get; set; }
        public double MemoryUsage {
          get; set; }
    }
    
  3. 日志记录

    public static class Logger
    {
         
        public static void Log(string message)
        {
         
            File.AppendAllText("plc_log.txt", 
                $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}{Environment.NewLine}");
        }
    }
    

参考代码 C#与三菱PLC通讯源码 串口通讯 www.youwenfan.com/contentalh/57509.html

五、完整项目结构

PLC_Communication/
├── MitsubishiPLC/          # 核心通信类
│   ├── MitsubishiPLC.cs    # 主类
│   └── ProtocolParser.cs   # 报文解析工具
├── TestApp/                # 测试程序
│   ├── MainForm.cs         # 主界面
│   └── PLCConfigForm.cs    # 参数配置界面
└── Resources/              # 配置文件
    └── plc_params.json     # PLC参数存储

六、注意事项

  1. 通信参数一致性:确保PLC与上位机的波特率、数据位、停止位、校验位完全一致

  2. 数据缓冲区管理:批量读写时注意PLC的接收缓冲区大小限制(通常≤256字节)

  3. 实时性要求:关键控制指令建议采用中断接收方式

  4. 安全防护:工业环境中需增加光电隔离和防浪涌电路

相关文章
|
6天前
|
人工智能 API 调度
Hermes Agent 与 OpenClaw:本质区别与选型深度解析
Hermes Agent 与 OpenClaw 同为热门开源AI框架,但理念迥异:OpenClaw 是“配置驱动”的灵活工具箱,强调人工编排与多模型调度;Hermes Agent 则是“学习驱动”的长期搭档,具备自主反思、记忆沉淀与持续进化能力。选前者重掌控力,选后者重省心度与长期协同效率。(239字)
|
15天前
|
人工智能 安全 算法
多态钓鱼对抗与 AI 安全合规闭环构建研究
本文剖析AI驱动的多态钓鱼新威胁与企业面临的AI安全合规差距,提出“动态防御+合规内生”双轮驱动闭环体系,涵盖智能检测、合规校验、全链路审计与动态迭代,并提供可工程化落地的Python代码示例,助力企业兼顾攻防实效与监管就绪。(239字)
100 9
|
24天前
|
人工智能 安全 机器人
阿里云JVS Claw创意技能大赏启动:三分钟实现“养虾自由”,AI智能体由你定义!
阿里云推出JVS Claw——零代码AI智能体平台:https://t.aliyun.com/U/IJbaxg 三分钟手机“养虾”,24小时待命、越用越聪明。支持多端同步、云端沙箱安全隔离、自进化技能体系。现启动“创意技能大赏”征集活动,邀全民共建智能体生态!
269 15
|
24天前
|
人工智能 自然语言处理 安全
小白龙虾!阿里云JVS Claw下载即用,不用部署不用代码,创建Clawbot三步搞定
阿里云JVS Claw是基于OpenClaw打造的AI龙虾助手:https://t.aliyun.com/U/IJbaxg 支持手机/PC/网页三端互通,一键下载即用、免验证码。提供云端(CloudSpace)与本地双部署模式,内置Word/Excel等办公技能及上千种可扩展Skills,新手也能快速上手。
662 9
|
24天前
|
人工智能 小程序 API
只需3步:阿里云无影云电脑一键安装OpenClaw龙虾AI助手、百炼API Key,并在飞书、钉钉、QQ及微信部署教程
阿里云OpenClaw部署官方页面:https://t.aliyun.com/U/McEnoK 阿里云无影云电脑上线OpenClaw龙虾AI助手专属镜像,预装VS Code、钉钉、WPS等,支持飞书/钉钉/微信/QQ多端唤醒。3步完成部署:购买套餐→启动云电脑→配置API Key,即可运行能操作文件、联动工具的个人Agent。
768 4
|
8天前
|
人工智能 自然语言处理 安全
【含新版链接】小白实操指南 OpenClaw(小龙虾)Windows 一键部署
2026最新版OpenClaw(小龙虾)Windows一键部署教程:零代码、纯本地、10分钟养出你的AI数字员工!支持文件整理、浏览器自动化、微信办公等,全程可视化操作,小白友好,隐私安全有保障。(239字)
【含新版链接】小白实操指南 OpenClaw(小龙虾)Windows 一键部署
|
8天前
|
人工智能 IDE 编译器
Karpathy的LLM Wiki:一种将RAG从解释器模式升级为编译器模式的架构
Andrej Karpathy提出的LLM Wiki,摒弃传统RAG“每次查询重检索”的模式,转而让大模型将原始资料**编译为结构化、可链接、自更新的Markdown Wiki**,实现知识的持久沉淀与复利增长——Obsidian为IDE,LLM为程序员,Wiki即代码库。
369 7
Karpathy的LLM Wiki:一种将RAG从解释器模式升级为编译器模式的架构
|
10天前
|
存储 弹性计算 运维
阿里云服务器ECS全方位介绍:架构、性能、适用场景、收费标准与活动价格
阿里云服务器ECS是卓越、稳定可靠的IaaS服务,免去前期IT硬件采购准备,实现计算资源即开即用与弹性伸缩,满足多种业务需求。ECS支持主流处理器架构,提供上百种实例规格,满足不同用户需求。其优势包括多样化计算能力、便捷易用、成本优化、弹性灵活、稳定可靠及安全保障。无论是轻量应用服务器还是第九代高性能实例,ECS均展现核心价值。阿里云还提供多种优惠活动和计费方式,助力企业和开发者低成本上云。
阿里云服务器ECS全方位介绍:架构、性能、适用场景、收费标准与活动价格
|
8天前
|
安全 数据安全/隐私保护 iOS开发
Cisco Catalyst 9800 WLC IOS XE 26.1.1 GA - 思科 Catalyst 9800 系列无线控制器 IOS XE 系统软件
Cisco Catalyst 9800 WLC IOS XE 26.1.1 GA - 思科 Catalyst 9800 系列无线控制器 IOS XE 系统软件
86 3
Cisco Catalyst 9800 WLC IOS XE 26.1.1 GA - 思科 Catalyst 9800 系列无线控制器 IOS XE 系统软件
|
13天前
|
域名解析 搜索推荐 网络协议
一级域名与二级域名的区别 功能及优缺点全解析
本文全面解析一级域名与二级域名的区别,详细介绍二者在所有权、管理方式、品牌价值、SEO权重等方面的差异,分析各自功能及优缺点,并给出实用的域名规划建议,同时提供专业的二级域名租用与管理解决方案,助力个人与企业合理选择域名。
一级域名与二级域名的区别 功能及优缺点全解析