MetaFacture软件模型
软件模型概述
MetaFacture 的软件模型用分层结构表示。每一层隐含其下面层的许多特性。软件模型描述基本的软件元素及其相互关系。这些软件元素包含:设备、应用、任务、全局变量、访问路径和应用对象,他们是现代软 PLC 的软件基础,其内部结构如图 2.1 所示,该软件模型与 IEC 61131-3 标准的软件模型保持一致。

该软件模型从原理上描述了如何将一个复杂程序分解为若干小的可管理部分,并在各分解部分之间有清晰和规范的接口方法。软件模型描述了一台可编程控制器如何实现多个独立程序的同时运行,如何实现对程序执行的完全控制等。
该软件模型从原理上描述了如何将一个复杂程序分解为若干小的可管理部分,并在各分解部分之间有清晰和规范的接口方法。软件模型描述了一台可编程控制器如何实现多个独立程序的同时运行,如何实现对程序执行的完全控制等。
-
设备
在模型的最上层是“设备”,MetaFacture 将其称之为“配置”。 “设备”可以等效于一个 PLC 所需的所有软件。针对大型复杂的应用系统,如整个产品线的自动化,可能需要多个 PLC 联机通讯,需要将一个 PLC 与其他多个设备接口实现总线通信。这时,可以将“设备”理解为一个特定类型的控制系统,它包括硬件装置、处理资源、I/O 地址映射和系统内存存储的能力,即等同于一个 PLC。
-
应用
在 PLC 系统中,设备将所有“应用”结合成组,为“应用”提供数据交换的手段。在每一个设备中,有一个或多个“应用”,应用位于软件模型的第二层,在MateFacture中称之为“资源”,“应用”不仅为运行程序提供了一个支持系统,而且它反映了PLC 的物理结构,在程序和PLC 物理 I/O 通道之间提供了一个接口。
应用被分配在一个 PLC 的 CPU 中,因此,可将应用理解为一个 PLC 中的微处理器单元。在应用内定义的全局变量在该应用内部是有效的。应用的主要成员包括全局变量、任务和程序组织单元(POU)等,在 2.3 节会详细对这些应用对象进行介绍。
-
访问路径
访问路径的主要功能是将全局变量、直接表示变量和程序组织单元的输入/输出变量联系起来,实现信息的存储。它提供在不同应用之间交换数据和信息的方法,每一个应用内的变量可通过其他远程配置来存取。
-
通讯功能
提供与其他系统、如其他可编程控制器系统、机器人控制器、计算机等装置的通讯,用于实现程序传输、数据文件传输、监视、诊断等。通常采用符合国际标准的通讯方式(如 RS232、RS485)或工业现场总线如 CANopen、Profibus、EtherCAT、Modbus、Ethernet/IP、DeviceNet 等。
软件模型的特点
该软件模型具有如下特点:
在一台 PLC 中能同时装载、启动和执行多个独立的程序。
-
实现对程序执行的完全控制能力:标准的任务机制,保证了 PLC 系统对程序执行的完全控制能力。传统 PLC 程序只能顺序扫描执行程序,对某一段程序不能按用户的实际要求定时执行,而该软件模型中任务的机制允许程序的不同部分在不同的时间、以不同的比率并行执行,这大大地扩大了 PLC 的应用范围。
-
该软件模型能够适应不同的 PLC 结构:该软件模型是一个国际标准的软件模型,它不是针对具体的 PLC 系统,而是具有很强的适用性。既能适合小型的 PLC 系统,也可适合较大的分散系统。
-
支持程序组织单元的重用特性:软件的重用性是 MetaFacture 的重要优点。
-
支持分层设计:一个复杂的软件通常可以通过一层层的分解,最终分解为可管理的程序单元。
设备
设备位于 MetaFacture 软件模型的最上层,设备代表了一个具体的目标,即硬件对象。该硬件对象可以是控制器,现场总线站点,总线耦合器,驱动器,输入/输出模块或是触摸屏等。每一个设备由一个“设备描述”文件定义,该设备描述文件安装在 MetaFacture 本机系统中,以供插入到设备树下(这里用“设备树”表示设备窗口中的树状列表)。
该设备描述文件确定了设备的相关配置、可编程性、和其他设备的互联性。设备是结构元素,它位于软件模型的最上层,在软件内部是大型的语言元素。
设备
新建一个工程时,系统会自动弹出对话框,如图所示。用户可以在模板选项中选择新建一个空工程或标准工程,在选择标准工程时,需要用户选择实际连接在“设备”中选择硬件设备。

确定后点击“确定”,在项目中可以看到如图中所示的设备树。图中的硬件为MetaFacture v1.0.7.1的版本。

在MateFacture中“PLC 配置”和“任务配置”中都整合在设备树中。当需要进行“PLC 配置”和“任务配置”时,会弹出相应的对话框,供用户进行具体的参数设置。在设备树中,通过各类“设备”对象表示了工程的硬件设备系统,这样可以组建一个包含多种控制器和总线结构的复杂系统。每个在设备树的节点都有名字,名字是可编辑的;还有设备类型来描述该设备。
一个设备的类型决定了其在资源树中的位置和哪些资源能配备给该设备。可供“设备”有如下两种, 可编程设备:“可编程设备”,会自动在该设备的节点下插入一个额外的虚节点: “
PLC 逻辑”这样来表示它是可编程的。在这个“PLC 逻辑“节点下,您可以插入对该设备编程所需要的对象(如应用程序,文本列表等),以及其他的功能对象(如参数管理器等)。
参数型设备:对“参数型设备”不能分配编程对象(如应用程序),而是在该设备的编辑对话框中设置它的参数。请注意,如果一个设备的属性允许的话,它是可以直接在这两种类型中切换的,而不需要把该设备删除后再重新插入到设备树中。
设备的通讯、输入/输出映射等参数在设备对话框(即设备编辑器)中设置,用户可以双击设备树中该设备的节点打开这个对话框。
-
组件管理器
所有的“设备”必须事先在“组件管理器”中进行安装,包管理器在“工具”菜单中可以选择,用户可以对其进行添加或删除包。
针对不同的硬件设备,需要不同的硬件配置参数,其中必须配置的参数有:代码生成器,内存管理,PLC 功能,I/O 模块配置,另外,必须链接库,网关驱动程序以及用于错误消息的 ini-files 和PLC 浏览器的相关信息。此外包中整合了特殊功能,包含了相应的库文件,设备描述文件等。
启动 MetaFacture后,在“工具”选项中选择“包管理器”进行安装目标支撑软件包(TSP-Target Support Package),它包含对于用 MetaFacture 建立的程序控制一个标准平台所必需的所有文件和配置信息。目标文件管理那些配置目标系统所必须的附加文件。几个目标文件可以共享这些附加的文件。目标文件是若干附加定义连接到在目标文件中的条目上,它们决定用户是否能看见和编辑在 MetaFacture 对话框中设置。在目标支撑软件包安装期间,每个目标的目标文件被放入一个 单独的目录中并且注册了它的路径。根据文件*.package 的信息,把相联系的文件拷贝到计算上。目标目录的名称与目标的名称一样。当启动 MetaFacture 时,读入用在目标支撑软件包安装的文件。用项目保存在 MetaFacture 对话框中完成的目标设置。图 2.4 为组件管理器的界面。

如果没安装目标支撑软件包,只能选择“None”,这意味必须在仿真模式下才能运行。
-
设备库
设备库是当用户添加或删除硬件设备信息时所需要做的操作,设备库是设备的数据库,安装后的所有数据被导入至用户本地系统中,供 MetaFacture 开发使用。设备库对话框如图 2.5 所示。

设备库可用于添加所有的硬件设备,通过在该选项中导入相应文件后,使对应数据在本地系统内生成,方便工程中调用。可添加的设备有供应商的 PLC,SoftMotion 运动控制设备(编码器,驱动器等)、现场总线及专用接口等设备,关于现场总线可添加的文件一般也为设备供应商所提供的相应设备描述文件,可分为如 CANopen 的 EDS 和 DCF 文件,EtherCAT 的 XML 文件、IO-Link 的IODD 和 Profibus DP 的 GSD 文件等。图 中具体选项的含义详见表所示:

此选项只负责安装、卸载以及供用户查看设备信息,只有从属设备在设备库添加完后,用户才能在“添加设备”选项卡中找到相应的从属设备,否则不能使用该从属设备。
注意:
安装过程中所引用的设备描述文件和所有的附加文件将会被复制到一个内部地址中。改变原始文档将不会影响已安装的设备。在设备被改变之后,改变设备描述的内部版本号是一种很好的做法。
内部设备库绝不能手动改变。不要从那里复制文件或者复制文件到那里。必须需要使用“设备库”对话框来重装,添加或者删除设备。
设备编辑器
设备编辑器是用于配置设备的对话框。通过选中设备图标
,通过鼠标右键“编辑对象”命令,或者通过在设备窗口中双击设备对象条目打开。 主对话框是根据设备类型,以设备名称来命名的 ,它提供了包含以下子对话框的选项卡,如表 2-2 所示。

