返回
SCL PLC 编程教程第三篇 变量、数据类型与地址概念:SCL 程序为什么不能乱定义变量


学 PLC 的时候,很多人一开始接触的是输入点、输出点、中间位、定时器、数据寄存器这些概念。
比如:

I0.0 是启动按钮
I0.1 是停止按钮
Q0.0 是电机接触器
M0.0 是内部运行位
DB 里存配方参数
AIW 里有模拟量原始值

这种思路没有错,而且在梯形图时代,它非常直观。
但当你进入 SCL 之后,会慢慢发现一个变化:

你眼前看到的,不再只是地址,而更多是“变量名”。

比如你不再只是盯着 I0.0,而是更习惯写成:

StartPB

你不再只是盯着 Q0.0,而是写成:

MotorRun

你也不再只把模拟量看成一个字,而是可能定义成:

TankLevel
TemperaturePV
PressureRaw
FlowSetpoint

这时候,很多初学者就会出现一个误区:
“反正就是起个名字而已,差不多就行。”

实际上,完全不是这样。

在 SCL 里,变量定义得对不对、变量类型选得准不准、变量作用范围清不清楚,会直接决定:

程序能不能正常编译
逻辑能不能稳定运行
数据会不会算错
后续维护会不会越来越乱
模块能不能复用
工程能不能做大

所以这一篇,我们就不急着往复杂语法冲,而是把最根上的东西讲清楚:

变量到底是什么
为什么 PLC 里不能乱定义变量
变量有哪些分类
数据类型有什么区别
地址和变量名到底是什么关系
为什么有些程序一运行,数据就莫名其妙错掉了

这一篇吃透了,后面你再学判断、循环、数组、功能块时,底会稳很多。

一、先把最核心的一句话记住:变量不是“名字”,而是“有类型、有作用、有边界的数据容器”

初学者最容易把变量理解成“随便起的一个标签”,这是不够的。

更准确的理解应该是:

变量是程序里用来存放和传递数据的容器,它不仅有名字,还有数据类型、作用范围、读写方式以及生命周期。

这句话听起来有点像书面定义,但它非常重要。

比如同样一个变量,看上去只是个名字:

MotorSpeed

但实际上,这个变量背后至少还有这些问题:

它是整数还是实数
它是输入变量还是输出变量
它是当前块临时使用的,还是需要长期保存
它是来自现场采集,还是程序内部计算结果
它是某个功能块专属的,还是全局共享的
它存的是速度设定值,还是实际反馈值

也就是说,变量从来不是“随便取个名”。
它其实是整个程序逻辑的承载单位。

你以后写 SCL 程序时,必须慢慢建立这种意识:

程序的本质,不只是写判断和循环,更是在组织变量之间的关系。

二、为什么很多 SCL 程序一开始就乱?往往不是逻辑乱,而是变量乱

很多项目后期不好维护,表面看是程序越来越大,实际上根源往往是变量体系从一开始就没建好。

常见的混乱有这些:

1. 变量命名随意

例如:

A1
Temp1
Bbb
RunX
Value2
TestOK1
M_Run_2_New

这些名字当时自己也许知道什么意思,但两个月后再看,往往就糊了。
更别说别人来接手。

2. 同一种意义的数据,到处命名不统一

比如有的地方叫:

StartPB

另一个块里又写:

StartBtn

还有的地方叫:

PB_Start

再有地方叫:

RunCmdInput

如果都是同一类信号,命名风格乱掉后,程序阅读成本会迅速上升。

3. 类型乱选

比如一个带小数的温度值,结果定义成 INT;
一个本来只是开关状态的变量,却拿 DINT 去存;
一个会超过 32767 的计数值,还在用 INT;
一个字符串报码被当成数字处理。

这类问题在小项目里也许一时看不出来,但一旦运行条件变复杂,就会开始出错。

4. 临时值、设定值、反馈值混在一起

比如:

Speed
Speed1
Speed2
SpeedNew

光看变量名,谁也不知道哪个是设定,哪个是反馈,哪个是滤波后的结果,哪个是限幅后的结果。

5. 不清楚变量该保留还是该临时使用

有些变量只是在本次扫描周期中参与计算,算完就可以丢。
有些变量则必须长期保存,比如:

当前步骤号
故障锁存状态
上次运行结果
累计计数值
配方参数

