C# 基于 HSL 与基恩士 PLC 通信

一、前置知识:基恩士 PLC 通信协议核心

基恩士(KEYENCE)PLC(如 KV 系列)主流通信方式为以太网通信,核心协议基于:

1. 协议基础

基恩士以太网通信默认采用 MC 协议(三菱兼容协议)基恩士专属以太网协议,其中:

  • MC 协议 :是工业常用的以太网协议,支持 TCP/IP 连接,端口默认5006(部分型号为8080);
  • 数据格式:采用二进制 / ASCII 码格式,支持位(X/Y/M)、字(D/W)、双字(DD)等寄存器读写;
  • 通信规则
  • 寄存器地址格式:基恩士 PLC 寄存器有固定命名规则,例如:
    • X:输入位(如 X0、X100);
    • Y:输出位(如 Y0、Y200);
    • M:内部辅助继电器(如 M100、M2000);
    • D:数据寄存器(如 D0、D1000,16 位);
    • DD:双字数据寄存器(如 DD0,32 位,由 D0+D1 组成);
    • W:链接寄存器(如 W0);
    • 地址映射:HSL 框架已封装地址转换,无需手动拼接协议帧,但需了解地址格式对应关系。

2. 通信前提

  • 基恩士 PLC 已配置以太网参数:IP 地址、子网掩码、网关(需与电脑同网段);
  • PLC 侧已启用以太网通信功能(无密码限制或已配置允许访问);
  • 关闭电脑防火墙(或放行 PLC 通信端口)。

二、HSL 框架介绍

HSL(HslCommunication)是国产工业通信框架,支持主流 PLC / 仪表 / 变频器通信,对基恩士 PLC 的 MC 协议做了高度封装,无需手动解析协议帧,只需调用 API 即可完成通信。

1. HSL 安装(.NET Framework 4.8)

通过 NuGet 安装(控制台 / Visual Studio):

  • Visual Studio:右键项目 → 管理 NuGet 程序包 → 搜索HslCommunication → 安装(选择适配.NET Framework 4.8 的版本,建议 5.0+);

  • 控制台安装:

    cs 复制代码
    Install-Package HslCommunication -Version 5.4.0

    三、完整控制台案例(.NET Framework 4.8)

