☰
S7-1200/1500 PLC数据类型详解:从基本类型到UDT与现场排错
2026/9/26 6:42:06 网站建设 项目流程

干了十几年自动化,我在现场碰到的大多数“诡异故障”,最后查到头都不是逻辑错、也不是接线错,而是数据类型错了。有一回,一台搅拌机的转速反馈在触摸屏上从0直接蹦到65535,操作工吓得按了急停。在线一查,PLC里累计的DINT时间值被塞进了一个INT变量,正数溢出直接变负数,换算完就成了天文数字。从那次以后,我带人做西门子S7-1200/1500项目,第一次要过的关不是梯形图,而是数据类型与数据结构。今天就把从基本数据类型到UDT自定义结构、从存储区到转换排错的实战经验讲透,适合刚接触PLC编程的人,也适合那些被数据类型坑过、想在下次调试时少走弯路的工程师。

1. 为什么数据类型搞不清楚,S7-1200/1500程序迟早要在现场翻车

1.1 一个让整线停机的DINT溢出事故

先说那个搅拌机的案例。设备工艺很简单:电机转一圈,接近开关给一个脉冲,PLC用高速计数器累计圈数,再换算成转速显示到触摸屏上。程序逻辑也简单,但偏偏显示值会在一段时间后变成负数或者65535这种离谱数值。

在线监控的时候,我把累计圈数、换算中间量、显示值三个变量拖进监视表,一路往下查。累计圈数用的是DINT,范围能到正负21亿,按设备转速算,连续跑几个月都不会溢出。可到了显示换算这一步,写程序的人图省事,把DINT直接送给了一个INT变量,再去做除法。INT的范围只有-32768到32767,圈数一旦超过32767,正数溢出就会翻成负数,再做除法自然全乱套。

这种问题在现场特别隐蔽,因为它不是每次都报错,而是运行一段时间才出现。而且很多底层开发环境里,较大的数据类型往较小数据类型赋值,工具不会给你弹硬错误,顶多给个警告,甚至什么都不说。你要是只盯梯形图逻辑,三天也找不出来。数据类型问题就是这样,平时看不见,一炸就是大故障。

1.2 什么人需要把这一课补扎实

如果你是刚入门PLC的初学者,可能觉得数据类型就是选个BOOL、选个INT的事,等到联调时发现触摸屏上数值不对、程序块调用报类型不匹配,才意识到问题。如果你是做设备维护的工程师,很可能遇到一个程序能跑但就是数据乱跳的情况,换传感器、换模块都没用,最后才发现是数据类型转换丢了精度或越了界。

我建议不管你是哪种角色,都先把这几件事搞清楚:基本数据类型的位宽和取值范围、字符串和时间类类型的内存消耗、数组和结构体的设计套路、UDT的复用方式、DB块和存储区的关系、类型转换的边界处理。这六块内容不是学院派知识点,而是每一个S7-1200/1500项目从编程到调试、再到交付维护都会反复碰到的实际问题。

2. 基本数据类型从裸奔到穿衣服:BOOL到LREAL的位宽和取值边界

2.1 布尔和整数家族:位数、范围和实际寻址

S7-1200/1500里最基础的数据类型是BOOL、BYTE、INT、DINT,还有S7-1500才完整支持的LINT。很多人记住范围就够了,但真正写程序时要清楚它们是几位、占几个字节、寻址怎么写。

下面这张表是我自己整理的核心基本类型速查,现场想不起来时就翻这个:

类型位宽取值范围典型用途
BOOL1位TRUE/FALSE按钮、继电器、状态标志
BYTE8位0~255单字节通信数据、状态字合并
INT16位-32768~32767常规计数值、模拟量原始值
DINT32位-2147483648~2147483647累计量、大范围计数
LINT64位非常大S7-1500才支持,极少场景
REAL32位约±3.402823e38工程值、模拟量定标结果
LREAL64位约±1.7976931348623158e308S7-1500专用,高精度运算

