即使对于S7-300和S7-400系列,在第7步中,提供给开发人员的计时器的经典版本也已足够-这些是作为功能块实现的标准IEC计时器和S5计时器(顺便说一下,S7-系列仍然存在) 1500)。但是,在某些情况下,开发人员没有使用标准工具,而是以功能的形式实现了自己的计时器。这种计时器功能是“ IT”编程方法所必需的,在这种方法中,它们不能与技术设备功能块的单独实例,具有输入和输出的相应绑定,而是具有结构数组一起运行。例如,一个离散输入类型结构的数组。或一个聚集结构的数组。这种编程方法已经存在,因为它可以使您认真节省CPU的工作内存,但是,另一方面,使得程序代码难以阅读。第三方程序员并在LAD上仅具有简单的程序外观就无法立即解决它,但是毫无疑问要使用大量索引,数组和用于处理它们的函数;这里没有软件的文档(当然也没有半升)。这些结构的数组通常在函数中进行处理。原则上,没有什么阻止功能块的处理,但是始终存在一个重要的问题-在这种情况下如何使用计时器?标准计时器采用数字(S5)或功能块(IEC)的实例。我提醒您,这是一个问题,需要处理经典的Simatic PLC的结构数组,以及将定时器编号“扭曲”到这些结构中,而且更何况实例很难或根本不可能。因此,我们创建了自己的计时器功能。原则上,对于任何计时器的操作,您只需要知道几件事-输入状态,时间设置以及自激活以来经过了多少时间。对于300和400系列,有两种确定时间的方法。第一种是查看主OB1(OB1本身中有一个对应的变量)或循环OB的运行时,并在每次调用计时器时增加内部时间累加器(假设输入了“真相”)。这不是一个好的选择,因为这对于OB1和循环OB来说是不同的。第二种方法是TIME_TCK系统函数,该函数在每次调用时都返回一个值-中央处理器的内部毫秒计数器。
因此,对于TON类型的计时器(延迟),操作算法如下:- 在响应请求的上升沿,重置输出并记住系统计时器TIME_TCK的当前值
- «» , ( , TIME_TCK 0 (2 ^ 31 — 1), ). , . , «», — «»
- «»,
随着“千”系列的出现,情况发生了一些变化。事实是,S7-1500系列继承了对TIME_TCK系统调用的支持,并且是“站立和吊床”方法的爱好者(您还能如何调用一个仅在处理蠕动索引的同时处理结构数组的程序?)冷静地继续使用自己的最佳做法。S7-1200系列基本控制器基于不同的体系结构,与S7-1500有很多差异。包括缺少TIME_TCK系统调用。在没有足够思维灵活性的开发人员队伍中,不满情绪已经消失了-无法执行旧程序的复制/粘贴。但是,可以使用运行时函数执行确定自上一次调用以来已过去了多少时间的任务。此函数以双精度实数LREAL返回自上次调用以来经过的时间(以秒为单位)。帮助中描述了详细信息。为了内部目的,需要一个附加的MEM变量(也是LREAL类型的)。我将给出函数的第一近似的来源,并给出一些注释。函数声明:FUNCTION "PerversionTON" : Void
VERSION : 0.1
VAR_INPUT
IN : Bool;
PT : Real;
END_VAR
VAR_OUTPUT
Q : Bool;
END_VAR
VAR_IN_OUT
INPrv : Bool;
MEM : LReal;
TimeACC : UDInt;
END_VAR
VAR_TEMP
udiCycle : UDInt;
udiPT : UDInt;
END_VAR
使用输入/输出,一切都很清楚:IN,Q和PT。我将时间设置设置为真正的时间设置,即秒。只是想(但徒劳,但下面有更多内容)。关于InOut区域的变量。由于我们有一个函数,所以没有STAT区域,没有变量在下一次函数调用期间保留其值,因此需要以下变量:INPrv-确定请求的上升沿MEM-系统调用的辅助变量,以在运行时运行TimeACC-时间累加器,它将存储当前运行延迟的微秒数。变量TimeACC,udiCycle和udiPT以UDINT格式指定,无符号整数,4个字节。尽管我将时间指定为实数,并且运行时函数返回的实数是双精度的,但我还是喜欢执行简单的求和运算并与整数操作数进行比较以节省处理器时间。在我的情况下,时间以微秒计。原因很简单-如果将时间粗略地设置为毫秒,然后使用几乎空的OB1(例如,如果整个控制器程序中只调用了一个定时器,仅此而已),则可能会“跳过”周期,该程序有时会运行250μs。但是在这种情况下,时间累加器的最大允许值为4,294秒,几乎为4,295(2 ^ 32-1 = 4,294,967,295)。没有什么可做的,这种“优化”需要牺牲。功能文本。#udiCycle := LREAL_TO_UDINT(RUNTIME(#MEM) * 1000000);
#udiPT := REAL_TO_UDINT(#PT * 1000000);
IF (#IN AND (NOT #INPrv)) THEN
#TimeACC := 0;
#Q := FALSE;
ELSIF (#IN AND #INPrv) THEN
#TimeACC += #udiCycle;
IF #TimeACC >= #udiPT THEN
#Q := TRUE;
#TimeACC := #udiPT;
ELSE
#Q := FALSE;
END_IF;
ELSE
#Q := FALSE;
#TimeACC := 0;
END_IF;
#INPrv := #IN;
ENO := #Q;
前两行是对计时器设置的重新计算,从REAL格式中指定的秒数到微秒数。还确定从上一个程序块调用起经过的时间(以微秒为单位)。此外,该算法如下,并且我已经给出了它:- 在输入IN的上升沿,重置输出Q并重置时间累加器
- 如果继续输入“真相”,我们将时间累加器增加已知的udiCycle值,并将其与时间设置进行比较。如果超过了时间设置,则计时器已工作,将输出设置为“ true”,否则将输出设置为“ false”
- 在对IN输入施加错误输入的情况下,请重置Q输出并重置时间累加器。
在函数的结尾,为了确定输入IN的边,请记住其先前的值。还要给输出ENO(当使用图形语言(例如LAD)使用函数时)的输出Q的值。我们确保该函数正常工作,然后评估其速度就变得很有趣,并在必要时进行改进(乍一看已经有很多计算要进行了。闲置而浪费CPU时间是徒劳的)。为了评估性能,我声明了一个包含1000个计时器数据结构的数组。结构的声明。它的字段复制了定时器功能的输入和输出变量。TYPE "typePervTONdata"
VERSION : 0.1
STRUCT
IN : Bool;
PT : Real;
Q : Bool;
INPrv : Bool;
MEM : LReal;
TimeACC : UDInt;
END_STRUCT;
END_TYPE
在TortureTON全局数据块中声明了一个结构数组:TONs : Array[0..999] of "typePervTONdata";
在组织块OB1中执行以下代码:FOR #i := 0 TO 999 DO
"TortureTON".TONs[#i].IN := "startton";
"PerversionTON"(IN := "TortureTON".TONs[#i].IN,
PT := "TortureTON".TONs[#i].PT,
Q := "TortureTON".TONs[#i].Q,
INPrv := "TortureTON".TONs[#i].INPrv,
MEM := "TortureTON".TONs[#i].MEM,
TimeACC := "TortureTON".TONs[#i].TimeACC);
END_FOR;
宣布了1000个“实例”计时器,每个实例设置10秒的时间。所有1000个计时器开始通过startton标记变量的值来计算时间。我启动控制器的诊断功能(S7-1214C DC / DC / DC,FW 4.4版本,Step7-V16版本),并观察控制器的扫描周期时间。在“ idle”(空闲)(当计时器的输入为“ false”时),平均处理整个数千个对象需要36-42毫秒。在十秒钟的倒计时过程中,该读数会增加约6-8毫秒,有时还会翻转50毫秒。我们看一下功能代码中可以改进的地方。首先,在程序块的最开始的几行:#udiCycle := LREAL_TO_UDINT(RUNTIME(#MEM) * 1000000);
#udiPT := REAL_TO_UDINT(#PT * 1000000);
无论计时器是计时,不计时还是已经计时,它们总是被调用。浪费大量金钱是要用涉及双精度材料的计算来加载不是很强大的1200系列CPU。合理的做法是将这两行都转移到处理倒数的代码部分(如果“真相”继续出现)。还必须将udiCycle计算复制到在计时器输入处处理上升沿的代码中。当输入值为假时,这将减轻计时器的“空闲操作”。实际上,可编程逻辑控制器中的计时器最常工作为“空闲”。例如,触点反弹的过滤时间为数十毫秒。离散输出的控制脉冲为几百毫秒,通常为0.5到1.0秒。监视单元命令执行的时间(例如,阀完全打开的时间)从几十秒到几分钟。生产中的PLC一年24天,每天365天(有时甚至更多天)工作。即,计时器的输入通常为“零”,并且计时器不进行任何计数,或者“单位”到达很长时间,并且计时器已经对所有内容进行了计数。要卸载第二级CPU空闲状态(计时器已经计数),有必要在“输入继续接收真值”阶段进行检查-如果计时器一直在计时并且将输出设置为true。在这种情况下,不应执行任何计算。通常,计时器的输入为“零”,并且计时器不进行任何计数,或者“单位”到达很长时间,并且计时器已经对所有内容进行了计数。要卸载第二级CPU空闲状态(计时器已经计数),有必要在“输入继续接收真值”阶段进行检查-如果计时器一直在计时并且将输出设置为true。在这种情况下,不应执行任何计算。通常,计时器的输入为“零”,并且计时器不进行任何计数,或者“单位”到达很长时间,并且计时器已经对所有内容进行了计数。要卸载第二级CPU空闲状态(计时器已经计数),有必要在“输入继续接收真值”阶段进行检查-如果计时器一直在计时并且将输出设置为true。在这种情况下,不应执行任何计算。要进行这些更改,必须将Q定时器的输出从OUTPUT区域传输到IN_OUT区域,并且输出值将存储在外部变量中(在此示例中,在结构数组中)。优化后,包括声明在内的整个功能代码如下:FUNCTION "PerversionTON" : Void
VERSION : 0.1
VAR_INPUT
IN : Bool;
PT : Real;
END_VAR
VAR_IN_OUT
Q : Bool;
INPrv : Bool;
MEM : LReal;
TimeACC : UDInt;
END_VAR
VAR_TEMP
udiCycle : UDInt;
udiPT : UDInt;
END_VAR
BEGIN
IF (#IN AND (NOT #INPrv)) THEN
#TimeACC := 0;
#Q := FALSE;
#udiCycle := LREAL_TO_UDINT(RUNTIME(#MEM) * 1000000);
ELSIF (#IN AND #INPrv) THEN
IF (NOT #Q) THEN
#udiCycle := LREAL_TO_UDINT(RUNTIME(#MEM) * 1000000);
#udiPT := REAL_TO_UDINT(#PT * 1000000);
#TimeACC += #udiCycle;
IF #TimeACC >= #udiPT THEN
#Q := TRUE;
#TimeACC := #udiPT;
END_IF;
END_IF;
ELSE
#Q := FALSE;
#TimeACC := 0;
END_IF;
#INPrv := #IN;
ENO := #Q;
END_FUNCTION
此后,执行时间得以改善:计时器的空闲时间处理为23 ms,工作过滤时间为37-40 ms。此功能代码不会检查计时器设置的无效值-负值(当材料转换为无符号整数时,该设置将变形)或大于4294.9秒的值(时间设置将溢出并变形)。您必须在代码中控制PT值的值,或者将检查时间设置范围(从0到4294.9秒)的任务委托给顶级操作员系统。通过PLC程序检查范围将处理时间增加到大约45-46 ms(通常,最正确的方法不是将计时器时间设置为REAL格式,而是设置为UDINT格式(以毫秒为单位),然后进行无意义的操作)。此处
提供了带有TIA Portal Step 7版本16环境的计时器的应用程序项目。