此表所罗列的为基本设备参数,不同的硬件设备可能与上表参数有略微的差别。
应用
应用在 MetaFacture中也称之为“资源”,是指在硬件设备(如 PLC)上运行程序时所需要的对象集合。这些对象与硬件设备平台无关,用户可以在程序组织单元(POU)中管理它们。然后在设备窗口中将它们实例化,分配到具体的设备中。这种方法符合面向对象编程的思想。
应用的对象包括任务,程序组织单元、任务配置、全局变量、库管理器和采样追踪等。在 MetaFacture 中资源对象只能在设备树中进行管理。在设备树中添加对象后,需要按一定的“规则”与被控设备进行映射。对象(如库和全局变量列表等)在工程中的有效范围,会依据设备树中应用和设备对象的层级关系而定,一般来说,一个应用中的对象对其“子应用”也有效,可以被使用。关于具体应用对象,在 2.4 节会对其进行详细介绍。
在一个设备内可有一个或多个应用,如图所示,该设备中有 App1 和 App2 两个应用。

任务
概述
一个程序可以用不同的编程语言来编写。典型的程序由许多互连的功能块组成,各功能块之间可互相交换数据。在一个程序中不同部分的执行通过“任务”来控制。“任务”被配置以后,可以使一系列程序或功能块周期性地执行或由一个特定的事件触发开始执行程序。
在设备树中有“任务管理器”选项卡,使用它除了声明特定的 PLC_PRG 程序外,还可以控制工程内其他子程序的执行处理。任务是用于规定程序组织单元在运行时的属性,它是一个执行控制元素,具有调用的能力。在一个任务配置中可以建立多个任务,而一个任务中,可以调用多个程序组织单元,一旦任务被设置,它就可以控制程序周期执行或者通过特定的事件触发开始执行。
在任务配置中,用名称、优先级和任务的启动类型来定义它。这启动类型可以通过时间(周期的,随机的)或通过内部或外部的触发任务时间来定义,例如使用一个布尔型全局变量的上升沿或系统中的某一特定事件。对每个任务,可以设定一串由任务启动的程序。如果在当前周期内执行此任务,那么这些程序会在一个周期的长度内被处理。优先权和条件的结合将决定任务执行的时序,任务设置界面如图 2.7 所示。

由于 MetaFacture 在任务配置时有如下的属性,编程者需遵循如下规则:
-
循环任务的最大数为 100;
-
自由运行任务的最大数为 100;
-
事件触发任务的最大数为 100;
-
主程序PLC_PRG 可能会在任何情况下作为一个自由程序执行,而不用手动插入任务配置中;
-
处理和调用程序是根据任务编辑器内自上而下的顺序所执行的。
PLC 程序执行过程
下图详细的描述了在 PLC 内部执行程序的完整流程,主要的流程有输入采样、程序执行和输出刷新这三个部分组成。

输入采样
每次扫描周期开始时,PLC 检测输入设备(开关、按钮等)的状态,将状态写入输入映像寄存区内。在程序执行阶段,运行系统从输入映像区内读取数据进行程序解算。需要特别注意的是输入的刷新只发生在一个扫描开始阶段,在扫描过程中,即使输出状态改变,输入状态也不会发生变化。
执行程序
在扫描周期的执行程序阶段,PLC 从输入映像区或输出映像区内读取状态和数据,并依照指令进行逻辑和算术运算,运算的结果保存在输出映像区相应的单元中。在这一阶段内,只有输入映像寄存器的内容保持不变,其他映像寄存器的内容会随着程序的执行而变化。
输出刷新
输出刷新阶段亦称为写输出阶段,PLC 将输出映像区的状态和数据传送到输出点上,并通过一定的方式隔离和功率放大,驱动外部负载。
PLC 在一个扫描周期内除了完成上述三个阶段的任务外,还要完成内部诊断、通信、公共处理以及输入/输出服务等辅助任务。
PLC 重复执行上述 1)至 3)的过程,每重复一次的时间就是一个工作周期(或扫描周期)。
由 PLC 的扫描方式可得知,PLC 为了迅速相应输入输出数据的变化,完成控制任务,扫描时间较短,PLC 的工作周期一般都控制在 ms 数量级,因此需要开发稳定、可靠、响应快的实时系统供 PLC 运行系统所用。
由于 PLC 采用循环的工作方式,输入信号只会在每个周期的开始阶段进行刷新,输出在每个工作周期的结束阶段进行集中输出,所以必然会产生输出信号相对输入信号的滞后现象。从 PLC的输入端有一个信号输入发生到变化到 PLC 的输出端对该输入信号的变化做出反应需要一段时间。滞后延时时间是设计 PLC 控制系统时应了解的一个重要参数。
通常,滞后延时时间的长短和以下因素有关:
-
输入电路的滤波时间,它由硬件 RC 滤波电路的时间常数决定,通过改变时间常数可调整输入延迟时间。如图 2.9 为某供应商的数字量输入模块技术参数,其中有一项名为“InputFilter”的技术参数,该参数即为该输入模块的滤波时间,图 2.9 中该滤波时间为 3ms。

-
输出电路的滞后时间,与输出电路的方式有关系,继电器输出方式的滞后时间一般为10ms 左右,晶体管输出方式滞后时间小于 1ms。
-
PLC 循环扫描的工作方式。
-
用户程序中语句的安排。
为了能够让读者更好的理解这整个过程,如下通过一个简单的梯形图程序例子来反应其输入输出及滞后现象的工作原理。程序逻辑如图所示。

bInput 与外部的输入按钮有硬件映射关系,当按钮被按下时 bInput 为 ON ;bOutput 与外部继电器的线圈也有硬件映射关系,当 bOutput 为 ON 时,继电器的线圈也会得电。
在 PLC 的内部,其处理的关系如图 2.11 所示,当输入按钮按下时, bInput 不会马上被置为 ON,因为输入采样只有在一个工作周期的开始阶段才能被程序执行,由于该按钮信号已经过了采样阶段,通常会在下一个周期开始阶段才被执行。在图 2.10 的程序中将 bInput 的状态赋值给 bOutput,由于在程序运行期间存在一定的程序计算,所以需要一定的程序处理时间 bOutput 才会被置为 ON。由于输出刷新是发生在程序处理的最后阶段,故在该周期的最后阶段 bOutput 通过输出刷新功能将其数值传递至实际硬件,最终线圈才能得电。图 2.11 是比较理想状态,最终的输出只有一个周期的延迟。

图 为比较理想的情况,那么也要考虑到比较糟的情况,当一个周期的输入采样刚刚结束的时候,此时外部输入按钮为 ON,由于需要在下一个周期开始时输入信号才能被载入至输入映像区,而实际输出则要等到第二个周期结束时才能被载入输出映像区,故整个过程如下图所示,在这种情况输出的延时接近于 2 个周期,这种情况为最迟的输出情况。

-
任务的执行类型
在任务配置树的最顶端有条目“任务配置”。其中的内容是当前定义的任务,每个通过任务名代表。特定任务的 POUs 调用没有显示在任务配置树中。
针对每个独立的任务可以对其进行执行的类型编辑及配置。包括固定周期循环、事件触发、自由运行和状态触发 4 种类型。详见图所示。

-
固定周期循环-Cyclic
根据程序中所使用的指令执行与否,程序的处理时间会有所不同,所以实际执行时间在每个扫描周期都发生不同的变化,执行时间有长有短。通过使用固定周期循环方式,能保持一定的循环时间反复执行程序。即使程序的执行时间发生变化,也可以保持一定的刷新间隔时间。在这里,也推荐大家优先选择固定周期循环任务启动方式。
例如,假设将程序对应的任务设定为固定周期循环方式,间隔时间设定为 10ms 时,实际程序执行的时序图如图所示。

如果程序实际执行时间在规定的固定周期循环设定时间内执行完,则空余时间用作等待。如应用中还有优先级较低的任务未被执行,则剩下的等待时间用来执行相对低优先级的任务。任务的优先级在后文会有详细的说明。
-
自由运行-Freewheeling
程序一开始运行任务就被处理,一个运行周期结束后任务将在下一个循环中被自动重新启动。该执行方式不受程序扫描周期的影响。即确保每次执行完程序的最后一条指令后才进入下一个循环周期。否则不会结束该程序周期。图 2.15 为自由运行执行顺序的时序图。

由于自由运行执行方式因为没有固定的任务时间,所以每次执行的时间可能都不一样。故不能保证程序的实时性,在实际的应用中选用此方式的场合较少。
-
事件触发-Event
如果事件区域的变量得到一个上升沿,任务开始。
-
状态触发-Status
如果事件区域的变量为 TRUE,任务开始。
状态触发方式与事件触发功能类似,区别在于状态触发的触发变量只要为 TRUE 程序就执行,为 FALSE 则不执行。而事件触发只采集触发变量的上升沿有效信号。
图 中针对事件触发和状态触发分别进行了比较,绿色实线为两种触发方式选择的布尔变量状态,表 2-3 为比较的结果。

在采样点 1~4(紫色)不同类型的任务展示了不同的反应。这个具体的事件为 TRUE 完成了状态驱动任务的条件,然而一个事件驱动任务需要事件从 FALSE 变为 TRUE。如果任务计划的采样频率过低,事件的上升沿可能检测不到。

-
系统事件
用户可选择的系统事件是根据实际的硬件目标系统而定的,由目标系统对应的库文件提供相应的系统事件,所以不同的目标硬件设备对应的系统事件可能会不同。但通常来说,通用的系统事件有:停止,开始,登入,改变等。在任务配置中,可以对任务配置中的系统事件进行设置。