寻址方面,I区、Q区、M区都可以按位、字节、字、双字访问。比如M区里的BOOL位是M0.0,字节是MB0,字是MW0,双字是MD0。注意MW0和MD0在物理上重叠,MD0包含MW0和MW2两个字的范围。你要是在MW0里写了一个数,接着又用MD0读,结果肯定会把你吓一跳。这种地址重叠问题在通信数据解析和共享数据区里非常常见,我建议先画一张偏移地址表再动手。

2.2 实数、字符串和时间:比想象中更占地方也更难伺候

REAL是32位浮点,按IEEE 754格式存的,7位左右有效十进制数字。这意味着你存1234567.89这种数,超出精度部分会被悄悄丢掉。LREAL是64位浮点,有效数字能到15位以上,但S7-1200的老实说并不支持LREAL,S7-1500才支持。跨型号移植程序时,看到LREAL就得警惕。

浮点比较是另一个大坑。两个REAL直接判断相等,经常因为浮点误差不成立。比如累计量除以时间得到的流量,在理论上等于某个定值,实际浮点运算结果可能差0.000001。正确的做法是判断两者之差的绝对值是否小于一个允许误差,比如0.01。我在PID和模拟量对中经常写这种容差比较,一次都没让我失望。

STRING在S7-1200/1500里默认是10个字符,如果你要存设备名称、批次号,最好显式写成STRING[80]甚至更长的。字符串在DB里占用的空间不是光字符本身,还有两字节长度头和实际字符空间。CHAR是一个字符,WSTRING是宽字符,用Unicode,占两字节一个字符,HMI跨语言显示时容易用到,但也更占DB空间。

TIME类型是32位毫秒计数值,DATE是16位从1990年1月1日起的天数,TOD是32位从0点开始到当前时刻的毫秒数。这些类型在做配方时间、生产节拍记录时很方便,但和HMI通信或者写第三方接口时,要注意很多上位机不一定认识PLC的原始时间格式,经常需要转成STRING再传。

2.3 强制转换的规矩:常见取整和隐式转换的差别

TIA Portal里做类型转换的指令很多,但关键是要分清楚取整方式。CONV指令是就近舍入,TRUNC是向零取整,CEIL向上取整,FLOOR向下取整。如果你把Real 3.7转成Int,CONV得到4,TRUNC得到3。如果不懂这个区别,配方值换算时差出一个数就可能造成定位不准或者加料量出错。

再看一个最典型的INT除法截断问题:

VAR a : Int; // 7 b : Int; // 2 cReal : Real; cInt : Int; END_VAR cInt := a / b; // 结果是3,整数除法直接截断 cReal := a / b; // 如果两个Int先除,结果是3,然后才转成Real,得到3.0 cReal := INT_TO_REAL(a) / b; // 这才是3.5

第二行尤其坑,很多新手以为把结果存到REAL里,除法就会得到小数,但实际上两个INT相除在中间过程已经是整数了。正确姿势是先转REAL再除。检查程序时我只要看到INT除以INT的写法,基本都会停下来确认用途,因为十有八九是隐患。

隐式转换在TIA里也存在,比如INT赋值给DINT、INT赋值给REAL通常会自动完成。但反过来,DINT赋值给INT、REAL赋值给INT,如果类型不安全,轻则警告,重则直接编译失败。我的习惯是:所有跨类型赋值都用显式转换指令,宁可多写两行,也不让工具替我决定精度去留。

3. 复合数据结构:数组、结构体和UDT,让工艺参数不再散落一地

3.1 ARRAY数组:下标、边界和典型用途

数组在PLC里最常见的三个用途是配方缓冲、报警记录、数据缓存。定义一个配方数组很简单:

VAR RecipeBuffer : ARRAY[0..9] OF Real; AlarmFlags : ARRAY[0..31] OF Bool; END_VAR

