为什么在 Codesys V3 中使用指针和引用?

问题描述 投票:0回答:1

我的问题是:使用指针和引用有什么好处?

我是 codesys 的新手,在之前的工作中,我在 TIA Portal(西门子)和 Sysmac Studio(欧姆龙)中进行编程,从未遇到过指针或类似的东西。我想我了解它们的工作原理,但不确定我自己何时应该使用它们。

例如,我刚刚从供应商那里收到了一个功能块:

Example of FB with pointers and reference

为什么他们不只用一个数组来输入和输出?

pointers automation reference plc codesys
1个回答
4
投票

我现在能想到的主要有6个:

  1. 类型双关语,您也可以使用

    UNION
    来实现,但您可能不希望为代码中的每个重新解释转换创建一个联合。

  2. 节省内存和复制执行时间。每当您将一些数据传递给函数/功能块时,它都会被复制。如果您的 PLC 有足够的 CPU 能力和内存,这不是一个大问题,但是如果您在低端 PLC 上处理特别大的数据,那么您可能会超出实时执行限制,或者耗尽内存。然而,当您传递指针/引用时,无论数据有多大,都只会复制并传递指针/引用,在 32 位系统中为 4 个字节,在 64 位系统中为 8 个字节。
    顺便说一句,由于 CODESYS 始终将使用接口类型声明的变量视为引用。,如果您一直将功能块作为接口传递,那么您已经在使用引用了。

  3. 如果您想传递一些数据并在函数内部对其进行修改,您可以返回修改后的数据的副本并覆盖原始数据,或者只是通过引用/作为指针传递它。 Codesys 通过使用 VAR_IN_OUT 来简化这一过程,它实际上只是底层的

    REFERENCE TO
    ,所以如果您曾经使用过它,那么您就已经使用了引用。
    
    

  4. 在 C 风格语言中,当您希望函数返回多个值而不必每次都创建自定义结构时,您可以使用指针/引用。您可以在这里执行相同的操作,但是在 CODESYS 函数中
  5. can

    有多个输出,例如:

  6. VAR_OUPUT out1 : INT; (*1st output variable *) out2 : INT; (*2nd output variable *) //... END_VAR
    其他语言的主要用途之一是动态内存分配,但在 CODESYS 中,默认情况下禁用它,并且并非所有 PLC 都支持它,而且几乎没有人(据我所知)使用它。但是,如果您确实启用并使用动态内存,则必须使用指针。
  1. 最后,也许是最重要的一点,指针对于泛型编程来说非常方便,即编写一个可以处理不同数据类型或情况的函数,而不是为它们编写一个单独的函数。
  2. 例如:

假设我们想要一个功能块来计算给定数字系列的

移动平均值

。一个简单的方法是这样的: FUNCTION_BLOCK MyMovingAvg VAR_INPUT nextNum: INT; END_VAR VAR_OUTPUT avg: REAL; END_VAR VAR window: ARRAY [0..50] OF INT; currentIndex: UINT; END_VAR

但是,这存在一个问题,即移动窗口大小是静态的且是预定义的。如果我们想要不同窗口大小的平均值,我们要么必须为不同窗口大小创建多个功能块,要么执行如下操作:

FUNCTION_BLOCK MyMovingAvg VAR CONSTANT maxWindowSize: UINT := 100; END_VAR VAR_INPUT nextNum: INT; windowSize: UINT (0..maxWindowSize); END_VAR VAR_OUTPUT avg: REAL; END_VAR VAR window: ARRAY [0..maxWindowSize] OF INT; currentIndex: UINT; END_VAR

我们只使用数组中从 0 到 
windowSize

的元素,其余的将被忽略。然而,这也存在问题,我们不能使用大于

maxWindowSize
的窗口大小,并且如果
maxWindowSize
设置得很高,可能会浪费大量内存。
有两种方法可以获得真正通用的解决方案:

使用动态分配。然而,正如我之前提到的,并非所有 PLC 都支持此功能,默认情况下禁用它,有缺点(您必须将内存分成两个块),几乎不使用并且不太像 CODESYS。
  1. 让用户定义他们想要的任何大小的数组并将该数组传递给我们的功能块:
  2. FUNCTION_BLOCK MyMovingAvg VAR_INPUT nextNum: INT; END_VAR VAR_OUTPUT avg: REAL; END_VAR VAR windowPtr: POINTER TO INT; // pointer to the first array element. You can access other elements with [] as usual (up to windowSize) windowSize: DINT; currentIndex: UINT; END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL;
    bInCopyCode: BOOL;
END_VAR
VAR_IN_OUT // basically REFERENCE TO
    window_buffer: ARRAY [*] OF INT; // array of any size
END_VAR

THIS^.windowPtr := ADR(window_buffer);
THIS^.windowSize := UPPER_BOUND(window_buffer, 1) - LOWER_BOUND(window_buffer, 1) + 1;
// usage:
PROGRAM Main
VAR
    avgWindow: ARRAY [0..123] OF INT; // whatever size you want!
    movAvg: MyMovingAvg(window_buffer := avgWindow);
END_VAR

movAvg(nextNum := 5);
movAvg.avg;
同样的原理可以应用于任何对数组进行操作的功能块(例如,我们也用它来排序)。

更新

:Codesys 引入了 VAR_GENERIC CONSTANT,您基本上可以在功能块中定义常量输入,例如 arraySize,并用它定义输入数组,从而降低上述方法的实用性。

此外,类似地,您可能希望有一个适用于任何整数/浮点数的函数。为此,您可以使用其中一种 

ANY

类型,它基本上是一个结构,包含指向数据第一个字节的指针、数据大小(以字节为单位)和类型枚举。

© www.soinside.com 2019 - 2024. All rights reserved.