用户可以通过鼠标选择“任务配置”->“系统事件“选择“添加时间处理器”进入图 2.17 中显示的界面。选择 “添加事件处理”按钮可以进行添加系统事件。打开后的界面如图 2.18 的 a)所示。

a) 添加事件处理 b)具体事件
可选择的“事件”类型如图所示。使用时必须在“函数调用”这里必须新建一个函数名,而不能使用 POU 中已经存在的函数。“实现语言”为对应函数的编程语言。设置完点击“确定”。
-
任务优先级
MetaFacture 中的可以对任务的优先级进行设置,一共可以设 32 个级别(0~31 之间的一个数字, 0 为最高优先级,31 为最低优先级)。当一个程序在执行时,优先级高的任务优先于优先级任务低的任务,高优先级任务 0 能中断同一资源中较低优先级的程序执行,使较低优先级程序执行被放缓。
注意
在任务优先级等级分配时,请勿分配具有相同优先级的任务。如果还存在其他任务视图先于具有相同优先级的任务,则结果可能不确定且不预知。
如果任务的类型为“Cyclic”,则按照“间隔”中的时间循环执行,具体设置如下图 2.19 所示。

【例 2.1】假设有 3 个不同的任务,分别对应三种不同的优先等级,具体分配如下。
:任务 1 具有优先级 0 和 循环时间 10 ms ,
:任务 2 具有优先级 1 和 循环时间 30 ms ,
:任务 3 具有优先级 2 和 循环时间 40 ms。
在控制器内部,各任务的时序关系如图 2.20 所示,具体说明如下:
0~10ms:先执行任务 1(优先级最高),如在本周期内已将程序执行完,剩余时间执行任务2 程序。但是如果此时任务 2 没有被完全执行完,但时间已经到了第 10ms 时,由于任务 1 是每10ms 执行一次的且优先级更高,此时将会打断任务 2 的执行。
10~20ms:先将任务 1 的程序执行完毕,如有剩余时间,再执行上个周期为完成的任务 2。
20~30ms:由于任务 2 是每 30ms 执行一次的,在 10~20ms 之间任务 2 已经全部执行完毕,此时不需要再执行任务 2,只需将优先级最高的任务 1 执行一次即可。
30~40ms:与之前类似。
40~50ms:此时出现了任务 3,任务 3 的优先级更低,所以只有在确保任务 2 彻底执行完后,才能执行任务 3。

-
看门狗
看门狗是一种控制器硬件式的计时设备,MetaFacture 内可以通过“任务配置”对其进行使能,默认配置已将看门狗功能禁用。
看门狗的主要功能是监控程序执行时出现的异常或内部时钟发生的故障。如当系统出现死机或当程序进入死循环时,看门狗计时器就会对系统发出重置信号或停止 PLC 当前运行的程序。我们可以形象的将它理解为一只小狗需要主人定时的去给它喂食,如果超过规定的时间没有喂,则他马上就会饿。要配置看门狗,必须定义两个参数,时间和灵明度,看门狗的配置如图所示。

时间
MetaFacture 针对每个任务可以配置独立的看门狗。如果目标硬件支持长看门狗时间设置,则可以设置上限和下限。默认的看门狗时间单位为毫秒(ms)。如果程序执行周期超过看门狗触发时间,将激活看门狗功能,并将中止当前任务。
灵敏度
“灵敏度”用于定义必须在控制器检测到应用程序错误之前发生的任务看门狗例外数。默认为1。

最终的看门狗触发时间=时间×灵明度。如果程序实际执行时间超过看门狗触发时间,系统则自动激活看门狗。
例如,循环时间设定为 10ms,灵明度设为 5,则实际看门狗触发的时间为 50ms,一旦任务的执行时间超过 50ms,则立即激活看门狗并将任务中止。
看门狗功能通常用于一些对实时性要求比较高或者安全等级比较高的应用场合,主要为了防止 PLC 死机或防止程序进入死循环,一旦出现故障会将所有输出断开并且重启 PLC,这样就能够有效的保护外围设备。
-
任务运行状态监视
每个任务可以直接启用或停用,系统会自动配置一个任务监视器,当进入在线模式后,用户可以使用系统自带的监视器对任务的平均/最大/最小循环时间等任务执行相关参数进行监控。如图2.22 所示。

在项目初期阶段,可以使用该功能测试程序最大/最小/平均循环时间,用于测定程序的稳定性及对程序设定任务周期时间的优化。监视窗口中每个参数的具体定义见表。


了解了如上各时间的定义后,应遵循如下的时间设定关系,按照此设定方法可以更好的优化程序任务周期及看门狗时间,保障程序的稳定性和程序的实时性。
看门狗触发时间>固定周期循环时间>程序最大循环时间
循环时间比固定周期循环时间长的情况下,CPU 会检测出程序有超出计数,此时,会影响程序的实时性。如果程序循环时间比看门狗时间设定长的情况下,CPU 会检测出看门狗故障,会停止程序的执行。
-
多子程序的运行
在实际的工程项目中,通常可以将程序按控制流程或者按照设备的对象分割成很多子程序,据此,设计人员将可以按各处理单元分别进行编程。如下图 2.23,以控制流程将主程序拆分为多个不同流程的子程序,拆分的目的主要是使主程序调理更清晰,并且方便今后的调试。

图右半部份是按流程进行分类的各子程序 PRG1,PRG2..PRGn,图的左半部分为主程序 PLC_PRG,在主程序中可以分别调用 PRG1..PRGn 的子程序。
多子程序运行的方式有两种,第一种在任务配置中添加子程序。第二种方法是在主程序中调用子程序,也是比较常用及灵活的一种方式,如下,分别对两种方式的进行详细说明。
-
任务配置中添加子程序
用户可以通过在任务配置页面中添加子程序实现多程序的运行。按子程序的执行顺序依次点击 “Add Call”进行添加子程序。如图 2.24 所示,添加后,对应的任务即按用户所指定的从上至下的顺序循环执行,也可以通过“Move Up”和“Move Down”功能再对顺序进行手动编辑。

使用此种方式只要对应的任务被执行,该子程序也会自动被加载执行。
-
主程序 PLC_PRG 中调用子程序
PLC_PRG 被系统默认为主程序,从某种意义上可以理解为汽车的电瓶,在生产汽车时,将各个零件进行组装,相当于子程序的编写;当汽车组装完成时,就要检查汽车是否可用,如果想启动汽车,就必须通过电瓶来启动汽车的各个部件,如发动机、车灯等,电瓶就相当于启动汽车的入口点。通过这样调用程序使操作性更强及使程序更灵活,能够在程序中加入判断语句等,且能实现嵌套。
PLC_PRG 是一个特殊的 POU,其默认的运行方式为“FreeWheeling”。系统默认每个控制周期调用该 POU,用户不需要对其进行额外的任务配置,在任务配置中也看该 POU 相应的配置。
用户可以通过它来实现对其他子程序的调用,更可以在调用时添加必要的条件选择,或实现子程序嵌套,使程序调用更为灵活。
如果要实现图 2.23 中的调用关系,可以在主程序 PLC_PRG 中写入如下代码。

如图中所示,主程序为 PLC_PRG,该主程序使用的是结构化文本编程语言,其中的程序的内容如下,
POU1();
POU2();
上述程序的主要功能是分别调用执行了 POU_1 和 POU_2 子程序。而 POU_1 中又分别调用了POU_3 和 POU_4,实际 PLC 内部实际按如下顺序执行程序,
-
PLC 先执行子程序 POU_1,
-
由于 POU_1 中依次调用了 POU_3 和 POU_4,故先执行POU_3,
-
执行 POU_4,POU_1 执行完成,
-
最后执行 POU_2,完成一个完整任务周期。
重复上述步骤 a)至 d)的即为 PLC 内部的执行顺序。
库文件
什么是库文件,它是怎么支持MetaFacture中的项目,本节会对库文件进行详细介绍。
-
概述
库文件用于存放MetaFacture中可多次使用的程序组织单元(POU)。这些POU可以从已有的项目中复制到库中,也可以是用户新建库项目自己定义库。
项目中已经使用的库文件视图如图2.26所示。 如果在MetaFacture下的库中存放有用户希望多次调用的功能块、函数或程序时,可以节省大量的编程时间,并提高效率。MetaFacture标准软件包中已经包 括 标 准 库 文 件 , 如 图 2.26 中 的Standard,3.5.2.0(System)即为标准库。

库文件除了是函数、功能块和程序的集合,其中还包含一些特殊定义的结构体,枚举类型等。从功能上分可以将库文件分为系统库文件、应用库文件、厂家自定义库文件以及IEC动作库。
默认的函数库文件是“.library”。
-
系统库文件
该库文件是一个支持MetaFacture软件系统的文件,它包括对软件结构和语法编写的支持以及I/O的支持。通常该库文件会在软件启动后自动导入到控制器中,不需要手动添加。
-
应用库文件
支持基本应用的文件库:
-
Util:包含了各种数学运算功能,位操作指令及控制器等功能。
-
Standard:包括定时器、计数器、边沿检测及双稳态触发器等函数及功能块。
该功能是作为一台PLC必备的功能,因此在打开MetaFacture后会自动调入该库文件。其他的需按要求导入的应用库文件如:一些Toolbox、PLCopen等,这些库文件都需要用户根据实际需求来进行添加。如图2.27所示。