如果这两类变量不分清,程序就会出现一种很常见的问题:
“怎么我这个值刚算出来,下一扫又没了?”

所以你会发现,SCL 程序写得稳不稳,第一步不是看逻辑多高级,而是看变量体系有没有章法。

三、变量在 PLC 程序里,到底承担什么角色

你可以把 PLC 程序想成一间工厂里的信息中转站。
变量就在里面承担不同角色。

1. 有的变量负责“接收现场信息”

比如按钮、限位、传感器、故障接点:

StartPB
StopPB
HomeSensor
OverloadAlarm
TankLevelRaw

这些变量很多时候来自输入采集。

2. 有的变量负责“存放控制结果”

比如:

MotorRunCmd
ValveOpenCmd
AlarmLamp
BuzzerOn

这些变量通常最终会输出到设备执行端。

3. 有的变量负责“中间计算”

比如:

LevelScaled
TempFiltered
SpeedLimitResult
AlarmPermit
StartConditionOK

它们不一定直接对应现场点位,但对逻辑非常重要。

4. 有的变量负责“保存程序状态”

比如:

StepNo
AlarmLatched
AutoCycleBusy
LastProductCode
BatchCount

这些变量往往决定程序是否能保持连续性和记忆性。

所以从工程角度讲,变量不是附属品。
变量本身就是程序结构的一部分。

四、SCL 中变量最常见的几种分类

不同平台在界面和叫法上会有差别,但从编程思想上,你可以先把变量理解为下面几大类。

1. 输入变量

输入变量通常是这个程序块“拿进来用”的数据。
它们往往由外部传入,供当前逻辑判断和计算。

例如一个电机控制功能块,输入可能有:

启动命令
停止命令
自动模式
故障信号
热继电器状态

这些输入变量的特点是:

它们是本块逻辑的依据
本块通常主要读取它们
它们的值往往来自上层调用或现场映射

你可以把它理解成:
别人把材料递给你,你在块里加工。

2. 输出变量

输出变量通常是当前块处理完后,要“交出去”的结果。

比如:

电机运行命令
就绪状态
故障状态
当前步骤完成信号
允许启动标志

这些变量的特点是:

它们是逻辑运算后的结果
会被上层逻辑或其他块继续使用
最终可能映射到实际输出点,或传给 HMI

可以理解成:
你处理完以后,把结果交出去。

3. 输入输出变量

有些变量既作为输入带进来,又在块内被修改后再带出去。
这类变量常见于:

累计值更新
配方结构体传递
参数块修改
某些复用处理模块

不过初学阶段建议先少用这种写法,或者至少要特别清楚变量的来龙去脉,否则很容易把数据流搞乱。

4. 临时变量

临时变量非常重要,但很多初学者最容易忽略。

它的特点是:

只在当前块执行期间参与中间计算
通常不需要长期保留
每次执行时重新计算使用

例如:

比较结果中间位
某个计算公式中的过渡值
临时索引
组合条件判断结果

比如:

StartPermit := AutoMode AND NOT Fault AND SafetyOK;

这里 StartPermit 就可能是一个中间条件变量。
它存在的意义是让逻辑更清楚,不一定非要长期保存。

但这里要注意一点:
临时变量如果用来保存“上次状态”或“跨周期状态”,通常是不合适的。

这也是很多人第一次写状态机、边沿检测、锁存逻辑时出问题的原因。

5. 静态变量或保持性变量

这类变量是很多 PLC 工程里最关键的一类变量。
它们的作用不是只在本次扫描中用一下,而是要 记住程序之前发生过什么。

例如:

当前步骤号 StepNo
故障锁存 AlarmLatched
运行累计次数
上次按钮状态
上个周期的测量值
滤波历史值
已确认但未清除的报警标志

这类变量的特点是:

不应每次扫描都重新清零
必须在程序运行过程中持续保存
很多顺控、状态机、边沿判断都离不开它们

你以后写 SCL 时,一定要养成一个习惯:
每定义一个变量,都先问自己:

这个变量只是本扫临时计算一下,还是要记住上次结果?

这个问题想清楚,很多逻辑错误会提前消失。

五、数据类型到底是什么,为什么它这么重要

很多初学者觉得,变量就是个名字,值放进去能用就行。
但程序不是这样工作的。

程序必须知道:
这个变量里存的到底是什么类型的数据。

