欧姆龙作为日系PLC的代表品牌,在国内自动化领域有广泛应用。其上位机通讯主要使用两种协议:经典的Host Link协议(串口)和现代的FINS协议(以太网)。在工厂升级改造中,往往需要同时对接老旧CP1H设备和新一代NJ/NX设备,理解两种协议的差异与对接实现至关重要。本文结合赢式科技在上海食品厂、苏州包装机械、无锡物流分拣等60+欧姆龙PLC项目的实战经验,深度解析Host Link与FINS协议的对比与C#上位机对接实战。

一、欧姆龙PLC产品线与协议演进

欧姆龙PLC产品线丰富,不同系列支持的通讯协议差异显著,先理清产品线是协议选型的基础。

系列典型型号定位支持协议
CP系列CP1H/CP1L/CP1E小型PLCHost Link(串口)、FINS/TCP(需以太网模块)
CJ系列CJ2M/CJ1M中型PLCHost Link、FINS/TCP/UDP、Controller Link
CS系列CS1G/CS1H大型PLCHost Link、FINS、Controller Link
NJ系列NJ501/NJ301机器自动化控制器FINS/TCP/UDP、OPC UA、EtherCAT
NX系列NX1P/NX7新一代小型PLCFINS、OPC UA、EtherCAT

二、Host Link协议深度解析

2.1 协议原理

Host Link协议是欧姆龙早期上位机通讯协议,基于主从架构,上位机为主站,PLC为从站。物理层通常为RS232C或RS422/RS485,链路层基于HDLC,报文使用ASCII字符表示。

Host Link报文结构:

@ | 单元号 | 头代码 | 文本 | FCS | * | CR
   |   01   |  RR    | ...  | 5A | * | \r

- @: 起始符
- 单元号: 00-31,PLC站号
- 头代码: 命令类型(RR=读CIO区, WR=写CIO区等)
- 文本: 命令参数与数据
- FCS: 帧校验(2字节ASCII)
- *: 结束符
- CR: 回车符(0x0D)

2.2 常用命令代码

头代码命令说明示例
RR读CIO区读取CIO区域继电器@00RR00010001 → 读CIO 0001
WR写CIO区写入CIO区域继电器@00WR000100014567 → 写4567
RL读LR区读取链接继电器@00RL0100...
RD读DM区读取数据存储器@00RD010001 → 读DM0100
WD写DM区写入数据存储器@00WD0100000A → 写10
TS读状态读取PLC运行状态@00TS
SC写状态切换PLC运行/停止@00SC03 → 启动运行

2.3 C#实现Host Link通讯

using System.IO.Ports;

public class OmronHostLinkClient
{
    private SerialPort _serialPort;

    public void Connect(string portName, int baudRate = 9600,
        Parity parity = Parity.Even, int dataBits = 7, StopBits stopBits = StopBits.Two)
    {
        _serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits)
        {
            ReadTimeout = 2000,
            WriteTimeout = 2000,
            NewLine = "\r"
        };
        _serialPort.Open();
    }

    /// <summary>读取DM区寄存器</summary>
    public short ReadDm(ushort dmAddress)
    {
        // 构造命令:@00RD010001{FCS}*\r
        string cmd = $"@00RD{dmAddress:D4}0001";
        string fcs = CalculateFcs(cmd);
        string frame = $"{cmd}{fcs}*\r";

        _serialPort.WriteLine(frame);
        string response = _serialPort.ReadLine();

        // 校验响应FCS
        if (!ValidateFcs(response))
            throw new Exception("FCS校验失败");

        // 响应格式:@00RD00{data}{FCS}*\r
        string dataHex = response.Substring(6, 4);
        return Convert.ToInt16(dataHex, 16);
    }

    /// <summary>计算FCS校验</summary>
    private string CalculateFcs(string data)
    {
        byte fcs = 0;
        foreach (char c in data)
        {
            fcs ^= (byte)c;
        }
        return fcs.ToString("X2");
    }

    private bool ValidateFcs(string response)
    {
        if (!response.StartsWith("@") || !response.EndsWith("*\r"))
            return false;

        string data = response.Substring(0, response.Length - 3);
        string fcsInFrame = data.Substring(data.Length - 2);
        string calculatedFcs = CalculateFcs(data.Substring(0, data.Length - 2));
        return fcsInFrame.Equals(calculatedFcs);
    }
}