数组的使用要注意两点。第一,下标边界。TIA里数组下标可以不是从0开始,比如ARRAY[1..10],但很多人思维里写成[0..9],一旦用错,越界访问不会稳定报错,有时读到隔壁变量的值,有时直接导致程序异常。优化访问的DB里,越界访问还不一定在编译期被抓出来,现场出现随机数据错乱就很难查。

第二,数组做FB接口时,建议用Variant类型而不是直接ARRAY[0..9] OF Real。S7-1200/1500支持Variant后,你可以把不同长度的数组传进同一个函数块,然后在块里用LOWER_BOUND和UPPER_BOUND动态判断边界。这样写一轮缓冲逻辑,几条产线都可以复用,不用为每种长度单独建FB。

3.2 STRUCT结构体:把设备的属性捆成一个整体

如果你用一个电机,程序里得维护启动命令、运行反馈、电流、转速、累计运行时间、故障代码这几个变量。如果这些变量散落在M区或者DB的不同位置,每写一条逻辑就要重复找地址,漏了一个下标还很难发现。

用结构体可以把它们捆在一起:

VAR motorA : STRUCT Command : Bool; Feedback : Bool; Current : Real; Speed : Real; FaultCode : Word; RunTimeMin : DInt; END_STRUCT; END_VAR

访问的时候就是motorA.Command、motorA.Speed,程序一眼能看懂归属于谁。更爽的是,结构体变量之间可以整体赋值,比如motorB := motorA,整个设备参数一次性拷贝,不需要一个个字段复制。做设备调试初始化时,一行就能把结构体全部清零。

3.3 UDT自定义类型:一次定义、到处实例化,以及更新时的风险

UDT是结构体的加强版。你可以在TIA Portal里专门定义一个类型,然后到处实例化。十台电机就创建十个UDT类型的实例,十台泵、十台阀都同样处理,代码量和出错概率都大幅下降。

定义UDT的SCL大概长这样:

TYPE "TypeMotor" VERSION : 0.1 STRUCT Command : Bool; Feedback : Bool; Current : Real; Speed : Real; FaultWord : Word; RunTimeMin : DInt; Reserved : DInt; // 预留字段,防后期扩展 END_STRUCT END_TYPE

然后在FB或者全局DB里实例化:

VAR Motor1 : "TypeMotor"; Motor2 : "TypeMotor"; Motor3 : "TypeMotor"; END_VAR

这样Motor1、Motor2、Motor3的结构完全一致,程序里的逻辑可以复制使用。但如果项目做到一半,你想给TypeMotor增加一个温度字段,就要特别注意:修改UDT定义后,所有用到这个UDT的实例都要重新编译,优化访问的DB可能不会自动保留原有数据。稳妥做法是先把DB在线快照导出来,改完定义后下载增量,再通过快照恢复数据。千万别直接全编译后下载,配方和累计值有可能会被初始化掉。

UDT还可以嵌套,比如先定义“阀体”UDT,再定义一个“设备组”UDT包含多个阀体。这样建模能力和上位机SCADA系统里的结构几乎一样,后期做数据采集和中控对接都省心。

4. 数据块与存储区:数据类型到底落在哪里,决定了你能不能加字段、换地址

4.1 I区、Q区、M区和DB:谁适合放什么数据

I区是输入映像区,周期刷新,读的是现场传感器信号。Q区是输出映像区,控制继电器、变频器。M区是全局位存储区,适合放通信握手标志、临时指令。DB数据块是真正用来放配方、工艺参数、累计数据的地方。

这里有个容易被忽略的点:I区和Q区在S7-1200/1500里是周期性刷新的。如果你用I0.0做高速计数或中断输入,只靠输入映像可能丢脉冲,得用高速计数功能或者直接读取外设输入。所以选择数据类型和存储位置时,要反问一句:这个数据是几毫秒级变化的现场信号,还是几秒钟更新一次的工艺参数?前者放I区走特殊通道,后者放DB块,逻辑清晰且不容易受扫描周期影响。

