Control Surface main
MIDI Control Surface library for Arduino
MCP23017Encoders.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/Hardware/ExtendedInputOutput/ExtendedInputOutput.hpp>
14
16
37template <class WireType, class EncoderPositionType = int32_t,
38 bool InterruptSafe = false>
40 private:
41 constexpr static uint8_t I2C_BASE_ADDRESS = 0x20;
42
43 WireType *wire;
44 uint8_t address;
46
49
51
52 protected:
54 template <size_t N>
55 void writeI2C(const uint8_t (&values)[N]) {
56 this->wire->beginTransmission(address);
57 this->wire->write(values, N);
58 this->wire->endTransmission();
59 }
60
69 template <class... Args>
70 void writeI2C(uint8_t addr, Args... values) {
71 const uint8_t v[] = {addr, static_cast<uint8_t>(values)...};
72 writeI2C(v);
73 }
74
76 uint16_t readGPIO() {
77 // No need to specify the register address, since this was done in the
78 // begin method, and the MCP23017 mode was set to Byte mode with
79 // IOCON.BANK = 0 (see §3.2.1 in the datasheet).
80 //
81 // TODO:
82 // For some reason, it sometimes seems to mess up though, and it'll read
83 // the wrong register, so we'll select the register again (for now).
84
85 writeI2C(0x12); // GPIOA
86
87 this->wire->requestFrom(address, uint8_t(2));
88 uint8_t a = this->wire->read();
89 uint16_t b = this->wire->read();
90 return a | (b << 8);
91 }
92
93 public:
112 MCP23017Encoders(WireType &wire, uint8_t addr_offset = 0,
113 pin_t interrupt_pin = NO_PIN)
114 : wire(&wire), address(I2C_BASE_ADDRESS | addr_offset),
115 interrupt_pin(interrupt_pin) {}
116
131 void begin() {
132 // Set the IOCON register (configuration register)
133 writeI2C(0x0A, // IOCON register for BANK=0
134 0b01100100);
135 // │││││││└─ Unimplemented
136 // ││││││└── INTPOL = Active-low
137 // │││││└─── ODR = Open-drain output (overrides the INTPOL bit)
138 // ││││└──── HAEN = Disables the MCP23S17 address pins
139 // │││└───── DISSLW = Slew rate enabled
140 // ││└────── SEQOP = Sequential operation disabled, address pointer does not increment
141 // │└─────── MIRROR = The INT pins are internally connected
142 // └──────── BANK = The registers are in the same bank (addresses are sequential)
143
144 // Set all GPIO pins to input mode
145 writeI2C(0x00, // IODIRA
146 0xFF, // input mode for GPIO A
147 0xFF); // input mode for GPIO B
148
149 // Enable all pin change interrupts
150 writeI2C(0x04, // GPINTENA
151 0xFF, // interrupt enable for GPIO A
152 0xFF); // interrupt enable for GPIO B
153
154 // Enable all internal pullups
155 writeI2C(0x0C, // GPPUA
156 0xFF, // pullup enable for GPIO A
157 0xFF); // pullup enable for GPIO B
158
159 // Interrupts are configured in open-drain mode, so enable
160 // the internal pullup resistor on the Arduino pin that
161 // reads the interrupt pin.
162 if (interrupt_pin != NO_PIN)
163 ExtIO::pinMode(interrupt_pin, INPUT_PULLUP);
164
165 // Set the address pointer to the GPIOA register.
166 // This means that subsequent reads will all toggle between the
167 // GPIOA and GPIOB register, so we can speedup reading the GPIO
168 // by not having to send an opcode/register address each time.
169 writeI2C(0x12); // GPIOA
170
172 encs.reset(readGPIO());
173 }
174
185 void update() {
186 // Only update if a pin change interrupt happened
187 if (interrupt_pin != NO_PIN &&
188 ExtIO::digitalRead(interrupt_pin) == HIGH)
189 return;
190 // Read both GPIO A and B
191 uint16_t newstate = readGPIO();
192 encs.update(newstate);
193 }
194
205 EncoderPositionType read(uint8_t idx) const { return encs.read(idx); }
206
217 EncoderPositionType readAndReset(uint8_t idx) {
218 return encs.readAndReset(idx);
219 }
220
231 void write(uint8_t idx, EncoderPositionType pos) { encs.write(idx, pos); }
232
238
246 MCP23017Encoder operator[](uint8_t index) { return encs[index]; }
247};
248
250
#define END_AH_NAMESPACE
#define BEGIN_AH_NAMESPACE
constexpr PinStatus_t HIGH
constexpr PinMode_t INPUT_PULLUP
#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
Class for reading 8 rotary encoders using a MCP23017 I²C port expander.
MCP23017Encoders(WireType &wire, uint8_t addr_offset=0, pin_t interrupt_pin=NO_PIN)
Constructor.
RegisterEncoderType encs
typename RegisterEncoderType::Encoder MCP23017Encoder
Proxy to access a single encoder of the 8 encoders managed by MCP23017Encoders.
EncoderPositionType read(uint8_t idx) const
Read the position of the given encoder.
uint16_t readGPIO()
Read the state of all GPIO pins.
void begin()
Initialize the MCP23017.
MCP23017Encoder operator[](uint8_t index)
Get a proxy to one of the encoders managed by this MCP23017.
EncoderPositionType readAndReset(uint8_t idx)
Read the position of the given encoder and reset it to zero.
void update()
If the state of the MCP23017's GPIO changed, read the new state and update the encoder positions.
void writeI2C(uint8_t addr, Args... values)
Write any data to the MCP23017.
void write(uint8_t idx, EncoderPositionType pos)
Set the position of the given encoder.
void writeI2C(const uint8_t(&values)[N])
Write any data to the MCP23017.
Proxy to access a single encoder of the encoders managed by RegisterEncoders.
Class for keeping track of the position of multiple rotary encoders.
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.
void pinMode(pin_t pin, PinMode_t mode)
An ExtIO version of the Arduino function.
PinStatus_t digitalRead(pin_t pin)
An ExtIO version of the Arduino function.
constexpr pin_t NO_PIN
A special pin number that indicates an unused or invalid pin.
uint16_t pin_t
The type for Arduino pins (and ExtendedIOElement pins).