4. 数据类型与变量
在西门子PLC结构化编程中,数据类型决定了变量在内存中占用的空间、可表示的数值范围以及允许参与的运算;变量则决定了这些数据在程序中的可见范围与生命周期。很多初学者写程序时只关注“逻辑对不对”,却忽略了数据类型与变量作用域,结果出现数值溢出、精度丢失、块间数据冲突、调试时找不到变量来源等问题。本节把数据类型、变量分类、变量表与符号寻址放在一起讲,是因为它们共同构成了PLC程序的“数据地基”。
一、基本数据类型:PLC数据的原子单位
基本数据类型是不可再分的最小数据单位,TIA Portal中常用的基本类型如下表所示。理解它们的关键不是死记硬背,而是抓住三个维度:位宽、取值范围、典型用途。
| 数据类型 | 位宽 | 取值范围/表示 | 典型用途 |
|---|---|---|---|
| Bool | 1位 | TRUE / FALSE | 按钮、限位、线圈状态 |
| Byte | 8位 | 0 ~ 255 | 原始字节、ASCII字符 |
| Word | 16位 | 0 ~ 65535 | 状态字、位组合 |
| Int | 16位 | -32768 ~ 32767 | 计数器、中小范围整数 |
| DInt | 32位 | -2^31 ~ 2^31-1 | 累计产量、大整数 |
| Real | 32位 | ±1.18e-38 ~ ±3.4e38 | 模拟量、温度、PID |
| Time | 32位 | T#-24d20h31m23s648ms ~ T#24d20h31m23s647ms | 延时、定时器设定 |
| Char | 8位 | 单个ASCII字符 | 字符串组成元素 |
二、复杂数据类型:把相关数据组织在一起
复杂数据类型由基本类型组合而成,常见的有:
- 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
四、变量表与符号寻址:让程序可读可维护
符号寻址是用有意义的名称代替绝对地址。例如用"Motor1_Start"代替I0.0,用"DB_Motor".Speed代替DB1.DBD4。TIA Portal中通过PLC变量表定义符号名、地址、数据类型和注释,程序中使用符号名访问。
符号寻址的优势:程序可读性大幅提升;地址变更时只需修改变量表,无需逐条改程序;配合交叉引用可快速定位变量使用位置。但要注意:符号名必须唯一,且不能与关键字冲突;修改符号名后需重新编译;绝对地址访问在优化块中受限,建议优先使用符号寻址。
五、案例:电机控制块的数据设计
假设要编写一个电机控制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是否已定义并复用于多个设备?
- 变量表中符号名是否唯一、无关键字冲突、注释完整?
- 是否优先使用符号寻址而非绝对地址?
- 修改符号名后是否重新编译并检查交叉引用?