4.2 优化访问与标准访问:S7-1500的符号寻址是怎么影响数据结构布局的

S7-1200/1500的DB块默认支持“优化访问”,这是和S7-300/400时代最大的区别之一。优化访问下,PLC内部会按符号名和数据结构的对齐要求自动分配地址,你在编程软件里看不到具体的字节偏移,也不建议去猜物理地址。

优化访问的好处很明显:改动DB里的字段顺序、增加字段,不容易把内存布局搞乱,数据也不会因为字节对齐问题互相覆盖。坏处是和第三方设备做内存映射通信时会麻烦,因为拿不到稳定的绝对地址。如果你的上位机要用外部变量表做协议解析,那就把特定DB改成标准访问,并手动排列好字段顺序,把偏移地址打印出来给通信工程师用。

标准访问下要特别注意数据对齐。比如一个结构体里先放BOOL,再放REAL,REAL的偏移可能被编译器调整到4字节对齐边界,中间会“插”出空隙。你在写通信解析时,如果按顺序假定每个字段紧密排列,解析出来的数据就对不上。这也是我坚持用UDT而不是靠手工计算偏移的原因——编译器自动处理对齐,比人脑可靠。

4.3 保持性与快照:修改数据块时不让工艺数据“人间蒸发”

DB块的保持性属性决定掉电后数据是否保留。S7-1200/1500里保持性存储区是专门的掉电保持区域,你可以在DB属性里勾选“保持”。BUT,保持性存储区是有限的,不能把几十个大数组都设为保持,否则编译会报无法分配保持内存。

下载程序时还有一个“初始化”按钮,很多工程师一不小心就把工艺数据清成初始值。我的做法是:在任何下载前,先在线生成快照;下载后再用快照恢复值,确保配方参数和运行时间不丢。这一步听着多余,碰上客户设备里有几万个配方参数的时候就知道了——没有快照,重灌一次就是一个通宵。

5. 类型转换、在线监视和报错排查:从现象反推数据类型的完整链路

5.1 在线监视时怎么判断数据类型有没有出问题

TIA Portal在线监视时,DB表、FB接口、监视表都能看到变量的实际值。如果某个变量显示为“#”或者“<不能读取>”,通常不是程序逻辑错误,而是在线连接没建立、块被修改后没重新下载,或者变量在优化访问DB里还没被分配物理视图。

我习惯在调试前先建立一个监视表,把所有关键信号按数据类型分组拉进去:输入原始值、定标后的REAL值、设备状态BOOL、累计DINT值。只要原始值和换算值同时显示,某一步突然不对,马上能定位到是采集模块坏了还是类型转换出错。和HMI联调时也一定确认HMI侧的变量类型和PLC一致,REAL在有些上位机里会被默认解释成Float32,DINT解释成Int32,但凡有一边类型对不上,显示就乱。

5.2 一次真实排查:电流显示为零但电机明明在转

有一台设备,电机实际在转,HMI上电流却一直显示0.0,偶尔还会跳出一个巨大的负数。我先查了变送器和模拟量模块,信号正常。接着打开PLC程序,发现AI模块原始值放在一个INT变量里,换算部分写的却是:

#currentDisplay := #rawAnalog / 5;

#rawAnalog是INT,#currentDisplay是REAL。两个INT相除的结果还是INT,电机电流小的时候,比如20mA对应80%量程,原始值被截断后可能只有50多,再一除以5,数值会丢失,低电流段直接变成0。而如果原始值偶发超限,INT再做除法也可能产生不合理的显示值。

修正方法是把每一个模拟量原始值都先转成REAL,再参与定标:

#realAnalog := INT_TO_REAL(#rawAnalog); #currentDisplay := #realAnalog / 5.0;

改完后HMI显示恢复正常。这类问题不是我第一次遇到,根因就一条:跨类型运算时没有先做显示转换。排查程序时,看到INT和REAL混用的除法,我建议立刻停下来把参与运算的每个操作数的类型都看一遍,这是性价比最高的动作。

