基于C#的上位机实例,用于与台达PLC进行Modbus通信源码

付费 ¥8.99付费 ¥8.99

该内容已接入现有会员/付费系统,解锁后可查看完整资料或下载附件。

查看解锁方式
基于C#的上位机实例,用于与台达PLC进行Modbus通信源码缩略图
本文目录
  1. 1. 一、台达PLC Modbus通讯概述
  2. 2. 二、C# Modbus通讯类实现
  3. 3. 三、完整使用示例
  4. 4. 四、Windows Forms上位机界面示例
  5. 5. 五、常见问题与排查
  6. 6. 六、最佳实践
  7. 7. 基于C#的上位机实例,用于与台达PLC进行Modbus通信源码¥8.99

台达(Delta)PLC是国内工业自动化领域应用最广泛的PLC品牌之一,支持Modbus RTU和Modbus TCP通讯协议。C#作为Windows平台上位机开发的主流语言,与台达PLC的Modbus通讯是工业自动化项目中的常见需求。本文提供完整的C#上位机与台达PLC Modbus通信实例,包括连接管理、寄存器读写、数据解析、断线重连、多设备管理以及Windows Forms界面示例。

一、台达PLC Modbus通讯概述

1.1 台达PLC支持的Modbus功能

功能码功能台达PLC对应区域
0x01读线圈M(辅助继电器)、Y(输出继电器)
0x02读离散输入X(输入继电器)
0x03读保持寄存器D(数据寄存器)
0x05写单个线圈M、Y
0x06写单个寄存器D
0x0F写多个线圈M、Y
0x10写多个寄存器D

1.2 台达PLC地址映射

台达PLC的Modbus地址与内部软元件的对应关系(以DVP系列为例):

软元件Modbus地址(十进制)说明
D0-D99990-9999数据寄存器,保持寄存器
M0-M99990-9999辅助继电器,线圈
Y0-Y2550-255(偏移2048)输出继电器,线圈
X0-X2550-255(偏移2048)输入继电器,离散输入
S0-S9990-999(偏移4096)状态继电器,线圈
T0-T255触点1024-1279定时器触点状态,线圈(功能码01/05)
C0-C255触点1536-1791计数器触点状态,线圈(功能码01/05)
T/C当前值视型号而定部分型号支持通过D寄存器映射读取,需查阅对应手册

1.3 通讯参数设置

  • Modbus TCP:PLC IP地址 + 端口502,从站地址默认1
  • Modbus RTU(COM1):9600bps,8数据位,无校验,1停止位(默认)
  • Modbus RTU(COM2):9600bps,8数据位,无校验,1停止位(默认)
  • 从站地址:可在PLC中设置,默认1
  • 通讯协议:需在PLC编程软件中设置为Modbus RTU Slave或Modbus TCP

二、C# Modbus通讯类实现

2.1 Modbus TCP客户端类

using System;
using System.Net.Sockets;
using System.Threading;

namespace DeltaPlcModbus
{
    public class ModbusTcpClient : IDisposable
    {
        private TcpClient _client;
        private NetworkStream _stream;
        private ushort _transactionId = 0;
        private readonly object _lock = new object();

        public string IpAddress { get; }
        public int Port { get; }
        public byte SlaveId { get; set; } = 1;
        public int Timeout { get; set; } = 3000;
        public bool IsConnected => _client?.Connected ?? false;

        public ModbusTcpClient(string ip, int port = 502)
        {
            IpAddress = ip;
            Port = port;
        }

        /// <summary>
        /// 连接PLC(带重试)
        /// </summary>
        public bool Connect(int retryCount = 3)
        {
            for (int i = 0; i < retryCount; i++)
            {
                try
                {
                    _client = new TcpClient();
                    _client.ReceiveTimeout = Timeout;
                    _client.SendTimeout = Timeout;
                    _client.Connect(IpAddress, Port);
                    _stream = _client.GetStream();
                    Console.WriteLine($"已连接到PLC:{IpAddress}:{Port}");
                    return true;
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"连接失败(第{i + 1}次):{ex.Message}");
                    Thread.Sleep(1000);
                }
            }
            return false;
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            _stream?.Close();
            _client?.Close();
            _client = null;
            _stream = null;
        }