因为不同类型的数据,PLC 会用不同方式去存储、计算和解释。

你可以把“数据类型”理解成给变量贴的说明书。
这个说明书告诉系统:

这个值是不是只有真和假
它是不是整数
它是不是带小数
它是不是很大的数
它是不是时间
它是不是文本

如果类型选错了,哪怕变量名再漂亮,程序也可能算错。

六、SCL 里最常见的数据类型,必须先吃透这几种
1. BOOL:布尔量

这是最基础的一种类型。
BOOL 只有两种状态:

TRUE
FALSE

它最适合表示“是或否”“有或无”“开或关”“成立或不成立”。

典型用途:

按钮状态
电机运行状态
故障标志
条件是否成立
阀门开到位
安全门是否关闭

例如:

IF StartPB AND NOT Fault THEN
    MotorRun := TRUE;
END_IF;

这里的 StartPB、Fault、MotorRun 一般都应该是 BOOL。

不要把 BOOL 想小了。
虽然它只是真假两态,但 PLC 里大量逻辑判断的基础都靠它。

2. INT:整数

INT 适合表示不带小数的整数值。
常见用途:

步骤号
报警编号
小范围计数
简单设定值
小范围下标

例如:

StepNo = 10
AlarmCode = 3
RetryCount = 5

但这里一定要记住:
INT 不是无限大的。
如果你拿 INT 去做很大的累计计数,后面可能会溢出。

所以它适合“范围不太大且不需要小数”的值。

3. DINT:长整数

当整数范围可能比较大时,往往就要考虑 DINT。
它常用于:

累计产量
总运行时间计数
大范围脉冲累计
较大的设备编号或索引值

很多初学者一开始只会用 INT,后面计数一大就出事。
这就是没有建立“值的范围意识”。

所以你定义变量时,不只要看“它是不是整数”,还要看:

它未来可能会增长到多大。

4. REAL:实数

REAL 是带小数的数,工程里使用非常多。
尤其在这些场景里几乎绕不开:

温度
压力
流量
液位
速度
比例系数
模拟量工程值
PID 相关参数

例如:

23.5℃
0.78MPa
4.2m
50.0Hz

如果你的数据本身有小数,或者计算过程中会出现小数,通常就应考虑 REAL。

例如模拟量换算:

LevelMeter := REAL_TO_REAL(LevelRaw) * 5.0 / 27648.0;

虽然具体转换函数后面还会详细讲,但这里你先记住一个大原则:

凡是涉及工程量、比例、滤波、平均值、模拟量换算的场景,REAL 通常非常常见。

5. TIME:时间类型

PLC 不是普通计算器,它经常要处理时间。
比如:

延时启动
延时停机
报警保持时间
动作超时
节拍统计
间隔触发

这些时候,TIME 类型就很重要。

比如:

T#5s
T#100ms
T#2m30s

时间不是普通整数,它有自己的表达和处理规则。
所以不要简单把“5000毫秒”永远都当整数思考,很多时候更合适的做法是用 TIME。

6. STRING:字符串

只要涉及文字、文本、条码、设备名称、报警描述,STRING 就会出现。

例如:

产品型号
条码内容
批次号
报警文字
配方名称

很多 PLC 工程师前期接触 STRING 不多,但等到 HMI、扫码、报文解析、配方管理做多了,就会越来越常见。

七、同样是“数字”,为什么 INT 和 REAL 不能乱混

这个问题非常常见。

很多人觉得:

10 就是 10
10.0 也是 10
差不多吧

从人眼看,好像差不多;
但从程序角度,它们不是同一回事。

例如:

10 可能是整数
10.0 可能是实数

当你用它们参与计算时,结果类型、计算方式、精度表现都可能不同。

这就是为什么有时候程序会报:

类型不匹配
不能直接赋值
运算对象不兼容

比如一个 REAL 变量去接收 INT 结果,有时候需要系统自动转换,有时候则需要你明确处理。

所以你一定要建立这种意识:

变量不仅有值,还有“值的类型”。

而程序是很较真的。
你脑子里觉得“都差不多”的东西,它不一定认。

八、地址是什么?SCL 都写变量名了,为什么还要懂地址

这是很多初学者的一个疑问。

既然 SCL 里都写的是:

StartPB
MotorRun
TempPV
StepNo

那我还需要懂 I、Q、M、DB 这些地址概念吗?

