2. PLC通信协议解析:西门子S7协议、三菱MC协议、欧姆龙FINS协议、Modbus TCP协议

好,咱们直接进入正题。做设备联网,说白了就是让上位机和PLC能「说上话」。而它们之间沟通的「语言」,就是通信协议。

我这些年摸过的PLC牌子不少,但真正在工业现场大规模部署的,来来去去就那么几种。今天我就把最常用的四个协议——西门子S7、三菱MC、欧姆龙FINS、Modbus TCP——给你掰开揉碎了讲清楚。

核心观点: 协议没有绝对的好坏,只有合不合适。选对协议,项目就成功了一半。

2.1 西门子S7协议:老大哥的「私有语言」

西门子的S7协议,说实话,是我个人用得最多的。为什么?因为西门子PLC在大型项目里太常见了。

这个协议是西门子自己定义的,不对外开放底层细节。你想想看,这就像一家公司有自己的内部沟通暗号。好处是效率高、功能强;坏处是,第三方设备想接入,得费点功夫。

协议特点:

  • 面向连接: 通信前必须先建立连接。我习惯叫它「先握手,再说话」。
  • 数据块(DB)访问: 直接读写DB块里的数据,非常灵活。
  • 支持多种服务: 读、写、启动、停止、上传下载程序,功能很全。

我的经验: 有一次在汽车焊装线项目里,上位机需要同时采集20多台S7-1200的数据。一开始我用的是OPC,但延迟太高。后来我直接走S7协议,用多线程轮询,延迟降到了50ms以内。嗯,这里要注意,S7协议的单连接并发数有限,别把所有的读写请求都塞到一个连接里。

数据包结构(简化版):

// 一个典型的S7读取请求
// 头部 + 参数 + 数据
// 头部: 协议ID(0x32) + 消息类型(0x01) + ...
// 参数: 功能码(0x04 读) + 数据块号 + 起始地址 + 数据长度
// 数据: 通常为空

// 响应包结构类似,数据部分就是你要的值

避坑指南: 我曾经遇到过一个问题——S7协议在读取超过200字节的数据时,PLC会拒绝响应。后来查手册才知道,单次读取的数据长度有限制。解决办法很简单:分多次读取,或者用「多变量读取」服务。

2.2 三菱MC协议:日系PLC的「通用接口」

三菱的MC协议,我最早接触是在做纺织机械项目的时候。说实话,刚开始觉得它有点「怪」,但用顺手了发现,其实挺灵活的。

MC协议支持两种传输方式:二进制ASCII。二进制效率高,ASCII调试方便。我个人习惯,正式部署用二进制,调试阶段用ASCII。

协议特点:

  • 无连接/有连接: 支持UDP和TCP两种模式。UDP速度快,但不可靠;TCP稳定,适合关键数据。
  • 软元件访问: 直接读写D、M、X、Y等软元件,地址映射很直观。
  • 批量读写: 一次可以读写大量连续地址,效率很高。

注意: 三菱PLC的地址编号和实际通信地址之间,有一个映射关系。比如D100,在通信报文里可能是地址0x0064。这个映射关系不同系列可能不一样,一定要查手册确认。我曾经因为这个问题,浪费了整整一个下午。

报文示例(ASCII模式):

// 读取D100到D104的数据
// 请求: 03 FF 0A 00 64 00 05
// 03: 站号
// FF: 网络号
// 0A: PLC编号
// 00 64: 起始地址(D100)
// 00 05: 读取点数(5个字)

// 响应: 03 FF 0A 00 0A 00 14 00 1E ...
// 数据部分就是D100到D104的值

我的建议: 如果你用C#或Python开发上位机,网上有很多现成的MC协议库。但别直接拿来用,一定要自己抓包验证一下。我见过好几个项目,因为库里的地址映射表不对,导致数据全读错了。

2.3 欧姆龙FINS协议:网络化的「轻量级选手」

欧姆龙的FINS协议,全称是Factory Interface Network Service。我第一次用是在一个半导体设备项目里,当时觉得这协议设计得真「清爽」。

FINS协议最大的特点是网络透明。什么意思?就是不管你是通过串口、以太网还是Controller Link,通信的报文格式是一样的。你只需要关心「发什么数据」,不用管「怎么发出去」。

