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,我一般遵循三个原则:

  1. 按功能分区:一个DB里放同一类设备的数据。比如DB_Pumps放所有泵的数据,DB_Valves放所有阀的数据。
  2. 预留扩展空间:每个数组多预留20%的容量。我吃过亏,项目做到一半甲方说要加3台设备,结果DB容量不够,改起来那叫一个痛苦。
  3. 命名规范统一: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设计好,绝对是值得的。