在 S7-1200 / S7-1500 PLC 的 TIA Portal(博途)编程中,STRING 类型常用于人机交互、设备通信、日志记录等场景。当你需要判断两个字符串是否相等时,直接使用 = 运算符比较两个 STRING 变量,表面上看似可行,实则隐藏严重逻辑漏洞——它可能在绝大多数情况下“碰巧”返回正确结果,但在关键工况下彻底失效,导致设备误动作、连锁保护失效或诊断信息丢失。
根本原因在于:STRING 不是“纯文本”,而是一个带长度前缀的结构体。其内存布局包含三部分:
- 第 0 字节:最大长度(
MAX_LEN),占 1 字节; - 第 1 字节:当前实际长度(
LEN),占 1 字节; - 第 2 字节起:最多
MAX_LEN个字节的字符数据(DATA[ ]),每个字符为CHAR(ASCII,1 字节)。
例如,声明 MyStr : STRING[10];,则该变量固定占用 1 + 1 + 10 = 12 字节。若你赋值 'ABC',内存实际存储为:
| 偏移 | 值(十六进制) | 含义 |
|---|---|---|
| 0 | 0A |
MAX_LEN = 10 |
| 1 | 03 |
LEN = 3 |
| 2 | 41 |
'A' |
| 3 | 42 |
'B' |
| 4 | 43 |
'C' |
| 5–11 | 00(共7个) |
未使用的填充字节(空字符 \0) |
注意:PLC 不自动在末尾添加 C 风格终止符 \0,但未赋值区域保持为 0x00。
为什么 = 比较会出错?
= 是字节级全内存比较运算符。它逐字节比对两个 STRING 变量所占全部 12 字节(以 STRING[10] 为例)是否完全一致。
这意味着:
- 若
Str1 := 'ABC';→LEN=3,后 7 字节为00; - 若
Str2 := 'ABC';但此前被其他逻辑写入过(例如先赋'XYZ'再改'ABC'),其LEN变为3,但第 5–11 字节可能仍残留旧值(如00 00 00 00 00 00 00或非零垃圾值); - 更典型的是通信接收场景:
StrRx来自 MODBUS TCP 或 S7 通信,其内容由外部设备填充。某些设备只写入LEN和前 N 个字符,剩余字节不初始化、不置零。此时StrRx的后缀可能为随机值(如FF FF 00 1A 8B...); - 此时
Str1 = Str2返回FALSE,尽管两者语义上完全相同(都是'ABC')。
换句话说:= 比较的是“内存镜像是否一模一样”,而非“用户意图表达的文本内容是否相同”。
正确解法:必须使用 COMPARE 指令
COMPARE(函数块 EQ_STRING 或指令 COMPARE)是专为字符串设计的语义级比较工具。它严格按以下三步执行:
- 检查
LEN值是否相等; - 若
LEN相同,则仅比较前LEN个DATA字节; - 忽略
MAX_LEN字段及超出LEN的所有字节。
因此,无论 StrA 和 StrB 的尾部填充是 00、FF 还是随机噪声,只要 LEN 相同且前 LEN 个字符一致,COMPARE 就返回 TRUE。
实操步骤:在 TIA Portal 中正确比较 STRING
1. 确认数据类型与声明方式
确保变量明确指定长度,避免隐式 STRING(默认 STRING[254],浪费内存且易引发越界):
// ✅ 推荐:显式声明合理长度
ProductName : STRING[32];
ErrorCode : STRING[16];
// ❌ 避免:无长度限制(实际仍为254,但可读性差)
FaultMsg : STRING;
2. 在 LAD/FBD 中调用 COMPARE 指令
- 在梯形图中,从指令库拖入
COMPARE指令(位于 “Extended Instructions > String Operations > COMPARE”); - 连接
IN1和IN2为待比较的STRING变量; LEN输入端留空(自动取IN1.LEN)或指定比较长度(如只比前5位);- 输出
OUT为BOOL:TRUE表示相等。
3. 在 SCL 中调用 EQ_STRING 函数
SCL 提供标准函数 EQ_STRING(IN1 := ..., IN2 := ...),返回 BOOL:
IF EQ_STRING(IN1 := ProductName, IN2 := 'Motor_123') THEN
// 执行匹配动作
StartMotor := TRUE;
END_IF;
⚠️ 注意:
EQ_STRING会自动忽略MAX_LEN和尾部填充,仅基于LEN和有效字符比较。
4. 处理大小写敏感问题
COMPARE 和 EQ_STRING 默认区分大小写。若需忽略大小写,必须预处理:
// 方法1:转大写后比较(使用 UPPER 函数)
IF EQ_STRING(IN1 := UPPER(ProductName), IN2 := 'MOTOR_123') THEN ...
// 方法2:转小写(LOWER)
IF EQ_STRING(IN1 := LOWER(RecCmd), IN2 := 'reset') THEN ...
5. 验证比较结果的可靠性
编写测试用例,覆盖边界场景:
| 测试项 | Str1 值 | Str2 值 | = 结果 |
COMPARE 结果 |
说明 |
|---|---|---|---|---|---|
| 纯相等 | 'OK' |
'OK' |
TRUE |
TRUE |
基础通过 |
| LEN 相同但尾部脏 | 'OK'(尾部含 FF) |
'OK'(尾部 00) |
FALSE |
TRUE |
= 失败,COMPARE 正确 |
| LEN 不同 | 'A'(LEN=1) |
'A '(LEN=2,含空格) |
FALSE |
FALSE |
语义不同,两者均正确 |
| 通信残留 | 'ERR'(LEN=3,字节5=0xAA) |
'ERR'(LEN=3,字节5=0x00) |
FALSE |
TRUE |
典型通信问题 |
常见陷阱与规避方案
❌ 陷阱1:用 MOVE 复制 STRING 后直接 =
MOVE(IN := 'ABC', OUT := StrBuf); // MOVE 只复制 LEN 和 DATA,不保证尾部清零
IF StrBuf = 'ABC' THEN ... // 可能失败!因 StrBuf 尾部残留旧值
✅ 解决:使用 FILL 清零整个变量,或改用 COMPARE:
FILL(OUT := StrBuf, LEN := SIZEOF(StrBuf), DATA := 0); // 清零全部12字节
MOVE(IN := 'ABC', OUT := StrBuf);
IF EQ_STRING(IN1 := StrBuf, IN2 := 'ABC') THEN ... // 安全
❌ 陷阱2:从 CHAR 数组构造 STRING 时未设置 LEN
ARRAY[0..9] OF CHAR := ['H','E','L','L','O']; // 未设 LEN
MyStr := ARRAY_TO_STRING(ARR := CharArr, LEN := 5); // ✅ 必须显式传 LEN
// 若漏掉 LEN 参数,MyStr.LEN = 0 → 比较永远为 FALSE
❌ 陷阱3:跨设备通信时假设对方会清零
MODBUS ASCII 帧、串口协议、OPC UA 字符串字段常不保证填充区为 0x00。务必以 COMPARE 为唯一可信比较手段。
性能与资源影响说明
COMPARE 指令执行时间为 O(n),其中 n 是较短字符串的 LEN。对于 STRING[32],最坏情况仅比较 32 字节,耗时远低于 1μs(S7-1500 CPU)。相比因逻辑错误导致的停机损失,此开销可忽略。
内存方面:COMPARE 为无状态指令,不占用额外 DB 或全局存储。
替代方案对比(不推荐,仅作技术澄清)
| 方法 | 是否安全 | 原理 | 缺陷 |
|---|---|---|---|
= 运算符 |
❌ 不安全 | 全内存字节比较 | 依赖尾部填充一致性,不可控 |
LEFT() 提取再比 |
⚠️ 伪安全 | LEFT(Str, 3) = 'ABC' |
若 Str.LEN < 3,LEFT 返回空字符串,比较失效 |
LEN() + MID() 循环比 |
✅ 安全但冗余 | 手动逐字符比对 | 代码臃肿,易出错,性能差 |
COMPARE / EQ_STRING |
✅ 唯一推荐 | 标准化语义比较 | 无缺陷,TIA 官方认证方案 |
最终检查清单(部署前必做)
- 所有
STRING比较操作均已替换为COMPARE指令或EQ_STRING函数; - 无任何
STRING变量直接参与=、<>、>=等关系运算; - 通信接收的
STRING变量,在首次比较前已确认LEN有效性(如LEN > 0 AND LEN <= MAX_LEN); - 大小写敏感需求已通过
UPPER/LOWER显式处理; - 测试用例覆盖
LEN不同、尾部填充随机、空字符串、全满字符串五种场景。
直接使用 = 比较 STRING,等于把逻辑安全押注在内存初始化的偶然性上。而 COMPARE 是唯一将“字符串相等”这一人类语义,精确映射到 PLC 字节世界的工业级契约。

暂无评论,快来抢沙发吧!