(* @NESTEDCOMMENTS := 'Yes' *) (* @PATH := '' *) (* @OBJECTFLAGS := '0, 8' *) (* @SYMFILEFLAGS := '2048' *) FUNCTION_BLOCK FbClickMode VAR_INPUT IN : BOOL := FALSE; END_VAR VAR_OUTPUT Q : BOOL := FALSE; SINGLE : BOOL := FALSE; DOUBLE : BOOL := FALSE; TP_LONG : BOOL := FALSE; LONG : BOOL := FALSE; END_VAR VAR_INPUT CONSTANT T_DEBOUNCE : TIME := T#30ms; T_LONG : TIME := T#500ms; END_VAR (* Internal use only *) VAR deb : FbDebounce; timer : TP; cnt : INT := 0; last : BOOL := FALSE; END_VAR (* @END_DECLARATION := '0' *) (* reset ouput for single- and double-click *) SINGLE := FALSE; DOUBLE := FALSE; (* debounce input *) deb(IN := IN, TD := T_DEBOUNCE); Q := deb.Q; (* when input goes high start the timer to decode pulses *) timer(IN := Q, PT := T_LONG); IF timer.Q THEN (* decode pulses while the timer is active *) IF (NOT Q AND last) THEN cnt := cnt + 1; END_IF; ELSE CASE cnt OF 1 : SINGLE := TRUE; (* single-click *) 2 : DOUBLE := TRUE; (* double-click *) END_CASE; cnt := 0; END_IF; TP_LONG := (NOT timer.Q) AND (NOT LONG) AND Q; LONG := (NOT timer.Q) AND Q; last := Q; END_FUNCTION_BLOCK (* @NESTEDCOMMENTS := 'Yes' *) (* @PATH := '' *) (* @OBJECTFLAGS := '0, 8' *) (* @SYMFILEFLAGS := '2048' *) FUNCTION_BLOCK FbDebounce VAR_INPUT IN : BOOL := FALSE; TD : TIME := T#30ms; PM : BOOL := FALSE; END_VAR VAR_OUTPUT Q : BOOL := FALSE; END_VAR (* Internal use only *) VAR deb : TOF; END_VAR (* @END_DECLARATION := '0' *) IF (NOT deb.Q AND IN) THEN (* rising edge on input detected *) Q := TRUE; ELSIF (NOT PM) THEN Q := deb.Q; ELSE Q := FALSE; END_IF; (* run debounce timer *) deb(in := IN, pt := TD); END_FUNCTION_BLOCK (* @NESTEDCOMMENTS := 'Yes' *) (* @PATH := '' *) (* @OBJECTFLAGS := '0, 8' *) (* @SYMFILEFLAGS := '2048' *) FUNCTION_BLOCK FbDimmI VAR_INPUT IN : BOOL := FALSE; VAL : BYTE := 255; EXT_IN : BOOL := FALSE; EXT_VAL : BYTE := 255; SET : BOOL := FALSE; RST : BOOL := FALSE; END_VAR VAR_INPUT CONSTANT T_DEBOUNCE : TIME := T#30ms; T_ON_MAX : TIME := T#0h; T_PRE_WARN : TIME := T#30s; T_PRE_WARN_SIGNAL : TIME := T#1s; T_DIMM_START : TIME := T#1s; T_DIMM : TIME := T#3s; MIN_ON : BYTE := 50; MAX_ON : BYTE := 255; SOFT_DIMM : BOOL := TRUE; DBL_TOGGLE : BOOL := FALSE; RST_OUT : BOOL := FALSE; IGNORE_EXT : BOOL := TRUE; END_VAR VAR_OUTPUT Q : BOOL := FALSE; DBL : BOOL := FALSE; EXT_Q : BOOL := FALSE; EXT_OUT : BYTE := 255; END_VAR VAR_IN_OUT OUT : BYTE; END_VAR (* Internal use only *) VAR dec : FbClickMode; dim : FbRampB; dir : BOOL; tx : TIME; t_on : TIME; xActive : BOOL := FALSE; xPreWarn : BOOL := FALSE; out_restore : BYTE; END_VAR (* @END_DECLARATION := '0' *) (* read system time *) tx := DWORD_TO_TIME(T_PLC_MS()); (* debounce and decode input *) dec(in := IN, t_debounce := T_DEBOUNCE, t_long := T_DIMM_START); (* ================================ *) (* === asynchronous reset and set first === *) (* ================================ *) IF RST THEN IF RST_OUT THEN OUT := 0; ELSIF xPreWarn THEN OUT := out_restore; (* restore original dimmer value *) END_IF; xPreWarn := FALSE; xActive := FALSE; Q := FALSE; (* set DIR to up when OUT < 128 otherwise set DIR to down; DIR is reversed because next action will reverse again! *) DIR := NOT (OUT < 128); ELSIF SET THEN t_on := tx; OUT := VAL; xPreWarn := FALSE; xActive := TRUE; Q := TRUE; (* set DIR to up when OUT < 128 otherwise set DIR to down; DIR is reversed because next action will reverse again! *) DIR := NOT (OUT < 128); END_IF; (* ================================================================================= *) (* === time check for pre-warning and auto switch-off function (not necessary as long as SET is active) === *) (* ================================================================================= *) IF (NOT SET AND xActive AND T_ON_MAX > T#0s) THEN IF ((T_PRE_WARN > T#0s) AND (T_PRE_WARN < T_ON_MAX) AND (tx - t_on >= T_ON_MAX - T_PRE_WARN)) THEN (* ---------------------------------------- *) (* --- within the pre-warn time --- *) (* ---------------------------------------- *) IF NOT xPreWarn THEN out_restore := OUT; (* store current dimmer value to restore at "reactivation" or auto switch-off *) xPreWarn := TRUE; END_IF; (* power off shortly for pre-warn signal? *) IF ((T_PRE_WARN_SIGNAL > T#0s) AND (T_PRE_WARN_SIGNAL < T_PRE_WARN)) THEN (* power off for a defined time *) IF ((tx - t_on >= T_ON_MAX - T_PRE_WARN) AND (tx - t_on < T_ON_MAX - T_PRE_WARN + T_PRE_WARN_SIGNAL)) THEN Q := FALSE; (* pre-warn signal: power off for a defined time *) ELSE Q := TRUE; END_IF; END_IF; (* if switch was activated during the pre-warn time, restart the auto-off mechanism *) IF (dec.SINGLE OR dec.TP_LONG) THEN xActive := FALSE; END_IF; END_IF; (* check if maximum activation time expired *) IF (xActive AND (tx - t_on >= T_ON_MAX)) THEN (* switch OFF *) xActive := FALSE; Q := FALSE; OUT := out_restore; (* restore original dimmer value *) xPreWarn := FALSE; (* prevent reactivation by the external input in the next step *) EXT_IN := FALSE; EXT_VAL := OUT; END_IF; END_IF; (* ============================================================ *) (* === check for single click or long click (but only if no set/reset is pending) === *) (* ============================================================ *) IF (NOT SET AND NOT RST) THEN (* ----------------------- *) (* --- single click --- *) (* ----------------------- *) IF dec.SINGLE THEN IF NOT xActive THEN (* switch ON (again, if switch was activated during the pre-warn time) *) t_on := tx; xActive := TRUE; Q := TRUE; (* change of OUT and DIR only outside the pre-warn time; during it just restore the old dimmer value *) IF xPreWarn THEN OUT := out_restore; (* restore original dimmer value *) xPreWarn := FALSE; ELSE (* when dimmer is turned on, we need to limit OUT to min and max limits *) OUT := LIMIT(MAX(MIN_ON, 1), OUT, MAX_ON); (* set the appropriate direction of dimmer; DIR is reversed because next action will reverse again! *) DIR := NOT (OUT < 128); END_IF; ELSE (* switch OFF *) xActive := FALSE; Q := FALSE; END_IF; (* -------------------- *) (* --- long click --- *) (* -------------------- *) ELSIF dec.TP_LONG THEN IF NOT xActive THEN (* switch ON (again, if switch was activated during the pre-warn time) *) t_on := tx; xActive := TRUE; Q := TRUE; (* change of OUT and DIR only outside the pre-warn time; during it just restore the old dimmer value *) IF xPreWarn THEN OUT := out_restore; (* restore original dimmer value *) xPreWarn := FALSE; ELSE IF SOFT_DIMM THEN OUT := 1; ELSE (* when dimmer is turned on, we need to limit OUT to min and max limits *) OUT := LIMIT(MAX(MIN_ON, 1), OUT, MAX_ON); END_IF; (* set the appropriate direction of dimmer; DIR is reversed because next action will reverse again! *) DIR := (OUT < 128); END_IF; ELSE (* reverse direction with every long click if dimmer is already active *) DIR := NOT DIR; END_IF; (* ----------------------------------------------------------------------------------------- *) (* --- external controlled (only if no dimming by switch is pending) --- *) (* ----------------------------------------------------------------------------------------- *) ELSIF (NOT dec.LONG AND NOT IGNORE_EXT) THEN (* switch ON *) IF (NOT xActive AND EXT_IN) THEN t_on := tx; xActive := TRUE; Q := TRUE; xPreWarn := FALSE; (* when dimmer is turned on, we need to limit OUT to min and max limits *) OUT := LIMIT(MAX(MIN_ON, 1), EXT_VAL, MAX_ON); (* set the appropriate direction of dimmer; DIR is reversed because next action will reverse again! *) DIR := NOT (OUT < 128); (* switch OFF *) ELSIF (xActive AND NOT EXT_IN) THEN xActive := FALSE; Q := FALSE; (* change of intensity during or outside the pre-warn time *) ELSIF (xPreWarn AND (out_restore <> EXT_VAL)) OR (NOT xPreWarn AND (OUT <> EXT_VAL)) THEN t_on := tx; OUT := EXT_VAL; IF xPreWarn THEN Q := TRUE; xPreWarn := FALSE; END_IF; END_IF; END_IF; END_IF; IF (NOT SET AND NOT RST AND dec.LONG) THEN (* while dimming by switch is active ramp the output OUT up or down *) dim(e := Q, dir := DIR, tr := T_DIMM, rmp := OUT); ELSE (* ramp down for pre-warn signal *) dim(e := (xPreWarn AND (T_PRE_WARN_SIGNAL > T#0s) AND (T_PRE_WARN_SIGNAL = T_PRE_WARN)), dir := FALSE, tr := T_PRE_WARN, rmp := OUT); END_IF; (* reverse direction when limits are reached *) IF OUT = 0 THEN DIR := TRUE; ELSIF OUT = 255 THEN DIR := FALSE; END_IF; (* set the double click output *) IF NOT DBL_TOGGLE THEN DBL := FALSE; END_IF; IF dec.DOUBLE THEN DBL := NOT DBL; END_IF; (* update external output *) EXT_Q := xActive; IF xPreWarn THEN EXT_OUT := out_restore; ELSE EXT_OUT := OUT; END_IF; END_FUNCTION_BLOCK (* @NESTEDCOMMENTS := 'Yes' *) (* @PATH := '' *) (* @OBJECTFLAGS := '0, 8' *) (* @SYMFILEFLAGS := '2048' *) FUNCTION_BLOCK FbImpulseRelay VAR_INPUT IN : BOOL := FALSE; EXT_IN : BOOL := FALSE; SET : BOOL := FALSE; RST : BOOL := FALSE; END_VAR VAR_INPUT CONSTANT T_DEBOUNCE : TIME := T#30ms; T_ON_MAX : TIME := T#0h; T_PRE_WARN : TIME := T#30s; T_PRE_WARN_SIGNAL : TIME := T#1s; IGNORE_EXT : BOOL := TRUE; END_VAR VAR_OUTPUT Q : BOOL := FALSE; EXT_Q : BOOL := FALSE; END_VAR (* Internal use only *) VAR tx : TIME; t_on : TIME; xActive : BOOL := FALSE; deb : FbDebounce; END_VAR (* @END_DECLARATION := '0' *) (* read system time *) tx := DWORD_TO_TIME(T_PLC_MS()); (* debounce input *) deb(in := IN, td := T_DEBOUNCE, pm := TRUE); IN := deb.Q; (* ================================ *) (* === asynchronous reset and set first === *) (* ================================ *) IF RST THEN xActive := FALSE; Q := FALSE; ELSIF SET THEN t_on := tx; xActive := TRUE; Q := TRUE; END_IF; (* ================================================================================= *) (* === time check for pre-warning and auto switch-off function (not necessary as long as SET is active) === *) (* ================================================================================= *) IF (NOT SET AND xActive AND T_ON_MAX > T#0s) THEN IF ((T_PRE_WARN > T#0s) AND (T_PRE_WARN < T_ON_MAX) AND (tx - t_on >= T_ON_MAX - T_PRE_WARN)) THEN (* ---------------------------------------- *) (* --- within the pre-warn time --- *) (* ---------------------------------------- *) IF ((T_PRE_WARN_SIGNAL > T#0s) AND (T_PRE_WARN_SIGNAL < T_PRE_WARN) AND (tx - t_on >= T_ON_MAX - T_PRE_WARN) AND (tx - t_on < T_ON_MAX - T_PRE_WARN + T_PRE_WARN_SIGNAL)) THEN Q := FALSE; (* pre-warn signal: power off for a defined time *) ELSE Q := TRUE; END_IF; (* if switch was activated during the pre-warn time, restart the auto-off mechanism *) IF IN THEN xActive := FALSE; END_IF; END_IF; (* check if maximum activation time expired *) IF (xActive AND (tx - t_on >= T_ON_MAX)) THEN xActive := FALSE; Q := FALSE; EXT_IN := FALSE; (* prevent reactivation by the external input in the next step *) END_IF; END_IF; (* ===================================================================== *) (* === check for single click to switch output ON or OFF (only if no set/reset is pending) === *) (* ===================================================================== *) IF (NOT SET AND NOT RST) THEN IF (NOT xActive AND (IN OR (NOT IGNORE_EXT AND EXT_IN))) THEN (* switch ON (again, if switch was activated during the pre-warn time) *) t_on := tx; xActive := TRUE; Q := TRUE; ELSIF (xActive AND (IN OR (NOT IGNORE_EXT AND NOT EXT_IN))) THEN (* switch OFF *) xActive := FALSE; Q := FALSE; END_IF; END_IF; (* update external output *) EXT_Q := xActive; END_FUNCTION_BLOCK (* @NESTEDCOMMENTS := 'Yes' *) (* @PATH := '' *) (* @OBJECTFLAGS := '0, 8' *) (* @SYMFILEFLAGS := '2048' *) FUNCTION_BLOCK FbRampB VAR_INPUT E : BOOL := TRUE; DIR : BOOL; (* true = up, false = down *) TR : TIME; END_VAR VAR_IN_OUT RMP : BYTE; END_VAR (* Internal use only *) VAR tx, tl, tn : TIME; init : BOOL := FALSE; last_dir : BOOL := FALSE; start : BYTE := 0; END_VAR (* @END_DECLARATION := '0' *) (* read system timer *) tx := DWORD_TO_TIME(T_PLC_MS()); IF (E AND init AND (DIR = last_dir) AND (RMP <> SEL(DIR, 0, 255)) AND (TR = tn)) THEN RMP := FuRampB(start, DIR, tx - tl, TR); ELSE init := TRUE; tl := tx; tn := TR; start := RMP; END_IF; last_dir := DIR; END_FUNCTION_BLOCK