4、数据类型与变量声明:基本数据类型、用户自定义类型(UDT)与全局数据块(DB)的设计
说到PLC编程,数据类型和变量声明这事儿,看着基础,但真容易栽跟头。我见过不少项目,程序跑着跑着突然出幺蛾子,查到最后发现是数据类型用错了。嗯,今天咱们就把这块彻底捋清楚。
4.1 基本数据类型:你得认识的几个老伙计
基本数据类型,说白了就是PLC能直接认识的最基础的数据格式。每个PLC品牌大同小异,但核心就那几种。
| 数据类型 | 关键字 | 位宽 | 取值范围 | 我常用的场景 |
|---|---|---|---|---|
| 布尔 | BOOL | 1位 | TRUE / FALSE | 启停信号、报警标志 |
| 字节 | BYTE | 8位 | 0 ~ 255 | 通信原始数据、状态字 |
| 字 | WORD | 16位 | 0 ~ 65535 | 模拟量原始值、计数器值 |
| 双字 | DWORD | 32位 | 0 ~ 4294967295 | 时间戳、累加计数值 |
| 整数 | INT | 16位 | -32768 ~ 32767 | 普通计数、小范围数值 |
| 双整数 | DINT | 32位 | -2147483648 ~ 2147483647 | 大范围计数、位置值 |
| 实数 | REAL | 32位 | ±1.18e-38 ~ ±3.40e+38 | 温度、压力、PID参数 |
| 时间 | TIME | 32位 | T#0ms ~ T#24d20h31m23s647ms | 定时器设定值、延时时间 |
我的小习惯: 能用INT别用DINT,能不用REAL就别用。为啥?占内存是一回事,关键是扫描周期会变长。我曾经在一个老项目里,把所有REAL都改成了DINT乘以1000来处理小数,扫描时间直接降了15%。
4.2 用户自定义类型(UDT):把散装零件打包成工具箱
你想想看,一个电机控制需要多少数据?启停命令、运行状态、电流值、转速、故障码……如果每个都单独声明变量,那程序看着就像散落一地的螺丝钉。UDT就是干这个的——把相关数据打包成一个整体。
我个人习惯,每个设备类型都先定义一个UDT。比如一个泵站,我会这样定义:
TYPE "UDT_Pump"
VERSION : 0.1
STRUCT
// 控制命令
Cmd_Start : BOOL; // 启动命令
Cmd_Stop : BOOL; // 停止命令
Cmd_Reset : BOOL; // 故障复位
// 状态反馈
Stat_Running : BOOL; // 运行中
Stat_Fault : BOOL; // 故障状态
Stat_Ready : BOOL; // 就绪状态
// 过程值
Current : REAL; // 电机电流 (A)
Speed : INT; // 转速 (RPM)
Temp : REAL; // 绕组温度 (°C)
// 参数设置
Set_Overload : REAL; // 过载保护值 (A)
Set_MaxTemp : REAL; // 最高温度 (°C)
// 运行统计
RunHours : TIME; // 累计运行时间
StartCount : DINT; // 启动次数
END_STRUCT
END_TYPE
重点来了: UDT定义好后,你在全局DB里声明一个数组,比如PumpArray : ARRAY[1..10] OF "UDT_Pump",瞬间就搞定了10台泵的数据管理。代码量直接砍半,而且可读性翻倍。
4.3 全局数据块(DB):你的数据仓库
全局DB,说白了就是程序里所有功能块都能访问的公共数据区。我刚开始做项目时,喜欢把变量到处乱放,结果查个信号要翻遍整个程序。后来学乖了——所有全局数据,统一放在DB里管理。
设计全局DB,我一般遵循三个原则:
- 按功能分区:一个DB里放同一类设备的数据。比如DB_Pumps放所有泵的数据,DB_Valves放所有阀的数据。
- 预留扩展空间:每个数组多预留20%的容量。我吃过亏,项目做到一半甲方说要加3台设备,结果DB容量不够,改起来那叫一个痛苦。
- 命名规范统一:DB名用大写+下划线,变量名用驼峰。比如DB_MotorCtrl,里面变量叫StartCmd、CurrentValue。
// 一个典型的全局DB设计示例
DATA_BLOCK "DB_PumpControl"
STRUCT
// 泵组数据
Pumps : ARRAY[1..12] OF "UDT_Pump"; // 12台泵的数据
// 公共参数
Common_SupplyPressure : REAL; // 母管压力
Common_FlowRate : REAL; // 总流量
// 系统状态
Sys_AutoMode : BOOL; // 自动模式
Sys_AlarmActive : BOOL; // 系统报警
// 预留扩展
Reserve_Word : ARRAY[1..50] OF WORD; // 预留50个字
END_STRUCT
END_DATA_BLOCK
避坑指南: 我曾经在一个项目中,把DB的起始地址设成了0,结果和系统保留区冲突了,PLC一上电就报错。查了整整半天才发现。所以,自定义DB的起始地址,建议从100开始,给系统区留够空间。
4.4 实战中的数据类型选择策略
选数据类型这事儿,看着简单,但真到现场就发现坑不少。我总结了几条经验:
- BOOL别省着用:一个BOOL只占1位,但西门子PLC里是按字节对齐的。所以别为了省那几位把多个BOOL塞到一个字节里,调试时你会疯掉的。
- REAL慎用:能转成INT就别用REAL。比如温度25.5°C,我习惯存成255(INT),显示时再除以10。这样既省内存,运算也快。
- TIME类型别乱用:TIME在PLC里是32位有符号数,单位是毫秒。如果你只需要秒级精度,用INT存秒数更直观。
- 数组下标从1开始:PLC里数组下标通常从1开始,和高级语言不一样。写代码时注意别搞混了,否则访问越界直接报错停机。
我的一个实用技巧: 在定义UDT时,把BOOL类型变量放在最前面,WORD、INT放中间,REAL和DINT放最后。这样内存对齐更高效,PLC处理起来也更快。别小看这个顺序,在大型项目里,能省下不少扫描时间。
嗯,数据类型和变量声明这块,说白了就是打好地基。地基稳了,上面的程序才能盖得高。我见过太多项目,前期图省事随便定义变量,后期维护时改得想哭。所以,花点时间把UDT和DB设计好,绝对是值得的。