blob: 2433c186a582b0de359dcec2db585c9b99a67dc7 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
(* @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.
dark theme is a modded version of: https://gist.github.com/Yoplitein/f4b671a2ec70c9e743fa