三、FINS协议深度解析

3.1 协议原理

FINS(Factory Interface Network Service)是欧姆龙专为工业以太网设计的应用层协议,支持TCP与UDP两种传输模式,TCP模式默认端口9600。FINS协议使用二进制报文,效率高于Host Link的ASCII格式。

FINS报文结构(TCP模式):

FINS帧结构:
┌──────────┬──────────┬──────────┐
│ FINS头    │ 命令数据  │ 数据     │
│ (12字节)  │ (4字节)  │ (变长)   │
└──────────┴──────────┴──────────┘

TCP封装(在FINS帧前加4字节长度头):
┌────────────┬───────────────────────────┐
│ 长度(4字节)│ FINS帧                     │
│ 大端序     │                            │
└────────────┴───────────────────────────┘

FINS头字段:
- 信息头(2字节): 0x8000(请求)/0xC000(响应)
- 源网络号(1字节)
- 源节点号(1字节)
- 源单元号(1字节)
- 目标网络号(1字节)
- 目标节点号(1字节)
- 目标单元号(1字节)
- 服务ID(1字节)

3.2 常用FINS命令

命令代码子命令功能说明
01010000读CIO区读取CIO区域数据
01020000写CIO区写入CIO区域数据
01010001读DM区读取DM区域数据
01020001写DM区写入DM区域数据
01010009读定时器PV读取定时器当前值
0601-读PLC状态读取运行/停止状态
04010000/0001切换状态切换运行/停止

3.3 C#实现FINS TCP通讯

using System.Net.Sockets;
using System.Text;

public class OmronFinsTcpClient
{
    private TcpClient _tcpClient;
    private NetworkStream _stream;
    private byte _sourceNode = 0;
    private byte _destNode = 0;
    private byte _serviceId = 0;

    public async Task ConnectAsync(string ip, int port = 9600)
    {
        _tcpClient = new TcpClient();
        await _tcpClient.ConnectAsync(ip, port);
        _stream = _tcpClient.GetStream();

        // FINS/TCP连接握手
        await SendConnectionHandshakeAsync();
    }

    private async Task SendConnectionHandshakeAsync()
    {
        // 客户端节点号信息(固定格式)
        byte[] handshake = new byte[20];
        handshake[0] = 0x46;  // 'F'
        handshake[1] = 0x49;  // 'I'
        handshake[2] = 0x4E;  // 'N'
        handshake[3] = 0x53;  // 'S'
        handshake[4] = 0x00; handshake[5] = 0x00; handshake[6] = 0x00; handshake[7] = 0x0C;
        handshake[8] = 0x00; handshake[9] = 0x00; handshake[10] = 0x00; handshake[11] = 0x00;
        handshake[12] = 0x00; handshake[13] = 0x00; handshake[14] = 0x00; handshake[15] = 0x01;
        // 客户端节点号
        handshake[16] = 0x00; handshake[17] = 0x00; handshake[18] = 0x00; handshake[19] = _sourceNode;

        await _stream.WriteAsync(handshake);

        byte[] response = new byte[24];
        await _stream.ReadAsync(response, 0, 24);
        _destNode = response[23];  // 服务端返回的PLC节点号
    }

    /// <summary>读取DM区寄存器</summary>
    public async Task<short> ReadDmAsync(ushort dmAddress, ushort count = 1)
    {
        byte[] finsHeader = BuildFinsHeader();
        byte[] command = BuildReadCommand(0x02, dmAddress, count);  // 0x02 = DM区代码

        byte[] request = new byte[finsHeader.Length + command.Length];
        Buffer.BlockCopy(finsHeader, 0, request, 0, finsHeader.Length);
        Buffer.BlockCopy(command, 0, request, finsHeader.Length, command.Length);

        // TCP封装:4字节长度头 + FINS帧
        byte[] lengthPrefix = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(request.Length));
        await _stream.WriteAsync(lengthPrefix);
        await _stream.WriteAsync(request);