-
厂商自定义库文件
它是根据不同生产厂商硬件设备的环境而配置的应用库。通常,只有使用该生产厂商的硬件才能匹配对应的库文件。故使用前需要详细阅读该库文件的说明文档。
-
IEC 动作库
IEC动作库如“Iecsfc”,其中包含SFC动作控制的库文件,如图2.28所示。

-
库文件的管理
库管理器显示与当前项目有关或调用的所有库。库的POU、数据类型和全局变量,都可以像普通定义的 POU、数据类一样。库管理器通过“Library Manager”(库管理器)命令打开,包括库在内的有关信息和项目一起进行保存。
如需在计算机上安装其他供应商所提供的库文件,则需要使用到库文件管理。库文件管理是通过使用菜单命令“工具”-->“库”来进行添加的,图2.29为库文件管理视图。

在图中的主窗口显示的是该MetaFacture目前已安装的库文件,并可以看到提供这些库文件的供应商。已安装的库文件已按功能类型进行分组,例如途中罗列的有应用类、通讯类、控制器类、设备类、系统类等。
只有在库文件管理器中安装过的库文件才能在项目中被调用,如果库文件不存在或版本过老,编程人员需要通过“安装”按钮进行手动安装供应商所提供的库文件。
-
库文件的安装
在使用一个库文件之前,必须先在“库”对话框中对其进行“安装”。安装后才可以在项目中调用该库。MetaFacture 共有三种类型的库文件可供用户安装,如图,具体区别如下所示。

- 编译的库文件
“*.compiled-library”是被保护的库文件,供应商由于出于对源代码知识产权的保护,编程人员不能直接打开库文件获取其源代码,但拥有权限正常调用库中所有的函数及功能块。
- 标准库文件
所有包含外部指令和内部功能块的执行代码都存放在“*.library”之中,该库文件格式也是MetaFacture标准类型的功能库文件格式。可使用MetaFacture打开库文件对其中的功能块或外部指令的执行程序进行修改。但当相应库装载到PLC中后,占用用户程序空间较大。
MetaFacture 库文件
“*.lib”是 MetaFacture 的库文件标准格式。MetaFacture 为了做到向下兼容,
-
库文件的调用
安装过库文件后,需要在项目中对库文件进行添加才能调用其中的函数或功能块等,此时需要使用库管理器实现此功能,具体步骤如图 2.31 所示。

-
在项目中双击“库管理器”;
-
添加库,点击“添加库”即可在库管理器中找到库文件对应的函数及功能块;
当选择“添加库”后,用户即可选择之前已安装过的库文件,并将其导入至项目中,添加库视图如图 2.32 所示,用户可以根据供应商名,库文件功能及版本号进行选择分类。

-
查看安装后库文件中的函数及功能块。
在标准项目或库文件项目中可以调用其他库(即库的调用),对调用的层数没有限制,可多层嵌套。如果在库管理器中添加了一个包含了其他库的库,则引用的库也将被自动添加。
注意
标准库 Standard.library 在工程建立时被自动载入,无需用户手动载入。
库文件只要被载入,即使不调用其中的功能块,也会占用用户程序空间,建议不要载入不使用的库文件
浏览库文件信息
如在库管理器中可以直接查看 CTU 增计数功能块的简单说明,如图 2.33 所示。在“输入/输出” 选项卡中可以了解到各输入/输出变量的类型及注释。

在 “文档”选项卡中能够直接浏览查看帮助文档,省去查阅帮助手册的时间。

-
库文件的多版本
一个函数库的不同版本可以同时安装在一个系统上。 在一个工程中可以包含相同函数库的不同版本。应用程序使用哪个版本是按照如下规定进行的:
-
如果在相同的函数库管理中多个版本都有相同的级别,那么将根据当前的 “属性”决定哪个版本将会被调用(默认的是最新版本)。
-
如果在同一个函数库管理中一个函数库的不同版本有不同的级别,特殊访问的库文件通过添加适当的命名空间决定使用哪个。
-
库文件的属性
针对库文件唯一访问性及代码的安全性,下文会针对这些特性逐一进行讲解。
-
唯一访问性
在一个项目中如果有几个模块或变量具有同一个名字,那么访问具有相同名字变量的路径必须是不同的(即“唯一访问”),否则就会发生编译错误。该规则对本地工程、库、被其他库引用的库中的模块或变量都适用。用户可以通过在模块或变量名前加上名字空间来实现唯一访问。
在“库属性”中,可以定义该库的默认名字空间,如果没有明确定义,名字空间则等同于库名称。当创建一个库工程时,可以在属性对话框中设置它的名字空间;当该库被其他工程引用时,也可以在属性对话框中修改名字空间。
例如:假设库“Lib1”的命名空间为“Lib1”,表 2-5 中左列中是变量 var1 在项目中定义的位置,右列是如何通过命名空间对 var1 进行唯一访问。
表2-5 库的唯一访问

-
库文件访问的安全性
MetaFacture 对开发者库文件源代码保护,拥有其安全功能,在“工程设置”中的“安全”设可以对其进行设置。如图置中 2.35 所示。

图 2.35 密码设置
比较常用的是采用“密码”的方式进行加密,设置完密码后,每次进入系统,系统都会提示要求输入密码,如图 2.36 的 a)所示,如果输入密码错误则不能打开库文件,系统也会有相应的加载错误提示,如图 2.36 的 b)所示。

a ) 输入密码提示框 b ) 输入密码错误,加载失败
图 2.36 密码提示
使用此种方式加密,如果用户端不知道密码,则不能使用及打开该库文件。
-
创建库文件
除了生产厂商提供的库文件,用户自己也可以根据工作经验,把常用的函数或功能块整理出来,建立属于自己的库文件,便于应用至其他项目,具体创建库文件的步骤如下:
-
建立库文件的准则
在用户需要开发一个完整的 MetaFacture 库文件之前,请先遵循如下的规则:
定义一个合适的库文件名;
-
基于 MetaFacture的库开发模板进行开发,以保持库文件格式的一致性;
-
尽可能的输入详细的工程信息;
-
合理的借鉴其他已有的库文件格式及规范 ;
-
开发是需要设计可供外部和内部使用的接口;
-
加载用户界面友好的故障处理;
-
选择合适的手段保护开发者的源代码;
-
变量名应遵循匈牙利命名法,使程序更整洁及保持一致性;
当需要修改库文件时,在编译新版本之前,需要考虑到 I/O 接口的兼容性问题,防止使用新库文件时,I/O 接口不匹配,导致编译出错。
-
开始创建库文件项目
使用菜单命令“文件”-->“新建工程”-->“函数库”--> “MetaFacture Library”,在图2.37 中的“3”处输入库的名称,选择完毕后,点击“确后定则”会自动生成一个新的库文件,具体步骤如图 2..37 所示。
图 2.37 中第 2 步选用的是“MetaFacture library”,此类型为 MetaFacture 标准库。
当创建一个库工程时,如果引用了其他库,可以在每个被引用库的“属性”栏中定义它被引用后的行为。请注意下面几点:

图 2.37 库文件的创建
-
在库管理器中,被调用的库一般是缩进列在引用它的“父”库下面;用户也可以设置不显示被引用的库,“隐藏”它们。
-
如果一个库被其他库引用 ,就应当考虑到这个库由于被其他库引用而被包含进一个工程中时,它的行为如何。这个“行为”包括版本处理、名字空间、可视性以及访问属性。这些都可以在被引用库的属性对话框中设置,以后当该库被包含进工程时就会依照这些设置。
-
可以创建“容器”库(MetaFacture Container library)。“容器”库指的是,该库本身没有何内容,就是引用了一些其他的库,就像一个“容器”。创建“容器”库一般是为了通过引用它,任从而很方便的引用所有被它引用的库。为了方便使用“容器”库,用户可以在创建它们时激活“发布(Publish)…”选项,这样可以将它们设为“顶层库”,从而可以在使用时忽略它们的名字空间。但是在激活“发布…”选项时请注意,应该只对“容器”库激活该选项。
-
进入 POU 设置主界面
建立库文件后,则可显示主界面,其框架主要包括枚举数据类型,功能块,函数全局变量,接口,结构体等,用户可以基于该结构在其文件夹下进行内容的扩展,如图 2.38 所示。

图 2.38 库文件主框架
-
库管理器,用户也可以在此添加和调用其他的库文件。
-
工程信息设置,双击“
”选项,可以编辑库文件的所属公司、库文件标题,版本号,作者名和库的简要说明等信息。工程信息视图如图 2.39 所示。

图 2.39 库文件项目工程信息
在工程信息中,图 2.39 中加粗字体的部分(公司、标题和版本)必须要填写。 “库类别” 为了便于今后在“库”和“库管理器”对话框中,根据“类别”选项来排序,方便分类查找。
-
建立 POU
在库文件中建立自己的函数及功能块:编程人员可在标准 MetaFacture 的框架下将自己编写好的库文件对应内容添加在其对应文件夹下。
如图 2.40 所示,将用户自定义的计数功能块 FB_COUNT 添加在 Function Block 文件夹下,该文件夹名可以根据编程人员实际分类进行编辑。

图 2.40 库文件中新建功能块
当完成库文件编写后,需要对其进行编译检查及保存。
-
“编译”--> “检查所有池对象”。
-
确定没有错误后,点击如图 2.41 中的黄色按钮“
”保存工程,并装入库后,即可实现对库文件的保存。