        /// <summary>
        /// 读保持寄存器(功能码03)- 读取D寄存器
        /// </summary>
        public ushort[] ReadHoldingRegisters(ushort startAddr, ushort count)
        {
            lock (_lock)
            {
                byte[] request = BuildReadRequest(0x03, startAddr, count);
                byte[] response = SendAndReceive(request);

                if (response[7] != 0x03)
                    throw new ModbusException(response[7], response[8]);

                byte byteCount = response[8];
                ushort[] result = new ushort[byteCount / 2];
                for (int i = 0; i < result.Length; i++)
                {
                    result[i] = (ushort)((response[9 + i * 2] << 8) | response[9 + i * 2 + 1]);
                }
                return result;
            }
        }

        /// <summary>
        /// 读线圈(功能码01)- 读取M/Y继电器
        /// </summary>
        public bool[] ReadCoils(ushort startAddr, ushort count)
        {
            lock (_lock)
            {
                byte[] request = BuildReadRequest(0x01, startAddr, count);
                byte[] response = SendAndReceive(request);

                if (response[7] != 0x01)
                    throw new ModbusException(response[7], response[8]);

                byte byteCount = response[8];
                bool[] result = new bool[count];
                for (int i = 0; i < count; i++)
                {
                    result[i] = (response[9 + i / 8] & (1 << (i % 8))) != 0;
                }
                return result;
            }
        }

        /// <summary>
        /// 读离散输入(功能码02)- 读取X输入
        /// </summary>
        public bool[] ReadDiscreteInputs(ushort startAddr, ushort count)
        {
            lock (_lock)
            {
                byte[] request = BuildReadRequest(0x02, startAddr, count);
                byte[] response = SendAndReceive(request);

                if (response[7] != 0x02)
                    throw new ModbusException(response[7], response[8]);

                byte byteCount = response[8];
                bool[] result = new bool[count];
                for (int i = 0; i < count; i++)
                {
                    result[i] = (response[9 + i / 8] & (1 << (i % 8))) != 0;
                }
                return result;
            }
        }

        /// <summary>
        /// 写单个寄存器(功能码06)- 写D寄存器
        /// </summary>
        public void WriteSingleRegister(ushort addr, ushort value)
        {
            lock (_lock)
            {
                byte[] request = new byte[12];
                BuildMBAPHeader(request, 6);
                request[7] = 0x06;
                request[8] = (byte)(addr >> 8);
                request[9] = (byte)(addr & 0xFF);
                request[10] = (byte)(value >> 8);
                request[11] = (byte)(value & 0xFF);

                byte[] response = SendAndReceive(request);
                if (response[7] != 0x06)
                    throw new ModbusException(response[7], response[8]);
            }
        }

        /// <summary>
        /// 写单个线圈(功能码05)- 写M/Y继电器
        /// </summary>
        public void WriteSingleCoil(ushort addr, bool value)
        {
            lock (_lock)
            {
                byte[] request = new byte[12];
                BuildMBAPHeader(request, 6);
                request[7] = 0x05;
                request[8] = (byte)(addr >> 8);
                request[9] = (byte)(addr & 0xFF);
                request[10] = (byte)(value ? 0xFF : 0x00);
                request[11] = 0x00;

                byte[] response = SendAndReceive(request);
                if (response[7] != 0x05)
                    throw new ModbusException(response[7], response[8]);
            }
        }

        /// <summary>
        /// 写多个寄存器(功能码16)- 批量写D寄存器
        /// </summary>
        public void WriteMultipleRegisters(ushort startAddr, ushort[] values)
        {
            lock (_lock)
            {
                byte byteCount = (byte)(values.Length * 2);
                byte[] request = new byte[13 + byteCount];
                BuildMBAPHeader(request, (ushort)(7 + byteCount));
                request[7] = 0x10;
                request[8] = (byte)(startAddr >> 8);
                request[9] = (byte)(startAddr & 0xFF);
                request[10] = (byte)(values.Length >> 8);
                request[11] = (byte)(values.Length & 0xFF);
                request[12] = byteCount;

                for (int i = 0; i < values.Length; i++)
                {
                    request[13 + i * 2] = (byte)(values[i] >> 8);
                    request[13 + i * 2 + 1] = (byte)(values[i] & 0xFF);
                }

                byte[] response = SendAndReceive(request);
                if (response[7] != 0x10)
                    throw new ModbusException(response[7], response[8]);
            }
        }