答案是:
当然需要,而且必须懂。

因为变量名只是“人看的名字”,而 PLC 最终还是要落到实际的数据存放位置和信号来源上。

你可以把它理解成这样:

1. 地址是 PLC 实际存放和访问数据的位置

例如:

输入区
输出区
内部存储区
数据块
保持区

这些都和实际地址有关。

2. 变量名是给这个数据位置起的“工程名字”

例如:

I0.0 这个输入点,可以起名为 StartPB
Q0.0 这个输出点,可以起名为 MotorContactor
DB10.DBD4 这块数据,可以起名为 TankLevelSetpoint

也就是说:

地址解决“它放在哪儿”,变量名解决“它叫什么、拿来干什么”。

如果你只知道变量名,不理解背后的地址和数据区,工程上会有几个问题:

不知道信号来自哪里
不知道数据是否保持
不知道掉电后是否会丢
不知道是否和 HMI、通信、其他块共用
不知道变量为什么明明改了却没生效

所以,SCL 虽然让你更多使用符号变量,但并不意味着地址概念可以丢掉。
恰恰相反,你要做到的是:

表面上用变量名写得清楚,脑子里仍然知道它背后属于哪一类地址和存储区域。

九、地址思维和变量思维,正确关系到底是什么

这个关系非常值得讲透。

你可以把 PLC 编程分成两个层面:

第一层:物理和存储层

这里关注的是:

输入点接在哪儿
输出点接在哪儿
模拟量放在哪个区
数据存在什么块里
哪些数据掉电保持
哪些数据是临时的

这是地址思维。

第二层:逻辑和功能层

这里关注的是:

这个信号在程序里代表什么
这个值是设定值还是反馈值
这个状态是允许启动还是运行中
这组数据属于哪个模块
这个变量该不该长期保存

这是变量思维。

成熟的 PLC 工程师,不是只会其中一种,而是两种都要有。
因为真正好用的工程程序,必须同时满足:

现场映射清楚
逻辑表达清楚

如果只有地址,没有变量名,程序很难读。
如果只有名字,不清楚背后存储区和来源,程序就容易飘。

十、为什么有些变量“看起来有值”,实际却不稳定

这也是实际工程里常见的问题。
很多初学者会说:

“我这个变量明明刚才算出来了,怎么下一次又不对了?”

背后常见原因有几类。

1. 用了临时变量去保存应该长期记住的状态

比如边沿检测、本周期和上周期比较、步骤保持、故障锁存,这些都不能只靠临时变量草草一放。

2. 没搞清变量是否被别处覆盖

同一个变量可能在:

主程序里写了一次
子程序里又写一次
HMI 又能改
通信也能改

结果看起来值在变,其实是被多处同时操作。

3. 数据类型不对,计算结果被截断

比如:

你算出来 23.8
但变量定义成 INT
最后只剩 23

或者某些比例运算本来该带小数,结果整个计算过程都是整数,精度直接丢掉。

4. 地址或映射关系不清楚

变量名很好看,但背后实际没正确关联到你以为的那个信号区,也会出现“看似有值、实际不对”的情况。

所以你以后排查 SCL 程序时,不要只看逻辑语句。
很多问题其实是变量层面的问题。

十一、变量命名为什么这么重要,甚至比你想象得更重要

很多人会觉得,命名只影响好不好看。
其实它影响的是 可维护性、出错率和协作效率。

例如下面这两组变量:

第一组:

X1
Temp2
RunA
DataNew
Flag5

第二组:

StartPB
ReactorTempPV
ConveyorRunCmd
RecipeDataValid
AlarmResetReq

你不用想都知道,第二组更好维护。

好的命名至少有几个好处:

1. 读变量名就知道用途

不需要来回翻注释和监控表。

2. 不容易误用

比如 PressurePV 和 PressureSP 一眼就能区分反馈值和设定值。

3. 更容易批量查找和整理

项目大了之后,统一命名风格特别重要。

4. 便于和 HMI、文档、电气图对应

程序不是孤立的,命名越清楚,跨部门协作越轻松。

所以从现在开始,你就应该慢慢建立一种习惯:
变量名不是随便取的,它是工程表达的一部分。

十二、给初学者一个非常实用的变量命名思路

不用一开始追求特别复杂,但至少可以遵循这几个原则。

1. 先体现对象,再体现含义

