The structure of the needed H-file

Neuron Power Engineer will automatically create the implementation stubs within the H-file, if you follow the best practice instruction specified within the article "Details: Creating a vendor block". Nevertheless it is useful, if you know the basic structure of a H-file.
The basic structure of the H-file must be as follows:

  • include of the required types
    If the vendor block is to support generic data types, all of the relevant elementary data types must be declared in this section – if not otherwise defined.

  • type definition for the data type of the new vendor block

  • initialization macro for the type definition (= INIT macro)

  • only required for function blocks:

    • initialization macro for warm boot (= WINIT macro)

    • name server data structure

  • implementation of the functionality

The contents of the type definition incl. the macros as well as of the implementation is influenced by:

  • the statement { ImplementationProperties ( ) } and some of its definitions (see article "Properties for implementing vendor blocks" for details) – for instance, the definition useUntypedTypeStructure.

  • the fact whether the vendor block is using generic data types or not 

The following examples illustrate the required contents of the H-file for a vendor block with elementary data types only:

 

(warning) If your vendor block is to support more complex data types (e.g. structures, arrays, references, strings), please contact Neuron.

Example 1: MUL_01 function with a concrete data type

The following interface declares a function with 2 inputs of data type INT.

Example 1 for interface
{CustomImplementation}
FUNCTION MUL_01 : INT
    VAR_INPUT
        IN1 : INT;
        IN2 : INT;
    END_VAR
END_FUNCTION

The application Test_01 is calling the specified vendor block MUL_01 and assigning its return value to the variable Var2.

Example 1 for application using the vendor block
PROGRAM Test_01
  VAR
    Var1 : INT := 5;
    Var2 : INT;
  END_VAR
  Var2:=MUL_01(IN1:=2, IN2:=Var1);
END_PROGRAM

The following lines are required within the H-file lcfu___MUL_01.h so that Neuron Power Engineer is able to build the application for Test_01. The lines /* ... */ are just comments that have been added to explain the structure.

Example 1 for H-file

/* IEC 61131-3 standard types */
#include <LC3CGBase.h> 
 
/* type definition */
typedef struct _LC_TD_Function_MUL_01
{
    LC_TD_BOOL LC_VD_ENO;
    LC_TD_INT LC_VD_MUL_01;
} LCCG_StructAttrib LC_TD_Function_MUL_01;
 
/*  Initialization macro */
#define LC_INIT_Function_MUL_01(p) \
{ \
    (p)->LC_VD_MUL_01 = 0;\
}
 
/* implementation */
#define lcfu___MUL_01(LC_this, IN1, IN2, pEPDB)  ((LC_this)->LC_VD_MUL_01 = 2*(IN1)*(IN2));

Here some basic explanations for this contents:

  • The default header file must be included because the default data type INT is used.

  • The type definition must declare the following elements of the POU and its data type:

     

    Function blocks

    Functions

    →input variable of the POU

    (tick)

    →in-out variable of the POU

    (tick)

    (tick)

    ENO output (its data type is always BOOL)

    (tick)

    (tick)

    →output variable of the POU

    (tick)

    (tick)

    local →variable of the POU

    (tick)

    return value of the function

    (tick)

    auxiliary variables that are not visible to the user
    (info) These auxiliary variables are necessary to keep its values from one execution of the application to the next one.

    (tick)

    The data type of the variables and return value depends on the specified interface. For the above example, the data type INT must be specified for the return value LC_VD_MUL_01.
    Declare the variables in the order as specified in the table. The order might become relevant when an application is updated by using the resource manager (= reloaded).
    This means:

    • The required order for variables in a vendor function is: VAR_IN_OUT – ENO – VAR_OUTPUT – return value

    • The required order for variables in a vendor function block is: VAR_INPUT – VAR_IN_OUT – ENOVAR_OUTPUTVAR – auxiliary variables that are not visible to the user 

  • The initialization macro is required for each element declared within the type definition but not for the ENO output.  The initialization macro is a type-specific initialization.

  • The implementation specifies a "C-function" telling what the POU should do when it is called. For the above example, MUL_01 calculates 2*IN1*IN2 and assigns its result to the return value LC_VD_MUL_01.
    As an alternative, the implementation can be done by using a C-file. In this case, the definition functionHasCFile within the statement { ImplementationProperties ( ) } is required within the vendor block. For information on the structure of a C-file, please contact Neuron.