        /// <summary>
        /// 写多个线圈(功能码15)- 批量写M/Y继电器
        /// </summary>
        public void WriteMultipleCoils(ushort startAddr, bool[] values)
        {
            lock (_lock)
            {
                byte byteCount = (byte)((values.Length + 7) / 8);
                byte[] request = new byte[13 + byteCount];
                BuildMBAPHeader(request, (ushort)(7 + byteCount));
                request[7] = 0x0F;
                request[8] = (byte)(startAddr >> 8);
                request[9] = (byte)(startAddr & 0xFF);
                request[10] = (byte)(values.Length >> 8);
                request[11] = (byte)(values.Length & 0xFF);
                request[12] = byteCount;
                
                for (int i = 0; i < values.Length; i++)
                {
                    if (values[i])
                        request[13 + i / 8] |= (byte)(1 << (i % 8));
                }
                
                byte[] response = SendAndReceive(request);
                if (response[7] != 0x0F)
                    throw new ModbusException(response[7], response[8]);
            }
        }
        
        // 构建读请求
        private byte[] BuildReadRequest(byte funcCode, ushort startAddr, ushort count)
        {
            byte[] request = new byte[12];
            BuildMBAPHeader(request, 6);
            request[7] = funcCode;
            request[8] = (byte)(startAddr >> 8);
            request[9] = (byte)(startAddr & 0xFF);
            request[10] = (byte)(count >> 8);
            request[11] = (byte)(count & 0xFF);
            return request;
        }

        // 构建MBAP头
        private void BuildMBAPHeader(byte[] buffer, ushort length)
        {
            _transactionId++;
            buffer[0] = (byte)(_transactionId >> 8);
            buffer[1] = (byte)(_transactionId & 0xFF);
            buffer[2] = 0x00;
            buffer[3] = 0x00;
            buffer[4] = (byte)(length >> 8);
            buffer[5] = (byte)(length & 0xFF);
            buffer[6] = SlaveId;
        }

        // 发送并接收
        private byte[] SendAndReceive(byte[] request)
        {
            if (_stream == null || !_client.Connected)
                throw new InvalidOperationException("未连接到PLC");

            _stream.Write(request, 0, request.Length);
            _stream.Flush();

            byte[] buffer = new byte[260];
            int bytesRead = _stream.Read(buffer, 0, buffer.Length);

            byte[] response = new byte[bytesRead];
            Array.Copy(buffer, response, bytesRead);
            return response;
        }

        public void Dispose()
        {
            Disconnect();
        }
    }

    public class ModbusException : Exception
    {
        public byte FunctionCode { get; }
        public byte ExceptionCode { get; }

        public ModbusException(byte funcCode, byte exceptionCode)
            : base($"Modbus异常:功能码=0x{funcCode:X2},异常码=0x{exceptionCode:X2}")
        {
            FunctionCode = funcCode;
            ExceptionCode = exceptionCode;
        }
    }
}

2.2 台达PLC专用操作类

using System;

namespace DeltaPlcModbus
{
    /// <summary>
    /// 台达PLC专用操作类,封装D/M/X/Y软元件的读写
    /// </summary>
    public class DeltaPlcClient
    {
        private readonly ModbusTcpClient _modbus;

        public DeltaPlcClient(string ip, int port = 502, byte slaveId = 1)
        {
            _modbus = new ModbusTcpClient(ip, port);
            _modbus.SlaveId = slaveId;
        }

        public bool Connect() => _modbus.Connect();
        public void Disconnect() => _modbus.Disconnect();
        public bool IsConnected => _modbus.IsConnected;

        /// <summary>
        /// 读取D寄存器值
        /// </summary>
        public ushort ReadD(int address)
        {
            return _modbus.ReadHoldingRegisters((ushort)address, 1)[0];
        }