比如:

Motor1_RunCmd
Valve2_OpenFB
Tank1_LevelPV
Heater_Enable

这样比:

Run1
Open2
Value3

清楚太多。

2. 设定值、反馈值、命令值、状态值尽量区分

例如:

SpeedSP:设定值
SpeedPV:实际值
StartCmd:启动命令
RunFB:运行反馈
AlarmAct:报警活动中
AlarmAck:报警已确认
3. 布尔量尽量让人一眼看出真假含义

例如:

AutoMode
FaultExist
StartPermit
ValveOpenFB
TempHighAlarm

布尔量命名越像一句成立与否的话,后面写 IF 越顺。

十三、初学 SCL 时,变量定义最容易犯的几个错误
错误一:所有数字都先用 INT

这是很典型的“先凑合”思路。
但一到模拟量、比例、平均值、滤波,就会出问题。

错误二:需要长期保存的量,当成临时量

状态机、边沿检测、锁存逻辑最容易中招。

错误三:同一个意义的量,不同地方叫法完全不同

时间一长,自己都会看晕。

错误四:只知道变量名,不知道它来自哪里、去向哪里

写的时候很顺,调试时一头雾水。

错误五:把设定值和实际值混用

比如:

温度设定和温度反馈都叫 Temp
速度命令和速度反馈都叫 Speed

这在项目里非常危险。

十四、初学阶段该怎么建立“变量思维”

这里给你一个非常实用的习惯。
以后每定义一个变量,都在脑子里问自己这五个问题:

1. 它存的是什么意义的数据

是按钮状态、温度值、步骤号,还是报警标志?

2. 它是什么类型

是 BOOL、INT、DINT、REAL、TIME,还是 STRING?

3. 它从哪里来

来自输入采集、HMI 设定、通信报文,还是程序中间计算?

4. 它要到哪里去

给输出、给别的块、给 HMI 显示,还是仅本块内部使用?

5. 它需不需要跨扫描周期保留

只本次计算一下,还是必须记住以前的状态?

这五个问题问清楚,变量定义基本就不会太离谱。

十五、从工程角度看,变量定义其实就是在搭程序骨架

很多人以为程序骨架是流程图、功能块图。
实际上,变量体系本身就是骨架的一部分。

因为程序最终靠什么运行?
靠的是:

输入进来
中间处理
结果输出
状态保留
数据传递

而这些全都落在变量身上。

所以一个好的 SCL 程序,往往在你还没写复杂逻辑前,它的变量表就已经透露出结构感了。
你看一眼变量表,就能大致猜出:

这个模块是干什么的
它有哪些输入条件
它会输出什么结果
它有没有状态保持
它是不是处理模拟量
它是不是做步骤控制

这就是为什么成熟工程师看程序,不只看代码,也很看重变量表。

十六、这一篇最后,给你一个很实在的理解方式

你可以这样理解:

地址,是 PLC 世界里的“房间号”;
变量名,是你给这个房间住户起的“名字”;
数据类型,是告诉你这个住户到底是什么身份、能做什么事。

如果只有房间号,没有名字,程序能运行,但很难看懂。
如果只有名字,不清楚房间号和存储区,工程就容易失控。
如果名字和身份说明都不清楚,后面逻辑再复杂也会越来越乱。

所以,SCL 学到这里,你最应该建立起来的,不是某个单独语法点,而是这套意识:

变量不是随手写的
数据类型不是随便选的
地址概念不能丢
是否保持、是否临时、是否输入输出,都要想清楚
变量体系越清楚,程序后面越稳
小结

这一篇你至少要记住下面这些核心点:

变量不是简单的名字,而是有类型、有作用范围、有生命周期的数据容器。
SCL 程序乱,很多时候不是判断语句乱,而是变量体系乱。
常见变量可以分为输入、输出、输入输出、临时变量、静态或保持性变量。
BOOL 适合开关量,INT 适合普通整数,DINT 适合较大整数,REAL 适合带小数的工程量,TIME 处理时间,STRING 处理文本。
数据类型不能乱选,否则会出现截断、溢出、精度丢失或类型不匹配。
SCL 虽然更多写变量名,但地址概念仍然必须懂,因为变量最终还是对应实际数据区和现场信号。
好的变量命名和清晰的数据分类,会直接决定程序后期是否容易维护。
下载资料前请先绑定手机号码