
变量指向(链接)内存中的一个存储空间,该存储空间含可以写入数值的位置。存储空间的大小取决于数据类型,这一点非常重要。那么,变量赋值,实际就是往这个空间里写入具体值!

下面的示例展示了名为VarA的变量如何获取变量VarB中存在的数值副本。这意味着VarA被赋值为VarB所持有的值。
VarA:= VarB;注意的是:符号 := 和 ;(冒号:、等号=和分号;)的使用。
如果往VarB里面赋值:
VarB := 17.6 ;如果VarB为INT类型,大部分PLC基本上会提示报错:

默认有小数点的数值当作是LREAL类型,所以报错:不能把LREAL类型直接转为INT类型。
当然原作者在这里举例HeartBeat:
Count:= Count + 1;
IF Count > 99 THEN //To avoid overrun
Count:= 0; //Reset counter
END_IF;利用Count的赋值判断程序是否在运行中,这个是很好的机制,常常用来判断PLC是否是在正常运行中,或者该程序、FB等在执行!
同样的,这种方式也可以用来做延时,由于每个周期会执行+1的操作,如果一个周期是1ms,当Count=100的时候,说明执行了100次循环,也可以精准的做延时!

除零问题
接下来主要介绍赋值里面几种问题,首先最开始的是除零问题:

出现这类问题,就是致命的故障!基本上程序只要有一处,PLC就会死机,从RUN模式自动切换到STOP模式!所以,除零是必须要在程序设计时候避免掉的!
VarC:= VarA / Temperature;书中主要用温度来举例说明,把传感器的值链接到Temperature变量,但是这类变量也确实存在Temperature为0的情况,一旦出现,PLC运行内核就会故障而STOP!
当然,也有解决的办法:
方法1:加条件判断
//Ensure temperature is not zero when calculating
IF Temperature <> 0 THEN
VarC:= VarA / Temperature;
END_IF;方法2:强制赋值一个不为0的值
//Ensure temperature is not zero when calculating
IF Temperature = 0 THEN
Temperature:= 0.0001;
END_IF;
VarC:= VarA / Temperature;需要注意的是:LN (x) and LOG (x)这两类数学函数的参数x不能为0.

INT和REAL实数类型的转换
在PLC编程中,既可以使用整数(INT)也可以使用浮点数(REAL)进行计算。当需要进行两个整数的除法运算时,必须特别注意变量的数据类型以及运算的执行方式。
下面简单举例说明:(三个INT变量类型的变量做除法运算)


我们预期的结果正常是8.8,由于iVar这个目标结果变量是INT类型,所以最终的结果是8.相当于对8.8做了取整数操作,只是这个是隐式结果,实际上显示的过程:

要得到预期结果,并不是把iVar类型定义为REAL就能成功的:

上图的rVar已经是REAL类型,但是结果依然是8.所以,我们正确的做法是:

这种方式实际就是把赋值中的表达式中其中一个变量iVar2,强制转换为REAL类型,最后参与到除法运算中!
还需要注意的是,下图的写法:

这种直接用数字做除法运算,默认和我们上面提到的定义两个INT类型数据结果是一样的,解决方法很简单:

所以,PLC里面的数学计算和我们日常的计算是不一样的,务必在进行交付前进行校验和调试!

REAL类型的精度
当使用REAL(浮点)数据类型进行计算时,有时会出现结果并非整数的情况。例如,预期变量结果应为规整的整数(如11),但实际计算结果却显示为10.999999。这种现象源于计算机底层只能处理整数数据类型,而REAL值实际上是经过调整的近似值。
关于REAL类型的数据怎么存储在计算机内部的演示视频,这里以圆周率Π来进行示例演示,详情请点击下述视频:
那了解到这个原理后,在处理下面类似这种应用的时候,就要避免这种情况导致非预期的结果:

这种写法,我们知道可能Sensor这个值不会刚好是11.0;所以,Lamp1的状态不会置TRUE。怎么解决呢?请看下面的例子:

当然,还有一种方式可以使用TRUNC转换成整数再比较。