        /// <summary>
        /// 批量读取D寄存器
        /// </summary>
        public ushort[] ReadD(int startAddress, int count)
        {
            return _modbus.ReadHoldingRegisters((ushort)startAddress, (ushort)count);
        }

        /// <summary>
        /// 写入D寄存器值
        /// </summary>
        public void WriteD(int address, ushort value)
        {
            _modbus.WriteSingleRegister((ushort)address, value);
        }

        /// <summary>
        /// 批量写入D寄存器
        /// </summary>
        public void WriteD(int startAddress, ushort[] values)
        {
            _modbus.WriteMultipleRegisters((ushort)startAddress, values);
        }

        /// <summary>
        /// 读取32位整数(占用2个D寄存器)
        /// </summary>
        public int ReadD32(int startAddress)
        {
            ushort[] regs = _modbus.ReadHoldingRegisters((ushort)startAddress, 2);
            return (regs[0] << 16) | regs[1];
        }

        /// <summary>
        /// 读取32位浮点数(占用2个D寄存器,ABCD大端序)
        /// </summary>
        public float ReadDFloat(int startAddress)
        {
            ushort[] regs = _modbus.ReadHoldingRegisters((ushort)startAddress, 2);
            // ABCD大端序:高字在前,高字节在前
            uint bits = ((uint)regs[0] << 16) | regs[1];
            byte[] bytes = BitConverter.GetBytes(bits);
            // BitConverter.GetBytes返回的是小端序数组,需要反转
            if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
            return BitConverter.ToSingle(bytes, 0);
        }
        
        /// <summary>
        /// 读取32位浮点数(CDAB字交换序)
        /// </summary>
        public float ReadDFloatCDAB(int startAddress)
        {
            ushort[] regs = _modbus.ReadHoldingRegisters((ushort)startAddress, 2);
            // CDAB:低字在前,高字节在前
            uint bits = ((uint)regs[1] << 16) | regs[0];
            byte[] bytes = BitConverter.GetBytes(bits);
            if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
            return BitConverter.ToSingle(bytes, 0);
        }

        /// <summary>
        /// 读取M辅助继电器状态
        /// </summary>
        public bool ReadM(int address)
        {
            return _modbus.ReadCoils((ushort)address, 1)[0];
        }

        /// <summary>
        /// 批量读取M辅助继电器
        /// </summary>
        public bool[] ReadM(int startAddress, int count)
        {
            return _modbus.ReadCoils((ushort)startAddress, (ushort)count);
        }

        /// <summary>
        /// 写入M辅助继电器
        /// </summary>
        public void WriteM(int address, bool value)
        {
            _modbus.WriteSingleCoil((ushort)address, value);
        }

        /// <summary>
        /// 读取X输入继电器状态
        /// 注意:X输入在台达PLC中Modbus地址偏移为2048
        /// </summary>
        public bool ReadX(int address)
        {
            return _modbus.ReadDiscreteInputs((ushort)(address + 2048), 1)[0];
        }

        /// <summary>
        /// 读取Y输出继电器状态
        /// 注意:Y输出在台达PLC中Modbus地址偏移为2048
        /// </summary>
        public bool ReadY(int address)
        {
            return _modbus.ReadCoils((ushort)(address + 2048), 1)[0];
        }

        /// <summary>
        /// 写入Y输出继电器
        /// </summary>
        public void WriteY(int address, bool value)
        {
            _modbus.WriteSingleCoil((ushort)(address + 2048), value);
        }

        /// <summary>
        /// 读取定时器当前值(T寄存器,偏移1024)
        /// </summary>
        public ushort ReadT(int address)
        {
            return _modbus.ReadHoldingRegisters((ushort)(address + 1024), 1)[0];
        }

        /// <summary>
        /// 读取计数器当前值(C寄存器,偏移1536)
        /// </summary>
        public ushort ReadC(int address)
        {
            return _modbus.ReadHoldingRegisters((ushort)(address + 1536), 1)[0];
        }
    }
}

三、完整使用示例

using System;
using System.Threading;