1. 案例功能

  • 连接 / 断开基恩士 PLC;

  • 读写位寄存器(M/X/Y);

  • 读写字寄存器(D);

  • 读写双字寄存器(DD);

  • 异常处理(连接失败、读写超时、地址错误等)。

    cs 复制代码
    using HslCommunication;
    using HslCommunication.Profinet.Keyence;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace C_与基恩士通信MC协议
    {
        internal class Program
        { // 全局PLC通信对象(基恩士MC协议)
            private static KeyenceMcNet keyencePlc;
            static void Main(string[] args)
            {
                Console.WriteLine("===== 基恩士PLC通信案例(HSL框架)=====");
                // 1. 初始化PLC通信对象
                InitPlcConnection();
                try
                {
                    // 2. 连接PLC
                    OperateResult connectResult = keyencePlc.ConnectServer();
                    if (connectResult.IsSuccess)
                    {
                        Console.WriteLine($"[{DateTime.Now}] PLC连接成功!");
    
                        // 3. 读写位寄存器(M100)
                        ReadWriteBitRegister();
    
                        // 4. 读写字寄存器(D100)
                        ReadWriteWordRegister();
    
                        // 5. 读写双字寄存器(DD100)
                        ReadWriteDoubleWordRegister();
                    }
                    else
                    {
                        Console.WriteLine($"[{DateTime.Now}] PLC连接失败!原因:{connectResult.Message}");
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"[{DateTime.Now}] 通信异常:{ex.Message}");
                }
                finally
                {
                    // 6. 断开连接
                    if (keyencePlc != null )
                    {
                        keyencePlc.ConnectClose();
                        Console.WriteLine($"[{DateTime.Now}] PLC连接已断开");
                    }
                }
    
                Console.WriteLine("按任意键退出...");
                Console.ReadKey();
            }
    
    
            /// <summary>
            /// 初始化PLC连接参数
            /// </summary>
            private static void InitPlcConnection()
            {
                // 核心参数:PLC IP地址 + MC协议端口(基恩士默认5006,部分型号8080)
                string plcIp = "172.0.0.1"; // 替换为你的PLC IP
                int plcPort = 8501; // 基恩士MC协议默认端口
    
                // 创建基恩士MC协议通信对象
                keyencePlc = new KeyenceMcNet(plcIp, plcPort);
                // 可选配置:超时时间(默认2000ms,建议根据网络调整)
                keyencePlc.ReceiveTimeOut = 3000;   
    
            }
            /// <summary>
            /// 读写位寄存器(M/X/Y,以M100为例)
            /// </summary>
            private static void ReadWriteBitRegister()
            {
                Console.WriteLine("\n----- 读写位寄存器(M100)-----");
                // 读位寄存器:M100
                OperateResult<bool> readBitResult = keyencePlc.ReadBool("M100");
                if (readBitResult.IsSuccess)
                {
                    Console.WriteLine($"[{DateTime.Now}] 读取M100值:{readBitResult.Content}");
                    // 写位寄存器:将M100置反
                    bool writeValue = !readBitResult.Content;
                    OperateResult writeBitResult = keyencePlc.Write("M100", writeValue);
                    if (writeBitResult.IsSuccess)
                    {
                        Console.WriteLine($"[{DateTime.Now}] 写入M100值:{writeValue} → 成功");
    
                        // 验证写入结果
                        OperateResult<bool> verifyResult = keyencePlc.ReadBool("M100");
                        if (verifyResult.IsSuccess)
                        {
                            Console.WriteLine($"[{DateTime.Now}] 验证M100值:{verifyResult.Content}");
                        }
                    }
                    else
                    {
                        Console.WriteLine($"[{DateTime.Now}] 写入M100失败!原因:{writeBitResult.Message}");
                    }
                }
                else
                {
                    Console.WriteLine($"[{DateTime.Now}] 读取M100失败!原因:{readBitResult.Message}");
                }
    
            }
    
            /// <summary>
            /// 读写字寄存器(D/W,以D100为例,16位)
            /// </summary>
            private static void ReadWriteWordRegister()
            {
                Console.WriteLine("\n----- 读写字寄存器(D100)-----");
    
                // 读字寄存器:D100(16位整数)
                OperateResult<short> readWordResult = keyencePlc.ReadInt16("D100");
                if (readWordResult.IsSuccess)
                {
                    Console.WriteLine($"[{DateTime.Now}] 读取D100值:{readWordResult.Content}");
    
                    // 写字寄存器:D100 = 1234
                    short writeValue = 1234;
                    OperateResult writeWordResult = keyencePlc.Write("D100", writeValue);
                    if (writeWordResult.IsSuccess)
                    {
                        Console.WriteLine($"[{DateTime.Now}] 写入D100值:{writeValue} → 成功");
    
                        // 验证写入结果
                        OperateResult<short> verifyResult = keyencePlc.ReadInt16("D100");
                        if (verifyResult.IsSuccess)
                        {
                            Console.WriteLine($"[{DateTime.Now}] 验证D100值:{verifyResult.Content}");
                        }
                    }
                    else
                    {
                        Console.WriteLine($"[{DateTime.Now}] 写入D100失败!原因:{writeWordResult.Message}");
                    }
                }
                else
                {
                    Console.WriteLine($"[{DateTime.Now}] 读取D100失败!原因:{readWordResult.Message}");
                }
            }
    
            /// <summary>
            /// 读写双字寄存器(DD,以DD100为例,32位)
            /// </summary>
            private static void ReadWriteDoubleWordRegister()
            {
                Console.WriteLine("\n----- 读写双字寄存器(DD100)-----");
    
                // 读双字寄存器:DD100(32位整数,等价于D100+D101)
                OperateResult<int> readDWordResult = keyencePlc.ReadInt32("DD100");
                if (readDWordResult.IsSuccess)
                {
                    Console.WriteLine($"[{DateTime.Now}] 读取DD100值:{readDWordResult.Content}");
    
                    // 写双字寄存器:DD100 = 987654
                    int writeValue = 987654;
                    OperateResult writeDWordResult = keyencePlc.Write("DD100", writeValue);
                    if (writeDWordResult.IsSuccess)
                    {
                        Console.WriteLine($"[{DateTime.Now}] 写入DD100值:{writeValue} → 成功");
    
                        // 验证写入结果
                        OperateResult<int> verifyResult = keyencePlc.ReadInt32("DD100");
                        if (verifyResult.IsSuccess)
                        {
                            Console.WriteLine($"[{DateTime.Now}] 验证DD100值:{verifyResult.Content}");
                        }
                    }
                    else
                    {
                        Console.WriteLine($"[{DateTime.Now}] 写入DD100失败!原因:{writeDWordResult.Message}");
                    }
                }
                else
                {
                    Console.WriteLine($"[{DateTime.Now}] 读取DD100失败!原因:{readDWordResult.Message}");
                }
            }
    
        }
    }

    四、核心知识点详解