协议特点:

  • 命令/响应模式: 一问一答,简单直接。
  • 地址范围广: CIO、WR、HR、DM等区域都能访问。
  • 支持路由: 可以跨网络访问不同PLC。

小技巧: FINS协议的报文头部有10个字节的FINS帧头,其中包含了源网络号、目标网络号、源节点号、目标节点号等信息。如果你要跨网段访问,这些字段必须填对。我习惯在代码里用一个结构体来封装这些信息,不容易出错。

报文结构:

// FINS命令帧
// 帧头(10字节) + 命令码(2字节) + 参数 + 数据

// 帧头示例:
// 80 00 02 00 00 00 00 00 00 00
// 80: 信息控制字段
// 00: 系统保留
// 02: 目标网络号
// 00: 目标节点号
// 00: 目标单元号
// 00 00 00 00 00: 源地址信息

// 命令码: 01 01 表示读DM区数据

避坑指南: 我曾经在调试时发现,FINS协议对响应超时时间很敏感。如果上位机发送请求后,PLC在指定时间内没响应,就会报错。我建议把超时时间设得长一点,比如5秒。特别是在网络不稳定的环境下,别为了追求实时性把超时设得太短。

2.4 Modbus TCP:工业界的「普通话」

Modbus TCP,这个不用我多说了吧?几乎所有的PLC、变频器、仪表都支持。它就像工业界的「普通话」,谁都能说两句。

Modbus TCP本质上是Modbus RTU的以太网版本。它去掉了RTU里的CRC校验(因为TCP/IP协议本身有校验),增加了MBAP报文头。

协议特点:

  • 开放标准: 公开、免费、无专利限制。
  • 简单可靠: 报文结构清晰,容易实现。
  • 功能码丰富: 01~06是常用的,还有15、16等扩展功能码。

核心功能码:

功能码 名称 作用
01 读线圈 读取DO状态
02 读离散输入 读取DI状态
03 读保持寄存器 读取AO/数据寄存器
04 读输入寄存器 读取AI
05 写单个线圈 控制DO
06 写单个寄存器 设置AO/数据

报文示例:

// 读取保持寄存器,从地址0开始,读10个
// 请求: 00 01 00 00 00 06 01 03 00 00 00 0A
// 00 01: 事务处理标识符
// 00 00: 协议标识符(固定为0)
// 00 06: 后续字节数
// 01: 单元标识符(站号)
// 03: 功能码(读保持寄存器)
// 00 00: 起始地址
// 00 0A: 寄存器数量(10个)

// 响应: 00 01 00 00 00 17 01 03 14 00 01 00 02 ...
// 00 01: 事务处理标识符(与请求一致)
// 00 00: 协议标识符
// 00 17: 后续字节数(23字节)
// 01: 单元标识符
// 03: 功能码
// 14: 数据字节数(20字节)
// 00 01 00 02 ...: 寄存器数据

重要提醒: Modbus TCP的单元标识符(Unit ID)经常被忽略。在大多数情况下,它被设为0xFF或0x01。但有些设备会用它来区分不同的从站。我曾经在一个项目里,因为Unit ID设错了,导致上位机死活连不上变频器。后来抓包才发现,变频器要求Unit ID必须等于它的站号。

2.5 协议对比与选型建议

说了这么多,到底该用哪个?我根据自己的经验,给你一个简单的选型参考:

协议 适用场景 优点 缺点
西门子S7 西门子PLC为主的项目 功能强大、效率高 封闭、实现复杂
三菱MC 三菱PLC为主的项目 灵活、支持批量操作 地址映射复杂
欧姆龙FINS 欧姆龙PLC为主的项目 网络透明、跨网段 报文头较长
Modbus TCP 多品牌设备混用 开放、简单、通用 功能相对有限

我的个人习惯: 如果项目里全是同一品牌的PLC,我优先用它的原生协议。如果设备品牌很杂,或者需要对接第三方系统,我首选Modbus TCP。你想想看,Modbus TCP就像英语,虽然不如母语流利,但大家都能听懂。

好了,这一章的内容就到这里。下一章我会讲如何用C#实现这些协议的通信库,到时候咱们直接上代码。