数据传输问题
在设计自动化解决方案时,经常需要将变量传输至其他计算机设备。本章重点阐述与变量数据传输相关的关键问题。
浮点数精度问题
不同设备在传输REAL(实数)型变量时可能产生问题,无论是传输至其他PLC、工控机、电气设备还是自动化仪表。这是由于不同计算机系统对REAL/FLOAT(浮点数)值的定义存在差异(计算机本质上更精确处理整数运算)。此外,不同编程版本、16/32/64/128位系统对浮点数的处理方式差异也会引发问题。
解决方案是将所有数值转换为整数传输:发送方可将数值乘以100转换为保留两位小数的整数,接收方再除以100还原原始值。具体的步骤:
VAR CONSTANT
DecimalFactor : REAL := 100; //10 for 1 digits, 100 for 2 digits,1000 for 3 digits
RealNumberBegin : REAL := 50.7172;
END_VAR
VAR
INTNumber: INT;
RealNumber: REAL ;
END_VAR
RealNumberRealNumber:= RealNumberBegin;
IF DecimalFactor > 0 THEN //Avoid division by zero (0)
RealNumber:= (RealNumber * DecimalFactor) + 0.5; //+ 0.5 to round up #1)
INTNumber:= REAL_TO_INT(RealNumber); // Convert tointeger #2)
RealNumber:= INT_TO_REAL(INTNumber); // Convert todecimal #3)
RealNumber:= RealNumber/DecimalFactor; // Add decimal #4)
END_IF;DecimalFactor是一个常数,一般定义常数是适用于经常要用的值和变量,当然如果直接用100,也是可以的,但是,当遇到这个系数需要变更那改动的位置就太多了,工作量剧增,所以用常量是一个很好的方式!
字符串(STRING)传输问题
字符串(STRING)传输同样存在挑战,这源于不同系统对字符串的处理方式差异——包括位宽标准不同(如Unicode与ASCII字符集选择差异)。特别需要注意的是,某些编程语言的字符串起始索引为0,而另一些则从1开始。将字符串转换为字节(BYTE)数组可简化传输过程。
数据传输时应始终以字(WORD)为单位进行读取。需注意某些计算机系统会交换字的高/低字节(最低8位与最高8位互换)。

若数值以0X开头则表示十六进制值。在某些PLC系统中,布尔型(BOOL)变量可能占用16位存储空间,因此也可能被识别为整型(INT)数据。
其他问题
结构体(STRUCT)不能直接传输,因其本质是模板化的数据结构。若PLC需要接收布尔变量(如报警信号、计数值、触发信号或启动信号),通常需要在PLC程序中配置为单触发(one-shot)模式。
建议为所有跨设备传输的变量建立协议文档,形成类似I/O清单的通信文档,以确保传输过程的可追溯性。

类型转换问题
我们在上面的例子里实际已经用到了类型的转换,INT转换成REAL类型,基本的格式:
TYPE1_TO_TYPE2()
TYPE1是()里面参数的类型,转换源;
TYPE2是转换目标类型,最终返回的结果。
有些PLC提供上100多种类型转换函数,非常方便。下面是一些常用的数据类型转换:

对于最后一项角度和弧度的转换,有些PLC就没有,可以自定义。需要注意的是有些运算必须加转换,例如REAL_TO_INT,将范围空间大的变量类型转换成更小的类型,是必须显式调用相应的转换函数,但是仍然要确保这种转换是PLC所支持的。
而INT_TO_REAL这类可以不用显式写出来,可以忽略,建议初学者还是写上最好:

DATE 由 PLC 硬件内部的电子电路转换而来。该电路从 UTC 时间 1970 年 1 月 1 日 00:00:00(UTC,原子钟基准)开始以秒为单位计时。
请注意,下一次 Y2K(千年虫)类似问题 将发生在 2038 年。具体参考:
我就搞个PLC,“Y2K”(千年虫问题)是个啥?跟我有啥关系?

如何在读取整形数据中的Bit的状态
对于一些数据通信中,往往要获取WORD中的0-15个Bit位的状态,到底是TRUE或者FALSE。
我们知道WORD是由两个字节BYTE,共计16Bit组成,那么要获取每一位的状态值,可以怎么操作呢?
第一种方式最直接,用.即可:

这是最直接的方式,直接用.,后面需要第几位就写多少。本例就是MyUINT的第0位,但是代码有一处不那么仔细看的错误!留言区请给答案哦!
第二种方式是用AND指令:

实际是INT和第0位为TRUE的数值做“与”运算,如果该位的值是TRUE,那么结果自然是TRUE,反之亦然。下图应该更清晰:

明显,运算结果只有两种可能:2#0001和2#0000 ,再转换成BOOL就得出对于Bit位的结果。进一步的
除了上述两种方式,当然还有其他的实现方式,这里留给大家自行思考!

数组Array类型赋值
原书用下图示例展示了如何使用变量控制阀门矩阵。在啤酒厂和乳品厂中,阀门矩阵用于将多个储罐中的液体排入共享管道系统。本示例采用5储罐3管道的阀门矩阵配置(如图所示):

假如第2条管道的第3排罐体没有液体,那么:


上述根据实际的管道ID(1,2,3)和罐体排数(1,2,3,4,5)共同形成一个二维数组,很方便把上述15个罐体的状态指示灯的亮灭表述出来。
如果用一维数组来表述的话,可以这样的定义,下图:

但是原书下面的表述,我个人认为是有问题的:

如果仍然要获取第2条管道的第3排罐体有没有,按照上面这个表述:
PipeMatrix[3,2] := PipeMatrixUINT[3] = 2#010;
最终的结果当然也是对的,因为目前:
PipeMatrixUINT[3]:= 2#010; //Tank 3
假如是下图这种情况:

此时,
PipeMatrixUINT[3]:= 2#110; //Tank 3
如果,再用下面的表达式赋值就不对了:
PipeMatrix[3,2] := PipeMatrixUINT[3] = 2#010;
下图监控的结果是FALSE:(错误结果)

正确的做法:
PipeMatrix[3,2]:=UINT_TO_BOOL(PipeMatrixUINT[3] AND 2#010);
下图监控的结果是TRUE:(正确结果)