图 2.41 编译及保存库文件
上述步骤完成后,即可保存库文件,并退出。在今后新建的项目中即可安装此库文件,通过选择“工具”菜单中的“库”来进行安装。安装完毕后,需要在项目中的库管理器中进行添加,添加后可以可根据名称分类,来最终选择要添加的库。添加后即可在项目中查看其中的功能块,如图2.42 所示。

图 2.42 加入的用户库文件
-
删除库
选择鼠标右键菜单“删除”,可以从工程和库管理器中删除已添加的库。
2.3.3 全局变量和局部变量
变量定义的范围确定其在哪个程序组织单元(POU)中是允许被调用的,从范围上来分可分为全局或局部。每个变量的范围由它被声明的位置和声明所使用的变量关键字所定义。
-
全局变量
在程序组织单元(POU)之外定义的变量称为外部变量,外部变量是全局变量。全局变量可以为本文件中其他程序组织单元所共用。全部程序可共享同一数据,它甚至能与其他网络进行数据交换。其原理示意图如图 2.45 所示。bIn1 能同时给程序 A 和程序 B 共用。

图 2.45 全局变量
一个系统中不能有相同名称的两个全局变量。所有的全局变量都在全局变量列表中进行声明,全局变量提供了两个不同程序和功能块之间非常灵活的交换数据的方法。全局变量的关键字如下。
VAR_GLOBAL
END_VAR
用户可以通过添加全局变量列表实现全局变量的添加。鼠标选中“Application”右键选择“添加对象”-->“全局变量列表”,系统则会自动弹出全局变量列表,用户只需输入列表名称,点击“确认”即可,具体步骤请参考图 2.46。

a ) 添加全局变量列表 b ) 输入列表名称
图 2.46 全局变量列表添加
-
局部变量
在一个程序组织单元(POU)内定义的变量都为内部变量,它只在该程序组织单元内有效,这些变量称为“局部变量”,其结构原理图如图 2.47 所示。

图 2.47 局部变量
用户使用局部变量后,在执行多个独立的程序时,编程时无需理会其他独立程序中的同名变量,他们之间没有映射关系,互不影响。局部变量的关键字如下。
VAR
END_VAR
2.3.4 访问路径
访问路径用于将全局变量、直接表示变量、功能块的输入/输出和局部变量联系起来,实现信息的存储。它提供在不同配置之间交换数据和信息的方法,每一个配置内的许多指定名称的变量可通过其他远程配置来存取。
访问路径功能已经集成在 MetaFacture 内部,用户不需要对其进行操作,所有的存取操作会在MetaFacture 的后台自动进行。
2.4 程序组织单元
程序组织单元(Program Organization Unit,POU)由声明区和代码区两部分组成,是用户程序的最小软件单元,它相当于传统编程系统中的块(Block),是全面理解新语言概念的基础。按功能分程序组织单元(POU)可分为函数(FUN)、功能块(FB)和程序(PRG)。
在“POU 窗口”中管理的编程对象在整个工程范围内都有效,且可以被工程中所有的“应用” 通过任务配置来调用,即实例化。在“设备窗口”中管理的编程对象(即针对特定应用的编程对象),只能被本应用来使用,或被本应用的“子应用”实例化后使用。
程序组织单元的标准部分(如函数、功能块、程序和数据类型等)都由中科时代公司提供,集成在库文件中。用户也可通过自己的逻辑思想自行设计程序组织单元,再对其进行调用和执行。
用户可以在项目中使用右键菜单的命令“添加对象”,选择“POU”,会弹出如图2.48 所示的对话框,用户可以选择添加程序,功能块或函数,下拉菜单中可以选择对应的编程语言。添加后,可以在左边的项目设备树中查看程序组织单元括号内对应的属性,FB 为功能块,FUN 为函数,PRG 为程序。

图 2.48 POU
程序组织单元具有如下特点:
-
可对每个应用领域设置用户的功能块库,便于工程的应用。例如,建立运动控制功能块库等;
-
可对功能块进行测试和记录;
-
能够提供全局范围内的库存取功能;
-
可重复使用,使用的次数无限制;
-
可改变编程,用于建立功能块网络;
2.4.1 程序组织单元结构
一个完整的 POU 由如下三大部分组成,结构图如 2.49 所示。
-
POU 类型及命名
-
变量声明部分
-
代码指令部分(POU 主体)

图 2.49 POU 的组成
在图 2.49 中,从具体功能来看,分别能构成左边的程序(PRG),中间功能块(FB),右边函数(FUN)。从每个功能的结构来看,都可以将其分为声明部分和代码部分。
用户声明的所有变量最终是给程序组织单元所用,变量声明中可声明接口变量和本地变量,通过下面的例子对整个程序组织单元做个初步的讲解。

图 2.50 POU 举例
图 2.50 中的结构可以用在功能块(FB)或程序(PRG),在此需要特别注意的是函数(FUN)只有一个返回值,故上述结构不适用于函数(FUN)。VarIn 作为输入变量,VarOut1 和 VarOut2 作为输出变量,VarLocal 作为本地变量。通过图形化标准语言中调用的效果如图 2.51 所示,用户可以根据图中的变量类型调用该功能块/程序。

图 2.51 图形化效果图
1.声明区
变量声明区是用来指定变量的名称、类型和赋初始值的区域。
变量声明编辑器用来声明 POU 变量以及声明数据类型。声明部分通常是文本编辑器,也可采用表格编辑器。所有将要在这个 POU 中的使用的变量则在 POU 的声明部分中声明,这些变量包括:输入变量、输出变量、输入/输出变量、本地变量、添加的变量和常量。声明格式都是基于 IEC61131-3 标准的,变量的声明采用下面的格式:
<标识符>{AT<Address>}:<数据类型>{:=<初始化>}:
{}中的部分是可选部分。
2.代码区
在代码区,MetaFacture 支持两种文本语言:指令表语言(IL)和结构化文本(ST)。四种图形化语言:功能块图(FBD)、梯形图(LD)、顺序功能图(SFC)以及连续功能图(CFC)。用户可以选择一种或几种语言在主体部分来进行程序设计。主体编辑器界面如图 2.52 所示,该图中采用的是梯形图(LD)程序语言。

函数
为了 PLC 编程语言的应用,函数(FUN)也被定义为一个程序组织单元。函数是一种可以赋予参数,但没有静态变量的程序组织单元。即用相同的输入参数调用某一函数时,该函数总能生成相同的结果作为函数值(返回值)。函数的一个重要特性是它们不能使用内部变量存储数值,这点与功能块完全不同。
函数(FUN)是没有内部状态(没有运行时的内存分配)的基本算法单元。也就是说只要给定相同的输入参数,调用函数必定得到相同的运算结果,绝对没有二义性。我们平时使用的各种数学运算函数,如 sin(x)、sqrt(x)等,就是典型的函数类型。
函数是有至少一个输入变量、无私有数据、仅有一个返回值的基本算法单元。MetaFacture 的标准库中已经预有标准函数。函数可以被函数、功能块、程序所使用。
1. 函数的表示和声明
(1) 自定义函数的表示
函数内部逻辑部分可以使用 6 种编程语言中的任意一种。函数名即是函数的返回值,也可以理解为是函数的输出值,如下为函数的语法表达式。
FUNCTION <函数名/返回值>: <返回值数据类型>
VAR_INPUT
… (*函数的输入接口变量声明*)
END_VAR
VAR
… (*函数的本地变量声明*)
END_VAR
…; (*函数内部逻辑*)
(2) 函数中变量的声明
用户自定义函数时,应注意如下事项:
- 函数可以拥有很多个输入变量,但只能有一个返回值(输出变量)。但是并没有限制返回值的数据类型,所以可以为一个结构体作为返回值;
- 函数的重要特征是它们不能在内部变量存储数值,这点与功能块截然不同;
- 函数没有指定的内存分配,不需要像功能块一样进行实例化;
- 函数只能调用函数,不能调用功能块;
- 配置到 VAR_INPUT 的自变量可以是空的、常数、变量或函数调用,在函数调用时,函数是作为实际的自变量被调用的。
-
标准函数
MetaFacture 支持所有 IEC 的 8 类标准函数。除此之外,还可以使用下列 IEC 标准未规定的函数:ANDN、ORN、XORN、INDEXOF 和 SIZEOF、ADR、BITADR 等。MetaFacture 共支持如下11 类函数,具体函数的使用及说明在第六章会做详细介绍。
-
函数的属性
重载性
对某一个函数来说,如果其输入量以类属数据类型描述,则称为重载函数。这表示该功能的输入量不限于单一的某种数据类型,而是可用于不同的数据类型。MetaFacture 所有标准函数都具有重载属性,他能够适用于不同的数据类型。如果函数只适用于某数据类型,则需在函数名中给予声明,这称为函数的类型化。
例如一个 PLC 能识别 INT、DINT 和 SINT,则它支持类属数据类型 ANY_INT(包括 BYTE、 WORD、DWORD、SINT、USINT、REAL 等)的重载功能 ADD。例如,ADD_INT 是一个限于数据类型的 INT 加法函数,它属于类型化函数,这样看重载功能是独立于类型的。重载函数说明如图所示。