namespace DeltaPlcModbus
{
    class Program
    {
        static void Main(string[] args)
        {
            // 创建台达PLC客户端
            var plc = new DeltaPlcClient("192.168.1.10", 502, slaveId: 1);

            try
            {
                // 连接PLC
                if (!plc.Connect())
                {
                    Console.WriteLine("无法连接到PLC");
                    return;
                }
                Console.WriteLine("PLC连接成功!\n");

                // 1. 读取D寄存器
                Console.WriteLine("=== 读取D寄存器 ===");
                ushort d0 = plc.ReadD(0);
                ushort d1 = plc.ReadD(1);
                ushort d2 = plc.ReadD(2);
                Console.WriteLine($"D0 = {d0}");
                Console.WriteLine($"D1 = {d1}");
                Console.WriteLine($"D2 = {d2}");

                // 批量读取
                ushort[] dValues = plc.ReadD(0, 10);
                Console.WriteLine("\nD0-D9:");
                for (int i = 0; i < dValues.Length; i++)
                {
                    Console.WriteLine($"  D{i} = {dValues[i]}");
                }

                // 2. 写入D寄存器
                Console.WriteLine("\n=== 写入D寄存器 ===");
                plc.WriteD(10, 1234);
                Console.WriteLine("D10 写入 1234");
                ushort check = plc.ReadD(10);
                Console.WriteLine($"D10 回读 = {check}");

                // 批量写入
                plc.WriteD(20, new ushort[] { 100, 200, 300, 400, 500 });
                Console.WriteLine("D20-D24 批量写入完成");

                // 3. 读取32位数据
                Console.WriteLine("\n=== 读取32位数据 ===");
                int d32 = plc.ReadD32(0);
                Console.WriteLine($"D0-D1(32位整数)= {d32}");

                // 4. 读取M继电器
                Console.WriteLine("\n=== 读取M继电器 ===");
                bool m0 = plc.ReadM(0);
                bool m1 = plc.ReadM(1);
                Console.WriteLine($"M0 = {(m0 ? "ON" : "OFF")}");
                Console.WriteLine($"M1 = {(m1 ? "ON" : "OFF")}");

                // 批量读取
                bool[] mValues = plc.ReadM(0, 8);
                Console.WriteLine("\nM0-M7:");
                for (int i = 0; i < mValues.Length; i++)
                {
                    Console.WriteLine($"  M{i} = {(mValues[i] ? "ON" : "OFF")}");
                }

                // 5. 写入M继电器
                Console.WriteLine("\n=== 写入M继电器 ===");
                plc.WriteM(0, true);
                Console.WriteLine("M0 置为 ON");
                Thread.Sleep(500);
                plc.WriteM(0, false);
                Console.WriteLine("M0 置为 OFF");

                // 6. 读取X输入
                Console.WriteLine("\n=== 读取X输入 ===");
                bool x0 = plc.ReadX(0);
                bool x1 = plc.ReadX(1);
                Console.WriteLine($"X0 = {(x0 ? "ON" : "OFF")}");
                Console.WriteLine($"X1 = {(x1 ? "ON" : "OFF")}");

                // 7. 读取Y输出
                Console.WriteLine("\n=== 读取Y输出 ===");
                bool y0 = plc.ReadY(0);
                Console.WriteLine($"Y0 = {(y0 ? "ON" : "OFF")}");

                // 8. 控制Y输出
                Console.WriteLine("\n=== 控制Y输出 ===");
                plc.WriteY(0, true);
                Console.WriteLine("Y0 置为 ON");
                Thread.Sleep(1000);
                plc.WriteY(0, false);
                Console.WriteLine("Y0 置为 OFF");

                // 9. 读取定时器/计数器触点状态
                Console.WriteLine("\n=== 读取定时器/计数器触点 ===");
                bool t0 = plc.ReadTContact(0);
                bool c0 = plc.ReadCContact(0);
                Console.WriteLine($"T0 触点 = {(t0 ? "ON" : "OFF")}");
                Console.WriteLine($"C0 触点 = {(c0 ? "ON" : "OFF")}");
                Console.WriteLine("注:定时器/计数器当前值请查阅对应型号手册的Modbus映射");

                // 10. 持续监控(10秒)
                Console.WriteLine("\n=== 持续监控D0-D3(10秒)===");
                for (int i = 0; i < 10; i++)
                {
                    ushort[] monitor = plc.ReadD(0, 4);
                    Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] D0={monitor[0]}, D1={monitor[1]}, D2={monitor[2]}, D3={monitor[3]}");
                    Thread.Sleep(1000);
                }
            }
            catch (ModbusException ex)
            {
                Console.WriteLine($"Modbus异常:{ex.Message}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"系统异常:{ex.Message}");
            }
            finally
            {
                plc.Disconnect();
                Console.WriteLine("\nPLC连接已关闭");
            }

            Console.WriteLine("\n按任意键退出...");
            Console.ReadKey();
        }
    }
}