1. KeyenceMcNet 类核心参数

|------------------------|-----------------------------------------------------|
| 参数 / 方法 | | 说明 | |----| |
| KeyenceMcNet(ip, port) | 构造函数:ip=PLC 以太网 IP,port=MC 协议端口(基恩士默认 5006) |
| Timeout | 通信超时时间(毫秒),默认 2000ms,网络差时建议设为 3000-5000ms |
| AsciiFormat | 是否启用 ASCII 格式(默认 false = 二进制),老款基恩士 PLC 可能需要设为 true |
| ConnectServer() | 连接 PLC,返回OperateResult(IsSuccess 判断是否成功) |
| ConnectClose() | 断开 PLC 连接 |
| IsConnected | 检查当前是否处于连接状态 |
| LogNet | 日志输出(调试用,可输出到控制台 / 文件) |

2. 寄存器地址格式(HSL 兼容)

  • HSL 对基恩士寄存器地址做了兼容,无需手动转换协议地址,直接用 PLC 原生地址:

    |-------|-----------------------|---------------------------------------|-----------------|
    | 寄存器类型 | HSL 地址格式 | | 示例 | |----| | | 说明 | |----| |
    | 输入位 | X + 数字 | |---------| | X0、X100 | | 只读 |
    | 输出位 | |--------| | Y + 数字 | | |---------| | Y0、Y200 | | 可读可写 |
    | 内部继电器 | M + 数字 | |------------|---| | M100、M2000 | | | 可读可写 |
    | 数据寄存器 | |--------| | D + 数字 | | D100、D1000 | 16 位,可读可写 |
    | 双字寄存器 | DD + 数字 | DD100 | 32 位(D100+D101) |
    | 链接寄存器 | W + 数字 | W0、W100 | 16 位,可读可写 |

    3. 核心读写方法

    |---------------------------|----------------------|---------------------------|----------------------------|
    | 数据类型 | 读方法 | | 写方法 | |-----| | 示例 |
    | 位(bool) | ReadBool("M100") | Write("M100", true) | 读写 M100 位 |
    | 16 位整数 | ReadInt16("D100") | Write("D100", (short)123) | 读写 D100(short 类型) |
    | 32 位整数 | ReadInt32("DD100") | Write("DD100", 987654) | 读写 DD100(int 类型) |
    | 浮点数 | ReadFloat("DD100") | Write("DD100", 3.14f) | 读写 DD100 存储的浮点数 |
    | |------|---| | 批量读位 | | | ReadBool("M100", 10) | | 读取 M100-M109 共 10 个位 |
    | 批量读字 | ReadInt16("D100", 5) | | 读取 D100-D104 共 5 个 16 位寄存器 |

    4. 异常处理核心

  • HSL 所有通信方法均返回OperateResult(或泛型OperateResult<T>),严禁直接忽略返回结果 ,必须通过IsSuccess判断是否成功:

  • IsSuccess=true:操作成功,Content(泛型)获取返回值;

  • IsSuccess=false:操作失败,Message获取失败原因(如 "连接超时""地址不存在")。