使用重载函数时,系统会自动选择合适的数据类型。例如,如果调用的 ADD 实参数据类型是DINT,则系统内部会调用 ADD_DINT 标准功能。
-
可扩展性
函数的输入变量个数可以扩展的属性称为函数的可扩展属性。例如,ADD 函数的输入变量可以不仅限于两个,它可以实现多个输入变量的加法运算,因此,可以称 ADD 函数具有可扩展属性。并非所有标准函数都具有可扩展属性,该功能的扩展限度受 PLC 所强制的上限、图形编程语言中方框高度限制或函数本身功能定义上的限制,如 DIV 函数就具有该属性。具有可扩展属性的函数可简化程序,降低所需的存储空间。图 2.54 是具有可扩展属性的一些函数示例。





图 2.54 具有可扩展性的函数示例
-
EN 和 ENO
只有在梯形图和功能块图编程语言中该属性才有效。EN 和 ENO 分别是函数的输入使能和输出使能。所有的函数都可使用或禁用该属性。
使能输入使能输出的应用原则如下:
-
当该输入函数被调用时,EN 的值为 False,则该函数体定义的操作不会被程序执行,同时ENO 的值为 False;
-
EN 为 True 时,该函数被调用,函数体定义的操作被执行,同时 ENO 的值为 True;
-
EN 和 ENO 属性是附加属性,可根据实际需要使用或禁用该属性。
对有 EN/ENO 的 ADD 函数和普通的 ADD 函数进行了比较。


具有 EN/ENO 属性的函数和普通属性函数比较
-
自定义函数举例
【例 2.1】使用 PLC 时常会遇到的实际问题是,很多情况需要将实际的模拟量信号转换为数字量信号,常用的模拟量电流信号有 0~20mA,4~20mA,电压信号有 0~10V,-10~10V 的,通过这些输入参数的类型选定,将其转换为数字量值。
函数声明:
FUNCTION F_iScaleOutput : INT
VAR_INPUT
rOutput: REAL; (* [] Physical output *)
rPhyMin: REAL; (* [] Physical minimum *)
rPhyMax: REAL; (* [] Physical maximum *)
eTerminal: E_Ctrl_TerminalType;
END_VAR
VAR
rTerMin: REAL; rTerMax: REAL; rPhyRange: REAL; rTerRange: REAL;
rTerOutput: REAL;
END_VAR (* [] Terminal type. *)
模拟量输入类型通过 E_Ctrl_TerminalType 枚举数据类型对其进行声明。
TYPE E_Ctrl_TerminalType :
(
eTerminal_0mA_20mA, eTerminal_4mA_20mA, eTerminal_0V_10V,
eTerminal_m10V_10V
);
END_TYPE
函数代码:
rTerMax:= 32768.0;
CASE eTerminal OF eTerminal_0mA_20mA: rTerMin:= 0.0;
eTerminal_4mA_20mA: rTerMin:= 0.0;
eTerminal_0V_10V: rTerMin:= 0.0;
eTerminal_m10V_10V: rTerMin:= -32768.0;
ELSE
rTerMin:= -32768.0;
END_CASE
rPhyRange:= rPhyMax - rPhyMin;
rTerRange:= rTerMax - rTerMin;
IF rPhyRange > 0.0 AND rTerRange > 0.0 THEN
rTerOutput:= rTerMin + (rTerRange * (rOutput - rPhyMin) / rPhyRange);
ELSE
rTerOutput:= 0.0;
END_IF
F_iScaleOutput:= REAL_TO_INT(rTerOutput);
程序调用该滤波函数结果如图所示,样例代码请参考样例程\01 Sample\01_F_iScaleOutput \。

图 2.56 模数转换函数调用结果
2.4.3 功能块
功能块(Function Block)是把反复使用的部分程序块转换成一种通用部件,他可以在程序中被任何一种编程语言所调用,反复被使用,不仅提高了程序的开发效率,也较少了编程中的错误,从而改善了程序质量。
功能块在执行时能够产生一个或多个值的程序组织单元。功能块保留有自己特殊的内部变量,控制器目标执行系统必须给功能块的内部状态变量分配内存,这些内部变量构成自身的状态特征。功能块的执行逻辑构成了自身的对象行为特征。所以,对于相同参数的输入变量值,由于可能存在不同的内部状态变量,当然就可能得到不同的计算结果。在控制系统中,功能块可以是某种控制算法,例如 PID 功能模块被用于闭环控制,其他功能块可用于计数器,斜坡和滤波等。
-
功能块的表示和声明
-
自定义功能块的表示
与函数一样,功能块内部逻辑部分可以使用 6 种编程语言中的任意一种。函数名即是函数的返回值,也可以理解为是函数的输出值,如下为功能块的语法表达式。
FUNCTION_BLOCK
VAR_INPUT
… (*功能块的输入接口变量声明*)
END_VAR
VAR_OUTPUT
… (*功能块的输出接口变量声明*)
END_VAR
VAR
… (*功能块的本地变量声明*)
END_VAR
…; (*功能块内部逻辑*)
-
功能块中变量的声明
功能块中变量声明与函数中变量声明类似,编写时,需注意如下事项:
-
功能块的内部和输出变量可用限定属性 RETAIN,用于表示该变量具有保持功能。而输入变量只能在调用时声明具有保持属性;
-
一般不允许对功能块输入变量赋值。只有当输入作为功能块的调用部分时,才允许对功能块输入变量赋值;
-
由于功能块可以调用函数和功能块,所以,也可将调用功能块实例作为其他功能块的实例的变量。如 DB_FF(S1:=DB_ON.Q, R:=DB_OFF.Q);
-
功能块的输入不赋值表示保持他们的初始值;
-
为确保功能块不依赖于硬件,功能块的变量声明中不允许将具有固定地址的地址变量(如%IX1.1,%QD12)作为局部变量,但在调用时可以给其赋值;
-
使用 VAR_INPUT 和 VAR_OUTPUT 会造成占用过多的内存,为此,在功能块编程时,可尽量使用 VAR_IN_OUT 替代,减少对存储区的占用。
-
标准功能块
在标准库中已包含双稳态元素、边沿检测、计时器和定时器等功能块,本书在第六章会详细对其进行说明。
-
功能块的属性
-
实例化
按照 IEC 61131-3 的标准,功能块的类型是抽象的结构类型的定义,而不是现实的数据实体,如果不对其进行定义将其实例化,则不能被程序调用和执行。所以功能块是需要实例化后才能被使用。
实例化后的功能块是拥有私有数据、可按照既定逻辑完成特定功能、完全封装的、独立的结构型变量,从而将之前的抽象类型定义转换为数据实体。首先通过例子 2.57 来了解一下如何将功能块进行实例化。功能块实例化的结构图如图 2.57 所示。

在图中,功能块实例化就如定义变量一样,MotorType 为功能块的类型,是用户通过 POU 自定义的功能块。当程序中需要调用该功能块时,只需在声明处将该功能块将其定义即可, Motor1 为最终的实例名也可以理解为变量名,程序执行过程中都是通过 Motor1 对该功能块进行读写操作。
实例需要按照类型进行合法定义,允许在程序中进行权限允许的调用。在控制器的目标执行平台上必须获得固定的静态内存分配(只不过 VAR_IN_OUT 方式定义的静态内存可共享使用,内存消耗少)。实例的类型可以同名,但实例名在同一 POU 中绝不允许相同。
功能块实例化示例
VAR
EmStop : BOOL; (*布尔变量*)
Time9 : TON; (*延时 ON 功能块*)
Time13 : TON; (*延时 ON 功能块*)
CountDown : CTD; (*减计数器*)
GenCounter : CTUD; (*增/减计数器*)
END_VAR
例中,尽管可以看到 Time9 和 Time13 的类型都为延时 ON 的 TON 功能块,但其实它们通过实例化后是两个独立且分开的功能块,代表了两个完全的定时功能块。
-
扩展性
MetaFacture 支持面向对象的编程方式,所以功能块也可以派生出“子”功能块。这样“子”功能块具有“父”功能块的属性,并且可以具有自己附加的特性,可以形象的认为“子”功能块是对“父”功能块的扩展。所以在本文中,把这个叫做“功能块的扩展”。在声明功能块时加上关键字“EXTENDS”就可以使用扩展功能。也可以通过在“添加对象”
对话框添加功能块时,选择“extends”选项来实现扩展。声明扩展功能块的格式如下:
FUNCTION_BLOCK EXTENDS
后面紧跟着是功能块中变量的声明。
【例 2.3】功能块的扩展性应用,定义功能块 fbA:
FUNCTION_BLOCK fbA VAR_INPUT
x:int;
....
定义功能块 fbB:
FUNCTION_BLOCK fbB EXTENDS fbA
VAR_INPUT
ivar:int;
....
功能块 fbB 包含功能块 fbA 中所有的变量和方法,在使用功能块 fbA 的地方都可以用 fbB 代替。
在功能块 fbB 中可以重写 fbA 中原有的方法。即可以在 fbB 中重新声明一个 fbA 中已有的方法,它的名称、输入、输出和 fbA 中的原有方法一样。
fbB 中不允许使用与 fbA 中同样名称的功能块变量,否则程序在编译时会会出错。使用功能块 fbB 时,可以直接使用 fbA 中的变量和方法,加上关键字“SUPER”即可(SUPER^.)。
-
EN 和 ENO
功能块具有 EN 和 ENO 的附属属性,与函数中 EN 和 ENO 的使用方法类似。
-
函数功能块的区别
综上所述,函数和功能块明显的区别如表总结:

