ST数据类型详解:BOOL、INT、REAL、STRING的定义与范围

发布于 2026-03-18 05:53:39 · 浏览 4 次 · 评论 0 条

ST(Structured Text)是IEC 61131-3标准定义的高级文本编程语言,广泛用于PLC(可编程逻辑控制器)开发,尤其在西门子S7-1200/1500、倍福TwinCAT、Codesys平台中作为核心编程语言之一。它语法接近Pascal,支持结构化表达、函数调用、条件判断和循环,是实现复杂控制逻辑(如运动同步、PID优化、数据预处理)的首选。其中,数据类型是ST程序的基石——类型错误会导致编译失败、运行时崩溃或隐性数值溢出。本文聚焦四种最基础、最高频使用的内置数据类型:BOOLINTREALSTRING,逐项说明其定义本质、内存占用、取值范围、实际约束、典型误用及安全写法,所有内容均基于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)
  • ✅ 正确做法:声明为 INTUDINT,并用 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

六、实战避坑清单(工程师每日核查)

  1. 声明即初始化:所有变量声明后立即赋初值,杜绝未定义行为

    b_start : BOOL := FALSE;  
    i_counter : DINT := 0;  
    r_setpoint : REAL := 100.0;  
    s_msg : STRING[20] := '';  
  2. 报警阈值用 REAL + EPSILON
    IF ABS(actual_temp - set_temp) > 0.5 THEN(非 >=

  3. 计数器一律 DINT:哪怕当前只到100,预留升级空间

  4. 字符串操作前必 LEN() 校验
    IF LEN(s_input) > 0 THEN process(s_input); END_IF

  5. 禁止跨类型赋值无转换
    r_value := i_raw_data; → 改为 r_value := REAL(i_raw_data);

  6. 调试时用 FORMAT 函数可视化
    s_debug := FORMAT('Temp=%f, Count=%d', r_temp, i_count);


类型选择决策树

  • 输入/输出开关量 → BOOL
  • 温度、压力、电压(带小数) → REAL
  • 计数、索引、状态码 → DINT(优先于 INT
  • 报警信息、设备ID、配方名称 → STRING[n](n ≥ 最大预期长度 × 1.5)

评论 (0)

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

扫一扫,手机查看

扫描上方二维码,在手机上查看本文