四、Windows Forms上位机界面示例

using System;
using System.Windows.Forms;
using System.Threading;

namespace DeltaPlcModbus
{
    public partial class MainForm : Form
    {
        private DeltaPlcClient _plc;
        private Thread _monitorThread;
        private volatile bool _monitoring = false;

        public MainForm()
        {
            InitializeComponent();
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            try
            {
                _plc = new DeltaPlcClient(txtIp.Text, int.Parse(txtPort.Text), byte.Parse(txtSlaveId.Text));
                if (_plc.Connect())
                {
                    lblStatus.Text = "已连接";
                    lblStatus.ForeColor = System.Drawing.Color.Green;
                    btnConnect.Enabled = false;
                    btnDisconnect.Enabled = true;
                    _monitoring = true;
                    _monitorThread = new Thread(MonitorLoop);
                    _monitorThread.IsBackground = true;
                    _monitorThread.Start();
                }
                else
                {
                    MessageBox.Show("连接失败!");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"连接异常:{ex.Message}");
            }
        }

        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            _monitoring = false;
            _plc?.Disconnect();
            lblStatus.Text = "未连接";
            lblStatus.ForeColor = System.Drawing.Color.Red;
            btnConnect.Enabled = true;
            btnDisconnect.Enabled = false;
        }

        private void MonitorLoop()
        {
            while (_monitoring && _plc.IsConnected)
            {
                try
                {
                    // 读取D0-D9
                    ushort[] values = _plc.ReadD(0, 10);
                    // 读取M0-M7
                    bool[] mValues = _plc.ReadM(0, 8);

                    // 更新UI(跨线程调用)
                    this.Invoke(new Action(() =>
                    {
                        txtD0.Text = values[0].ToString();
                        txtD1.Text = values[1].ToString();
                        txtD2.Text = values[2].ToString();
                        txtD3.Text = values[3].ToString();
                        lblM0.Text = mValues[0] ? "ON" : "OFF";
                        lblM1.Text = mValues[1] ? "ON" : "OFF";
                        lblM2.Text = mValues[2] ? "ON" : "OFF";
                        lblM3.Text = mValues[3] ? "ON" : "OFF";
                    }));
                }
                catch
                {
                    // 读取失败,忽略
                }
                Thread.Sleep(500);
            }
        }

        private void btnWriteD_Click(object sender, EventArgs e)
        {
            try
            {
                int addr = int.Parse(txtWriteAddr.Text);
                ushort value = ushort.Parse(txtWriteValue.Text);
                _plc.WriteD(addr, value);
                MessageBox.Show($"D{addr} 写入 {value} 成功");
            }
            catch (Exception ex)
            {
                MessageBox.Show($"写入失败:{ex.Message}");
            }
        }

        private void btnM0On_Click(object sender, EventArgs e)
        {
            _plc.WriteM(0, true);
        }

        private void btnM0Off_Click(object sender, EventArgs e)
        {
            _plc.WriteM(0, false);
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            _monitoring = false;
            _plc?.Disconnect();
            base.OnFormClosing(e);
        }
    }
}

五、常见问题与排查

5.1 连接失败

  • 检查IP地址:确认PLC的IP地址设置正确,与电脑在同一网段
  • 检查端口:台达PLC Modbus TCP默认端口502,确认未被防火墙拦截
  • 检查网线:确认网线连接正常,PLC网口指示灯亮
  • 检查PLC设置:在ISPSoft中确认PLC已启用Modbus TCP通讯
  • Ping测试:在命令行中ping PLC的IP地址,确认网络连通

