西门子 S7-1200 与上位机通信选型实战:S7 协议 vs Modbus TCP

分类:plc  |  时间:2026-10-08 15:22:58  |  阅读:56
场景背景

给一条包装线做上位机监控,控制器是西门子 S7-1200(CPU 1214C DC/DC/DC),上位机是 C# WinForms,要实时读 60 个变量(速度、温度、计数、报警字),并在 HMI 触摸屏上同步显示。

选型时纠结了很久:到底用西门子自家的 S7 协议(S7.Net / Snap7),还是把所有数据映射到 Modbus TCP 寄存器让上位机读?这两种方案我都跑过一遍,下面把配置步骤、代码和实测数据都摆出来。

工业控制柜

方案一:S7 协议直连(S7.Net / Snap7)

优点是不用改 PLC 程序,直接按 DB 块地址读写,变量名和 PLC 里一一对应。

先在 TIA Portal 里做两件事,缺一不可:

1. CPU 属性 → 防护与安全 → 连接机制
   ☑ 允许来自远程对象的 PUT/GET 通信访问     ← 不勾这个,上位机连不上
2. 属性 → 常规 → PROFINET 接口 → 以太网地址
   IP: 192.168.0.10    子网掩码: 255.255.255.0


然后建一个全局 DB 块,取消勾选"优化的块访问"(否则没有绝对地址,S7.Net 读不到):

DB1 "DataBlock"(非优化访问)
  偏移 0.0   Real    Speed        速度
  偏移 4.0   Real    Temperature  温度
  偏移 8.0   DInt    Counter      计数
  偏移 12.0  Word    AlarmWord    报警字


上位机侧用 S7.Net 读取:

csharp
using S7.Net;

// 建立连接(Rack 0 / Slot 1 是 S7-1200 的默认值)
var plc = new Plc(CpuType.S71200, "192.168.0.10", 0, 1);
plc.Open();

// 一次性读回整个 DB1 的前 14 个字节,再按偏移解析
byte[] buf = plc.ReadBytes(DataType.DataBlock, 1, 0, 14);
double speed = S7.Net.Types.Real.FromByteArray(buf.Skip(0).Take(4).ToArray());
double temp  = S7.Net.Types.Real.FromByteArray(buf.Skip(4).Take(4).ToArray());
int    cnt   = S7.Net.Types.DInt.FromByteArray(buf.Skip(8).Take(4).ToArray());
ushort alarm = S7.Net.Types.Word.FromByteArray(buf.Skip(12).Take(2).ToArray());


注意:一定要"一次读回一整块再解析",而不是按变量逐个 Read。S7 协议每次请求都有往返开销,逐变量读 60 个变量就是 60 次往返,实测要 900ms 以上。

方案二:Modbus TCP 映射(通用性更强)

如果上位机不是 C# 而是组态软件、或者要多品牌 PLC 统一接入,Modbus TCP 更省心。代价是要在 PLC 里写搬运程序。

S7-1200 做 Modbus TCP 从站,需要装指令库 MB_SERVER:

scl
// OB1 中调用 MB_SERVER(需先安装 "MODBUS TCP" 指令库)
"MB_SERVER_DB"(
    DISCONNECT := FALSE,
    CONNECT_ID := 1,
    IP_PORT    := 502,          // Modbus 标准端口
    MB_HOLD_REG:= "ModbusRegs".Reg,   // 映射到保持寄存器数组
    ND_DR      := 0,
    DR         := 0,
    NDR        := #NDR,
    DR_        := #DR,
    ERROR      := #ERROR,
    STATUS     := #STATUS);


再把要暴露的变量搬进保持寄存器数组:

scl
// 每个 Real 占 2 个寄存器(32 位)
"ModbusRegs".Reg[0] := REAL_TO_WORD_ARRAY("DB1".Speed);
"ModbusRegs".Reg[2] := REAL_TO_WORD_ARRAY("DB1".Temperature);
"ModbusRegs".Reg[4] := DINT_TO_WORD_ARRAY("DB1".Counter);


上位机侧用 NModbus4 读 40001 起始的保持寄存器:

csharp
var client = new TcpClient("192.168.0.10", 502);
var master = ModbusIpMaster.CreateIp(client);

// 起始地址 0 对应 40001,一次读 6 个寄存器
ushort[] regs = master.ReadHoldingRegisters(0, 6);
float speed = ModbusToFloat(regs, 0);


实测对比

同一台 S7-1200,读 60 个变量,采样周期 500ms,连续跑 1 小时:

                    S7 协议(S7.Net)      Modbus TCP
首次连接耗时        ~120 ms              ~60 ms
单次读取 60 变量     ~28 ms(整块读)     ~35 ms
需要改 PLC 程序      否                   是(写搬运逻辑)
跨品牌通用性         仅西门子             全品牌通吃
安全机制             PUT/GET 需授权       无认证,纯明文
调试难度             地址要对齐 DB 偏移    寄存器地址要换算


结论:纯西门子环境用 S7 协议(省事、不用动 PLC 程序、可整块读);多品牌混合或上位机是第三方组态软件时用 Modbus TCP(通用、但要多写 PLC 搬运码、且没有认证)。

HMI 实时流程画面

踩过的坑

S7-1200 默认固件是"仅允许 HMI 连接",不勾"允许 PUT/GET"的话,S7.Net 连上去立刻报 Connection refused,而且 TIA 里看不出来,要抓包才发现。这个开关藏在"防护与安全 → 连接机制"里。

DB 块如果忘了取消"优化的块访问",ReadBytes 会读到全 0——因为优化访问下变量顺序由编译器决定,没有绝对偏移。

Modbus 的寄存器地址有两种数法:协议地址从 0 开始,文档地址从 40001 开始。NModbus4 用的是协议地址,所以你想读 40010 要传 9,不是 10。这个差一位的坑几乎每个人都踩过。

Real 类型在 Modbus 里是大端双字,而 .NET 读到的是 ushort[2],拼接时高低字顺序不能反,否则速度会变成 1e-38 这种诡异数字。

用 S7.Net 一定要在 finally 里 plc.Close(),S7-1200 的并发连接数是有限的,连接泄漏跑半天就会连不上。

小结

通信选型没有绝对优劣,只有场景匹配。西门子单品牌、要快速落地,S7 协议是首选;一旦涉及多品牌、或者要用通用组态软件,Modbus TCP 的通用性就值回票价。无论选哪种,把"整块批量读"和"连接必须归还"这两条守住,稳定性就有基本保障。
S7-1200PLCModbusTIA通信