5.3 一个实用的数据类型自查清单

检查项检查内容常见问题建议做法
变量类型与物理量匹配INT、REAL、DINT是否符合量程和精度需要INT存大累计量,REAL存整数编号累计量用DINT,浮点运算用REAL
除法与混算除法操作数是否都是整数INT/INT截断丢精度先转REAL再除
数组边界下标是否可能越界越界不报错,读写相邻变量用LOWER_BOUND/UPPER_BOUND动态判断
UDT更新修改定义后实例是否保持数据下载后配方清零先快照,再增量下载,后恢复
HMI通信类型PLC变量和HMI变量类型是否一致显示乱码、数值跳变在变量表里统一规划
模拟量定标是否用NORM_X/SCALE_X规范处理手写换算错量程用标准定标指令

6. 我这些年在数据类型上踩过的坑,以及现在的书写习惯

6.1 别把BOOL数组当配方标志位,也别轻易用M区存工艺数据

有一版设备程序,上一任工程师用M0.0到M31表示32个配方标志位,后来要增加一个工艺批次号,他又随手用了MD100。结果M0.0到M31这32个BOOL位其实分散在MB0到MB3里,和后来用的某个通信模块共享地址,半夜HMI一操作,设备自己开始乱切配方。从此我规定:凡是带工艺含义的数据,一律放DB块并用UDT/STRUCT组织,M区只允许做临时标志位和通信握手,不承载业务数据。这个规定救过我好几次。

BOOL数组还有个尴尬点:你很难对一个“标志位数组”做结构化管理。如果你真需要一组标志位,定义一个STRUCT包含明确的标志字段,可读性比ARRAY[0..31] OF BOOL强太多。HMI变量表里也能按名显示,维护的人不用数下标。

6.2 模拟量换算的正确姿势:先转REAL再定标

模拟量处理是目前PLC项目里最容易犯数据类型错误的地方。模块原始值通常是INT,工程值可能是温度、压力、流量,带小数点。正确的做法是用NORM_X把原始值归一化到0.0~1.0,再用SCALE_X放大到工程量程,比如0到100度。NORM_X的MIN/MAX写模块量程上下限,SCALE_X的MIN/MAX写传感器物理量程上下限,这样换个量程的传感器,只改参数,不用动公式。

如果只想手写换算,记住每步都用REAL:

#percent := (INT_TO_REAL(#rawValue) / 27648.0) * 100.0;

这里27648对应4~20mA或0~10V的满量程值。千万不要把27648写成整数参与除法,不然结果永远是对一半。这个坑我见得太多了,每次讲模拟量必定提一遍。

6.3 面向未来的数据结构:版本、预留和统一单位

最后一个习惯,是在设计UDT或DB结构时预留字段和版本号。设备调试阶段改需求很频繁,今天加一个温度点,明天多一个流量累计,这时候如果UDT没有预留位,每加一次字段就要大范围重编译、重新下载,甚至影响HMI变量连接。我会在结构体尾部放一两个DINT/REAL的Reserved字段,并放一个Word类型Version字段记录结构版本。这样第一版调试时就有余量,后期扩展不用动骨架。

同时,所有工程值单位必须统一。压力要么全用MPa,要么全用kPa;温度要么全用摄氏度,要么全用开尔文。数据类型解决的是存储精度问题,单位解决的是语义一致问题,两者在PLC项目里缺一不可。我见过报警记录里的温度一会是32.5度一会是305.5K,看起来是数据类型问题,本质是单位没有在取名和注释阶段统一。

数据类型这个东西,平时不显山不露水,等现场设备半夜报警、售后电话打到你手机上的时候,你才会明白之前花的时间都值了。我现在写每一段S7-1200/1500程序前,都会先问自己一句:这个变量是什么物理量?它可能到多大?它要经过哪些运算?想清楚这三个问题,数据类型选错的可能性已经去了一大半。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询