4. 数据类型与变量

在西门子PLC结构化编程中,数据类型决定了变量在内存中占用的空间、可表示的数值范围以及允许参与的运算;变量则决定了这些数据在程序中的可见范围与生命周期。很多初学者写程序时只关注“逻辑对不对”,却忽略了数据类型与变量作用域,结果出现数值溢出、精度丢失、块间数据冲突、调试时找不到变量来源等问题。本节把数据类型、变量分类、变量表与符号寻址放在一起讲,是因为它们共同构成了PLC程序的“数据地基”。

一、基本数据类型:PLC数据的原子单位

基本数据类型是不可再分的最小数据单位,TIA Portal中常用的基本类型如下表所示。理解它们的关键不是死记硬背,而是抓住三个维度:位宽、取值范围、典型用途。

数据类型 位宽 取值范围/表示 典型用途
Bool1位TRUE / FALSE按钮、限位、线圈状态
Byte8位0 ~ 255原始字节、ASCII字符
Word16位0 ~ 65535状态字、位组合
Int16位-32768 ~ 32767计数器、中小范围整数
DInt32位-2^31 ~ 2^31-1累计产量、大整数
Real32位±1.18e-38 ~ ±3.4e38模拟量、温度、PID
Time32位T#-24d20h31m23s648ms ~ T#24d20h31m23s647ms延时、定时器设定
Char8位单个ASCII字符字符串组成元素
常见误区:用Int存储模拟量原始值。例如模拟量模块输出0~27648对应0~100%,若用Int接收,27648未溢出,但一旦做乘法运算(如27648×100)就会超出Int范围,结果变成负数。正确做法是先将Int转换为DInt或Real再运算。

二、复杂数据类型:把相关数据组织在一起

复杂数据类型由基本类型组合而成,常见的有:

  • String:字符串,最大长度在声明时指定,如String[20]。
  • Array:同类型元素的数组,如Array[1..10] of Int,适合批量处理。
  • Struct:结构体,可包含不同类型成员,如电机结构体包含启动、停止、故障、电流等。
  • UDT(用户自定义数据类型):在PLC数据类型中定义,可被多个DB复用,是结构化编程的核心工具。
  • DTL:日期时间结构,包含年、月、日、时、分、秒、毫秒。

复杂类型的价值在于“一次定义、多处复用”。例如定义一个UDT名为Motor,包含Start : Bool、Stop : Bool、Fault : Bool、Current : Real,然后在DB中声明Motor1 : Motor、Motor2 : Motor,程序里用"DB".Motor1.Current访问,结构清晰且不易出错。

三、全局变量与局部变量:作用域决定可见性

全局变量在DB、M存储区或I/Q区中定义,整个项目所有块均可访问;局部变量在FC、FB的接口区或临时变量区定义,只在当前块内有效。FB的静态变量(Static)会随背景DB保持,而临时变量(Temp)每次调用后不保留。

// FB接口区示例
VAR_INPUT
    Start : Bool;      // 输入参数
    Setpoint : Real;   // 设定值
END_VAR
VAR_OUTPUT
    Running : Bool;    // 输出状态
END_VAR
VAR_IN_OUT
    Actual : Real;     // 引用传递
END_VAR
VAR
    Timer1 : TON;      // 静态变量,保持
END_VAR
VAR_TEMP
    tempCalc : Real;   // 临时变量,不保持
END_VAR
选择原则:只在单个块内使用的中间结果用Temp;需要跨周期保持的用Static;需要与其他块交换的用In/Out/InOut;需要全局共享的才放到全局DB。滥用全局变量会导致块间耦合严重,调试时难以定位修改来源。

四、变量表与符号寻址:让程序可读可维护

符号寻址是用有意义的名称代替绝对地址。例如用"Motor1_Start"代替I0.0,用"DB_Motor".Speed代替DB1.DBD4。TIA Portal中通过PLC变量表定义符号名、地址、数据类型和注释,程序中使用符号名访问。

符号寻址的优势:程序可读性大幅提升;地址变更时只需修改变量表,无需逐条改程序;配合交叉引用可快速定位变量使用位置。但要注意:符号名必须唯一,且不能与关键字冲突;修改符号名后需重新编译;绝对地址访问在优化块中受限,建议优先使用符号寻址。

数据类型、变量作用域与符号寻址关系图 基本数据类型 Bool / Byte / Word Int / DInt / Real Time / Char 位宽 / 范围 / 用途 复杂数据类型 String / Array Struct / UDT DTL 日期时间 组合复用 变量作用域 全局变量 DB / M / I / Q 局部变量 In/Out/Static/Temp FB背景DB保持静态变量 Temp 每次调用后释放 变量表与符号寻址 符号名 "Motor1_Start" 绝对地址 I0.0 / DB1.DBX0.0 数据类型 Bool / Real / UDT 程序中使用符号名访问 → 编译时映射到绝对地址 → 地址变更只改变量表 交叉引用可追溯每个变量的使用位置,提升调试效率

五、案例:电机控制块的数据设计

假设要编写一个电机控制FB,需求是:接收启动/停止信号,输出运行状态,累计运行时间,故障时记录故障代码。数据设计如下:

// UDT: MotorData
Start      : Bool;    // 启动命令
Stop       : Bool;    // 停止命令
Fault      : Bool;    // 故障标志
RunTime    : DInt;    // 累计运行秒数
FaultCode  : Int;     // 故障代码

// FB接口
VAR_INPUT
    Cmd : MotorData;  // 使用UDT作为输入
END_VAR
VAR_OUTPUT
    Status : MotorData;
END_VAR
VAR
    tonRun : TON;     // 静态定时器
END_VAR
VAR_TEMP
    tempSec : DInt;   // 临时计算
END_VAR

这样设计后,多个电机只需在DB中声明多个MotorData实例,FB代码完全复用。调试时通过变量表监控"DB_Motor".Motor1.Status.RunTime即可,无需记忆绝对地址。

六、复盘与检查清单

  • 模拟量运算前是否已从Int转换为Real或DInt?
  • 需要跨周期保持的数据是否放在Static而非Temp?
  • UDT是否已定义并复用于多个设备?
  • 变量表中符号名是否唯一、无关键字冲突、注释完整?
  • 是否优先使用符号寻址而非绝对地址?
  • 修改符号名后是否重新编译并检查交叉引用?
核心结论:基本类型决定“能存什么”,复杂类型决定“怎么组织”,变量作用域决定“谁能看见”,变量表与符号寻址决定“怎么访问”。四者配合,才能写出结构清晰、易于调试和维护的PLC程序。