If you are building the application for Test_01, Neuron Power Engineer automatically generates the following lines within the file lcpu___test_01.c for the line Var2:=MUL_01(IN1:=2, IN2:=Var1);:

(This file is located within the sub-folder src-gen of the project. This sub-folder is not visible by default.)

Example 1 for generated C-code
{
    LC_TD_Function_MUL_01 lFunction_MUL_01;
    LC_INIT_Function_MUL_01(&lFunction_MUL_01);
    lFunction_MUL_01.LC_VD_ENO = LC_EL_true;
    lcfu___MUL_01(&lFunction_MUL_01, (LC_TD_INT)2, LC_this->LC_VD_VAR1, pEPDB);
    LC_this->LC_VD_VAR2 = lFunction_MUL_01.LC_VD_MUL_01;
  }

Here are some basic explanations for this code (if you are interested in details, please contact Neuron):

  • Line LC_TD_Function_MUL_01 lFunction_MUL_01; = usage of the type definition (specified in the H-file); Also, the variable lFunction_MUL_01 is created.

  • Line LC_INIT_Function_MUL_01(&lFunction_MUL_01); = initialization of the created variable with the initialization macro (specified in the H-file)

  • Line lcfu___MUL_01(&lFunction_MUL_01, (LC_TD_INT)2, LC_this->LC_VD_VAR1, pEPDB); = calling the function MUL_01 with assigned parameters; At first, a variable is assigned to the return value, then the values or variables are assigned to the input variables (here: literal 2 and variable VAR1 are assigned. At last, the internal parameter pEPDB is always listed. 
    (info) If input variables of the function would have been omitted in the call of the function, the initial value of the data type would be assigned to those input variables.
    (info) Usually, the parameter pEPDB (pointer Entry Point Description Block) is not required to implement a C-block. Hence, you can ignore this internal parameter. However, if you are using specific RTS APIs, the parameter pEPDB is required to be passed on.

  • Line LC_this->LC_VD_VAR2 = lFunction_MUL_01.LC_VD_MUL_01; = assignment of the results to the variable VAR2.

Result of the above implementation: If you load the application onto the built-in PLC, the Values of Variables view will display the value 20 for variable Var2.

Example 2: MUL_02 hfunction with a generic data type

The following interface declares a function with 2 inputs but now with the generic data type ANY.

Example 2 for interface
{CustomImplementation}
FUNCTION MUL_02 : ANY
    VAR_INPUT
        IN1 : ANY;
        IN2 : ANY;
    END_VAR
END_FUNCTION

The application Test_02 is calling the specified vendor block MUL_02 (one call type-coded with INT and the other call type-coded with SINT) and assigning its return value to an appropriate variable.

Example 2 for application using the vendor block
PROGRAM Test_02
  VAR
    Var1 : INT := 5;
    Var3 : INT;
    Var4 : SINT := 5;
    Var5 : SINT;
  END_VAR
  Var3:=MUL_02(IN1:=INT#4, IN2:=Var1);
  Var5:=MUL_02(IN1:=SINT#4, IN2:=Var4);
END_PROGRAM

As a consequence, the following lines are required within the H-file lcfu___MUL_02.h.
(warning) If you would use the call with other data types than INT and SINT, the H-file would require the appropriate lines for those data types as well.

Example 2 for H-file
#include <LC3CGBase.h>
 
/* type definition for INT */
typedef struct _LC_TD_Function_MUL_02__INT
{
    LC_TD_BOOL LC_VD_ENO;
    LC_TD_INT LC_VD_MUL_02;
} LCCG_StructAttrib LC_TD_Function_MUL_02__INT;
 
 
/* type definition for SINT */
typedef struct _LC_TD_Function_MUL_02__SINT
{
    LC_TD_BOOL LC_VD_ENO;
    LC_TD_SINT LC_VD_MUL_02;
} LCCG_StructAttrib LC_TD_Function_MUL_02__SINT;
 
/*  initialization macro */
#define LC_INIT_Function_MUL_02__INT(p) \
{ \
    (p)->LC_VD_MUL_02 = 0;\
}
#define LC_INIT_Function_MUL_02__SINT(p) \
{ \
    (p)->LC_VD_MUL_02 = 0;\
}
 
/* implementation - observe that different implementations are specified for INT and SINT */
#define lcfu___MUL_02__INT(LC_this, IN1, IN2, pEPDB)  ((LC_this)->LC_VD_MUL_02 = 3*(IN1)*(IN2));
#define lcfu___MUL_02__SINT(LC_this, IN1, IN2, pEPDB)  ((LC_this)->LC_VD_MUL_02 = 3+(IN1)+(IN2));

Result of the above implementation: If you load the application onto the built-in PLC, the Values of Variables view will display the value 60 for variable Var3 and the value 12 for variable Var5.

Example 3: FB_MUL_01 function block with a concrete data type

The following interface declares a function block with 2 inputs of data type INT and 1 output of data type INT.

Example 3 for interface
{CustomImplementation}
FUNCTION_BLOCK FB_MUL_01
    VAR_INPUT
        IN1 : INT;
        IN2 : INT;
    END_VAR
    VAR_OUTPUT
        OUT : INT;
    END_VAR
END_FUNCTION_BLOCK

The application Test_01 is calling the specified vendor block FB_MUL_01 and assigning its output to the variable Var2.

Example 3 for application using the vendor block
PROGRAM Test_01
  VAR
    Var1 : INT := 5;
    Var2 : INT;
    Inst01 : FB_MUL_01;
  END_VAR
  Inst01(IN1:=2, IN2:=Var1, OUT=>Var2);
END_PROGRAM

The following lines are required within the H-file lcfu___fb_mul_01.h so that Neuron Power Engineer is able to build the application for Test_01. The lines /* ... */ are just comments that have been added to explain the structure.

Example 3 for H-file
/* IEC 61131-3 standard types */
#include <LC3CGBase.h> 
 
/* type definition */
typedef struct _LC_TD_FunctionBlock_FB_MUL_01
{
  LC_TD_INT LC_VD_IN1;
  LC_TD_INT LC_VD_IN2;
  LC_TD_BOOL LC_VD_ENO;
  LC_TD_INT LC_VD_OUT;
} LCCG_StructAttrib LC_TD_FunctionBlock_FB_MUL_01;
 
/*  initialization macro */
#define LC_INIT_FunctionBlock_FB_MUL_01(p) \
{ \
  LC_INIT_INT(&((p)->LC_VD_IN1)); \
  LC_INIT_INT(&((p)->LC_VD_IN2)); \
  LC_INIT_INT(&((p)->LC_VD_OUT)); \
}
 
/* initialization macro for warm boot */
#define LC_WINIT_FunctionBlock_FB_MUL_01(p,RF) { if (RF==0) LC_INIT_FunctionBlock_FB_MUL_01(p) }
 
/* name server data structures - The contents of this section looks different, if it is auto-generated. Keep the auto-generated contents. */
#define LCNS_EL_CNT___FB_MUL_01 0
 
/* implementation */
#define lcfu___FB_MUL_01(LC_this, pEPDB)  ((LC_this)->LC_VD_OUT = 3 * (LC_this)->LC_VD_IN1 * (LC_this)->LC_VD_IN2);

Here are the basic differences to the example for functions:

  • The type definition must declare the inputs of the function block and its data type.
    Local variables would have to be declared as well within the type definition. But this function block does not have any local variables.

  • Hence, the inputs of the function block are required within the initialization macro as well.

  • The initialization macro for the warm boot (= WINIT macro) is required. The WINIT macro defines the behavior of the function block in case of a warm restart whereas the INIT macro defines the behavior in case of a cold restart.
    The WINIT macro is similar to the INIT macro but has the additional retain flag RF.
    The line #define LC_WINIT_FunctionBlock_FB_MUL_01(p,RF) { if (RF==0) LC_INIT_FunctionBlock_FB_MUL_01(p) } is sufficient to guarantee a correct code generation, if an instance of the function block would be declared in a VAR ... END_VAR section with the keyword RETAIN. Although this example declares the function block in in a VAR ... END_VAR section without the keyword RETAIN, it is a good idea to integrate this line for the WINIT macro – you, as the manufacturer of the function block – cannot be sure whether the function block will be used with or without the keyword RETAIN.
    If the function block block itself contains a section with the keyword RETAIN, the contents of the WINIT macro must look different. Please contact Neuron for details on this contents.

  • The name server data structure is required as well.
    The line #define LCNS_EL_CNT___FB_MUL_01 0 is sufficient to guarantee a correct code generation. But when the H-file is auto-generated, the content looks different. In this case, do not have to change the auto-generated content.

  • The implementation specifies the "C-function" for the function block not specifying the inputs. Reason: The inputs are already provided by the data structure (LC_this).

Result of the above implementation: If you load the application onto the built-in PLC, the Values of Variables view will display the value 30 for variable Var2.