ST(Structured Text)是IEC 61131-3标准定义的高级文本编程语言,广泛用于PLC(可编程逻辑控制器)开发,尤其在西门子S7-1200/1500、倍福TwinCAT、Codesys平台中作为核心编程语言之一。它语法接近Pascal,支持结构化表达、函数调用、条件判断和循环,是实现复杂控制逻辑(如运动同步、PID优化、数据预处理)的首选。其中,数据类型是ST程序的基石——类型错误会导致编译失败、运行时崩溃或隐性数值溢出。本文聚焦四种最基础、最高频使用的内置数据类型:BOOL、INT、REAL、STRING,逐项说明其定义本质、内存占用、取值范围、实际约束、典型误用及安全写法,所有内容均基于IEC 61131-3:2013标准,并结合主流PLC厂商(西门子、倍福、施耐德)的工程实践验证。
一、BOOL:布尔量——不是“真假”,而是“电平状态”
BOOL 是ST中最轻量级的数据类型,仅占用 1个字节(8位)中的1位(bit),但PLC底层按字节对齐分配,因此实际内存占用为1字节(BYTE),其余7位被忽略或填充为0。
定义本质:
BOOL 不表示抽象的“真/假”逻辑概念,而是直接映射物理输入/输出模块的离散电平状态:
TRUE对应高电平(通常为24 V DC,取决于硬件配置);FALSE对应低电平(通常为0 V)。
取值范围:
仅两个确定值:
FALSE(等价于0)TRUE(等价于1)
⚠️ 重要警告:BOOL 不支持任何数值运算。以下写法全部非法:
b1 := b2 + b3; // 编译错误:操作符 '+' 不支持 BOOL 类型
b1 := 5 > 3; // 合法:关系表达式结果为 BOOL
i1 := INT(b1); // 合法:显式类型转换(b1=TRUE → i1=1;b1=FALSE → i1=0)
典型误用与纠正:
- ❌ 误将
BOOL当作计数器:motor_run_count := motor_run_count + 1;(若motor_run_count声明为BOOL,则永远只能是 0 或 1) - ✅ 正确做法:声明为
INT或UDINT,并用BOOL信号触发计数:IF start_button THEN motor_run_count := motor_run_count + 1; END_IF
安全写法口诀:
读取物理点用
BOOL,参与计算转整数,禁止直接加减乘除。
二、INT:有符号整数——16位世界的边界与陷阱
INT 是带符号16位整数,符合二进制补码规则,占用 2个字节(16位)。
取值范围:
$$
-32768 \leq \text{INT} \leq 32767
$$
即最小值为 $-2^{15}$,最大值为 $2^{15} - 1$。
内存布局示例(以值 32767 为例):
高位字节(MSB):01111111(十进制 127)
低位字节(LSB):11111111(十进制 255)
合起来:01111111 11111111(16位二进制)
关键约束:
- 溢出行为未定义(厂商实现不同):西门子默认静默截断(32768 → -32768),倍福默认保持上限 32767,施耐德可能触发运行时错误。
- 不可用于浮点运算:
3.14 * INT_VAR需先转REAL。
典型误用与纠正:
- ❌ 直接赋值超限常量:
temp := 50000;(编译报错:“常量超出 INT 范围”) - ✅ 安全写法:使用类型后缀或显式转换:
temp := 50000 : INT; // 强制类型声明(部分平台支持) temp := INT(50000); // 编译期截断为 -15536(补码解释)→ 高风险! temp := 50000 MOD 65536; // 手动模运算 → 仍得 -15536,不推荐 - ✅ 推荐方案:改用更大范围类型:
temp : DINT; // 32位整数,范围 -2147483648 ~ 2147483647 temp := 50000; // 无警告,无溢出
安全写法口诀:
温度用
INT够用,计数超万换DINT,乘除前先转REAL,溢出检查靠LIMIT函数。
三、REAL:单精度浮点数——精度、舍入与比较的雷区
REAL 是IEEE 754标准的32位单精度浮点数,占用 4个字节,由1位符号位、8位阶码、23位尾数构成。
取值范围与精度:
- 可表示绝对值范围约 $1.18 \times 10^{-38}$ 至 $3.4 \times 10^{38}$;
- 有效精度仅约6~7位十进制数字;
- 无法精确表示多数十进制小数,例如
0.1在内存中存储为近似值0.10000000149011612。
致命陷阱:浮点比较
以下代码永远为假:
IF temperature = 100.0 THEN // 危险!因 100.0 可能被存为 99.999999 或 100.000001
alarm := TRUE;
END_IF
✅ 正确写法(引入容差 EPSILON):
VAR
EPSILON : REAL := 0.001; // 根据工艺要求设定(如温度±0.1℃)
END_VAR
IF ABS(temperature - 100.0) < EPSILON THEN
alarm := TRUE;
END_IF
其他高危操作:
- 累加误差:1000次
sum := sum + 0.1后,sum可能 ≠100.0; - 类型混合运算:
INT_VAR / 3得整数结果(如5/3 = 1),而REAL_VAR / 3.0才得浮点结果(5.0/3.0 ≈ 1.6666667)。
安全写法口诀:
测量值用
REAL,比较必加ABS(x-y)<ε,整数除法显式添.0,高精度需求上LREAL(64位)。
四、STRING:字符序列——长度、编码与截断的硬规则
STRING 是定长字符数组,声明时必须指定最大长度:STRING[32] 表示最多容纳32个字符(含结尾空字符 \0)。
内存结构:
- 第1字节:当前实际长度(
BYTE类型,范围 0~255); - 后续字节:UTF-8编码的字符数据(PLC普遍不支持Unicode,实际为ASCII扩展);
- 总内存占用 =
1 + n字节(n为声明长度)。
取值范围与行为:
- 最大声明长度:厂商限制不同,西门子S7-1500最大
STRING[32767],Codesys常见上限STRING[255]; - 赋值超长时自动截断,不报错:
s1 : STRING[5]; s1 := 'Hello World'; // 实际存储为 'Hello'(前5字符),长度字节 = 5 - 空字符串
''长度为0,非空格;首字符为空格' '是合法字符。
关键操作规范:
- 连接:
s3 := s1 + s2;(结果长度 =LEN(s1) + LEN(s2),超限则截断); - 截取:
s2 := MID(s1, 2, 3);(从第2位起取3字符,索引从1开始); - 查找:
pos := FIND(s1, 'ab');(返回首次出现位置,未找到返回0)。
典型误用与纠正:
- ❌ 用
=比较含空格字符串:IF s1 = 'ABC ' THEN(末尾空格易被忽略,导致逻辑失效); - ✅ 正确做法:用
TRIM()去空格或明确比对长度:IF (LEN(s1) = 3) AND (MID(s1, 1, 3) = 'ABC') THEN - ❌ 动态拼接大量字符串(如日志记录)导致性能骤降;
- ✅ 替代方案:用
CONCAT()函数或预分配大缓冲区+指针操作。
安全写法口诀:
声明长度留余量,赋值前用
TRIM清空格,连接看总长防截断,日志拼接用CONCAT。
五、类型混用对照表:何时转换?如何安全转换?
当不同数据类型需协同工作时,隐式转换规则有限,强烈建议始终使用显式转换函数,避免平台差异。
| 源类型 | 目标类型 | 推荐转换函数 | 安全要点 |
|---|---|---|---|
BOOL |
INT |
INT(x) |
TRUE → 1, FALSE → 0 |
INT |
REAL |
REAL(x) |
精度无损(整数可精确表示) |
REAL |
INT |
INT(ROUND(x)) |
禁用 TRUNC 或强制类型转换,否则 2.9 → 2(非四舍五入) |
INT |
STRING |
STRING_INT(x) 或 INT_TO_STRING(x) |
厂商函数名不同,勿用 STRING(x)(可能返回乱码) |
STRING |
INT |
STRING_TO_INT(s) |
若 s 含非数字字符,返回0且不报错 → 必须先 FIND 校验 |
示例:安全解析字符串温度值
VAR
temp_str : STRING[10] := '25.3';
temp_real : REAL;
is_valid : BOOL;
END_VAR
// 步骤1:检查是否含小数点
is_valid := FIND(temp_str, '.') <> 0;
// 步骤2:转换(厂商函数,此处以Codesys为例)
IF is_valid THEN
temp_real := STRING_TO_REAL(temp_str);
ELSE
temp_real := REAL(STRING_TO_INT(temp_str));
END_IF
六、实战避坑清单(工程师每日核查)
-
声明即初始化:所有变量声明后立即赋初值,杜绝未定义行为
b_start : BOOL := FALSE; i_counter : DINT := 0; r_setpoint : REAL := 100.0; s_msg : STRING[20] := ''; -
报警阈值用
REAL+EPSILON:
IF ABS(actual_temp - set_temp) > 0.5 THEN(非>=) -
计数器一律
DINT:哪怕当前只到100,预留升级空间 -
字符串操作前必
LEN()校验:
IF LEN(s_input) > 0 THEN process(s_input); END_IF -
禁止跨类型赋值无转换:
r_value := i_raw_data;→ 改为r_value := REAL(i_raw_data); -
调试时用
FORMAT函数可视化:
s_debug := FORMAT('Temp=%f, Count=%d', r_temp, i_count);
类型选择决策树:
- 输入/输出开关量 →
BOOL - 温度、压力、电压(带小数) →
REAL - 计数、索引、状态码 →
DINT(优先于INT) - 报警信息、设备ID、配方名称 →
STRING[n](n ≥ 最大预期长度 × 1.5)

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