COSA
An Object-Oriented Platform for Arduino Programming
Main Page
Related Pages
Namespaces
Classes
Files
File List
File Members
Rotary.cpp
Go to the documentation of this file.
1
22
#include "
Rotary.hh
"
23
24
/*
25
* Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3.
26
* Contact: bb@cactii.net
27
*
28
* A typical mechanical rotary encoder emits a two bit gray code
29
* on 3 output pins. Every step in the output (often accompanied
30
* by a physical 'click') generates a specific sequence of output
31
* codes on the pins.
32
*
33
* There are 3 pins used for the rotary encoding - one common and
34
* two 'bit' pins.
35
*
36
* The following is the typical sequence of code on the output when
37
* moving from one step to the next:
38
*
39
* Position Bit1 Bit2
40
* ----------------------
41
* Step1 0 0
42
* 1/4 1 0
43
* 1/2 1 1
44
* 3/4 0 1
45
* Step2 0 0
46
*
47
* From this table, we can see that when moving from one 'click' to
48
* the next, there are 4 changes in the output code.
49
*
50
* - From an initial 0 - 0, Bit1 goes high, Bit0 stays low.
51
* - Then both bits are high, halfway through the step.
52
* - Then Bit1 goes low, but Bit2 stays high.
53
* - Finally at the end of the step, both bits return to 0.
54
*
55
* Detecting the direction is easy - the table simply goes in the other
56
* direction (read up instead of down).
57
*
58
* To decode this, we use a simple state machine. Every time the output
59
* code changes, it follows state, until finally a full steps worth of
60
* code is received (in the correct order). At the final 0-0, it returns
61
* a value indicating a step in one direction or the other.
62
*
63
* It's also possible to use 'half-step' mode. This just emits an event
64
* at both the 0-0 and 1-1 positions. This might be useful for some
65
* encoders where you want to detect all positions.
66
*
67
* If an invalid state happens (for example we go from '0-1' straight
68
* to '1-0'), the state machine resets to the start until 0-0 and the
69
* next valid codes occur.
70
*
71
* The biggest advantage of using a state machine over other algorithms
72
* is that this has inherent debounce built in. Other algorithms emit
73
* spurious output with switch bounce, but this one will simply flip
74
* between sub-states until the bounce settles, then continue along the
75
* state machine.
76
*
77
* A side effect of debounce is that fast rotations can cause steps to
78
* be skipped. By not requiring debounce, fast rotations can be accurately
79
* measured.
80
*
81
* Another advantage is the ability to properly handle bad state, such
82
* as due to EMI, etc. It is also a lot simpler than others - a static
83
* state table and less than 10 lines of logic.
84
*
85
* @see also
86
* http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html
87
*/
88
89
// No complete step yet
90
#define DIR_NONE 0x0
91
// Clockwise step
92
#define DIR_CW 0x10
93
// Anti-clockwise step
94
#define DIR_CCW 0x20
95
96
// Half-cycle states
97
#define R_START 0x0
98
#define R_CCW_BEGIN 0x1
99
#define R_CW_BEGIN 0x2
100
#define R_START_M 0x3
101
#define R_CW_BEGIN_M 0x4
102
#define R_CCW_BEGIN_M 0x5
103
136
const
uint8_t
Rotary::Encoder::half_cycle_table
[6][4]
__PROGMEM
= {
137
// R_START (00)
138
{
R_START_M
,
R_CW_BEGIN
,
R_CCW_BEGIN
,
R_START
},
139
// R_CCW_BEGIN
140
{
R_START_M
|
DIR_CCW
,
R_START
,
R_CCW_BEGIN
, R_START},
141
// R_CW_BEGIN
142
{
R_START_M
|
DIR_CW
,
R_CW_BEGIN
,
R_START
, R_START},
143
// R_START_M (11)
144
{
R_START_M
,
R_CCW_BEGIN_M
,
R_CW_BEGIN_M
, R_START},
145
// R_CW_BEGIN_M
146
{
R_START_M
,
R_START_M
,
R_CW_BEGIN_M
, R_START |
DIR_CW
},
147
// R_CCW_BEGIN_M
148
{
R_START_M
,
R_CCW_BEGIN_M
,
R_START_M
, R_START |
DIR_CCW
},
149
};
150
#undef R_CCW_BEGIN
151
152
// Full-cycle states
153
#define R_CW_FINAL 0x1
154
#define R_CW_BEGIN 0x2
155
#define R_CW_NEXT 0x3
156
#define R_CCW_BEGIN 0x4
157
#define R_CCW_FINAL 0x5
158
#define R_CCW_NEXT 0x6
159
196
const
uint8_t
Rotary::Encoder::full_cycle_table
[7][4]
__PROGMEM
= {
197
// R_START
198
{
R_START
,
R_CW_BEGIN
,
R_CCW_BEGIN
,
R_START
},
199
// R_CW_FINAL
200
{
R_CW_NEXT
,
R_START
,
R_CW_FINAL
, R_START |
DIR_CW
},
201
// R_CW_BEGIN
202
{
R_CW_NEXT
,
R_CW_BEGIN
,
R_START
, R_START},
203
// R_CW_NEXT
204
{
R_CW_NEXT
,
R_CW_BEGIN
,
R_CW_FINAL
, R_START},
205
// R_CCW_BEGIN
206
{
R_CCW_NEXT
,
R_START
,
R_CCW_BEGIN
, R_START},
207
// R_CCW_FINAL
208
{
R_CCW_NEXT
,
R_CCW_FINAL
,
R_START
, R_START |
DIR_CCW
},
209
// R_CCW_NEXT
210
{
R_CCW_NEXT
,
R_CCW_FINAL
,
R_CCW_BEGIN
, R_START},
211
};
212
213
void
214
Rotary::Encoder::SignalPin::on_interrupt(uint16_t arg)
215
{
216
UNUSED
(arg);
217
Rotary::Encoder::Direction
change = m_encoder->
detect
();
218
if
(change)
Event::push
(
Event::CHANGE_TYPE
, m_encoder, change);
219
}
220
221
Rotary::Encoder::Direction
222
Rotary::Encoder::detect
()
223
{
224
uint8_t pins = ((
m_dt
.
is_set
() << 1) |
m_clk
.
is_set
());
225
if
(m_mode ==
FULL_CYCLE
)
226
m_state
= pgm_read_byte(&
full_cycle_table
[
m_state
& 0xf][pins]);
227
else
228
m_state
= pgm_read_byte(&
half_cycle_table
[
m_state
& 0xf][pins]);
229
return
((
Direction
) (
m_state
& 0xf0));
230
}
231
R_CCW_FINAL
#define R_CCW_FINAL
Definition:
Rotary.cpp:157
__PROGMEM
const uint8_t Rotary::Encoder::half_cycle_table[6][4] __PROGMEM
Definition:
Rotary.cpp:136
Rotary::Encoder::half_cycle_table
static const uint8_t half_cycle_table[6][4]
Definition:
Rotary.hh:158
R_CCW_NEXT
#define R_CCW_NEXT
Definition:
Rotary.cpp:158
Rotary::Encoder::full_cycle_table
static const uint8_t full_cycle_table[7][4]
Definition:
Rotary.hh:161
R_START_M
#define R_START_M
Definition:
Rotary.cpp:100
DIR_CW
#define DIR_CW
Definition:
Rotary.cpp:92
Event::CHANGE_TYPE
Definition:
Event.hh:58
Pin::is_set
bool is_set() const
Definition:
Pin.hh:112
Rotary::Encoder::FULL_CYCLE
Definition:
Rotary.hh:76
Rotary::Encoder::Direction
Direction
Definition:
Rotary.hh:65
R_CW_NEXT
#define R_CW_NEXT
Definition:
Rotary.cpp:155
UNUSED
#define UNUSED(x)
Definition:
ATmega328P.hh:31
R_CCW_BEGIN_M
#define R_CCW_BEGIN_M
Definition:
Rotary.cpp:102
DIR_CCW
#define DIR_CCW
Definition:
Rotary.cpp:94
R_CW_FINAL
#define R_CW_FINAL
Definition:
Rotary.cpp:153
Rotary::Encoder::m_dt
SignalPin m_dt
Definition:
Rotary.hh:165
Rotary::Encoder::m_clk
SignalPin m_clk
Definition:
Rotary.hh:164
Rotary.hh
R_START
#define R_START
Definition:
Rotary.cpp:97
R_CCW_BEGIN
#define R_CCW_BEGIN
Definition:
Rotary.cpp:156
Rotary::Encoder::m_state
uint8_t m_state
Definition:
Rotary.hh:166
R_CW_BEGIN
#define R_CW_BEGIN
Definition:
Rotary.cpp:154
Rotary::Encoder::detect
Direction detect()
Definition:
Rotary.cpp:222
Event::push
static bool push(uint8_t type, Handler *target, uint16_t value=0)
Definition:
Event.hh:180
R_CW_BEGIN_M
#define R_CW_BEGIN_M
Definition:
Rotary.cpp:101
libraries
Rotary
Rotary.cpp
Generated on Thu Aug 31 2017 17:02:04 for COSA by
1.8.11