五、常见问题与解决方案

1. 连接失败(ConnectServer 返回失败)

  • 原因 1:PLC IP 错误 / 电脑与 PLC 不在同网段 → 检查 IP 配置,ping PLC IP 是否通;
  • 原因 2:端口错误 → 基恩士 MC 协议默认 5006,部分型号用 8080,可咨询基恩士技术手册;
  • 原因 3:PLC 未启用以太网通信 → 进入 PLC 编程软件(KV STUDIO)启用以太网功能;
  • 原因 4:防火墙拦截 → 关闭电脑防火墙,或放行 PLC 端口(5006/8080);
  • 原因 5:PLC 密码限制 → 检查 PLC 是否设置了通信密码,需在 HSL 中配置(keyencePlc.Password = "123456")。

2. 读写失败(Read/Write 返回失败)

  • 原因 1:地址格式错误 → 确认寄存器地址是否存在(如 M9999 超出 PLC 范围);
  • 原因 2:数据类型不匹配 → 写 D 寄存器时需用 short 类型,写 DD 寄存器用 int 类型;
  • 原因 3:ASCII / 二进制格式不匹配 → 尝试设置keyencePlc.AsciiFormat = true
  • 原因 4:PLC 处于停机状态 → 确保 PLC 处于 RUN 模式;
  • 原因 5:通信超时 → 增大Timeout值(如 5000ms)。

3. 数据读取错误(值不对)

  • 原因 1:字节序问题 → 基恩士默认大端序,HSL 已适配,无需手动转换;
  • 原因 2:双字寄存器地址错误 → DD100 对应 D100+D101,而非 D100 单独;
  • 原因 3:浮点数格式错误 → 确认 PLC 中浮点数存储格式(IEEE 754),HSL 默认兼容。

六、调试技巧

  1. 启用日志 :通过keyencePlc.LogNet = new LogNetConsole()输出通信日志,查看协议帧交互过程;
  2. ping 测试:先 ping PLC IP,确保网络连通;
  3. 端口测试 :用 Telnet 测试 PLC 端口是否开放(telnet 192.168.1.100 5006);
  4. PLC 侧监控:用基恩士 KV STUDIO 的 "在线监控" 功能,验证寄存器值是否正确;
  5. 简化测试:先测试读 M100/D100 等简单地址,排除复杂地址问题。
  6. 批量操作:频繁读写时优先用批量读写方法(如ReadInt16("D100", 10)),减少通信次数;
  7. 资源释放:通信完成后必须调用ConnectClose()释放连接,避免端口占用。
相关推荐
张人玉5 小时前
c# DataSet 类
数据库·c#·dataset
秦苒&5 小时前
【C语言】详解数据类型和变量(一):数据类型介绍、 signed和unsigned、数据类型的取值范围、变量、强制类型转换
c语言·开发语言·c++·c#
c#上位机6 小时前
C#异步编程之async、await
开发语言·c#
郑州光合科技余经理6 小时前
实战分享:如何构建东南亚高并发跑腿配送系统
java·开发语言·javascript·spring cloud·uni-app·c#·php
用户298698530147 小时前
如何在 C# .NET 中将 Markdown 转换为 PDF 和 Excel:完整指南
后端·c#·markdown
天天进步20157 小时前
工厂模式的应用:数据读取与算法创建的解耦—— QuantConnect/Lean 源码分析系列二
c#
xiaowu0807 小时前
C# GetType的常规用法汇总
开发语言·c#
老朱佩琪!7 小时前
Unity桥接模式
unity·设计模式·c#·桥接模式
我是小狼君7 小时前
【Unity/C# 基础算法】从入门到进阶:线性、插值与斐波那契查找深度解析
c#