Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

04 标准库与常用功能: TIA Portal 指令全景与实战 (S7-1200 + SCL)

面向有 10 年 C/Python 经验、正在学 S7-1200 + TIA Portal + SCL 的工程师。本章把 TIA Portal 的指令体系当作“标准库“来梳理: 怎么查找、怎么在 SCL 里调用、每类常用指令的精确语义, 以及模拟量 / PID / HSC / 运动控制 / 通信 / 诊断这些“应用层大件“的完整走查。所有 SCL 代码均按 TIA Portal V16~V20、S7-1200 (一代, 固件 V4.x) 可用语法书写, 变量声明区与代码区分开。

目录

  1. 指令体系如何组织与查找
  2. SCL 中的调用形态
  3. 基础指令精讲: MOVE 与选择类
  4. 转换指令族
  5. 移位与字逻辑
  6. 模拟量: 27648 的世界与 NORM_X / SCALE_X
  7. PID: PID_Compact 完整走查
  8. 高速计数器 HSC
  9. 运动控制 (PTO 基本定位)
  10. 通信: OUC / Modbus TCP / PROFINET / OPC UA / Web server
  11. 库工程化: 项目库、全局库、第三方库
  12. 诊断类指令与系统功能一览
  13. 收尾: 用库思维写 SCL

1. 指令体系如何组织与查找

TIA Portal 的程序编辑器右侧有一条 Instructions task card(指令任务卡), 指令按三个层级组织:

层级内容类比
Basic instructions位逻辑、定时器、计数器、比较、数学、MOVE、转换、移位、字逻辑C 的 stdlib.h / 运算符
Extended instructions日期时间、中断、诊断、通信 (TSEND_C、MB_CLIENT)、PID、字符串、数据日志等Python 的 batteries included 标准库
Libraries项目库 (Project library)、全局库 (Global library)、西门子自带库 (如 LBC, Library of General Functions)npm 包 / 公司内部 pip index

几点精确说明:

  • Basic / Extended 的区分只是组织方式, 不代表编译产物不同; 两者在 SCL 里都是“函数/功能块调用“或“表达式风格指令“。
  • Technology(工艺)指令单独成组: PID_Compact/PID_Temp 在 Extended > PID control; HSC 的 CTRL_HSC 在 Technology > Counting (High-speed counters); 运动控制 MC_xxx 在 Technology > Motion control。
  • 指令的 CPU 支持范围以在线帮助条目标题为准: 官方文档对 SCL 参考里每条指令都标注 (S7-1200, S7-1500)(S7-1500), 后者即 1200 不可用 (典型: REF 引用类型、POINTER/ANY 参数、遗留 S5 定时器/计数器、TypeOfDB、ARRAY DB、断点调试、LTIME 等)。
  • 查找方式: 在 task card 顶部的搜索框输入指令名 (如 MUX) 或在编辑器内直接键入指令名前几个字母, TIA 会弹出匹配列表 (类似 IDE 的补全); 按 F1 打开该指令的在线帮助页, 这是唯一权威语义来源。

一个重要的“程序员直觉校准“: TIA 的指令不是宏, 而是编译器内建操作 + 预编译库块的混合体。Basic 指令多数编译为内建操作 (无调用开销感), Extended/Technology 指令多数是库 FB/FC, 拖入时会让你创建 instance DB。

2. SCL 中的调用形态

2.1 三种形态