-
自定义功能块举例
自定义一个递增/递减功能块,分为三个输入,增计数,减计数,复位,当前计数值为输出。
FUNCTION_BLOCK FB_Counter
VAR_INPUT
bUp:BOOL; (*递增信号输入*)
bDown:BOOL; (*递减信号输入*)
bReset:BOOL; (*复位、清零信号输入*)
END_VAR
VAR_OUTPUT
nValue:INT; (*输出数值*) END_VAR
IF bUp THEN
nValue:=nValue+1; (*数据递增*)
END_IF IF bDown THEN
nValue:=nValue-1; (*数据递减*)
END_IF
IF bReset THEN
nValue:=0; (*数据清零*)
END_IF

图 2.58 功能块调用结果示例
其运行结果图如图 2.58 所示。当输入信号 Up 为 On 时,则输出值进行不断累加,Down 为 On时输出值进行不断递减,当 bReset 为 On 时,输出值被清零。代码请参考样例程序\01_Sample\ 02_FB_Counter\。
【例 2.5】自定义一个简单 PT1 一阶低通滤波函数,输入值为 rInput, 通过调整增益参数 rK 及时间常数 rT 使输出值更平滑。功能块声明:
VAR_INPUT
rInput: REAL:= 0.0; //输入数据
rK: REAL:= 1.0; //增益
tT: TIME; //时间参数
END_VAR
VAR_OUTPUT
rOutput: REAL:= 0.0; //输出数据
END_VAR
VAR
_rT: LREAL;
_rY: LREAL;
END_VAR
功能块代码:
IF _rT = 0.0 THEN
rOutput:= rInput; //输出数据初始化
END_IF
_rT:= TIME_TO_REAL(tT) /10; //10 为 PLC 的采样周期,单位为 ms
IF ABS(rOutput - rInput)
rOutput:= rInput; //防止除零
ELSIF _rT > 0.0 THEN
rOutput:= (rK / _rT) * rInput + (1.0 - (1.0 / _rT)) * _rY;
ELSE
rOutput:= rInput; //如果时间常数小于等于零请输出等于输入
END_IF
_rY:= rOutput;

PT1 功能块调用结果
程序运行结果如图 2.59 所示,代码请参考样例程序\01_Sample\ 03_FB_PT1Filter\。
程序
程序(Program)是规划一个任务的主核心,程序拥有最大的调用权,可以调用功能块及函数。一般而言分为主程序、子程序,广义上讲,也包含硬件配置、任务配置、通讯配置及目标设置信息。
一般在程序中定义普通全局变量、映射硬件地址全局变量、局部变量。通过程序间调用实现应用逻辑。
-
程序的表示和声明
自定义程序采用如下的语法表达式表示,程序逻辑部分可以使用 6 种编程语言中的任意一种。
PROGRAM
VAR_INPUT
… (*程序的输入接口变量声明*)
END_VAR
VAR_OUTPUT
… (*程序的输出接口变量声明*)
END_VAR
VAR
… (*程序的本地变量声明*)
END_VAR
…; (*程序逻辑*)
-
程序的性能
一个程序可包含地址的配置。允许声明存放 PLC 物理地址的直接表示变量,直接表示的地址配置仅用于程序中内部变量的声明。直接表示变量允许分级寻址方式描述,可以有如下的表示。
可以在程序声明中按如下格式填写,
bTest AT %IX10.3: INT;
在程序编辑区可用如下的语句给直接表示变量赋值。
%QX0.0:=TRUE;
程序组织单元不能直接或间接调用其本身,即程序组织单元不能调用由相同类型和相同名称的程序组织单元实例。
程序仅在资源中实例化。在资源内被声明。程序的实例只需将程序与一个任务结合,否则程序不会被执行。而功能块仅能在程序或其他功能块中实例化。
-
程序调用
-
程序调用关系
在程序中允许调用功能块实例,函数甚至调用其他程序。图 2.60 显示了程序组织单元的调用的关系。

根据图中的显示,函数和功能块用于构成子程序,程序用于构成用户主程序,因此,程序被认为是全局的。程序是程序组织单元中的最大形式,它可以调用函数,功能块及程序。
功能块允许调用其它功能块及函数。由于函数不存在私有变量,故函数只能调用其他函数,不能调用功能块实例。
-
如何调用程序
一般而言,在功能块实例的调用中,需要赋值的输入参数,可以将实参显式传递形参,不要赋值或保持原值的输入参数,可以不用任何显式赋值。功能块实例的调用允许不做任何形参的赋值。在函数调用中,如果出现形参,则必须将全部的形参都做显示赋值。如果不出现形参,则必须按照定义的顺序,将全部实参写入到函数的调用体内。
程序可以被其他的 POU 调用,但函数中不可以调用程序,程序也没有实例。
如果一个 POU 已调用一个程序,从而引起这个程序的值的改变,这些改变将会保持不变,直到该 POU 下一次调用这个程序,即使其间该程序被其他的 POU 调用。注意这与功能块的调用不同,只要功能块实例被调用了,而不管是被哪个 POU 调用的,它内部的值都会改变。
如果是在文本编辑器中,想在程序调用时设置输入/输出参数,那么可以在程序名后面的圆括号中给各个参数赋值。对于输入参数,用“:=”赋值,就像在变量声明部分初始化变量一样。对于输出参数,则使用“=>”,请看后面的示例。
如果您是在文本编辑器的代码实现窗口中,通过“输入助手”的“带参数插入”选项插入一个程序,那么会自动列出该函数的所有参数。当然,您并不一定要给所有的参数都赋值。
-
文本编程语言调用:使用文本编程语言时,在调用程序时必须添加括号,示例如下。
PRGexample(); erg := PRGexample.out_var;
上述例子通过先调用程序,将其输出的 out_var 变量赋值给 erg 变量。此外也可以通过如下的方式调用程序,功能块及函数。通过在键盘输入 F2,使用“输入助手”的帮助为参数赋值,当有输入或输出参数时,可以直接填写在括号中,具体表示方法如下:
PRGexample(in_var:=33, out_var=>erg );
-
图形化编程语言调用:使用图形化编程语言时,显示的更为直观,只需要直接在接口处填写变量及数值即可,调用示例如图所示。

-
自定义程序举例
实时输出输入变量 in_var 与内部变量 i_var 相加后结果,并当结果为 23 时,内部变量 bvar 置为 ON,样例代码请参考样例程序\04_PRG_Basic\。
PROGRAM PLC_PRG VAR_INPUT
in_var:INT;
END_VAR
VAR_OUTPUT
out_var:INT;
END_VAR
VAR
i_var:INT;
bvar:BOOL;
END_VAR
out_var:=in_var+i_var;
IF out_var=23 THEN
bvar:=TRUE;
END_IF
创建的原则
建议广大的编程者,编程中可以遵循如下的 POU 创建原则:
-
可以将您的工程按工艺或功能分成几段程序;
-
在主程序中进行调用需要反复调用的程序段可以建成功能块,这样,在您的程序中只需调用你的功能块实例即可。例如,有 100 台开关阀需要通过程序来控制开或关,那么,在项目中只需要建立一个开关阀的功能块,再分别调用 100 次即可;
-
个别的算法程序可以建成函数,得出的结果可以参与表达式运算;
-
一个 POU 的名字不能包括任何空格;
-
一个 POU 不能有与其它 POU 有相同的名字,或一个数据类型相同的名字;
-
一个数据类型不能接受与其它数据类型相同的名字或一个 POU 相同的名字;
-
在同一个 POU 中一个动作不能有与其它动作相同的名字。
应用对象
应用对象属于应用(Application),在应用菜单中单击右键,选择“添加对象”。下面介绍几个常用的应用对象。
采样跟踪
在程序的调试和诊断过程中,该采样追踪是个非常实用和有效的工具,有时数据变化是一闪而过的,不容易看出产生的影响,此功能可以于把一个程序的执行过程全程记录下来,其中的成员就是在实际系统中要关注的命令字、状态字、电机运动的速度,位置等。通过对这些数据的追踪记录,可以清晰地看到系统运行的整个过程,该功能如图的 a)所示。

a ) 采样跟踪功能 b ) 设备树中设置多个跟踪对象
-
概述
采样追踪提供“跟踪配置”和“跟踪对象”两个插件,可以对 PLC 中的过程数据进行录取波形,类似于示波器功能。此外,可以通过设置触发信号对数据进行采集。
用户可在 MetaFacture 中可以设置多个跟踪配置文件,并可将其进行保存,如 2.62 的 b)所示。
-
新建采样追踪
-
鼠标放在应用(Application)点右键打开下拉菜单,在菜单中选中添加对象,并按导向引出的菜单找到“追踪”并按鼠标左键确认,如图 2.63 的 a)所示。

a )添加 追踪 b )输入追踪名称
-
确认跟踪后,会弹出如下对话框,在如下对话框中,填写跟踪的名称,比如“Trace1”,如图2.63 的 b)所示,点击“打开”。
-
配置要记录的变量。
打开跟踪配置后,选择“添加变量”可以进行跟踪变量的添加,如图 2.64 所示。
点击“变量”栏右边的浏览图标,可以弹出输入助手,在随即打开的窗口中选择要跟踪的变量。如图所示。

弹出输入助手后,在其中选择要监控的变量,设置完成后点击确定,如图 2.66 所示。

在图中点击“确定”,能编辑曲线的 X 轴和 Y 轴的长度和网格,这样就能够编辑图形的分辨率和采样长度,如图所示。

