Edge Detection
Edge detection function blocks let you respond to transitions rather than
states. Instead of asking “is the ignition on?” you can ask
“did the ignition just turn on?” The output is TRUE for
exactly one program scan cycle when the transition occurs, making these blocks ideal for
one-shot actions: incrementing counters, toggling states, sending a single CAN frame,
or triggering an initialization sequence.
R_TRIG - Rising Edge Detector
R_TRIG detects the rising edge - the moment when
CLK transitions from FALSE to TRUE. The output
Q is TRUE for exactly one scan cycle, then
returns to FALSE regardless of whether CLK remains
TRUE.
Parameters
| Parameter | Type | Direction | Description |
|---|---|---|---|
CLK |
BOOL | Input | Input signal to monitor |
Q |
BOOL | Output | TRUE for one scan on rising edge (FALSE → TRUE) |
Timing Behavior
CLK: _____|‾‾‾‾‾‾‾‾‾‾‾‾‾|_______|‾‾‾‾‾‾|______
Q: _____|‾|_____________|_______|‾|______|______
^ ^
one scan one scan
Basic Example: Ignition Just Turned On
VAR
R_TRIG_IGN : R_TRIG;
SIGNAL_TANDNING : BOOL;
ignJustOn : BOOL;
END_VAR;
R_TRIG_IGN(CLK := SIGNAL_TANDNING);
// Q is TRUE for one scan cycle when ignition transitions FALSE → TRUE
ignJustOn := R_TRIG_IGN.Q;
Example: Send CAN Frame Once on Ignition
VAR
R_TRIG_IGN : R_TRIG;
CANSEND : CAN_TX;
SENDDATA : ARRAY[0..7] OF BYTE;
SIGNAL_TANDNING : BOOL;
END_VAR;
R_TRIG_IGN(CLK := SIGNAL_TANDNING);
IF R_TRIG_IGN.Q THEN
// Send a wake-up DID request exactly once when ignition turns on
SENDDATA[0] := 0x02;
SENDDATA[1] := 0x10; // Diagnostic Session Control
SENDDATA[2] := 0x01; // Default Session
CANSEND(ENABLE := TRUE, ID := 0x7E0, EXT := FALSE, DATALENGTH := 8, DATA := SENDDATA);
END_IF;
F_TRIG - Falling Edge Detector
F_TRIG detects the falling edge - the moment when
CLK transitions from TRUE to FALSE. Like
R_TRIG, the output Q is TRUE for exactly one
scan cycle.
Parameters
| Parameter | Type | Direction | Description |
|---|---|---|---|
CLK |
BOOL | Input | Input signal to monitor |
Q |
BOOL | Output | TRUE for one scan on falling edge (TRUE → FALSE) |
Timing Behavior
CLK: ‾‾‾‾‾|_______________|‾‾‾‾‾‾‾|______|‾‾‾‾‾‾
Q: ______|‾|_______________|‾‾‾‾‾‾‾|‾|____|‾‾‾‾‾‾
^ ^
one scan one scan
Basic Example: Ignition Just Turned Off
VAR
F_TRIG_IGN : F_TRIG;
SIGNAL_TANDNING : BOOL;
COUNTER : INT := 0;
END_VAR;
F_TRIG_IGN(CLK := SIGNAL_TANDNING);
IF F_TRIG_IGN.Q THEN
// Ignition was just turned off - executes ONCE
COUNTER := COUNTER + 1;
END_IF;
Example: Save State on Shutdown
VAR
F_TRIG_IGN : F_TRIG;
SIGNAL_TANDNING : BOOL;
shutdownSequence : BOOL := FALSE;
END_VAR;
F_TRIG_IGN(CLK := SIGNAL_TANDNING);
IF F_TRIG_IGN.Q THEN
// Ignition just turned off - begin shutdown sequence
shutdownSequence := TRUE;
END_IF;
Common Edge Detection Patterns
Toggle on Button Press
Use a rising edge detector to toggle a boolean value each time a signal transitions:
VAR
R_TRIG_BTN : R_TRIG;
SIGNAL_KNAPP : BOOL;
lampActive : BOOL := FALSE;
END_VAR;
R_TRIG_BTN(CLK := SIGNAL_KNAPP);
IF R_TRIG_BTN.Q THEN
lampActive := NOT lampActive;
END_IF;
Count Transitions
Count how many times a signal has toggled. Useful for diagnostics and monitoring:
VAR
R_TRIG_SIGNAL : R_TRIG;
F_TRIG_SIGNAL : F_TRIG;
SIGNAL_DORR : BOOL;
openCount : INT := 0;
closeCount : INT := 0;
END_VAR;
R_TRIG_SIGNAL(CLK := SIGNAL_DORR);
IF R_TRIG_SIGNAL.Q THEN
openCount := openCount + 1; // Door opened
END_IF;
F_TRIG_SIGNAL(CLK := SIGNAL_DORR);
IF F_TRIG_SIGNAL.Q THEN
closeCount := closeCount + 1; // Door closed
END_IF;
Detect Signal Value Changes
Detect when a multi-value signal changes by comparing the current value to the previous:
VAR
currentGear : INT;
previousGear : INT := -1;
gearChanged : BOOL;
END_VAR;
gearChanged := (currentGear <> previousGear);
IF gearChanged THEN
// Gear just changed - take action
previousGear := currentGear;
END_IF;
High Beam Latch Pattern
A common recipe pattern: use R_TRIG to latch the high beam signal for
instant response, rather than waiting for a debounce period:
VAR
R_TRIG_HELLJUS : R_TRIG;
SIGNAL_HELLJUS : BOOL;
helljusLatched : BOOL := FALSE;
END_VAR;
R_TRIG_HELLJUS(CLK := SIGNAL_HELLJUS);
// Latch on rising edge (instant response)
IF R_TRIG_HELLJUS.Q THEN
helljusLatched := TRUE;
END_IF;
// Clear when signal goes away
IF NOT SIGNAL_HELLJUS THEN
helljusLatched := FALSE;
END_IF;
HW.BUTTON - Already Edge-Triggered
The hardware button input (
HW.BUTTON) returns its value for only
one program cycle. It is inherently a one-shot signal - you do
not need to wrap it with R_TRIG. Just check the value
directly.
VAR_SIGNAL
HW : HARDWARE;
END_VAR;
HW();
// CORRECT - no edge detector needed
IF HW.BUTTON = BUTTON_CLICK THEN
// Single click detected (one cycle only)
END_IF;
IF HW.BUTTON = BUTTON_LONGCLICK THEN
// Long press detected (one cycle only)
END_IF;
VAR_SIGNAL
HW : HARDWARE;
END_VAR;
HW();
// UNNECESSARY - R_TRIG on an already-edge-triggered signal
// This works but wastes a function block instance for no benefit
VAR
RT : R_TRIG;
END_VAR;
RT(CLK := (HW.BUTTON = BUTTON_CLICK)); // Redundant!
IF RT.Q THEN
// ...
END_IF;
R_TRIG and F_TRIG for signals that
represent a continuous state (ignition on/off, door open/closed, CAN signal
present/absent). For signals that are already transient one-shot events (button presses,
CAN frame received), direct comparison is sufficient.
Summary
| Block | Detects | Q Duration |
Typical Use |
|---|---|---|---|
R_TRIG |
Rising edge (FALSE → TRUE) | One scan cycle | Initialization, toggle, count activations |
F_TRIG |
Falling edge (TRUE → FALSE) | One scan cycle | Shutdown actions, count deactivations |