These examples demonstrate the evaluation differences of Structured Text's EDGEPOS
function and the R_TRIG
function block.
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.
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:
- Use an instance of the
R_TRIG
function block. Call the instance every scan. - 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
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.
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.