-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathEncoder.cpp
More file actions
288 lines (240 loc) · 6.37 KB
/
Encoder.cpp
File metadata and controls
288 lines (240 loc) · 6.37 KB
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
/*File: Encoder.cpp
*
* Arduino library for 2 phase rotary encoder, with push switch.
*
* Author: Dave Harris (c) 18-June-2020 v1.3
*/
#include <Arduino.h>
#include <Encoder.h>
enum swStatus_t : byte // switchISR(), encoderISR() & class Encoder use this
{
_DOWN = 0 // ISR flags DOWN
, _UP = 1 // ISR flags UP
, _PROCESSED = 2 // Processed and has been notified to user
, _TIMEOUT = 3 // flag DOWN for too long
};
// switchISR(), encoderISR() & class Encoder use the below variable
byte _pinB; // set by .begin no default
byte _pinSw; // set by .begin no default
bool _wrap; // set by .begin default wrap mode true
bool _pinB_ACW; // set by .begin default 0
byte _maxPos = 255;
byte _minPos = 0;
volatile byte _pos = 32;
volatile unsigned long _swFirstDown_ms;
volatile unsigned long _swLastUp_ms;
volatile swStatus_t _swStatus;
press_t Encoder::switchPressed(void) //-------------------switchPressed()----
/*
* returns enum
* PRESS_NONE not pressed
* PRESS_LONG after a Long press
* PRESS_SHORT after a Short press
* PRESS_TIMEOUT still pressed down after 5 seconds
*
* globals variables: _swStatus, _swFirstDown_ms, _swLastUp_ms
* calls : millis(), delay()
*/
{
unsigned int duration_ms = 0;
if (_swStatus >= _PROCESSED) // PROCESSED or TIMEOUT
{
return PRESS_NONE; // no pressed
}
// so is switch DOWN or UP?
while (_swStatus == _DOWN) // wait while switch is DOWN
{
duration_ms = (unsigned int) (millis() - _swFirstDown_ms);
if (duration_ms < 5)
{
delay(duration_ms); // simple debounce, if needed
}
if (duration_ms > STUCKDOWN_ms)
{
//_swFirstDown_ms = millis();
_swStatus = _TIMEOUT;
return PRESS_TIMEOUT; // its down way too long
}
} // switch is UP now
_swStatus = _PROCESSED; // replace UP status with PROCESSED
if ((millis() - _swLastUp_ms) > HISTORIC_ms) // too historic?
{
return PRESS_NONE;
}
if (duration_ms > LONG_PRESS_ms)
{
return PRESS_LONG;
}
return PRESS_SHORT;
}
static void switchISR() //------------------------------------switchISR()----
/*
* switch Interrupt Service Routine attached to PinSw on Change.
*
* @16 MHz it typically takes 8.3 us
*
* global variables: _swStatus, _swFirstDown_ms, _swLastUp_ms
* calls : millis(), digitalRead()
*/
{
if (digitalRead(_pinSw) == LOW) // switch went High to Low = DOWN
{
if (_swStatus >= _PROCESSED ) // PROCESSED or TIMEOUT
{
_swFirstDown_ms = millis(); // note the time
}
_swStatus = _DOWN;
}
else // switch went Low to High = UP
{
if (_swStatus == _TIMEOUT)
{
_swStatus = _PROCESSED;
}
else
{
_swStatus = _UP;
_swLastUp_ms = millis(); // note the time
}
}
} //return from interrupt
static void encoderISR() //----------------------------------encoderISR()----
/*
* Encoder Interrupt Service Routine attached to PinA on Falling.
*
* @16 MHz it typically takes 11.5 us
*
* global variables: _pos, _minPos, _maxPos, _wrap
* calls : millis(), digitalRead()
*/
{
static unsigned long last_ms = 0; // carried over to next interrupt
unsigned int duration_ms = (millis() - last_ms);
if (duration_ms < VERYFAST_ms)
{
return; // pulses can be corrupt = no action
}
last_ms = millis();
byte step = 1;
if (duration_ms < FAST_ms)
{
step = FAST_STEP;
}
// entering ISR, pinA just went Low, so test pinB to determine direction
if (digitalRead(_pinB) == _pinB_ACW) // ACW, so decrease position
{
if (_pos <= _minPos) // at minimum (or below), do we need to wrap?
{
if (_wrap)
{
_pos = _maxPos; // wrap to maximum
}
else // stay at minimum (if was below then correct)
{
_pos = _minPos; // (defensive code)
}
}
else // we are above minimum
{
if (_pos >= (_minPos + step)) // enough to do a fast step?
{
_pos -= step; // step down position (slow or fast)
}
else // we are close to minPos
{
_pos--; // step down slow
}
}
}
else // so clockwise, so increase position
{
if (_pos >= _maxPos) // at max (or above), do we need to wrap?
{
if (_wrap)
{
_pos = _minPos; // wrap to minimum
}
else // stay at maximum (if was above then correct)
{
_pos = _maxPos; // (defensive code)
}
}
else // we are below maximum
{
if (_pos <= (_maxPos - step)) // enough to do a fast step?
{
_pos += step; // step up position (slow or fast)
}
else // we are close to maxPos
{
_pos++; // step up slow
}
}
}
} //return from interrupt
void Encoder::begin( //-------------------------------------------begin()----
byte pinA, byte pinB, byte pinSw, bool wrap, bool pinB_ACW)
/*
* Set the encoder pins and start the encoder.
* pinA has to be an interrupt on change pin.
*/
{
_pinB = pinB; // for later use in ISR
_pinSw = pinSw; // for use in switch code
_wrap = wrap; // wrap mode
_pinB_ACW = pinB_ACW; // pinB_ACW: true=high level, false=low level
pinMode(pinA, INPUT);
pinMode(pinB, INPUT);
pinMode(pinSw, INPUT);
_swStatus = ( digitalRead(pinA) == 0 ? _DOWN : _PROCESSED );
attachInterrupt(digitalPinToInterrupt(pinA), encoderISR, FALLING);
attachInterrupt(digitalPinToInterrupt(pinSw), switchISR, CHANGE);
};
bool Encoder::switchState(void) //--------------------------switchState()----
/*
* return push switch state 0 = pressed, 1 = not pressed
*/
{
return digitalRead(_pinSw);
}
byte Encoder::position(void) //--------------------------------position()----
/*
* read back encoder current position
*/
{
return _pos;
}
void Encoder::position(byte newPos) //-------------------position(newPos)----
/*
* set the position. If outside of minPos and maxPos then adjust.
*/
{
if (newPos < _minPos)
{
_pos = _minPos;
}
else
{
if (newPos > _maxPos)
{
_pos = _maxPos;
}
else
{
_pos = newPos;
}
}
}
void Encoder::limits(byte min, byte max) //---------------limits(min,max)----
/*
* set encoder position limits
*/
{
if (max > min) // defensive check
{
_minPos = min;
_maxPos = max;
position(_pos); // does _pos need adjusting?
}
}
//---------------------------------------------------------------------EoF----