        // 读取响应
        byte[] respLenBytes = new byte[4];
        await _stream.ReadAsync(respLenBytes, 0, 4);
        int respLen = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(respLenBytes, 0));

        byte[] respData = new byte[respLen];
        await _stream.ReadAsync(respData, 0, respLen);

        // 检查响应状态(偏移12-13为结束代码,00 00表示成功)
        if (respData[12] != 0x00 || respData[13] != 0x00)
            throw new Exception($"FINS读取失败 错误码: 0x{respData[12]:X2}{respData[13]:X2}");

        // 数据从偏移14开始,大端序
        return (short)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(respData, 14));
    }

    private byte[] BuildFinsHeader()
    {
        return new byte[]
        {
            0x80, 0x00,             // 信息头(请求)
            0x00,                    // 源网络号(本地网络)
            _sourceNode,             // 源节点号
            0x00,                    // 源单元号
            0x00,                    // 目标网络号
            _destNode,               // 目标节点号
            0x00,                    // 目标单元号(PLC CPU)
            ++_serviceId             // 服务ID(每次递增)
        };
    }

    private byte[] BuildReadCommand(byte areaCode, ushort address, ushort count)
    {
        return new byte[]
        {
            0x01, 0x01,              // 命令: 读区域
            0x00, 0x00,              // 子命令: 默认
            (byte)((address >> 8) & 0xFF),  // 起始地址(高字节)
            (byte)(address & 0xFF),          // 起始地址(低字节)
            0x00,                     // 位地址
            areaCode,                 // 区域代码(0x02=DM)
            (byte)((count >> 8) & 0xFF),  // 数量(高字节)
            (byte)(count & 0xFF)          // 数量(低字节)
        };
    }
}

四、CP1H与NJ系列对接差异

在工厂改造中,常见CP1H与NJ系列并存的情况,二者的对接方式存在显著差异:

对比维度CP1H(经典系列)NJ(新一代)
原生通讯接口RS232串口以太网(双口)
主推协议Host Link(串口)FINS/TCP + OPC UA
以太网扩展需CP1W-CIF41模块原生支持
地址空间CIO/DM/LR/HR等变量(符号化)
符号访问仅绝对地址支持符号名访问
OPC UA支持不支持内置服务器
典型通讯速率9600bps(串口)100Mbps(以太网)
开发复杂度中等(需封装Host Link)低(OPC UA原生)

4.1 CP1H以太网扩展方案

CP1H本身仅有串口,扩展以太网有两种方案:

  • 方案A:CP1W-CIF41以太网模块:直接插在CP1H扩展槽,支持FINS/TCP,配置简单,约1500元
  • 方案B:串口服务器+Host Link over TCP:将CP1H串口接入串口服务器(Moxa NPort等),上位机通过TCP封装Host Link报文,成本低约500元

五、协议选型建议

基于60+项目的实战经验,针对不同场景给出协议选型建议:

场景推荐协议选型理由
CP1H老设备无网口Host Link(串口)无需硬件改造,成本最低
CP1H+ CIF41模块FINS/TCP性能优于串口,支持远程
CJ2M中型PLCFINS/TCP原生以太网,性能稳定
NJ/NX新设备OPC UA内置服务器,符号化访问
多PLC混采FINS统一协议栈复用,便于维护
跨厂商整合OPC UA标准化,跨品牌通用

六、实际项目案例

2026年5月,赢式科技承接上海某食品厂的产线数据采集改造项目,现场设备情况:

  • 8台CP1H(2008年采购,仅串口) → 用Host Link over TCP方案
  • 3台CJ2M(2015年采购) → 用FINS/TCP
  • 2台NJ501(2022年采购) → 用OPC UA
  • 总计采集点位1800点

项目实施关键数据:

设备类型通讯协议采集周期稳定性
CP1H(串口)Host Link2秒99.7%
CJ2M(以太网)FINS/TCP500ms99.95%
NJ501(以太网)OPC UA200ms99.98%

七、总结

欧姆龙PLC的协议演进清晰反映了工业通讯从串口到以太网、从专有到开放的演进趋势。Host Link作为经典协议,在老旧CP1H设备改造中仍不可或缺;FINS作为过渡的以太网协议,在CJ/CS系列中广泛应用;OPC UA则是NJ/NX新一代控制器的首选。赢式科技基于60+欧姆龙PLC项目积累,形成了Host Link/FINS/OPC UA的统一开发框架,支持CP/CJ/CS/NJ/NX全系列欧姆龙PLC的快速接入,项目平均交付周期3-5周,系统可用率99.95%以上。

需要欧姆龙PLC上位机对接服务?

赢式科技提供免费需求调研、30分钟出方案、2万元起接单,服务覆盖上海、苏州、无锡、昆山等长三角城市,24小时上门交付。咨询电话:15001875806

相关阅读