Control Surface main
MIDI Control Surface library for Arduino
Loading...
Searching...
No Matches
RegisterEncoders.hpp
Go to the documentation of this file.
1#pragma once
2
5
7#include <AH/STL/limits>
8#include <AH/STL/type_traits>
9
11
14constexpr static int8_t RegisterEncodersLUT[16] = {
15 0, // 0 0 0 0
16 +1, // 0 0 0 1
17 -1, // 0 0 1 0
18 +2, // 0 0 1 1
19 -1, // 0 1 0 0
20 0, // 0 1 0 1
21 -2, // 0 1 1 0
22 +1, // 0 1 1 1
23 +1, // 1 0 0 0
24 -2, // 1 0 0 1
25 0, // 1 0 1 0
26 -1, // 1 0 1 1
27 +2, // 1 1 0 0
28 -1, // 1 1 0 1
29 +1, // 1 1 1 0
30 0, // 1 1 1 1
31};
32
50template <class RegisterType, uint8_t NumEnc,
51 class EncoderPositionType = int32_t, bool InterruptSafe = false>
53 private:
54 static_assert(std::is_unsigned<RegisterType>::value,
55 "RegisterType should be an unsigned integer type");
56
58 typename std::conditional<InterruptSafe, volatile EncoderPositionType,
60
62 typename std::conditional<InterruptSafe, volatile RegisterType,
63 RegisterType>::type;
64
65 StateStorageType state = std::numeric_limits<RegisterType>::max();
67
68 public:
70 void reset() {
71 positions = {{}};
72 state = std::numeric_limits<RegisterType>::max();
73 }
74
77 positions = {{}};
78 state = resetstate;
79 }
80
97 RegisterType oldstate = state;
98
99 // If the state didn't change, do nothing
100 if (newstate == oldstate)
101 return false;
102
103 // Save the new state
104 state = newstate;
105
106 // For each encoder, compare the new state of its two pins
107 // to the old state. Combine the four states into a 4-bit
108 // number and use a lookup table to determine the delta between
109 // the two encoder positions.
110 for (uint8_t i = 0; i < NumEnc; ++i) {
111 // Top two bits are new pin states
112 uint8_t change = static_cast<uint8_t>(newstate) & 0b11;
113 change <<= 2;
114 // Bottom two bits are old pin states
115 change |= static_cast<uint8_t>(oldstate) & 0b11;
116 auto delta =
118 if (delta != 0) // small speedup on AVR
119 positions[i] += delta;
120 oldstate >>= 2;
121 newstate >>= 2;
122 }
123 return true;
124 }
125
137 if (InterruptSafe) {
138 noInterrupts();
139 EncoderPositionType ret = positions[idx];
140 interrupts();
141 return ret;
142 } else {
143 return positions[idx];
144 }
145 }
146
158 if (InterruptSafe) {
159 noInterrupts();
160 EncoderPositionType ret = positions[idx];
161 positions[idx] = 0;
162 interrupts();
163 return ret;
164 } else {
165 EncoderPositionType ret = positions[idx];
166 positions[idx] = 0;
167 return ret;
168 }
169 }
170
182 if (InterruptSafe) {
183 noInterrupts();
184 positions[idx] = pos;
185 interrupts();
186 } else {
187 positions[idx] = pos;
188 }
189 }
190
195 class Encoder {
196 private:
197 friend class RegisterEncoders;
198
201
202 Encoder(EncoderPositionStorageType *position) : position(position) {}
203
204 public:
208 if (InterruptSafe) {
209 noInterrupts();
210 EncoderPositionType ret = *position;
211 interrupts();
212 return ret;
213 } else {
214 return *position;
215 }
216 }
217
221 if (InterruptSafe) {
222 noInterrupts();
223 EncoderPositionType ret = *position;
224 *position = 0;
225 interrupts();
226 return ret;
227 } else {
228 EncoderPositionType ret = *position;
229 *position = 0;
230 return ret;
231 }
232 }
233
237 if (InterruptSafe) {
238 noInterrupts();
239 *position = pos;
240 interrupts();
241 } else {
242 *position = pos;
243 }
244 }
245
246#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
247 // begin_if_possible doesn't work on GCC 4.x and earlier, so provide a
248 // dummy begin method instead.
249 void begin() {}
250#endif
251 };
252
260 if (index >= NumEnc)
261 index = NumEnc - 1;
262 return &positions[index];
263 }
264};
265
#define END_AH_NAMESPACE
#define BEGIN_AH_NAMESPACE
Proxy to access a single encoder of the encoders managed by RegisterEncoders.
EncoderPositionStorageType * position
A pointer to the position value inside of the RegisterEncoders class.
void write(EncoderPositionType pos)
Set the position of the encoder.
EncoderPositionType read() const
Read the position of the encoder.
EncoderPositionType readAndReset()
Read the position of the encoder and reset it to zero.
Encoder(EncoderPositionStorageType *position)
Class for keeping track of the position of multiple rotary encoders.
Encoder operator[](uint8_t index)
Get a proxy to one of the encoders managed by this object.
void reset(RegisterType resetstate)
Reset the positions to zero and the state to the given value.
EncoderPositionType read(uint8_t idx) const
Read the position of the given encoder.
typename std::conditional< InterruptSafe, volatile EncoderPositionType, EncoderPositionType >::type EncoderPositionStorageType
bool update(RegisterType newstate)
Update the encoder positions based on the new state.
EncoderPositionType readAndReset(uint8_t idx)
Read the position of the given encoder and reset it to zero.
typename std::conditional< InterruptSafe, volatile RegisterType, RegisterType >::type StateStorageType
void reset()
Reset the positions to zero and the state to 0xFF...FF.
void write(uint8_t idx, EncoderPositionType pos)
Set the position of the given encoder.
A class for serial-in/parallel-out shift registers, like the 74HC595 that are connected to the SPI bu...
static constexpr int8_t RegisterEncodersLUT[16]
Lookup table that maps rotary encoder (2-bit gray code) state changes to position deltas.