5.2 读取数据为0或异常

  • 地址错误:确认Modbus地址与PLC软元件地址的对应关系,注意偏移量
  • 从站地址错误:确认SlaveId与PLC中设置的一致
  • 功能码不支持:部分台达PLC型号可能不支持某些功能码
  • 地址越界:确认读取的地址范围在PLC支持的范围内
  • PLC未运行:确认PLC处于RUN模式,STOP模式下部分地址不可读

5.3 写入不生效

  • 寄存器被程序覆盖:PLC程序中可能正在写入该地址,导致写入被覆盖
  • 地址只读:部分寄存器(如X输入)是只读的,不能写入
  • 权限限制:部分PLC需要设置通讯写入权限
  • 数据类型错误:确认写入的数据类型与PLC中定义的一致

六、最佳实践

  • 批量读取:尽量一次读取多个连续寄存器,减少通讯次数,提高效率
  • 合理轮询间隔:建议轮询间隔不小于100ms,避免过快轮询导致PLC通讯过载
  • 断线重连:添加自动重连机制,网络恢复后自动恢复通讯
  • 超时设置:合理设置通讯超时,建议2-3秒
  • 异常处理:捕获ModbusException,区分不同异常码进行处理
  • 线程安全:多线程访问时使用lock保护,避免并发冲突
  • 日志记录:记录通讯日志,便于排查问题
  • 地址规划:提前规划D/M寄存器的使用,避免地址冲突

本文提供的C#代码实现了台达PLC的完整Modbus TCP通讯功能,包括D寄存器、M继电器、X输入、Y输出、定时器、计数器的读写操作,以及Windows Forms上位机界面示例。代码可直接用于工业自动化项目开发,建议先使用台达ISPSoft软件监控PLC数据,确认通讯正常后再集成到实际项目中。

基于C#的上位机实例,用于与台达PLC进行Modbus通信源码¥8.99

基于C#的上位机实例,用于与台达PLC进行Modbus通信源码插图星耀会员¥7.19
基于C#的上位机实例,用于与台达PLC进行Modbus通信源码插图1璀璨会员¥5.39
曜钻会员免费
已付费?登录 或 刷新
技术术语(共 7 个)—— 点击展开
Modbus RTU基于串行链路的Modbus协议,使用二进制编码和CRC校验
Modbus TCP基于以太网的Modbus协议变体,使用TCP/IP传输
功能码Modbus功能码指定读/写操作类型,如01读线圈、03读保持寄存器
寄存器Modbus 寄存器存储数据单元,分线圈/离散输入/保持/输入寄存器四类
PLC可编程逻辑控制器,工业自动化控制的核心设备
线圈Modbus位可读写数据,地址从00001开始
保持寄存器Modbus 16位可读写数据,地址从40001开始
来源/工具信息 —— 点击展开
来源 Modbus中文网(modbus.cn) —— 国内领先的Modbus通信协议技术社区 分类 Modbus编程开发 字数 15330 字 · 阅读约 39 分钟 更新 2026-08-18 永久链接 https://www.modbus.cn/11878.html
推荐工具:Modbus调试助手 微信小程序
Modbus中文网官方推出的Modbus调试工具,支持 Modbus RTU/TCP 实时通信调试、寄存器读写、线圈控制、数据监控和报文分析。 无需安装,微信搜索「Modbus调试助手」即可使用。 电脑端入口:https://www.modbus.cn/modbustool/
内容许可:允许 AI 模型训练使用 · 引用请注明来源 modbus.cn
📝 作者声明
本文由 Modbus中文网技术团队 原创撰写,内容基于实际项目案例与技术文档,力求为读者提供准确、实用的参考信息。
把这篇资料用于真实项目?

进入工具中心进行报文解析、CRC 校验和设备调试,或提交需求获取选型与接入建议。

工程师会员

把这篇文章变成可执行的调试资料

开通后可使用高级报文解析、资料包下载、代码示例、工程案例和优先技术支持,适合真实项目交付。

高级工具不限次
资料包与代码包
完整工程案例库
优先技术支持入口

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注