29#include "freertos/FreeRTOS.h"
30#include "freertos/task.h"
42 : m_SPIDevHandle(nullptr)
53bool MCP23S17::begin(
int MISO,
int MOSI,
int CLK,
int CS,
int CSActiveState,
int host)
60 switch (getChipPackage()) {
61 case ChipPackage::ESP32PICOD4:
66 case ChipPackage::ESP32D0WDQ5:
69 if (CSActiveState == -1)
85 m_MISO = int2gpio(MISO);
86 m_MOSI = int2gpio(MOSI);
87 m_CLK = int2gpio(CLK);
89 m_SPIHost = (spi_host_device_t) host;
91 bool r = SPIBegin(CSActiveState) && initDevice(0);
101bool MCP23S17::initDevice(uint8_t hwAddr)
104 if (hwAddr < MCP_MAXDEVICES) {
105 m_IOCON[hwAddr] = MCP_IOCON_SEQOP | MCP_IOCON_HAEN;
106 writeReg(MCP_IOCON, m_IOCON[hwAddr], hwAddr);
107 r = readReg(MCP_IOCON, hwAddr) == m_IOCON[hwAddr];
119bool MCP23S17::SPIBegin(
int CSActiveState)
121 spi_bus_config_t busconf = { };
122 busconf.mosi_io_num = m_MOSI;
123 busconf.miso_io_num = m_MISO;
124 busconf.sclk_io_num = m_CLK;
125 busconf.quadwp_io_num = -1;
126 busconf.quadhd_io_num = -1;
127 busconf.flags = SPICOMMON_BUSFLAG_MASTER;
128 auto r = spi_bus_initialize(m_SPIHost, &busconf, MCP_DMACHANNEL);
129 if (r == ESP_OK || r == ESP_ERR_INVALID_STATE) {
130 spi_device_interface_config_t devconf = { };
132 devconf.clock_speed_hz = MCP_SPI_FREQ;
133 devconf.spics_io_num = m_CS;
134 devconf.flags = (CSActiveState == 1 ? SPI_DEVICE_POSITIVE_CS : 0);
135 devconf.queue_size = 1;
136 r = spi_bus_add_device(m_SPIHost, &devconf, &m_SPIDevHandle);
137 if (r != ESP_OK && !FileBrowser::mountedSDCard())
138 spi_bus_free(m_SPIHost);
145void MCP23S17::SPIEnd()
147 if (m_SPIDevHandle) {
148 spi_bus_remove_device(m_SPIDevHandle);
149 m_SPIDevHandle =
nullptr;
150 if (FileBrowser::mountedSDCard()) {
151 if (getChipPackage() == ChipPackage::ESP32D0WDQ5) {
152 fabgl::configureGPIO(m_CS, GPIO_MODE_OUTPUT);
153 gpio_set_level(m_CS, 1);
156 spi_bus_free(m_SPIHost);
162void MCP23S17::writeReg(uint8_t addr, uint8_t value, uint8_t hwAddr)
164 spi_device_acquire_bus(m_SPIDevHandle, portMAX_DELAY);
166 uint8_t txdata[3] = { (uint8_t)(0b01000000 | (hwAddr << 1)), addr, value };
167 spi_transaction_t ta;
171 ta.rx_buffer =
nullptr;
172 ta.tx_buffer = txdata;
173 spi_device_transmit(m_SPIDevHandle, &ta);
175 spi_device_release_bus(m_SPIDevHandle);
179uint8_t MCP23S17::readReg(uint8_t addr, uint8_t hwAddr)
181 spi_device_acquire_bus(m_SPIDevHandle, portMAX_DELAY);
183 uint8_t txdata[3] = { (uint8_t)(0b01000001 | (hwAddr << 1)), addr };
184 uint8_t rxdata[3] = { 0 };
185 spi_transaction_t ta;
189 ta.rx_buffer = rxdata;
190 ta.tx_buffer = txdata;
192 if (spi_device_transmit(m_SPIDevHandle, &ta) == ESP_OK)
195 spi_device_release_bus(m_SPIDevHandle);
201void MCP23S17::writeReg16(uint8_t addr, uint16_t value, uint8_t hwAddr)
203 spi_device_acquire_bus(m_SPIDevHandle, portMAX_DELAY);
205 uint8_t txdata[4] = { (uint8_t)(0b01000000 | (hwAddr << 1)), addr, (uint8_t)(value & 0xff), (uint8_t)(value >> 8) };
206 spi_transaction_t ta;
210 ta.rx_buffer =
nullptr;
211 ta.tx_buffer = txdata;
212 spi_device_transmit(m_SPIDevHandle, &ta);
214 spi_device_release_bus(m_SPIDevHandle);
218uint16_t MCP23S17::readReg16(uint8_t addr, uint8_t hwAddr)
220 spi_device_acquire_bus(m_SPIDevHandle, portMAX_DELAY);
222 uint8_t txdata[4] = { (uint8_t)(0b01000001 | (hwAddr << 1)), addr };
223 uint8_t rxdata[4] = { 0 };
224 spi_transaction_t ta;
228 ta.rx_buffer = rxdata;
229 ta.tx_buffer = txdata;
231 if (spi_device_transmit(m_SPIDevHandle, &ta) == ESP_OK)
232 r = rxdata[2] | (rxdata[3] << 8);
234 spi_device_release_bus(m_SPIDevHandle);
240void MCP23S17::enableINTMirroring(
bool value, uint8_t hwAddr)
242 writeReg(MCP_IOCON, value ? m_IOCON[hwAddr] | MCP_IOCON_MIRROR : m_IOCON[hwAddr] & ~MCP_IOCON_MIRROR, hwAddr);
246void MCP23S17::enableINTOpenDrain(
bool value, uint8_t hwAddr)
248 writeReg(MCP_IOCON, value ? m_IOCON[hwAddr] | MCP_IOCON_ODR : m_IOCON[hwAddr] & ~MCP_IOCON_ODR, hwAddr);
252void MCP23S17::setINTActiveHigh(
bool value, uint8_t hwAddr)
254 writeReg(MCP_IOCON, value ? m_IOCON[hwAddr] | MCP_IOCON_INTPOL : m_IOCON[hwAddr] & ~MCP_IOCON_INTPOL, hwAddr);
258void MCP23S17::configureGPIO(
int gpio,
MCPDir dir,
bool pullup, uint8_t hwAddr)
260 uint8_t mask = MCP_GPIO2MASK(gpio);
262 uint8_t reg = MCP_GPIO2REG(MCP_IODIR, gpio);
263 if (dir == MCPDir::Input)
264 writeReg(reg, readReg(reg, hwAddr) | mask, hwAddr);
266 writeReg(reg, readReg(reg, hwAddr) & ~mask, hwAddr);
268 reg = MCP_GPIO2REG(MCP_GPPU, gpio);
269 writeReg(reg, (readReg(reg, hwAddr) & ~mask) | ((
int)pullup * mask), hwAddr);
273void MCP23S17::writeGPIO(
int gpio,
bool value, uint8_t hwAddr)
275 uint8_t olat = readReg(MCP_GPIO2REG(MCP_OLAT, gpio), hwAddr);
276 uint8_t mask = MCP_GPIO2MASK(gpio);
277 uint8_t reg = MCP_GPIO2REG(MCP_OLAT, gpio);
278 writeReg(reg, value ? olat | mask : olat & ~mask, hwAddr);
282bool MCP23S17::readGPIO(
int gpio, uint8_t hwAddr)
284 return readReg(MCP_GPIO2REG(MCP_GPIO, gpio), hwAddr) & MCP_GPIO2MASK(gpio);
288void MCP23S17::enableInterrupt(
int gpio,
MCPIntTrigger trigger,
bool defaultValue, uint8_t hwAddr)
290 uint8_t mask = MCP_GPIO2MASK(gpio);
292 if (trigger == MCPIntTrigger::DefaultChange) {
294 writeReg(MCP_GPIO2REG(MCP_INTCON, gpio), readReg(MCP_GPIO2REG(MCP_INTCON, gpio), hwAddr) | mask, hwAddr);
295 writeReg(MCP_GPIO2REG(MCP_DEFVAL, gpio), (readReg(MCP_GPIO2REG(MCP_DEFVAL, gpio), hwAddr) & ~mask) | ((
int)defaultValue * mask), hwAddr);
298 writeReg(MCP_GPIO2REG(MCP_INTCON, gpio), readReg(MCP_GPIO2REG(MCP_INTCON, gpio), hwAddr) & ~mask, hwAddr);
301 writeReg(MCP_GPIO2REG(MCP_GPINTEN, gpio), readReg(MCP_GPIO2REG(MCP_GPINTEN, gpio), hwAddr) | mask, hwAddr);
305void MCP23S17::disableInterrupt(
int gpio, uint8_t hwAddr)
307 uint8_t reg = MCP_GPIO2REG(MCP_GPINTEN, gpio);
308 writeReg(reg, readReg(reg, hwAddr) & ~MCP_GPIO2MASK(gpio), hwAddr);
312void MCP23S17::writePort(
int port,
void const * buffer,
size_t length, uint8_t hwAddr)
316 writeReg(MCP_IOCON, m_IOCON[hwAddr] | MCP_IOCON_SEQOP | MCP_IOCON_BANK);
318 spi_device_acquire_bus(m_SPIDevHandle, portMAX_DELAY);
320 spi_transaction_ext_t ta = { };
323 ta.base.cmd = 0b01000000 | (hwAddr << 1);
324 ta.base.addr = MCP_BNK1_OLAT + port * 0x10;
325 ta.base.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR;
326 ta.base.length = 16 + 8 * length;
327 ta.base.rxlength = 0;
328 ta.base.rx_buffer =
nullptr;
329 ta.base.tx_buffer = buffer;
330 spi_device_polling_transmit(m_SPIDevHandle, (spi_transaction_t*) &ta);
332 spi_device_release_bus(m_SPIDevHandle);
335 writeReg(MCP_BNK1_IOCON, m_IOCON[hwAddr]);
339void MCP23S17::readPort(
int port,
void * buffer,
size_t length, uint8_t hwAddr)
343 writeReg(MCP_IOCON, m_IOCON[hwAddr] | MCP_IOCON_SEQOP | MCP_IOCON_BANK);
345 spi_device_acquire_bus(m_SPIDevHandle, portMAX_DELAY);
347 spi_transaction_ext_t ta = { };
350 ta.base.cmd = 0b01000001 | (hwAddr << 1);
351 ta.base.addr = MCP_BNK1_GPIO + port * 0x10;
352 ta.base.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR;
353 ta.base.length = 16 + 8 * length;
354 ta.base.rxlength = 8 * length;
355 ta.base.rx_buffer = buffer;
356 ta.base.tx_buffer =
nullptr;
357 spi_device_polling_transmit(m_SPIDevHandle, (spi_transaction_t*) &ta);
359 spi_device_release_bus(m_SPIDevHandle);
362 writeReg(MCP_BNK1_IOCON, m_IOCON[hwAddr]);
This file contains the MCP23S17 driver class.
MCPDir
Represents GPIO directioon.
MCPIntTrigger
Represents interrupt trigger mode.