Skip to content

Instantly share code, notes, and snippets.

@tmatijevich
Last active February 20, 2024 15:04
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tmatijevich/dc08349272c23161432ebd8f0ae6a7ce to your computer and use it in GitHub Desktop.
Save tmatijevich/dc08349272c23161432ebd8f0ae6a7ce to your computer and use it in GitHub Desktop.
Structured Text EDGEPOS versus R_TRIG

Structured Text EDGEPOS versus R_TRIG

These examples demonstrate the evaluation differences of Structured Text's EDGEPOS function and the R_TRIG function block.

Overview
ELSIF
CASE
FOR

Overview

EDGEPOS

  • The EDGEPOS function detects a rising edge on a digital input, ReturnValue := EDGEPOS(InputValue).
  • The rising edge is detected by registering an internal variable to store the previous value of the input.
  • NOTE: This internal variable is only updated when the EDGEPOS function is called.
  • Therefore, if not called every scan of the program, the internal variable will hold an old and perhaps misleading value.

R_TRIG

  • The user must create a function block instance or R_TRIG, R_TRIG_0.
R_TRIG_0.CLK := InputValue;
R_TRIG_0();
ReturnValue := R_TRIG_0.Q;
  • The function block's definition contains a local variable recording the input value and locally storing the previous know value.
  • As long as the function block instance is called every program scan, the previous known value will be valid.

ELSIF

EDGEPOS function calls in ELSIF condition statements can lead to misleading triggers.

Prepare the variables.

VAR
    Trigger             : BOOL;
    Delay               : USINT;
    fbR_TRIG            : R_TRIG;
    fbR_TRIG_ELSIF      : R_TRIG;
    R_TRIG_Count        : USINT;
    EDGEPOS_Count       : USINT;
    R_TRIG_ELSIF_Count  : USINT;
    EDGEPOS_ELSIF_Count : USINT;
END_VAR

Consider both methods within a standard IF condition.

IF EDGEPOS(Trigger) THEN
    EDGEPOS_Count := EDGEPOS_Count + 1;
END_IF

fbR_TRIG(CLK := Trigger);
IF fbR_TRIG.Q THEN
    R_TRIG_Count := R_TRIG_Count + 1;
END_IF

If Trigger changes from FALSE to TRUE, both counters will increment on the same scan.

Now consider the trigger detection within and ELSIF condition.

IF Trigger AND Delay < 20 THEN // First 20 scans
    Delay := Delay + 1;
ELSIF EDGEPOS(Trigger) THEN
    EDGEPOS_ELSIF_Count := EDGEPOS_ELSIF_Count + 1;
END_IF

fbR_TRIG_ELSIF(CLK := Trigger);
IF Trigger AND Delay < 20 THEN
    // Only update Delay once
ELSIF fbR_TRIG_ELSIF.Q THEN
    R_TRIG_ELSIF_Count := R_TRIG_ELSIF_Count + 1;
END_IF

IF Trigger = FALSE THEN Delay := 0; END_IF // Reset the delay count

When Trigger is set, both IF operands are TRUE for 20 scans. On the 21st scan, EDGEPOS_ELSIF_Count is incremented while R_TRIG_ELSIF_Count remains. fbR_TRIG_ELSIF.Q registered TRUE only on the first scan after Trigger was TRUE.

However, the EDGEPOS(Trigger) never updated it's internal variable until the 21st scan because the ELSIF condition was never reached - leading to a registered rising edge on the 21st scan.

To avoid old internal values:

  1. Use an instance of the R_TRIG function block. Call the instance every scan.
  2. Declare a local variable to store the EDGEPOS return value and assign the variable outside of any conditional statement.
ReturnValue := EDGEPOS(Trigger); // Always capture valid rising edges
IF Trigger AND Delay < 20 THEN 
    Delay := Delay + 1;
ELSIF ReturnValue THEN // Use the local variable
    EDGEPOS_ELSIF_Count := EDGEPOS_ELSIF_Count + 1;
END_IF

CASE

EDGEPOS function calls within a CASE statement, regardless of any conditional statements, can lead to misleading triggers.

Prepare the variables.

VAR
    Trigger                : BOOL;
    State                  : USINT;
    Delay                  : USINT;
    fbR_TRIG_State10       : R_TRIG;
    R_TRIG_State10_Count   : USINT;
    EDGEPOS_State10_Count  : USINT;
END_VAR

Consider a scenario when a trigger occurs while not in the state where the trigger is monitored.

IF EDGEPOS(Trigger) THEN // Monitor trigger
    State := 10;
END_IF

CASE State OF
    // Initial state
    0:
        // Monitor Trigger
        IF EDGEPOS(Trigger) THEN
            EDGEPOS_Count := EDGEPOS_Count + 1;
        END_IF 
        
        IF fbR_TRIG.Q THEN
            R_TRIG_Count := R_TRIG_Count + 1;
        END_IF
        
    // Alternative state
    10:
        IF Delay >= 20 THEN
            Delay := 0;
            State := 0; // Return to the initial state
        ELSE
            Delay := Delay + 1;
        END_IF
END_CASE

// Call state machine function blocks
fbR_TRIG(CLK := Trigger);

To create this scenario, Trigger is set and held while State changes from 0 to 10 on the rising edge of Trigger. The case statement remains in state 10 for 20 program scans, then returns to state 0.

The last scan the case statement entered state 0, Trigger was false. Therefore, upon returning to state 0 with Trigger TRUE, EDGEPOS(TRIGGER) unexpectedly returns TRUE, even after a 20 scan delay.

R_TRIG_Count correctly reports 0.

EDGEPOS function calls should not take place inside a case statement due to these consequences.

FOR

EDGEPOS function calls inside a FOR loop will always lead to unexpected behavior.

Prepare the following variable declaration.

VAR
    Trigger        : ARRAY[0..4] OF BOOL;
    fbR_TRIG       : ARRAY[0..4] OF R_TRIG;
    R_TRIG_Count   : ARRAY[0..4] OF USINT;
    EDGEPOS_Count  : ARRAY[0..4] OF USINT;
    LoopIndex      : USINT;
END_VAR

Now, consider the following FOR loop.

FOR LoopIndex := 0 TO 4 DO
    // Monitor the trigger
    IF EDGEPOS(Trigger[LoopIndex]) THEN
        EDGEPOS_Count[LoopIndex] := EDGEPOS_Count[LoopIndex] + 1;
    END_IF
    
    fbR_TRIG[LoopIndex](CLK := Trigger[LoopIndex]);
    IF fbR_TRIG[LoopIndex].Q THEN
        R_TRIG_Count[LoopIndex] := R_TRIG_Count[LoopIndex] + 1;
    END_IF
END_FOR

Set any one element in the Trigger[] array, and the corresponding EDGEPOS_Count[] will increment every scan. Set Trigger[2] then Trigger[3], EDGEPOS_Count[2] increments every scan and EDGEPOS_Count[3] remains 0.

This confusing behavior comes from the single EDGEPOS function for an array of triggers. Only a single internal "previous value" variable exists for this loop. Therefore Trigger[3] looks at the previous value variable of Trigger[2], Trigger[4] to Trigger[3], and so on.

Do not use the EDGEPOS function inside a FOR loop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment