summaryrefslogblamecommitdiff
path: root/exp/FB_INC_DECODER.EXP
blob: 2433c186a582b0de359dcec2db585c9b99a67dc7 (plain) (tree)




































































































































                                                                                                                
(* @NESTEDCOMMENTS := 'Yes' *)
(* @PATH := '' *)
(* @OBJECTFLAGS := '0, 8' *)
(* @SYMFILEFLAGS := '2048' *)
FUNCTION_BLOCK FB_INC_DECODER
VAR_INPUT
	IN_xChA			: BOOL;		(* Channel A *)
	IN_xChB			: BOOL;		(* Channel B, 90-degree rotated to Channel A *)
	IN_xReset		: BOOL;		(* Reset counter to 0 *)

	IN_xEnPosWindow	: BOOL; 	(* TRUE if you want to overflow the counting at min/max values below *)
	IN_diMinVal		: DINT;
	IN_diMaxVal		: DINT;
END_VAR
VAR_OUTPUT
	OUT_diActVal	: DINT;
END_VAR
VAR_OUTPUT PERSISTENT
	OUT_pdiActVal	: DINT;
END_VAR
VAR
	rtChA		: R_TRIG;
	rtChB		: R_TRIG;
	ftChA		: F_TRIG;
	ftChB		: F_TRIG;
	xFirstCycle	: BOOL;
END_VAR

(*

Incremental Encoder Decoder FB
Author: mo
Date: 2019-02

	alternative OSCAT implementation (faulty!) below:
	
	<OSCAT>
	axb := cha XOR chb;
	
	(* create pulses for channel a *)
	clka := cha XOR edgea;
	edgea := cha;
	
	clkb := chb XOR edgeb;
	edgeb := chb;
	
	(* create pulses for both channels *)
	clk := clka OR clkb;
	
	(* set the direction output *)
	IF axb AND clka THEN dir := TRUE; END_IF;
	IF axb AND clkb THEN dir := FALSE; END_IF;
	
	(* increment or decrement the counter *)
	IF clk AND dir THEN cnt := cnt + 1; END_IF;
	IF clk AND NOT dir THEN cnt := cnt -1; END_IF;
	
	(* reset the counter if rst active *)
	IF rst THEN cnt := 0; END_IF;
	</OSCAT>

=> problem here is: if your first pulse is e.g. a rising edge on CHb
   with a high signal on CHa, it will increment cnt, but in the FALSE
   direction instead of the TRUE direction. so your first pulse might count
   into the wrong direction!


This implementation accounts for all possible transitions.
You can use OUT_diActVal or the persistent Version OUT_pdiActVal if your hardware
is not moving while powered off to avoid reference runs at each restart of the PLC.

  1  |----|    |----|    |-
A 0 _|    |____|    |____|

  1     |----|    |----|    |-
B 0    _|    |____|    |____|

*)

(* @END_DECLARATION := '0' *)
rtChA(clk:=IN_xChA);
rtChB(clk:=IN_xChB);
ftChA(clk:=IN_xChA);
ftChB(clk:=IN_xChB);

(* don't evaluate the changing edges when PLC starts up *)
IF xFirstCycle THEN
	IF 		(rtChA.Q AND NOT IN_xChB)
		OR	(ftChA.Q AND IN_xChB)
		OR	(rtChB.Q AND IN_xChA)
		OR	(ftChB.Q AND NOT IN_xChA)
	THEN
		OUT_diActVal := OUT_diActVal + 1;
		OUT_pdiActVal := OUT_pdiActVal + 1;
	END_IF

	IF		(rtChA.Q AND IN_xChB)
		OR	(ftChA.Q AND NOT IN_xChB)
		OR	(rtChB.Q AND NOT IN_xChA)
		OR	(ftChB.Q AND IN_xChA)
	THEN
		OUT_diActVal := OUT_diActVal - 1;
		OUT_pdiActVal := OUT_pdiActVal - 1;
	END_IF
END_IF

IF IN_xReset THEN
	OUT_diActVal := 0;
	OUT_pdiActVal := 0;
END_IF

IF IN_xEnPosWindow THEN
	IF OUT_diActVal < IN_diMinVal THEN
		OUT_diActVal := IN_diMaxVal - 1;
	END_IF

	IF OUT_diActVal >= IN_diMaxVal THEN
		OUT_diActVal := IN_diMinVal;
	END_IF

	(* for persistent counter *)
	IF OUT_pdiActVal < IN_diMinVal THEN
		OUT_pdiActVal := IN_diMaxVal - 1;
	END_IF

	IF OUT_pdiActVal >= IN_diMaxVal THEN
		OUT_pdiActVal := IN_diMinVal;
	END_IF
END_IF

xFirstCycle := TRUE;
END_FUNCTION_BLOCK
in each repos: see "about"-tab (if existing) for more details / README.
mailto contact at omeckman dot net
all timestamps in UTC (German winter time: UTC+01:00, summer time: UTC+02:00)
dark theme is a modded version of: https://gist.github.com/Yoplitein/f4b671a2ec70c9e743fa