1) 表达式风格 (仅 Basic 类):  #y := SEL(G := #g, IN0 := #a, IN1 := #b);
2) FC 调用 (无状态):          #ok := "LBC".Denoise(...);        // 返回值可接可不接
3) FB 调用 (必须有实例):      "PID_DB"(Setpoint := 100.0, Input := #pv);
  • FC (Function): 无实例、无状态 (VAR_TEMP 生命周期仅当次调用), 相当于纯函数 —— 但注意 S7-1200 的 FC 也允许写全局 DB / Q, 编译器不强制纯度。
  • FB (Function Block): 必须绑定 instance DB (单独实例) 或作为 multi-instance 壌在调用者 FB 的 static 区 (相当于“调用者对象内嵌子对象“)。SCL 中 FB 调用形如 "InstDB"(参数表), 参数表里没出现的引脚沿用实例 DB 中的上次值 —— 这是与 C 最大的差异: FB 调用是“有状态的调用“, 省略参数不等于使用默认值, 而是沿用上次调用的值
  • 从 task card 拖 FB 到 SCL 代码区, TIA 弹出“调用选项“对话框, 让你选 single instance (新建独立 DB) 还是 multi-instance (存在当前 FB 的 static 区)。SCL 亦可在块接口的 Static 区手写 MyInst : TON; 然后 #MyInst(IN := ...)

2.2 EN/ENO 语义 (必懂)

多数指令支持 EN 与 ENO。SCL 中:

  • 块属性 “Set ENO automatically” (自动生成 ENO) 开启时, 运行期错误 (除零、CONV 溢出等) 会把 ENO 置 FALSE 并继续执行下一条语句 —— 类似 Python 的“异常被吞掉但留了 flag“, 而不是 C 的段错误。
  • => 语法接 ENO: "CONV_DB"(..., ENO => #ok); 或对表达式风格指令 #ok := ... 之外的独立检测。
  • 已核实的错误行为 (S7-1200): 整数除零 → 结果置 0、ENO=0、保持 RUN; CONV 溢出 → ENO=0 (OUT 取值不同版本文档表述有差异, 以 ENO 为准); 数组越界 → 记入诊断缓冲、保持 RUN, 但 ENO 不会因此置 FALSE。详见第 12 节与 03 章。

3. 基础指令精讲: MOVE 与选择类

3.1 MOVE

MOVE 把 IN 复制到 OUT (可挂多个输出)。在 SCL 里 := 赋值就是 MOVE, 所以 MOVE 指令的真正用途只剩两个: 需要在 F1 帮助里查 ENO 行为时; 以及需要在 EN=FALSE 时保持“整段复制不发生“的可视化场景。SCL:

// 两种写法等价:
"DB_Data".Cmd := #cmd;                                  // 赋值即 MOVE
MOVE(IN := #cmd, OUT1 => "DB_Data".Cmd, OUT2 => #bak);  // 指令形态: 一次复制两份

注意: MOVE 走的是“符号赋值“语义, 数据类型必须赋值兼容 (隐式转换规则见 4.1); 要按原始字节复制请用 POKE_BLK 或对标准 DB 用 PEEK/POKE 家族 (见 03 章)。

3.2 选择类指令 (SEL/MUX/MIN/MAX/LIMIT)

这一族是 SCL 里最像“三元运算符 / switch / clamp“的东西, 但语义有细节差异, 逐个给出:

指令语义C/Python 类比类比不成立之处
SEL(G, IN0, IN1)G=FALSE 选 IN0, G=TRUE 选 IN1G ? IN1 : IN0注意参数顺序: FALSE 走 IN0, 与 C 三元表达式前后位置相反, 极易写反
MUX(K, IN0..INn, ELSE)整数 K 选第 K 个输入; K 超范围 → ELSE 且 ENO=0数组下标 + defaultMUX 是“位置参数即下标“, 不是数组访问; 超范围不报异常而是走 ELSE
MIN(IN1..) / MAX(IN1..)多输入取最小/最大Python min()/max()结果类型跟随输入类型 (整型家族按声明类型截断/符号处理), 无 Python 的跨类型提升
LIMIT(MN, IN, MX)夹紧到闭区间max(MN, min(IN, MX))MN > MX 时 ENO=0 且行为未定义 (C 里同样 UB, 这点类比成立)

示例 FB —— 一个“限幅三态选择器“, 把四种选择指令串起来用:

// FB 接口:
//   Input:  selMode : Int      // 0=取最小 1=取最大 2=取A 3=取B 4=按K选择
//          valueA  : Int
//          valueB  : Int
//          clampLo : Int
//          clampHi : Int
//          k       : Int       // MUX 的选择号
//   Output: result  : Int
//          valid   : Bool
//   Static: 无
// 代码区:
CASE #selMode OF
    0:  // 两个输入取较小者
        #result := MIN(IN1 := #valueA, IN2 := #valueB);
        #valid := TRUE;
    1:  // 取较大者
        #result := MAX(IN1 := #valueA, IN2 := #valueB);
        #valid := TRUE;
    2:  // 选 A
        #result := SEL(G := FALSE, IN0 := #valueA, IN1 := #valueB);
        #valid := TRUE;
    3:  // 选 B
        #result := SEL(G := TRUE, IN0 := #valueA, IN1 := #valueB);
        #valid := TRUE;
    ELSE  // 4 或其它值: 用 MUX 在 A/B/夹紧后的A 三者中选
        MUX(K := #k,
            IN0 := #valueA,
            IN1 := #valueB,
            IN2 := LIMIT(MN := #clampLo, IN := #valueA, MX := #clampHi),
            ELSE => #result,
            ENO => #valid);      // K 超范围: ELSE 生效, valid=FALSE
END_CASE;

// 最终结果统一限幅; MN>MX 时 ENO=0
LIMIT(MN := #clampLo, IN := #result, MX := #clampHi, OUT => #result, ENO => #valid2);

实践建议: SEL/MUX/LIMIT 的参数必须用命名参数 (G := , IN0 := ) 写, 位置参数在这里几乎是可读性灾难。

4. 转换指令族

4.1 隐式转换 vs 显式 CONV

SCL 赋值/传参时, 整型家族 (SInt..DInt/USInt..UDInt) 与 REAL 之间按“宽度提升“隐式转换; 但有以下硬规则:

  • REAL → 整型必须显式 (ROUND/TRUNC/CEIL/FLOOR), 直接 #i := #r; 不编译 —— 比 C 严格, 类似 Rust 的 as 语义要求显式。
  • String ↔ 数值必须走 S_CONV / STRG_VAL / VAL_STRG (字符串细节见 03 章)。
  • 位串家族 (Byte/Word/DWord) 与整型之间可以 CONV, 但 Bool → Byte 之类的“放大“要用 CONVERT 指令形态。

4.2 指令清单

指令语义备注
CONV显式类型转换溢出: ENO=0 (OUT 取值以 ENO 为准, 不要依赖); 这是“越界不抛异常但留痕“的又一例
ROUNDREAL → 整型, 就近取整.5 边界的舍入方向以在线帮助为准 (待核实)
TRUNCREAL → 整型, 向零截断等价 C 的 (int)x
CEIL / FLOOR向 +∞ / −∞等价 math.ceil/floor
NORM_X / SCALE_X归一化 / 定标见第 6 节, 模拟量核心
S_CONV / STRG_VAL / VAL_STRG字符串 ↔ 数值非法字符/超范围: ENO=0 且 OUT=0 (已核实)
CHARINTCONV 的具体实例#i := CONV(Int, #ch); — 待核实: V16~V20 具体拼写以帮助为准

示例: 把 Word 原始值安全转成工程量 (显式 + 错误检测):

// Input:  rawW  : Word    // 如来自 %IW
// Output: asInt : Int
//         asReal: Real
//         ok    : Bool
CONV(IN := #rawW, OUT => #asInt, ENO => #ok);
IF #ok THEN
    #asReal := TRUNC(IN := CONV(Real, #asInt));  // 先显式 Int->Real, 再截断回 Int? 不: 见下
END_IF;

上面第二行故意写成错的示范 —— TRUNC 的输出是整型, 不能赋给 #asReal。正确写法:

#asReal := CONV(Real, #asInt);   // Int -> REAL 显式转换 (也可隐式, 显式更清晰)

CONV(目标类型, 表达式) 是 V16+ 的函数风格写法; 拖拽指令时 TIA 会生成带 => 的指令形态, 两种都合法。待核实: 函数风格 CONV(Real, x) 的确切括号语法在你所用 TIA 版本的编辑器里可能是 Real#(x) 之外的写法, 以 F1 帮助为准。

5. 移位与字逻辑

5.1 SHL/SHR/ROL/ROR 与 SWAP

指令语义备注
SHL(IN, N)左移, 空位补 0操作数按位模式处理; 有符号整型右移不保留符号位 (待核实: 帮助中 SHR 的空位填充描述为 0)
SHR(IN, N)右移, 空位补 0对 Int 想要算术右移时, 先转无符号再移, 或用除法
ROL / ROR循环左/右移移位数按操作数位宽取模
SWAP(IN)字节序交换 (16/32 位)Word/DWord/Real 通信场景必备, 等价 Python struct.unpack('>H', ...) 前的手工翻转

示例: 从 Word 中按位打包/解包状态 (比 AT 覆盖更直观, 比 PEEK 便宜):

// Input:  statusWord : Word
//         bitNo      : UInt
// Output: bitOut     : Bool
//         highByte   : Byte
// Static: tmp : Word

// 取第 bitNo 位 (0 = LSB)
#tmp := SHR(IN := #statusWord, N := #bitNo);   // 右移使目标位到 bit0
#bitOut := (#tmp MOD 2) = 1;                   // 或用 PEEK_BOOL 亦可

// 取高字节: 右移 8 位后 CONV 成 Byte
#highByte := CONV(Byte, SHR(IN := #statusWord, N := 8) AND 16#00FF);

// 反过来打包: 把 8 个 Bool 装进一个 Byte (用 SHL 累积)
// Input: b : Array[0..7] of Bool   Output: packed : Byte
#packed := 0;
FOR #i := 0 TO 7 DO
    IF #b[#i] THEN
        #packed := #packed OR SHL(IN := CONV(Byte, 1), N := #i);
    END_IF;
END_FOR;

5.2 字逻辑: AND/OR/XOR/INV + DECO/ENCO

  • AND / OR / XOR: 位逻辑, 操作数是位串或整型; SCL 里对 Bool 表达式也用同名关键字 (IF #a AND #b THEN), 语义与 C 的 &/|/^ 对齐。
  • INVNOT: 按位取反; SCL 表达式 #y := NOT #x; 对整型同样按位取反。
  • DECO / ENCO: n 选 1 位译码 / 1-of-n 编码 (K → bit K 置 1; 反向求最高/最低置位位号)。批量指示灯/优先编码时比手写循环清晰。

SWAP 的典型场景 (见 10.3 字节序坑):

#swapped := SWAP(IN := #rawWord);   // 大端 <-> 小端 (按字节对换)

6. 模拟量: 27648 的世界与 NORM_X / SCALE_X

6.1 机制: 为什么是 27648

S7-1200 模拟量输入/输出通道的“额定范围“统一映射到整数 0..27648 (单极性) 或 −27648..27648 (双极性)。27648 不是 2 的幂, 它 = 0x6C00, 是西门子历史选择的“满量程标称值“, 让各种量程有公共的整数标尺。类比: 这是一个定点数格式, 小数点位置由通道量程定义, 27648 即 100.0%。

关键分区表 (以 4..20 mA 电流输入为例, 数值已对照官方手册):

数字值物理电流含义
32767> 约 22.81 mA溢出 / 故障态 (如断线诊断值)
27649..3251120..约 22.81 mA过量程 (overrange): 合法但超出标称
0..276484..20 mA额定范围 (normal range): 正常工作区
−6912..−10..4 mA欠量程 (underrange): 0 mA 恰对应 −6912
≤ −32768断线/短路故障态, 配合通道诊断

注意: 若通道组态为 0..20 mA, 则 0 mA 对应 0 而非 −6912; 上表仅适用于组态为 4..20 mA 时。CPU 本体自带的 AI (0..10 V) 分辨率为 10 位, 信号板/信号模块 (SB/SM 1231 等) 可到 12~16 位 (以模块手册为准)。输出侧对称: AQ 额定 0..27648, 过量程到 32511。

6.2 NORM_X + SCALE_X: 标准两段式

思路与 Python 手写 x/(hi-lo)lo + x*(hi-lo) 相同: 先把任意量纲归一到 0.0..1.0, 再定标到工程单位。

// FB "FB_Ai4to20" 接口:
//   Input:  raw        : Int        // 来自 %IW (Word 读入后 CONV 成 Int, 或直接 Int)
//          eMin, eMax : Real       // 工程量程, 例: 0.0 ~ 100.0 (单位自定)
//   Output: value      : Real       // 工程值
//          quality     : Int        // 0=好 1=过量程 2=欠量程 3=故障(溢出)
//          valid       : Bool       // quality=0 时 TRUE
//   Static: norm01     : Real
// 代码区:
// 第一步: 判质量。参考 6.1 表 (4..20 mA 组态)
IF #raw >= 32767 OR #raw <= -32768 THEN
    #quality := 3;                    // 溢出/断线区, 数值不可信
ELSIF #raw > 32511 THEN
    #quality := 1;                    // 过量程上限之外 (32512..32766)
ELSIF #raw > 27648 THEN
    #quality := 1;                    // 过量程: 合法, 可选择是否使用
ELSIF #raw < -6912 THEN
    #quality := 2;                    // 深度欠量程
ELSIF #raw < 0 THEN
    #quality := 2;                    // 欠量程 (4 mA 以下)
ELSE
    #quality := 0;
END_IF;

// 第二步: NORM_X 把 4..20 mA 的额定区间 (0..27648) 归一到 0.0..1.0
// MIN/MAX 必须与 VALUE 同型 (这里 Int); 输出建议 Real
NORM_X(MIN := 0, VALUE := #raw, MAX := 27648, OUT => #norm01);

// 第三步: SCALE_X 把 0.0..1.0 定标到工程量程
// 注意 SCALE_X 的 VALUE 期望 0.0..1.0, MIN/MAX 是工程单位
SCALE_X(MIN := #eMin, VALUE := #norm01, MAX := #eMax, OUT => #value);

// 第四步: 对带"质量非好"的值做限幅与置无效
IF #quality <> 0 THEN
    // 钳到边界, 避免下游拿到外推值; 断线区保持上一拍工程值也可以是另一种策略
    #value := LIMIT(MN := #eMin, IN := #value, MX := #eMax);
END_IF;
#valid := (#quality = 0);

语义细节 (容易踩):

  • NORM_X(MIN, VALUE, MAX) 允许 MIN > MAX (反向换算), SCALE_X 同理; 但两段必须方向一致, 否则换算翻转。
  • NORM_X/SCALE_X 对区间外输入不报错也不夹紧, 只会线性外推 —— 所以质量判断必须自己做, 这是与“带饱和的库函数“的本质区别。
  • 若模块组态为 0..20 mA 而现场仪表是 4..20 mA (活零点), 把 NORM_X 的 MIN 改为 5529 (4 mA 在 0..20 mA 标尺上的数字值, 27648 × 0.2 ≈ 5529), MAX 仍 27648, 即可让 4 mA 对应 0%。
  • 输出侧同理: 工程值 → NORM_X → SCALE_X 到 0..27648 → CONV 到 Word → %QW。

6.3 滤波与平滑

一阶低通 (PT1) 滤波、滑动平均、以及“坏点剔除 + 保持“的质量机, 在练习章里会以独立练习完整实现 (y += k*(x-y), k 由扫描周期与时间常数折算), 本章不重复展开。要点预告: S7-1200 没有内建的模拟量滤波指令 (部分 SM 模块有硬件滤波, 组态里开), 软件滤波必须自己考虑采样周期不等间隔的问题 (scan cycle 抖动), 所以滤波器要挂循环中断 OB 而不是 OB1。

7. PID: PID_Compact 完整走查

7.1 定位与版本

适用说明
PID_Compact通用过程量 (压力/流量/液位/速度)V1.x 遗留; V2.x 需 S7-1200 固件 V2.0 以上 (待核实: 具体最低固件以帮助 Requirements 为准); 新项目直接用 V2.x
PID_Temp温度专用S7-1200 需固件 V4.0 + STEP 7 V13 SP1 引入; 支持加热+制冷双输出、级联; V2.0 起支持纯制冷

PID_Compact 与 PID_Temp 都是库 FB, 拖入即建实例 DB; 所有组态 (量程、输出限幅、采样时间、PID 参数) 都存在实例 DB 的 Config 结构里, 可在 TIA 的组态界面 (工艺对象组态窗口) 里填, 也可在 SCL 里运行期改 —— 但 Config 属于保持性数据, 下载/上载会覆盖, 自整定结果也写在实例 DB 中。

7.2 从组态到 SCL 调用 (完整流程)

  1. 拖 PID_Compact (Extended > PID control) 到 OB1 的 SCL 代码区, 选 single instance, 命名 PID_DB
  2. 打开组态界面 (双击调用处或项目树 “Technology objects” 下的对应项):
    • Controller basic settings: Input type 选 Input (工程单位 REAL, 推荐: 你自己先做 NORM_X/SCALE_X) 或 Input_PER (直接接 Word 原始值, 块内自动换算); 标定 PV 上下限; Output 类型同理 (Output / Output_PER / Output_PWM)。
    • Advanced settings: 输出限幅、手动值限幅、采样时间 CycleTime (默认由块估计, 见 7.4)。
  3. 在循环中断 OB (如 OB30, 100 ms) 中调用它 —— PID 的采样时间假设“每次调用间隔恒定“, 放 OB1 会被扫描周期抖动破坏。
// OB30 (100 ms 循环中断) 中的调用 —— 变量在 OB30 的接口/全局 DB 中:
//   "PID_DB"       : PID_Compact 的实例 DB
//   "DB_Analog".PV : Real   // 6.2 换算好的工程值
//   "DB_Analog".SP : Real   // 设定值
//   "DB_PID".heat  : Real   // 输出 0..100 (%)

"PID_DB"(Setpoint      := "DB_Analog".SP,
         Input         := "DB_Analog".PV,
         // 手动模式接管 (见 7.5):
         ManualEnable  := "DB_PID".manualMode,
         ManualValue   := "DB_PID".manualOut,
         // 复位/模式切换 (上升沿有效):
         Reset         := "DB_PID".reset,
         ModeActivate  := "DB_PID".modeAct);

"DB_PID".heat    := "PID_DB".Output;      // 0..100.0
"DB_PID".state   := "PID_DB".State;       // 控制器状态 (Int)
"DB_PID".err     := "PID_DB".Error;
"DB_PID".errBits := "PID_DB".ErrorBits;   // DWord, 见下

接口要点 (V2.x):

  • InputInput_PER 二选一 (组态决定哪个生效); Output / Output_PER / Output_PWM 同理。
  • Reset 上升沿: 控制器回到未激活状态, 输出置 0; 再由 ModeActivate 上升沿切换模式。
  • ModeActivate 上升沿: 自动/手动模式切换的“确认键“。上电或 Reset 后控制器处于未激活态, 必须给一次 ModeActivate 边沿才会按当前 ManualEnable 进入自动或手动。
  • State (Int) 指示当前所处阶段 (未激活/预整定/精确整定/自动/手动等), 取值表见在线帮助 (待核实: 各值枚举以帮助为准)。
  • Error/ErrorBits: ErrorBits 是 DWord 位掩码; 已核实的典型值 16#00000800 = 采样时间错误 (PLCSIM 仿真最常见)。

7.3 自整定 (auto-tuning) 流程

  1. 组态好 Input/Output 标定与输出限幅, 接好真实 (或高保真模拟的) 过程。
  2. 控制器先处于自动模式且过程量稳定在设定值附近 (预整定要求过程处于稳态, 否则整定结果差)。
  3. 在调试界面 (工艺对象 > Commissioning) 点 Start pre-tuning + fine-tuning (或分步), 或在 SCL 里对实例 DB 的整定控制位写上升沿 (组态界面里可查到对应位名; 待核实: 位名称随版本变化)。
  4. 整定完成后新的 Gain/Ti/Td 写回实例 DB 的 sPid_Calc 结构, 并提示“是否将参数复制回组态“ (不复制则只存在于实例 DB, 覆盖下载会丢)。

7.4 PLCSIM 仿真的限制 (已核实)

官方明确: PID_Compact V2.x 在一代 S7-1200 上不支持 PLCSIM 仿真; 只有 S7-1500 与 S7-1200 G2 可仿真。此外, 仿真中 PLCSIM 的循环时钟抖动会触发 ErrorBits = 16#00000800 (采样时间错误) 并中止进行中的整定, 官方给出的对策是在实例 DB 中关闭相关检测:

// PID_Compact V2.x (S7-1500 / 1200 G2 仿真时), 在线/离线访问实例 DB:
"PID_DB".CycleTime.EnEstimation := FALSE;   // 关闭采样时间自动估计
"PID_DB".CycleTime.EnMonitoring := FALSE;   // 关闭采样时间监视

V1.x 的对应做法 (官方 V1 页面): 关闭 sb_EnCyclEstimation / sb_EnCyclMonitoring, 手动给 sPid_Calc.r_Cycle 赋采样时间。两代成员名不同, 以你版本的帮助为准。

7.5 手动模式接管示例

运维常见需求: 自动到一定条件切手动开度。注意 ManualEnable 是电平, ModeActivate 是边沿, 组合逻辑如下:

// "DB_PID" 接口: manualMode, manualOut (0..100), reset, modeAct : Bool
// 上升沿检测:
"DB_PID".modeAct := "DB_PID".manualMode AND NOT "DB_PID".manualMode_last;

IF "DB_PID".manualMode THEN
    // 手动: 输出直接跟踪 manualOut (块内部做无扰切换准备)
    ;
ELSE
    // 自动: 正常控制
    ;
END_IF;

"PID_DB"(Setpoint     := "DB_Analog".SP,
         Input        := "DB_Analog".PV,
         ManualEnable := "DB_PID".manualMode,
         ManualValue  := LIMIT(MN := 0.0, IN := "DB_PID".manualOut, MX := 100.0),
         Reset        := "DB_PID".reset,
         ModeActivate := "DB_PID".modeAct);

"DB_PID".manualMode_last := "DB_PID".manualMode;

PID_Temp 与 PID_Compact 的调用形态几乎一致, 差异在: 加热/制冷双输出 (OutputHeat/OutputCool), 加热制冷切换的死区/滞后组态, 以及针对温度过程 (大滞后) 的整定策略。做温控直接选 PID_Temp, 不要拿 PID_Compact 硬凑。

8. 高速计数器 HSC

8.1 组态思路

S7-1200 的 HSC 是 CPU 内建硬件, 不是指令: 在 Device configuration > CPU 属性 > High speed counters (HSC) 里启用并组态。注意 HSC 与 PTO/PWM 共用物理输出/输入资源 (脉冲发生器占用高速输入作反馈等, 具体复用规则以 CPU 手册为准), 组态冲突 TIA 会报错。

组态项: 功能选择 (counting 计数 / frequency 频率测量 / period 周期测量, 依 HSC 编号可用性不同)、计数类型 (单相加/减、加/减双脉冲、A/B 正交 ×1/×2/×4)、初始/比较值、以及中断事件 (如 CV = RV 比较匹配触发硬件中断 OB)。组态完成后, 当前计数值被映射到一个 32 位输入映像区地址 (I 区, 如 %ID1000), 每个扫描周期由操作系统自动刷新 —— 这是最重要的一个事实: 读 HSC 不需要任何指令, 直接读 %ID

8.2 SCL 读取与控制

// 周期性读取当前值 (放在循环中断 OB 中):
"DB_Speed".count := %ID1000;      // 直接读输入映像, DInt

// CTRL_HSC (Technology > Counting): 运行期改变计数参数 (方向/写新计数 CV/写新参考值 RV)
// 典型用途: 回零时写 0, 或改比较值触发下一档中断
// "HSC_Ctrl_DB" : CTRL_HSC 实例, 组态时已关联具体 HSC 硬件对象
IF "DB_Hsc".doWriteZero THEN
    "HSC_Ctrl_DB".CV  := TRUE;    // 允许写新当前值
    "HSC_Ctrl_DB".NewCV := 0;     // 新计数初值 (成员名待核实, 以帮助为准)
    "HSC_Ctrl_DB".REQ  := TRUE;
END_IF;
// REQ 完成后由 Busy=FALSE 收尾, 具体成员名以在线帮助的 CTRL_HSC 参数表为准

上面的 CTRL_HSC 成员名在不同 TIA 版本 (legacy CTRL_HSC 与较新的 CTRL_HSC_EXT) 有差异, 使用前以 F1 参数表核对; 但“读 %ID 不需要指令“这一点在所有版本都成立。

8.3 编码器测速示例思路

两种做法:

  1. 频率测量模式: HSC 组态为 frequency measurement, %ID 直接给出 Hz (每秒脉冲数)。换算: rpm := freq_Hz * 60.0 / pulses_per_rev(pulses_per_rev = 编码器线数 × 倍频)。适合中高速; 低速时刷新周期内脉冲太少导致量化误差大。
  2. 周期测量模式 (T 法, 低速优): HSC 组态为 period measurement, 测量固定脉冲数的时间, %ID 给出周期值, rpm ∝ 1/period
  3. M 法 (通用): 计数模式 + 循环中断固定周期采样 %ID 差分: rpm := DINT_TO_REAL(count_now - count_last) * 60.0 / (pulses_per_rev * sample_s), 差分要用无符号回绕安全的算法 (DInt 减法天然模 2^32 回绕, 直接相减再解释即可, 与 C 的无符号减法同理)。

选型口诀: 高速用 M 法 (频率模式), 低速用 T 法 (周期模式), 全范围用 M/T 切换 —— 与编码器测速的所有嵌入式方案同构。

9. 运动控制 (PTO 基本定位)

S7-1200 的运动控制 = PTO (Pulse Train Output) 脉冲输出 + 工艺对象 (Technology Object, TO) + PLCopen 指令。要求: S7-1200 固件 V4.0 起支持 (对应 TIA V13 时代引入), TO 本身有版本 (V4.0~V7.x, 在指令任务卡 Technology > Motion control 下选版本); 一代 CPU 各型号集成 2~4 路 PTO (1215C 为 4 路, 其余多为 2 路, 以样本手册为准)。步进/伺服驱动器接脉冲+方向, 位置闭环在驱动器内, PLC 侧是“脉冲数即位置“的半闭环。

三步走:

  1. 组态 TO: 项目树 Add new technology object > Positioning axis (TO_PositioningAxis), 组态 PTO 脉冲源、方向输出、加减速、回零方式、软限位、跟随误差。
  2. MC_Power 使能轴 (必须每周期调用):
// "Axis1_Power_DB" 等实例 DB; 轴与 TO 的关联在插入指令时组态 (不是形参)
"Axis1_Power_DB"(Enable := "DB_Mc".powerOn,
                 StopMode := FALSE);          // FALSE=受控停止
  1. MC_MoveJog (点动) / MC_MoveRelative (相对定位):
// 点动: JogForward 按住正转
"Axis1_Jog_DB"(JogForward := "DB_Mc".jogF,
               JogBackward := "DB_Mc".jogB,
               Velocity    := 100.0);        // 单位由 TO 组态决定 (如 mm/s 或 rpm)

// 相对移动: Execute 上升沿启动, 走 500.0 个单位
"Axis1_Rel_DB"(Execute  := "DB_Mc".startRel,
               Distance := 500.0,
               Velocity := 200.0);

// 读取轴状态
"DB_Mc".busy     := "Axis1_Rel_DB".Busy;
"DB_Mc".done     := "Axis1_Rel_DB".Done;
"DB_Mc".axisPos  := "Axis1".ActualPosition;  // TO 的动态数据 (符号访问)

完整指令族一览: MC_Power(使能), MC_Reset(故障复位), MC_Home(回零), MC_Halt(暂停), MC_MoveJog(点动), MC_MoveRelative(相对), MC_MoveAbsolute(绝对), MC_MoveVelocity(速度控制), MC_GroupInterrupt/MC_GroupContinue(组中断/恢复, 版本相关, 待核实 1200 可用性)。共同编程规则: MC_Power/MC_Reset 等“管理类“每周期调用; “执行类”(Move/Home) 用 Execute 上升沿触发, Busy/Done/CommandAborted/Error 报状态 —— 这是一台显式状态机, 和 C 里手写 motion state machine 的差别在于状态机被标准化了。

10. 通信

10.0 PLCSIM 仿真支持度总表 (先看这个再选方案)

功能标准 PLCSIM (V16~V21)说明
TSEND_C/TRCV_C (OUC)部分仅经内部 Softbus 在本机内互通, 无真实 TCP/IP; 与外部 PC 程序通信不可仿真
MB_CLIENT / MB_SERVER (Modbus TCP)不支持需真实 CPU 或 PLCSIM Advanced (Advanced V8+ 才支持 1200 G2, 一代 1200 不支持)
PROFINET IO controller不支持真实 IOPLCSIM 内无 PROFINET IO 设备可挂 (仿真 PN 设备需第三方工具)
OPC UA Server不仿真官方对比表明确无 OPC UA
Web server官方对比表明确

结论: 通信类功能的学习与验证必须上真实 CPU; PLCSIM 只能验证指令调用逻辑与状态机 (还常因时钟问题误报错误)。

10.1 TSEND_C / TRCV_C (开放用户通信, 与 socket 类比)

OUC = CPU 集成网口上的原生 TCP/UDP 套接字通信。类比表:

socket APITIA 对应
socket() + connect()TCON (或 TSEND_C/TRCV_C 内建连接管理)
send()TSEND / TSEND_C
recv()TRCV / TRCV_C
close()TDISCON
bind()+listen()TCON 组态为被动 (服务端) 模式

TSEND_C = “建连 + 发送” 的合体 (带 CONT 参数控制连接保持), TRCV_C = “建连 + 接收” 的合体。连接参数 (对端 IP/port、连接 ID、TCP 还是 UDP) 不在参数表里, 而在指令实例的属性面板 (Configuration > Connection data) 中组态, 存在一个连接 DB (如 TCON_IP_v4) 里。

最小可用示例 —— 每 1 秒发送 8 字节, 异步接收:

// OB1 / 循环中断中调用。变量:
//   "DB_OUC".sendReq  : Bool   // TON 产生的 1s 脉冲
//   "DB_OUC".txBuf    : Array[0..7] of Byte
//   "DB_OUC".rxBuf    : Array[0..63] of Byte
//   "DB_OUC".rcvdLen  : UInt
//   实例: "TS_CDB" (TSEND_C), "TR_CDB" (TRCV_C)

// 发送: REQ 上升沿触发; CONT=TRUE 保持连接
"TS_CDB"(REQ  := "DB_OUC".sendReq,
          CONT := TRUE,
          DATA := "DB_OUC".txBuf,
          LEN  := 8);

// 接收: EN_R=TRUE 常开接收; NDR=TRUE 表示收到新数据
"TR_CDB"(EN_R := TRUE,
          CONT := TRUE,
          DATA := "DB_OUC".rxBuf,
          LEN  := 0,                 // 0 = 接收最多到 DATA 尺寸
          NDR  => "DB_OUC". newData,
          RCVD_LEN => "DB_OUC".rcvdLen,
          ERROR => "DB_OUC".rxErr,
          STATUS => "DB_OUC".rxStatus);

要点:

  • 这些指令是异步的: REQ 发出后要靠 BUSY/DONE/ERROR/STATUS 状态机轮询, 不是阻塞调用 —— 类似非阻塞 socket + 事件标志, 而不是 Python socket.send() 一把梭。
  • STATUS (Word) 在 ERROR=FALSE 时是通知码 (如 1=状态切换), ERROR=TRUE 时是错误码, 必须区分读。
  • 字节序: S7 内存按大端存 16/32 位值, TCP 线上原样发送, 所以 Python 端用 struct.unpack('>h', ...) 即可直接解析, 无需翻转。

配套 Python 测试端 (真机调试用):

# echo 服务器: 与 PLC 的 TSEND_C/TRCV_C 对测 (PLC 侧组态对端 IP=本机, port=2000)
import socket
srv = socket.socket(); srv.bind(('0.0.0.0', 2000)); srv.listen(1)
conn, _ = srv.accept()
while True:
    data = conn.recv(64)
    if not data: break
    print(data.hex())
    conn.send(data)   # 回发, PLC 的 TRCV_C 会收到

10.2 Modbus TCP (MB_CLIENT / MB_SERVER)

S7-1200 既是 Modbus TCP client (MB_CLIENT) 也可作 server (MB_SERVER)。与 OUC 的区别: Modbus TCP 是带标准寄存器模型的应用层协议, 适合与第三方设备 (变频器、仪表、网关) 交互; 同网口可共存。

布局硬性要求 (寄存器 ↔ DB 映射):

  • 数据缓冲 (MB_CLIENT.DATA_PTR / MB_SERVER.MB_HOLD_REG) 必须指向标准 (非优化) DB 或 M 存储区 —— 优化 DB 会报错 (典型 STATUS 8383/8384 类错误)。做法: 新建 DB 时在属性里取消勾选 “Optimized block access”。
  • Modbus 寄存器 = 16 位 Word; DB 里用 Array[0..N] of Word (或连续的 Word/DWord 结构体) 排布, 不要在中间插 Bool/Byte 破坏连续性; Bool 量凑成 16 的倍数打包进 Word。
  • 字节序坑: S7 的 Word 本身是大端 (与 Modbus 线序一致), 单寄存器无问题; 但 32 位值 (DInt/Real) 占两个寄存器, 很多第三方设备 (尤其部分国产表/网关) 是 little-endian 字序 (低字在前), 这时要么设备侧改字序设置, 要么 PLC 侧用 SWAP + 拼接手动换:
// Input: regLo, regHi : Word (两个保持寄存器, 字序存疑时)
// Output: valBE, valLE : DInt
// 大端字序 (高字在前 = regHi 是高 16 位):
#valBE := CONV(DInt, SHL(IN := CONV(DWord, #regHi), N := 16) OR CONV(DWord, #regLo));
// 小端字序 (低字在前): 交换两个字即可
#valLE := CONV(DInt, SHL(IN := CONV(DWord, #regLo), N := 16) OR CONV(DWord, #regHi));
// Real 场景: 同样拼出 DWord 后用 SCAT/位重解释, 或标准 DB 中直接以 Real 声明并整体 SWAP

MB_CLIENT 最小示例 (周期读 4 个保持寄存器, 40001 起):

// 变量:
//   "MBMap" (标准 DB): holdRegs : Array[0..3] of Word
//   "DB_Mb".req : Bool (定时脉冲), .done, .busy, .err : Bool; .status : Word
//   实例 "MB_CL" : MB_CLIENT
//   连接参数在实例属性中组态: 对端 IP、端口 502、连接 ID、MB_UNIT_ID

"MB_CL"(REQ       := "DB_Mb".req,
        MB_ADDR   := 1,           // Modbus 站地址 (unit id), 0..247
        MODE      := 0,           // 0=读 1=写
        DATA_ADDR := 40001,       // 保持寄存器起始 (Modbus 习惯地址; 40001=第一个 4x 寄存器)
        DATA_LEN  := 4,           // 读 4 个寄存器
        DATA_PTR  := "MBMap".holdRegs,
        DONE      => "DB_Mb".done,
        BUSY      => "DB_Mb".busy,
        ERROR     => "DB_Mb".err,
        STATUS    => "DB_Mb".status);

MB_SERVER 最小用法: 组态端口 (默认 502), MB_HOLD_REG 指向标准 DB 的 Word 数组, 客户端 (如 PC 上 pymodbus) 读 40001 起的寄存器即映射到数组 [0]:

"MB_SRV"(DISCONNECT := FALSE,
         MB_HOLD_REG := "MBMap".holdRegs);   // 供客户端读写的寄存器区

注意: MB_SERVER 在 1200 上主要支持保持寄存器 (读写 4x 区); 线圈/离散量 I/O 的支持情况随固件版本演进, 用前查帮助 (待核实)。地址习惯: DATA_ADDR 用 Modbus 习惯地址 (4xxxx), 而非 0 基协议地址 —— 这是与 pymodbus (read_holding_registers(0, ...) 用 0 基) 对接时最常见的差一错误源。

10.3 PROFINET IO controller (概念)

S7-1200 的集成网口默认可充当 PROFINET IO controller: 在 Devices & Networks > Network view 里拖入 IO 设备 (西门子 ET 200SP 远程 IO、带 GSD 文件的第三方设备, 或另一台组态为智能设备 (IO device) 的 CPU), 用网线/拓扑连接并分配 IO 设备名与 IP。之后不需要写任何通信代码: 远程 IO 的输入/输出被操作系统周期性 (PROFINET RT, 默认 1~2 ms 发送时钟, 组态可调) 映射进本地过程映像, SCL 里就像访问本地 I/O 一样 %I0.0 / 符号访问。类比: PROFINET GSD+组态 ≈ Linux device tree + 内核驱动, 你写的是应用层。诊断: 断线等产生机架/站故障 OB86 类事件 (S7-1200 上对应拔插/站故障事件, 优先级 6), 或在组态里为子模块设硬件中断。注意一代 S7-1200 只支持 RT, 不支持 IRT (G2 才支持, 见 02 章硬件对比)。

10.4 OPC UA Server (组态 + 客户端读取)

固件要求 (已核实): 一代 S7-1200 固件 V4.4 起内置 OPC UA Server (V4.5 增加方法调用与结构化数据访问); G2 亦内置 (需较新固件, 第三方资料称 v4.1 起, 待核实)。注意区别于 Web server: OPC UA 是面向 SCADA/IT 的数据接口, 不是网页。

组态步骤:

  1. CPU 属性 > OPC UA Server: 勾选激活; 选地址 (默认 4840); 配 Server interface (新增一个 server interface 块, 把要暴露的变量拖进去, 可分组/设读写权限); 安全策略按需 (最低 None 无加密, 生产建议至少 Basic256Sha256 签名加密并管好证书)。
  2. 编译下载。客户端测试 (Python, asyncua 或 UaExpert 图形客户端):
# pip install asyncua
from asyncua.sync import Client
with Client("opc.tcp://192.168.0.1:4840") as c:
    v = c.get_node("ns=3;s=\"DB_Opc\".\"speed\"")   # 命名空间与节点名以 TIA 生成的地址为准
    print(v.read_value())

限制: 一代 1200 的 UA Server 以读/写变量为主 (V4.5 前无方法与结构化类型); 订阅 (monitored items) 数量与采样率受 CPU 性能限制, 大量高频数据建议走别的路 (如 OUC)。

10.5 内置 Web server

CPU 属性 > Web server: 勾选激活, 配置用户与权限 (只读诊断用户/管理员), 浏览器访问 http://<cpu-ip>。能看到的标准页面 (以 CPU 手册 Web server 章为准):

  • 概览/标识页: CPU 状态、运行模式、IP、固件版本;
  • 诊断缓冲 (diagnostic buffer): 与 TIA 在线看到的一致, 现场 laptop-less 排障第一入口;
  • 模块状态、连接表、PROFINET 拓扑;
  • Data log 页面: 把 SCL 数据日志 (Data log 指令写入的 CSV) 直接下载回 Excel 分析;
  • 用户管理下可远程改 CPU 运行模式 (需相应权限)。

自定义页面: 用西门子 Web2PLS 工具 (SIOS 68011496 应用示例) 把 HTML+JS 处理成 WWW 数据块下载进 CPU, 页面通过 AWP 命令读写 PLC 变量 —— 相当于给 CPU 挂一个只读为主的轻量 dashboard。注意 PLCSIM 无 Web server, 须真机调试。

11. 库工程化: 项目库、全局库、第三方库

11.1 两级库与版本控制 (类比 npm + git)

概念TIA 机制类比
项目库 (Project library)随项目文件保存的私有库项目内 vendor/ 目录或 private registry
全局库 (Global library)独立 .al16/.al17/.al18/... 文件 (扩展名随 TIA 版本), 跨项目共享npm registry / git 子模块
库类型 (Library type)库中的 FB/FC/UDT/全局 DB 常量等, 带版本号, 可随修改递增版本、保留历史版本npm 包的 version + git tag
库副本 (Library copy)从库拖到项目里的一次使用实例; 项目内修改不回写库, 除非“作为类型上传回库“node_modules 里的副本 vs registry 里的包
类型版本调用项目中的调用可钉在旧版本, 或“更新调用“升到新版本package.json 钉版本 vs npm update

工程化要点:

  • 把稳定代码沉到库, 把项目差异留在调用参数: 库类型接口一旦被别的项目引用, 改接口必须升版本而不是原地改语义 —— 和 semver 的破坏性变更同理。
  • 版本更新链路: 修改项目中的库副本 → 右键“上传到库“并选升版本 (如 0.1 → 0.2) → 其他项目“更新调用“。全局库文件本身用 git 管理 (.al18 是二进制, diff 无意义, 但可追踪版本节点); 也有团队用“库项目 (library project)+ 库版本发行“工作流, 官方称 Type/version 一致性检查会在下载时校验版本漂移。
  • 全局库文件向后兼容性差: 高版本 TIA 可打开低版本库 (升级), 反向不行 —— 与 TIA 项目一样只能往上走。

11.2 导出/导入与第三方库

  • 全局库导出/导入即拷贝 .al1x 文件; 老项目块可用 “External source” (导出 .scl/.awl 源) 交换纯代码, UDT/FB/FC 均可 (类似把 C 源码 tar 出来)。
  • 西门子官方库: LBC (Library of General Functions, 通用函数库) 自带于 TIA, 含边沿检测、去抖、限幅、看门狗等通用封装, 新手可直接读它的 SCL 源码学风格。
  • OSCAT (Open Source Community for Automation Technology, oscat.de) 是工业自动化领域最大的开源块库 (数百个 FB/FC: 数学、控制、字符串、网络)。它原生面向 CODESYS, 社区有人移植到 TIA。移植注意:
    1. OSCAT 大量使用 ANY/POINTER/引用与内存映射, 而 S7-1200 接口不支持 POINTER/ANY/REF_TO (1500 才有) —— 这些块必须改写为 VARIANT 或显式数组参数才能在 1200 用;
    2. 数组变址下限: OSCAT 常按 0 基, TIA 数组上下限任意, 逐块核对;
    3. STRING 处理 (前 2 字节头 + 长度语义) 与 CODESYS 不同, 字符串块几乎都要手改;
    4. 优化的访问模型差异: OSCAT 里若用了绝对地址/AT 覆盖, 在优化块上不成立。 结论: OSCAT 在 1200 上“当算法参考实现“价值最大 (读伪代码级 SCL/ST 源码), 整库搬用前逐块审查。

12. 诊断类指令与系统功能一览

12.1 GET_ERROR / GET_ERR_ID

S7-1200 没有 OB121/OB122 (编程错误/I/O 访问错误组织块), 替代机制是块内局部错误处理:

// 在任意块中, 手动读最近一次块内错误 (在块内放置 GET_ERROR/GET_ERR_ID 指令即启用该块的局部错误处理——这不是块属性开关):
//   "DB_Diag".err : ErrorStruct (系统预定义结构体类型)
IF "DB_Diag".checkErr THEN
    GET_ERROR(ERR => "DB_Diag".err);        // 详细: 错误 ID + 块/偏移 + 区域信息
    GET_ERR_ID(ID => "DB_Diag".errId);      // 轻量: 只要 16 位错误码
END_IF;

语义: 这两个指令读走当前块最近发生的编程/访问错误 (读后即清), 相当于 errno + strerror 的组合; 不启用局部处理时, 错误写诊断缓冲且 CPU 保持 RUN。用它做“防御性日志点“比依赖 CPU STOP 更符合 24×7 连续运行的需求。

12.2 RD_SYS_T 与时间类

// 读系统时间 (DTL, 12 字节: 1970-01-01 ~ 2262):
RD_SYS_T(OUT => "DB_Clock".now);
// 写系统时间 (需权限, 慎用): WR_SYS_T(IN := "DB_Clock".set);
// 读本地时间 (按 CPU 时区组态偏移): RD_LOC_T(OUT => "DB_Clock".local);
// 计时基准: 注意 TIME_TCK() 是 S7-1500 专属指令, 在 1200 上编译报错!
// S7-1200 测耗时用 RUNTIME() (返回 LReal 秒; 内部计数器溢出时返回 <= 0, 该次读数应忽略):
"DB_Clock".elapsed := RUNTIME();

TIME 类型是 32 位有符号毫秒, 范围 ±24d20h31m23.647s; S7-1200 不支持 LTIME (1500 专属)。

12.3 常用系统功能一览表

指令类别用途备注
GET_ERROR / GET_ERR_ID诊断读块内错误 (1200 的 OB121 替代)见 12.1
RD_SYS_T / WR_SYS_T / RD_LOC_T时间系统时间读写 (DTL)WR 需相应权限
RUNTIME时间耗时测量, 返回 LReal 秒 (1200 可用)内部计数器溢出时返回 <= 0 应忽略; 注意 TIME_TCK() 为 S7-1500 专属, 1200 编译报错
RE_TRIGR看门狗重启当前扫描的监视计时当前周期须 < 10×最大扫描时间 (默认 150 ms, 1..6000 ms 可配)
STP控制把 CPU 切到 STOP调试/致命错误兜底
ATTACH / DETACH中断运行期把事件 (如硬件中断) 动态挂/摘到指定 OB比静态组态灵活
RDREC / WRREC诊断读写模块数据记录 (细粒度模块诊断/组态)参数复杂, 配合模块手册
RALRM中断在硬件中断 OB 里取中断详情1200 支持 (待核实: 依固件版本)
TSEND/TRCV/TCON/TDISCONOUC底层 socket 族见 10.1
TSEND_C/TRCV_COUC带连接管理的封装见 10.1
MB_CLIENT/MB_SERVERModbusModbus TCP 主/从见 10.2
CTRL_HSCHSC运行期改 HSC 参数读计数值用 %ID 直读
PID_Compact / PID_TempPID见第 7 节
Data log (LOG_GEN 等)数据记录写 CSV 到 CPU/存储卡, 经 Web server 下载PLCSIM 不仿真 Web
RTM / 运行小时计维护操作系统运行时计数器1200 支持情况以 CPU 属性/帮助为准 (待核实)

诊断缓冲的软件侧入口: TIA 在线诊断对话框; Web server 诊断页 (10.5); 或 GetSystemError 类库块 (待核实名称与 1200 可用性) —— 一般前两者已够用。

13. 收尾: 用库思维写 SCL

把本章所有内容压缩成可执行的工程习惯:

  1. 先搜库再写循环: LBC、PID_Compact、MC_xxx、MB_CLIENT 都是“官方 pip 包“; 自研一个通用函数前先 F1 搜指令卡与 LBC。90% 的“我写个工具 FB“在西门子生态里已有带测试的版本。
  2. 每个 FB 都按“库类型“标准写: 参数全命名化、接口一次定稳、语义内聚 (一个 FB 一件事)、不偷写全局 DB —— 今天的项目块就是明天的库类型。接口演进靠版本号, 不靠原地改语义。
  3. 状态机思维而不是阻塞思维: TSEND_C 的 DONE/BUSY/ERROR、MC 指令的 Busy/Done、PID 的 State, 全是显式状态机。写 SCL 时自己的功能也按“输入边沿触发 + 状态字 + 完成位“的模式封装, 调用者用起来就和官方库一致。
  4. 错误处理三件套: ENO/ERROR+STATUS 结构贯穿所有库块, 自己的库块也输出同构的 (done, busy, error, status) 四元组; 1200 上没有异常机制, “留痕不中断“是唯一正解, GET_ERROR 兜底。
  5. 数据接口要“布局自觉“: 模拟量按 0..27648 标尺 + 质量位; 通信缓冲用标准 DB + Word 对齐 + 字节序显式约定 (SWAP 在手); 跨界数据结构写成 UDT 进库, 别散落各 DB。
  6. 能组态的不编程: PID、HSC、运动控制、PROFINET 的参数都在组态/实例 DB 里, 代码只做“调用 + 状态机 + 边沿“; 组态参数变化要纳入版本管理 (项目文件即配置)。
  7. PLCSIM 是逻辑仿真器, 不是控制器仿真器: 时钟敏感 (PID) 与网络功能 (OUC/Modbus/OPC UA/Web) 不仿真或行为失真, 上真机前把这两类验证单列。
  8. F1 是唯一权威: 本章所有事实均可在 TIA 在线帮助 / S7-1200 manual collection 对应页复核; 标注 (待核实) 之处, 使用前请以你手上版本的 F1 为准。

参考来源:

  • S7-1200 手册集合 (TIA Portal V20 在线帮助): https://docs.tia.siemens.cloud/r/simatic_s7_1200_manual_collection_enus_20
  • SCL 指令参考 (S7-1200/S7-1500, V20): https://docs.tia.siemens.cloud/r/en-us/v20/scl-s7-1200-s7-1500
  • PID_Compact V2.x PLCSIM 仿真: https://docs.tia.siemens.cloud/r/en-us/v20/using-pid_compact-s7-1200-s7-1500/pid_compact-as-of-v2-s7-1200-s7-1500/simulating-pid_compact-as-of-v2-with-plcsim-s7-1200-s7-1500
  • PLCSIM 产品对比 (TCP/Web/OPC UA/Trace): https://docs.tia.siemens.cloud/r/simatic_s7_1500_et_200mp_manual_collection_enus_21/comprehensive-information/cpu-function-manuals/s7-plcsim-advanced/product-overview/s7-plcsim-products
  • MB_CLIENT 指令 (S7-1200 手册集合): https://docs.tia.siemens.cloud/r/simatic_s7_1200_manual_collection_enus_20/communication-processor-and-modbus-tcp/modbus-communication/modbus-tcp/modbus-tcp-instructions/mb_client-communicate-using-profinet-as-modbus-tcp-client-instruction
  • CTRL_HSC 指令 (S7-1200 手册集合): https://docs.tia.siemens.cloud/r/simatic_s7_1200_manual_collection_enus_20/technology-instructions/counting-high-speed-counters/legacy-ctrl_hsc-control-high-speed-counter-instruction/ctrl_hsc-control-high-speed-counter
  • S7-1200 运动控制功能手册 (109773400): https://support.industry.siemens.com/cs/attachments/109773400/s71200_motion_control_function_manual_en-US_en-US.pdf
  • 模拟量表示范围 (S7-1200 G2 手册集合, 数值对照): https://docs.tia.siemens.cloud/r/simatic_s7_1200_g2_manual_collection_zhcn_20/technical-specifications/analog-signal-boards-sbs/measurement-ranges-of-the-analog-inputs-for-voltage-and-current
  • OPC UA for S7-1200/1500 (固件 V4.4 要求): https://docs.tia.siemens.cloud/r/simatic_et_200bl_manual_collection_frfr_20/comprehensive-information/communication-function-manuals/communication/opc-ua-communication/what-you-need-to-know-about-opc-ua/opc-ua-for-s7-1200/s7-1500-cpus
  • 用户自定义 Web 页面应用示例 (SIOS 68011496): https://support.industry.siemens.com/cs/document/68011496/
  • Web server 概览 (S7-1200 手册集合): https://docs.tia.siemens.cloud/r/simatic_s7_1200_manual_collection_frfr_20/web-server/web-server-overview
  • PID_Temp 多区控制应用例 (109740463): https://cache.industry.siemens.com/dl/files/463/109740463/att_993000/v1/109740463_PidTemp_MultiZone_DOC_V11_en.pdf
  • MB_CLIENT DATA_PTR 标准库要求 (SIOS 91696622): https://support.industry.siemens.com/cs/mdm/91696622
  • 4-20 mA 换算数值解说 (第三方对照): https://industrialmonitordirect.com/blogs/knowledgebase/siemens-s7-1200-analog-input-4-20ma-scaling-explained