Control Surface main
MIDI Control Surface library for Arduino
Loading...
Searching...
No Matches
MCP23017Encoders.hpp
Go to the documentation of this file.
1#pragma once
2
4
8
10
31template <class WireType, class EncoderPositionType = int32_t,
32 bool InterruptSafe = false>
34 private:
35 constexpr static uint8_t I2C_BASE_ADDRESS = 0x20;
36
40
43
45
46 protected:
48 template <size_t N>
49 void writeI2C(const uint8_t (&values)[N]) {
50 this->wire->beginTransmission(address);
51 this->wire->write(values, N);
52 this->wire->endTransmission();
53 }
54
63 template <class... Args>
64 void writeI2C(uint8_t addr, Args... values) {
65 const uint8_t v[] = {addr, static_cast<uint8_t>(values)...};
66 writeI2C(v);
67 }
68
71 // No need to specify the register address, since this was done in the
72 // begin method, and the MCP23017 mode was set to Byte mode with
73 // IOCON.BANK = 0 (see §3.2.1 in the datasheet).
74 //
75 // TODO:
76 // For some reason, it sometimes seems to mess up though, and it'll read
77 // the wrong register, so we'll select the register again (for now).
78
79 writeI2C(0x12); // GPIOA
80
81 this->wire->requestFrom(address, size_t(2));
82 uint8_t a = this->wire->read();
83 uint16_t b = this->wire->read();
84 return a | (b << 8);
85 }
86
87 public:
107 pin_t interrupt_pin = NO_PIN)
108 : wire(&wire), address(I2C_BASE_ADDRESS | addr_offset),
109 interrupt_pin(interrupt_pin) {}
110
125 void begin() {
126 // Set the IOCON register (configuration register)
127 writeI2C(0x0A, // IOCON register for BANK=0
128 0b01100100);
129 // │││││││└─ Unimplemented
130 // ││││││└── INTPOL = Active-low
131 // │││││└─── ODR = Open-drain output (overrides the INTPOL bit)
132 // ││││└──── HAEN = Disables the MCP23S17 address pins
133 // │││└───── DISSLW = Slew rate enabled
134 // ││└────── SEQOP = Sequential operation disabled, address pointer does not increment
135 // │└─────── MIRROR = The INT pins are internally connected
136 // └──────── BANK = The registers are in the same bank (addresses are sequential)
137
138 // Set all GPIO pins to input mode
139 writeI2C(0x00, // IODIRA
140 0xFF, // input mode for GPIO A
141 0xFF); // input mode for GPIO B
142
143 // Enable all pin change interrupts
144 writeI2C(0x04, // GPINTENA
145 0xFF, // interrupt enable for GPIO A
146 0xFF); // interrupt enable for GPIO B
147
148 // Enable all internal pullups
149 writeI2C(0x0C, // GPPUA
150 0xFF, // pullup enable for GPIO A
151 0xFF); // pullup enable for GPIO B
152
153 // Interrupts are configured in open-drain mode, so enable
154 // the internal pullup resistor on the Arduino pin that
155 // reads the interrupt pin.
156 if (interrupt_pin != NO_PIN)
157 ExtIO::pinMode(interrupt_pin, INPUT_PULLUP);
158
159 // Set the address pointer to the GPIOA register.
160 // This means that subsequent reads will all toggle between the
161 // GPIOA and GPIOB register, so we can speedup reading the GPIO
162 // by not having to send an opcode/register address each time.
163 writeI2C(0x12); // GPIOA
164
166 encs.reset(readGPIO());
167 }
168
179 void update() {
180 // Only update if a pin change interrupt happened
181 if (interrupt_pin != NO_PIN &&
182 ExtIO::digitalRead(interrupt_pin) == HIGH)
183 return;
184 // Read both GPIO A and B
185 uint16_t newstate = readGPIO();
186 encs.update(newstate);
187 }
188
199 EncoderPositionType read(uint8_t idx) const { return encs.read(idx); }
200
212 return encs.readAndReset(idx);
213 }
214
225 void write(uint8_t idx, EncoderPositionType pos) { encs.write(idx, pos); }
226
231 using MCP23017Encoder = typename RegisterEncoderType::Encoder;
232
240 MCP23017Encoder operator[](uint8_t index) { return encs[index]; }
241};
242
#define END_AH_NAMESPACE
#define BEGIN_AH_NAMESPACE
constexpr PinStatus_t HIGH
constexpr PinMode_t INPUT_PULLUP
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.
A class for serial-in/parallel-out shift registers, like the 74HC595 that are connected to the SPI bu...