-
触发采样
在跟踪配置表中可以填写触发变量,如图所示。

各配置项目定义如下:
-
触发变量:
该功能是可选的,他与其他一些条件共同决定了跟踪的时间范围。
该变量可以是一个布尔变量、一个表达式或一个模拟变量,也可以输入枚举变量或属性变量。当该变量满足了定义的值—-该值根据“触发边沿”类型来决定,跟踪将在采样一段时间后停止-— 该采样时间段由“位置”的百分比来决定。也就是说一旦触发变量变为真或满足某一特定值,跟踪将继续一段定义好的周期。
-
触发沿:
-
无:无触发;
-
正向:布尔型触发变量的上升沿,或模拟触发变量增大至“触发水平”值时触发;
-
负向:布尔型触发变量的下降沿,或模拟触发变量减少至“触发水平”值时触发;
-
后触发(P)(采样):
触发事件发生后,要记录跟踪变量的测量值百分比。例如,如果在这里输入 25, 则当触发事件发生时, 其之前显示的是测量值的 25% 的数据, 其之后显示的是测量值的 75% 的数据,然后跟踪终止。如果希望已发生触发事件,就开始跟踪,则需要填入 100。
-
触发水平:
当使用模拟量作为触发变量,在此处定义该变量为多少时产生触发事件。可以直接输入一个数值,或用变量定义该数值。默认值为空。
-
任务:
在可用的任务列表中进行选择,该任务被执行后从中读取出跟踪变量的值。
-
记录条件:
此处可输入一个布尔变量、一个数值或一个布尔表达式。如该条件为真,则启动跟踪采样。若此处没有任何输入,则在下载跟踪配置并且应用开始运行后,立即开始跟踪记录。
-
注释:
在此输入有关当前记录的注释文本。
-
数据的保存
当数据采集完后,选择“保存跟踪”选项对数据进行本地保存,方便今后对数据进行分析,如图 2.69 的 a)所示。

a ) 右键“保存跟踪” b ) 使用Excel打开跟踪数据
保存的格式可以为“.trace”或“.txt”后缀。如图 2.69 的 a)是将文件保存为“.txt”文档,使用 MicroSoft Excel 打开,在 Excel 中做一些数据的排列分割,既可以看到如图 2.69 的 b)中的效果。保存的数据的配有时间戳、变量名及具体数据。
程序中的配置请参考样例程序 \ 01 Sample\第2章\05_TraceConfiguration\。
-
跟踪常用功能选项
常用功能选项如表所示。

持续变量
-
概述
在设计 PLC 控制系统时,常常会需要在外部改变 PLC 内部的数据,比如计数器、定时器或其它变量的值,以适应生产过程的需要。而且要求系统关机或异常断点以后,这些数据还能够保存在PLC 内部,当下次开机后,这些数据可以被调出,并保持断电前的数据继续被程序使用。
MetaFacture 内包含的保持型变量有 RETAIN, PERSISTENT RETAIN 这两种。PERSISTENT RETAIN 在实际的工程应用中使用的更为频繁。
-
新建持续变量
选中“Application”右键选择“添加对象”-->
,系统则会自动弹出持续变量列表,用户只需输入列表名称,点击“确认”即可,具体步骤请先按照图 2.70 的 a)先添加列表,再按照图 2.70 的 b)修改列表名称,点击“确定”即可完成添加持续变量列表。

a )添加持续变量列表 b )输入列表名称
使用“PERSISTENT RETAIN”保存的变量是在 PLC 程序 “Rebuild all”(重新编译)之后进行初始化。而持续变量保持其原有的值。
注意
PERSISTENT 和 PERSISTENT RETAIN/RETAIN PERSISTENT 实现的功能相同。
持久变量必须为全局变量。
Persistent 变量只能在 special global variables list 中定义 “Persistent Variables ”。
-
持续变量使用
持续变量的声明
RETAIN 变量的声明格式如下:
VAR_GLOBAL RETAIN
… (*变量声明*)
END_VAR
PERSISTENT RETAIN 变量的声明格式如下:
VAR_GLOBAL PERSISTENT RETAIN
… (*变量声明*)
END_VAR
持续变量的复位
保留变量用关键字“RETAIN”来识别,这些变量保持它们的值即使是在控制器的非正常关闭时和正常的关闭和其中的一个控制器或在命令“热复位”时。当程序重新运行时,存储的值将进行进一步的处理。一个具体的例子是生产线上的饼形计数器在电源故障后重新开始计数。所有其它的变量从新初始化,不是用它们的初始化值或标准初始化的值。与永久变量相反,保留变量在程序的一个新的下载时重新初始化。
永久变量通过关键字“PERSISTENT RETAIN”来识别。不象保留变量,这些变量在一个重新下载或在执行命令“ 冷复位”或“原始复位”之后还会继续保留它们的值。如表 2-8 所示,在何种在线命令时会复位持续变量。
表2-8 持续变量在线命令行为一览表
x = 保留值 - = 初始值

注意
针对不同的硬件控制器,保持变量所占用的内存容量也各不相同,需要考虑实际变量占用的内存。
如一个局部变量定义为保留变量,变量将也将保存在保留区。
如果在函数中的局部变量定义为保留变量,这不起任何作用,变量将不保存在保留区内。
如果在功能模块中的一个局部变量定义为保留变量,功能模块的整个实例将会保存在保留区(POU 的所有数据),因而只有定义的保留变量才处理为保留变量。
数据单元类型
用户可以自定义自己的数据类型,生成结构体、枚举、别名和联合都可以被看作是数据单元类型 DUT(Data Unit Type Editor)。
选中“Application”右键选择“添加对象”-->
,系统则会自动弹出添加数据单元列表,用户需输入列表名称并选择数据单元的类型,点击“确认”即可,具体步骤请先按照图 2.71 a所示添加列表,再按照图 2.71 b)修改列表名称及选择数据类型,点击“确定”。

a )添加数据单元 DUT b )输入数据单元 DUT 列表名称
类型可以选择结构体、枚举、别名和联合。在“结构体”情况下可能会使用继承的方式,因此需要面向对象的编程方式。也可以通过一个已经在另一个工程中定义的 DUT 再扩展另一个 DUT。这意味着扩展 DUT 的定义将会自动针对当前 DUT 进行定义。针对这个目的自动使能“扩展”。“扩展”在原有的结构基础上对元素的补充。可以使用默认的语法,也可以使用扩展结构进行定义,扩展必须之前定义过的结构体。
全局网络变量
全局网络变量列表编辑器(GNVL 编辑器)是一个用于编辑网络变量列表的声明编辑器 。此编辑器工作在文本编辑器的当前设置状态下,在线状态下,此编辑器的布局和变量声明编辑器的布局相似。在网络变量列表编辑器中,变量声明必须以关键字“VAR_GLOBAL”开始,以关键字 “END_VAR”结束。这两个关键字会在打开编辑器的时候自动加入。在这两个关键字之间,用户可以声明 合法的全局变量。
一个 GNVL 对象只能被添加到应用中,如果一个 GVL 具有特殊的网络属性(网络变量列表)定义在网络中的一个设备上,无论变量是否在相同或者不同的定义项中。如果一些合适的 GVLs 变量定义在当前网络的当前工程中,可以在将 GNVL 添加到“添加对象”对话框中时从列表中进行选择。其他工程中的 GVLs 变量必须先进行导入。这意味着在当前设备 (接收器)中的每个 GNVL 对应另一台设备(发送器)中的 GVL。用户可以通过“应用”-->“添加对象” --> “全局网络变量列表”来添加列表,如图所示。

为了进行网络变量的处理,在添加 GNVL 的时候必须进行定义或者直接选择一个“发送器” 从其他设备发送 GVL,一个“GVL”导出文件 “*.gvl”可以直接通过以前通过“链接到文件”指定的 GVL 文件。
在其他的工程项目中也必须定义 GVL。为了实现 “发送端”可以“从文件中导入”的功能,用户可以在“发送者(S)”中选择“从文件导入”,然后在“从文件导入(I)”选项中浏览本地路径需要导入的“*.gvl”文件,点击“打开”进行确认。
配方管理器
配方是一组参数值,它用来提供生产产品和控制生产过程所需的信息。例如饼干的配方包括黄油、白糖、鸡蛋、面粉和烹调时间等参数的数据类型和参数值等。配方可以用来设置和监视 PLC 的控制参数。为了这个目的,它们可以从 PLC 中读出和写入,也可从文件载入和存成文件。这些相互作用通过已经设置好的视图元素是可能的。
配方管理器是一个源对象,可以通过“应用”-->“添加对象” --> “配方管理器”来添加配方管理器,添加后的界面如图 2.73 所示,a)为添加配方管理器界面,b)为配方管理器的配置界面。

a )添加配方管理器 b )配方管理器配置
如图的 b)所示,存储配方可选择 “存储类型”(文本的或二进制),通过指定 “文件路径”具体存储位置,定义存储配方的“文件扩展名”。文本的存储会根据所选的“分隔符”将其分开。
对话框左上部分显示了所有配方定义的列 “可用列”。右侧包含配方定义“选择列” ,这些列会被存储。其它列可以通过选中条目点击
或
移到左侧或右侧,也可使用
或
将所有条目一次性移动。按钮
和
可用来调整选择列的顺序,这将代表存储文件中列的顺序。
如图,为可视化界面配合配方管理器的示例画面。
