Control Surface main
MIDI Control Surface library for Arduino
RegisterEncoders.hpp
Go to the documentation of this file.
1#pragma once
2
5
6AH_DIAGNOSTIC_WERROR() // Enable errors on warnings
7
9#include <AH/Arduino-Wrapper.h>
11
12#include <AH/Containers/Array.hpp>
13#include <AH/STL/limits>
14#include <AH/STL/type_traits>
15
17
20constexpr static int8_t RegisterEncodersLUT[16] = {
21 0, // 0 0 0 0
22 +1, // 0 0 0 1
23 -1, // 0 0 1 0
24 +2, // 0 0 1 1
25 -1, // 0 1 0 0
26 0, // 0 1 0 1
27 -2, // 0 1 1 0
28 +1, // 0 1 1 1
29 +1, // 1 0 0 0
30 -2, // 1 0 0 1
31 0, // 1 0 1 0
32 -1, // 1 0 1 1
33 +2, // 1 1 0 0
34 -1, // 1 1 0 1
35 +1, // 1 1 1 0
36 0, // 1 1 1 1
37};
38
56template <class RegisterType, uint8_t NumEnc,
57 class EncoderPositionType = int32_t, bool InterruptSafe = false>
59 private:
60 static_assert(std::is_unsigned<RegisterType>::value,
61 "RegisterType should be an unsigned integer type");
62
64 typename std::conditional<InterruptSafe, volatile EncoderPositionType,
65 EncoderPositionType>::type;
66
68 typename std::conditional<InterruptSafe, volatile RegisterType,
69 RegisterType>::type;
70
73
74 public:
76 void reset() {
77 positions = {{}};
79 }
80
82 void reset(RegisterType resetstate) {
83 positions = {{}};
84 state = resetstate;
85 }
86
102 bool update(RegisterType newstate) {
103 RegisterType oldstate = state;
104
105 // If the state didn't change, do nothing
106 if (newstate == oldstate)
107 return false;
108
109 // Save the new state
110 state = newstate;
111
112 // For each encoder, compare the new state of its two pins
113 // to the old state. Combine the four states into a 4-bit
114 // number and use a lookup table to determine the delta between
115 // the two encoder positions.
116 for (uint8_t i = 0; i < NumEnc; ++i) {
117 // Top two bits are new pin states
118 uint8_t change = static_cast<uint8_t>(newstate) & 0b11;
119 change <<= 2;
120 // Bottom two bits are old pin states
121 change |= static_cast<uint8_t>(oldstate) & 0b11;
122 auto delta =
123 static_cast<EncoderPositionType>(RegisterEncodersLUT[change]);
124 if (delta != 0) // small speedup on AVR
125 positions[i] += delta;
126 oldstate >>= 2;
127 newstate >>= 2;
128 }
129 return true;
130 }
131
142 EncoderPositionType read(uint8_t idx) const {
143 if (InterruptSafe) {
144 noInterrupts();
145 EncoderPositionType ret = positions[idx];
146 interrupts();
147 return ret;
148 } else {
149 return positions[idx];
150 }
151 }
152
163 EncoderPositionType readAndReset(uint8_t idx) {
164 if (InterruptSafe) {
165 noInterrupts();
166 EncoderPositionType ret = positions[idx];
167 positions[idx] = 0;
168 interrupts();
169 return ret;
170 } else {
171 EncoderPositionType ret = positions[idx];
172 positions[idx] = 0;
173 return ret;
174 }
175 }
176
187 void write(uint8_t idx, EncoderPositionType pos) {
188 if (InterruptSafe) {
189 noInterrupts();
190 positions[idx] = pos;
191 interrupts();
192 } else {
193 positions[idx] = pos;
194 }
195 }
196
201 class Encoder {
202 private:
203 friend class RegisterEncoders;
204
207
208 Encoder(EncoderPositionStorageType *position) : position(position) {}
209
210 public:
213 EncoderPositionType read() const {
214 if (InterruptSafe) {
215 noInterrupts();
216 EncoderPositionType ret = *position;
217 interrupts();
218 return ret;
219 } else {
220 return *position;
221 }
222 }
223
226 EncoderPositionType readAndReset() {
227 if (InterruptSafe) {
228 noInterrupts();
229 EncoderPositionType ret = *position;
230 *position = 0;
231 interrupts();
232 return ret;
233 } else {
234 EncoderPositionType ret = *position;
235 *position = 0;
236 return ret;
237 }
238 }
239
242 void write(EncoderPositionType pos) {
243 if (InterruptSafe) {
244 noInterrupts();
245 *position = pos;
246 interrupts();
247 } else {
248 *position = pos;
249 }
250 }
251
252#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
253 // begin_if_possible doesn't work on GCC 4.x and earlier, so provide a
254 // dummy begin method instead.
255 void begin() {}
256#endif
257 };
258
265 Encoder operator[](uint8_t index) {
266 if (index >= NumEnc)
267 index = NumEnc - 1;
268 return &positions[index];
269 }
270};
271
273
#define END_AH_NAMESPACE
#define BEGIN_AH_NAMESPACE
#define AH_DIAGNOSTIC_EXTERNAL_HEADER()
Definition: Warnings.hpp:38
#define AH_DIAGNOSTIC_POP()
Definition: Warnings.hpp:37
#define AH_DIAGNOSTIC_WERROR()
Definition: Warnings.hpp:36
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.
typename std::conditional< InterruptSafe, volatile EncoderPositionType, EncoderPositionType >::type EncoderPositionStorageType
typename std::conditional< InterruptSafe, volatile RegisterType, RegisterType >::type StateStorageType
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.
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.
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.
constexpr auto max(const T &a, const U &b) -> decltype(a< b ? b :a)
Return the larger of two numbers/objects.
Definition: MinMaxFix.hpp:22
static constexpr int8_t RegisterEncodersLUT[16]
Lookup table that maps rotary encoder (2-bit gray code) state changes to position deltas.