我想知道在使用模拟输入卡(例如带有 TwinCAT 的热电偶输入卡)时应采用哪些最佳实践。我具体感兴趣的是:
我对 4 通道输入热电偶卡的尝试如下:
// Term 4 EL3314: thermocouple signals
TcInputValue_CH AT %I* : ARRAY[1..4] OF INT;
TcInputStatus_CH AT %I* : ARRAY[1..4] OF WORD;
{attribute 'TcLinkTo' := 'TIID^Device 1 (EtherCAT)^Term 1 (EK1100)^Term 4 (EL3314)^TC Inputs Channel 1^Value'}
但是我不知道如何对数组执行此操作。TYPE ST_TC :
STRUCT
Temperature : REAL;
Status : WORD;
END_STRUCT
END_TYPE
还有一个全局变量:
"Kiln GVL"
(* Sensor variables *)
EastTC : ST_TC;
WestTC : ST_TC;
// other thermocouples could be added
然后我创建了两个程序。第一个程序是“ReadSensors”,它基本上帮助将模拟输入卡值格式化为适当的变量。我这样做:
// Analog input Thermocouple conversion to Celsius
Kiln.EastTC.Temperature := TO_REAL(IO.TcInputValue_CH[1]) / 10.0;
Kiln.WestTC.Temperature := TO_REAL(IO.TcInputValue_CH[3]) / 10.0;
// Thermocouple Fault detection
Kiln.EastTC.Status := IO.TcInputStatus_CH[1];
Kiln.WestTC.Status := IO.TcInputStatus_CH[3];
第二个程序用于安全检查,通过查看状态字寄存器并检查每个地址,我创建了一些错误代码 状态词例如低于范围的位地址
VAR
// Error codes
Underrange : WORD := 16#1;
Overrange : WORD := 16#2;
Error : WORD := 16#40;
END_VAR
然后使用按位 AND 执行错误处理,因此检测到错误并可以保存类型以供进一步处理,例如在 HMI 警报消息中。
IF TO_BOOL(Kiln.EastTC.Status AND Error) THEN
Kiln.Error := E_ERROR_MESSAGE.TcError;
// describe error type:
IF TO_BOOL(Kiln.EastTC.Status AND Underrange) THEN
Kiln.TcErrorType := E_TC_ERROR.UnderRange;
END_IF
// describe error type:
IF TO_BOOL(Kiln.EastTC.Status AND Overrange) THEN
Kiln.TcErrorType := E_TC_ERROR.OverRange;
END_IF
END_IF
// repeat for all other channels...
我的方法有效吗?对于读取大量模拟输入来说,它可能非常冗长,所以我想知道如何对其进行优化。任何建议将不胜感激!
代码已编译并且似乎可以工作,尽管我还没有使用真实的硬件对其进行测试。最重要的是,我想知道如何更专业地做到这一点。
为多个输入声明变量的正确方法
我真的不认为有创建和声明 IO 变量的“正确方法”。这取决于你在做什么。我个人更喜欢将所有 IO 放在一个结构中,通常称为
ST_<machine name>TerminalInputs/Outputs
,然后将该结构放置在将执行逻辑的 FB
或 Program
内。所有从/到 IO 变量的链接都是在 POU
内完成的,它控制专用部分的对象(通过依赖项注入或通过内部声明的单元的输入和输出)。
对于某些事情,在 FB 内声明 IO 是有意义的,但就我个人而言,我并不经常这样做,因为就我而言,我总是通过 ADS 访问这些变量到“HMI”软件,因此可以访问所有变量集中在一个位置使事情变得更加容易。
您应该知道的一件事是,对于大多数 Beckhoff EC 从站来说,还有一个选项可以创建 PLC 数据类型并直接链接它,但请记住,如果您更改 PDO,数据类型将会更改,并且新的 GUID 将发生变化。以适当的结构生成:
如您所见,整个输入结构已创建,您也不需要“思考”如何解析状态:
我不经常使用该功能,但有时它很好。缺点是,正如我提到的,如果更改 PDO,生成的数据类型可能会“消失”。
将所述变量链接到输入通道的正确方法
我以前从未使用过属性来链接变量。也许它很有用,但我个人更喜欢手动链接,它把“集群”从 PLC 代码中剔除,当你的电工把事情搞混时,你只需点击 2 次鼠标就可以解决你的问题,而不是查看表格在这个过程中投入和迷失,只是我的意见。就数组而言,您可以在这个 Beckhoff link 上找到一个示例。这是我引用的例子:
{attribute 'TcLinkTo' := '[1].bIn := TIID^Device 1 (EtherCAT)^Term 1 (EK1100)^Term 2 (EL1008)^Channel 4^Input;
[1].bOut := TIID^Device 1 (EtherCAT)^Term 1 (EK1100)^Term 3 (EL2008)^Channel 4^Output;
[2].bIn := TIID^Device 1 (EtherCAT)^Term 1 (EK1100)^Term 2 (EL1008)^Channel 5^Input;
[2].bOut := TIID^Device 1 (EtherCAT)^Term 1 (EK1100)^Term 3 (EL2008)^Channel 5^Output'}
aModule : ARRAY[1..2] OF FB_Module;
通过使用“状态”一词处理错误的正确方法
我想说,如果你想处理各个通道的错误,你应该为TC模块创建一个FB(没有复制粘贴代码),而不仅仅是数据类型。在 FB 内部处理错误并将其传递到您希望它们出现的任何位置。作为一个例子,这可能是这样的:
TYPE ST_TcInputData :
STRUCT
Temperature : INT;
Status : WORD;
END_STRUCT
END_TYPE
FUNCTION_BLOCK FB_TcHanlder EXTENDS FB_HasInstanceName // Basically i get the instance name and path, look at {attribute 'refelection'}
VAR_INPUT
scalingFactor : INT := 10; // Scaling factor, depends on the precision of the module as well as CoE settings
END_VAR
VAR_OUTPUT
temperature : REAL; // Actual, calculated temperature
error : BOOL;
underrange : BOOL;
overrange : BOOL;
END_VAR
VAR
Inputs : ST_TcInputData;
_logger : I_LoggerEx;
END_VAR
VAR
incorrectScalingTrigger : R_TRIG;
errorTrigger : R_TRIG;
END_VAR
逻辑
incorrectScalingTrigger(CLK := scalingFactor <= 0);
IF incorrectScalingTrigger.Q THEN
_logger.LogWarning(CONCAT('Incorrect scaling parameter at instance ', THIS^.InstanceName));
END_IF
IF scalingFactor = 0 THEN
// Handle incorrect parameter setting... example
temperature := Inputs.Temperature;
ELSE
temperature := DINT_TO_REAL(Inputs.Temperature) / INT_TO_REAL(scalingFactor);
END_IF
underrange := Inputs.Status.0;
overrange := Inputs.Status.1;
error := Inputs.Status.2;
errorTrigger(CLK := error);
IF errorTrigger.Q THEN
// Log the events, this is just an example of how I use logging, TcEventLogger is probably the more used way
IF underrange THEN
_logger.LogError(CONCAT('An error occurred (underrange) at ', THIS^.InstanceName));
ELSIF overrange THEN
_logger.LogError(CONCAT('An error occurred (overrange) at ', THIS^.InstanceName));
ELSE
_logger.LogError(CONCAT('An error occurred (unspecified) at ', THIS^.InstanceName));
END_IF
END_IF
您的 MAIN 或您希望实例化这些 FB 的任何地方将减少到只有几行代码:
PROGRAM MAIN
VAR
EL3314_Ch1_TcGeneratedData : MDP5001_330_56D5BDE3;
MyTcFunctionBlocks : ARRAY[0..7] OF FB_TcHanlder;
i : INT;
END_VAR
FOR i := 0 TO 7 BY 1 DO
MyTcFunctionBlocks[i](scalingFactor := 10);
END_FOR
归根结底,这都是主观的。我从一个项目到另一个项目改变我的方法,也许我看到了别人做事的方式,我喜欢它并想尝试它。这只是我的看法,希望我没有让事情变得过于复杂,但恕我直言,这是一个非常广泛且基于意见的主题。