Welcome to our new forum
All users of the legacy CODESYS Forums, please create a new account at account.codesys.com. But make sure to use the same E-Mail address as in the old Forum. Then your posts will be matched. Close

Declaring retains in a function block

snovva
2015-04-12
2015-04-23
  • snovva - 2015-04-12

    I would like to declare a few variables in a function block i.e:
    VAR PERSISTENT RETAIN
    keepMeAfterRestart : REAL ; (I.e storing the last Set Point of this PID Regulator)
    END_VAR

    VAR_INPUT PERSISTENT RETAIN
    parameterOption1ForFb : INT ; (I don't want to be hard coded if nothing is connected to me)
    END_VAR

    This didn't go well because the Eaton PLCs only have 32k retain area. Still, this didn't make any sense, until i read in the documentation that:
    "If a local variable in a function block is declared as VAR RETAIN, then the complete instance of the function
    block will be saved in the retain area (all data of the POU), whereby only the declared retain variable will be
    handled as a retain."

    Does anybody have a solution to declaring variables under the VAR PERSISTENT RETAIN section in a Function Block without using the entire retain area to the function block instance??

    Remember that variables created in the VAR section is local variables meaning they are private to that instance. Using globals or any other variable outside the function block to store the retain local variables are out the question, that is just bad programming.

    Is see many programmers use VAR_IN_OUT to store the variables outside of the function block instance. This is obviously a desperate and wrongful move, don't be exposing your privates!

     
  • Anonymous - 2015-04-15

    Originally created by: scott_cunningham

    Global variables is the easiest solution. I have come to realize that embedded/machine programming is different than pc software code and using global vars is ok for certain things - ultimately a machine is a type of global - the robot is 5 axis and is this brand, and this digital input is this photo-eye. You can pass the global vars to your var in-outs to avoid future "how many globals did I bury in my FB?", but this adds processing time. Or, you can pass the pointer to the global var once under var ins and your FB is linked to the memory storage without always mapping it..

    "Cleanest" solution: Define your FB and your GVL_FB files. Name spaces are locked in with your "mini" global var object. Use attribute qualified_only to force namespace

    Also, be a bit wary of persistent retain - a Reset Cold clears them out - it is not the same as storing in flash memory!

    GVL_FB example:

    {attribute 'qualified_only'}
    VAR_GLOBAL PERSISTENT RETAIN
    Β  Β keepMeAfterRestart : REAL ; (*I.e storing the last Set Point of this PID Regulator*)
    Β  Β parameterOption1ForFb : INT ; (*I don't want to be hard coded if nothing is connected to me*)
    END_VAR
    

    Then in your FB, access the var:

    GVL_FB.keepMeAfterRestart := 1.2345;
    

    Or to avoid buried GVL var calls inside FB, pass GVLs to VAR_IN_OUTs (adds call time):

    FUNCTION_BLOCK FB
    VAR_INPUT
    Β  Β parameterOption1ForFb: INT; //read only - can never push value from FB to GVL
    END_VAR
    VAR_IN_OUT
    Β  Β keepMeAfterRestart: REAL; //read-write - updated value in FB appears in GVL
    END_VAR
    ...
    

    PLC_PRG call to FB:

    Fbinst(keepMeAfterRestart:= GVL_FB.keepMeAfterRestart, parameterOption1ForFb:= GVL_FB.parameterOption1ForFb);
    

    A real-world example:

    IMG: gvl.png

     
  • snovva - 2015-04-15

    Thank you for replying Scott.
    I agree with your solution to this problem. Of course I still think that this is a flaw in the architecture.

    I try to initialize all inputs to a variable, let's say parameterOption1ForFb : INT := 1;
    This is usually correct for most of the instances for one particular Function Block "class".

    One useful method you can use on the object/instances where this parameter diffrent is to override this in the function block declaration:
    I.e
    Fbinst:FB := (parameterOption1ForFb =2);
    This will decrease the damage of retain loss for function block parameters at least.

    For set points and variables that needs to be retained during run-time i don't see any other way then to do it with a VAR_IN_OUT.
    PS! It is quite upseting that the compiler makes you connect something to the VAR_IN_OUT pin, say you have 40 instances, and this is only relevant for 10 of them, you still have to make the 40 variables and connect them. Why can't the compiler just create a dummy if nothing is connected to VAR_IN_OUT pin.
    Do you know if it is possible do remove this restriction? Some kind of compiler option to disable this requirement?

     
  • Anonymous - 2015-04-15

    Originally created by: scott_cunningham

    Yes, VAR_IN_OUT forces always linking variable. I have found no way around this - perhaps this will never change as this reminds us that these variables can be read and written...

    We get around this by passing in pointers once instead our in/out type variables. We pass in either by using the FB_init() or other method we call once or by VAR_IN. This helps execution speed. If you don't care about execution speed, then define a structure of several/many vars and pass that one structure as the VAR_IN_OUT. At least then it's only "one line of visible code".

    For most vars I want as persistent retain, I use flash file access instead. Such as learned positions or learned PID tuning, etc. That way, when some guy "resets cold" or replaces the device, we can pull the files and not start from ground zero.

     
  • snovva - 2015-04-23

    Yes, I am using structures for the VAR_IN_OUT.
    Using csv file or similar is an good idea to keep the values safely, need to back them up once in a while though.

    Thanks for your insights!

     

Log in to post a comment.