2023-02-28 16:54:43 +00:00
|
|
|
/*
|
2023-03-01 09:12:47 +00:00
|
|
|
xdrv_67_mcp23xxx.ino - MCP23008/MCP23017/MCP23S17 GPIO Expander support for Tasmota
|
2023-02-28 16:54:43 +00:00
|
|
|
|
|
|
|
SPDX-FileCopyrightText: 2023 Theo Arends
|
|
|
|
|
|
|
|
SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
2023-03-01 15:52:34 +00:00
|
|
|
#if defined(USE_I2C) || defined(USE_SPI)
|
2023-02-28 16:54:43 +00:00
|
|
|
#ifdef USE_MCP23XXX_DRV
|
|
|
|
/*********************************************************************************************\
|
2023-03-02 10:24:54 +00:00
|
|
|
* MCP23008/17 (I2C) and MCP23S17 (SPI) GPIO Expander to be used as virtual button/switch/relay only
|
2023-02-28 16:54:43 +00:00
|
|
|
*
|
|
|
|
* Docs at https://www.microchip.com/wwwproducts/en/MCP23008
|
|
|
|
* https://www.microchip.com/wwwproducts/en/MCP23017
|
|
|
|
*
|
|
|
|
* I2C Address: 0x20 - 0x26 (0x27 is not supported)
|
|
|
|
*
|
2023-03-01 15:52:34 +00:00
|
|
|
* The goal of the driver is to provide a sequential list of pins configured as Tasmota template
|
2023-02-28 16:54:43 +00:00
|
|
|
* and handle any input and output as configured GPIOs.
|
|
|
|
*
|
|
|
|
* Restrictions:
|
2023-03-02 10:24:54 +00:00
|
|
|
* - Uses incremental I2C addresses / SPI Chip select until template pin count reached
|
2023-02-28 16:54:43 +00:00
|
|
|
* - Max support for 28 switches (input), 32 buttons (input), 32 relays (output)
|
|
|
|
*
|
|
|
|
* Supported template fields:
|
|
|
|
* NAME - Template name
|
2023-03-24 17:00:24 +00:00
|
|
|
* BASE - Optional. 0 = use relative buttons and switches (default), 1 = use absolute buttons and switches
|
2023-02-28 16:54:43 +00:00
|
|
|
* GPIO - Sequential list of pin 1 and up with configured GPIO function
|
2023-03-02 10:24:54 +00:00
|
|
|
* Function Code Description
|
|
|
|
* ------------------- -------- ----------------------------------------
|
|
|
|
* None 0 Not used
|
|
|
|
* Button1..32 B 32..63 Button to Gnd with internal pullup
|
|
|
|
* Button_n1..32 Bn 64..95 Button to Gnd without internal pullup
|
|
|
|
* Button_i1..32 Bi 96..127 Button inverted to Vcc with internal pullup
|
|
|
|
* Button_in1..32 Bin 128..159 Button inverted to Vcc without internal pullup
|
|
|
|
* Switch1..28 S 160..187 Switch to Gnd with internal pullup
|
|
|
|
* Switch_n1..28 Sn 192..219 Switch to Gnd without internal pullup
|
2023-03-24 17:00:24 +00:00
|
|
|
* Relay1..32 R 224..255 Relay
|
|
|
|
* Relay_i1..32 Ri 256..287 Relay inverted
|
2023-03-03 09:44:35 +00:00
|
|
|
* Output_Hi Oh 3840 Fixed output high
|
|
|
|
* Output_lo Ol 3872 Fixed output low
|
2023-02-28 16:54:43 +00:00
|
|
|
*
|
|
|
|
* Prepare a template to be loaded either by:
|
2023-03-02 10:24:54 +00:00
|
|
|
* - a rule like: rule3 on file#mcp23x.dat do {"NAME":"MCP23017 A=Ri8-1, B=B1-8","GPIO":[263,262,261,260,259,258,257,256,32,33,34,35,36,37,38,39]} endon
|
|
|
|
* - a script like: -y{"NAME":"MCP23017 A=Ri8-1, B=B1-8","GPIO":[263,262,261,260,259,258,257,256,32,33,34,35,36,37,38,39]}
|
|
|
|
* - file called mcp23x.dat with contents: {"NAME":"MCP23017 A=Ri8-1, B=B1-8","GPIO":[263,262,261,260,259,258,257,256,32,33,34,35,36,37,38,39]}
|
2023-02-28 16:54:43 +00:00
|
|
|
*
|
2023-03-03 09:44:35 +00:00
|
|
|
* S3 S2 B2 B3 Oh B1 S1 R1 R4 R2 R3 S4
|
|
|
|
* {"NAME":"MCP23S17 Shelly Pro 4PM","GPIO":[194,193,65,66,3840,64,192,0,224,0,0,0,227,225,226,195]}
|
2023-03-01 15:52:34 +00:00
|
|
|
*
|
2023-03-02 10:24:54 +00:00
|
|
|
* Inverted relays and buttons Ri8 Ri7 Ri6 Ri5 Ri4 Ri3 Ri2 Ri1 B1 B2 B3 B4 B5 B6 B7 B8
|
|
|
|
* {"NAME":"MCP23017 A=Ri8-1, B=B1-8","GPIO":[263,262,261,260,259,258,257,256,32,33,34,35,36,37,38,39]}
|
2023-02-28 16:54:43 +00:00
|
|
|
*
|
2023-03-02 10:24:54 +00:00
|
|
|
* Inverted relays and buttons Ri1 Ri2 Ri3 Ri4 Ri5 Ri6 Ri7 Ri8 B1 B2 B3 B4 B5 B6 B7 B8
|
|
|
|
* {"NAME":"MCP23017 A=Ri1-8, B=B1-8","GPIO":[256,257,258,259,260,261,262,263,32,33,34,35,36,37,38,39]}
|
2023-02-28 16:54:43 +00:00
|
|
|
*
|
2023-03-02 10:24:54 +00:00
|
|
|
* Relays and buttons R1 R2 R3 R4 R5 R6 R7 R8 B1 B2 B3 B4 B5 B6 B7 B8
|
|
|
|
* {"NAME":"MCP23017 A=R1-8, B=B1-8","GPIO":[224,225,226,227,228,229,230,231,32,33,34,35,36,37,38,39]}
|
2023-02-28 16:54:43 +00:00
|
|
|
*
|
2023-03-02 10:24:54 +00:00
|
|
|
* Buttons and relays B1 B2 B3 B4 B5 B6 B7 B8 R1 R2 R3 R4 R5 R6 R7 R8
|
|
|
|
* {"NAME":"MCP23017 A=B1-8, B=R1-8","GPIO":[32,33,34,35,36,37,38,39,224,225,226,227,228,229,230,231]}
|
2023-02-28 16:54:43 +00:00
|
|
|
*
|
2023-03-02 10:24:54 +00:00
|
|
|
* Buttons, relays, buttons and relays B1 B2 B3 B4 B5 B6 B7 B8 R1 R2 R3 R4 R5 R6 R7 R8 B9 B10B11B12B13B14B15B16R9 R10 R11 R12 R13 R14 R15 R16
|
|
|
|
* {"NAME":"MCP23017 A=B1-8, B=R1-8, C=B9-16, D=R9-16","GPIO":[32,33,34,35,36,37,38,39,224,225,226,227,228,229,230,231,40,41,42,43,44,45,46,47,232,233,234,235,236,237,238,239]}
|
2023-02-28 16:54:43 +00:00
|
|
|
*
|
2023-03-02 10:24:54 +00:00
|
|
|
* {"NAME":"MCP23017 A=R1-8, B=B1-8, C=R9-16, D=B9-16","GPIO":[224,225,226,227,228,229,230,231,32,33,34,35,36,37,38,39,232,233,234,235,236,237,238,239,40,41,42,43,44,45,46,47]}
|
2023-02-28 16:54:43 +00:00
|
|
|
*
|
2023-03-02 10:24:54 +00:00
|
|
|
* 32 relays R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 R30 R31 R32
|
|
|
|
* {"NAME":"MCP23017 A=Ri1-8, B=Ri9-16, C=Ri17-24, D=Ri25-32","GPIO":[256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287]}
|
|
|
|
* {"NAME":"MCP23017 A=R1-8, B=R9-16, C=R17-24, D=R25-32","GPIO":[224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255]}
|
2023-02-28 16:54:43 +00:00
|
|
|
*
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
|
|
#define XDRV_67 67
|
2023-03-03 09:44:35 +00:00
|
|
|
#define XI2C_77 77 // See I2CDEVICES.md
|
2023-02-28 16:54:43 +00:00
|
|
|
|
2023-03-03 09:44:35 +00:00
|
|
|
#define MCP23XXX_ADDR_START 0x20 // 32
|
|
|
|
#define MCP23XXX_ADDR_END 0x26 // 38
|
2023-02-28 16:54:43 +00:00
|
|
|
|
|
|
|
#define MCP23XXX_MAX_DEVICES 6
|
|
|
|
|
2023-03-03 09:44:35 +00:00
|
|
|
#define MCP23XXX_SPI_CLOCK 1000000 // SPI clock speed set to 1MHz in case of signal interference at higher speed (Max is 10MHz)
|
|
|
|
|
2023-02-28 16:54:43 +00:00
|
|
|
/*********************************************************************************************\
|
|
|
|
* MCP23017 support
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
|
|
enum MCP23S08GPIORegisters {
|
|
|
|
MCP23X08_IODIR = 0x00,
|
|
|
|
MCP23X08_IPOL = 0x01,
|
|
|
|
MCP23X08_GPINTEN = 0x02,
|
|
|
|
MCP23X08_DEFVAL = 0x03,
|
|
|
|
MCP23X08_INTCON = 0x04,
|
|
|
|
MCP23X08_IOCON = 0x05,
|
|
|
|
MCP23X08_GPPU = 0x06,
|
|
|
|
MCP23X08_INTF = 0x07,
|
|
|
|
MCP23X08_INTCAP = 0x08,
|
|
|
|
MCP23X08_GPIO = 0x09,
|
|
|
|
MCP23X08_OLAT = 0x0A,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum MCP23X17GPIORegisters {
|
|
|
|
// A side
|
|
|
|
MCP23X17_IODIRA = 0x00,
|
|
|
|
MCP23X17_IPOLA = 0x02,
|
|
|
|
MCP23X17_GPINTENA = 0x04,
|
|
|
|
MCP23X17_DEFVALA = 0x06,
|
|
|
|
MCP23X17_INTCONA = 0x08,
|
|
|
|
MCP23X17_IOCONA = 0x0A,
|
|
|
|
MCP23X17_GPPUA = 0x0C,
|
|
|
|
MCP23X17_INTFA = 0x0E,
|
|
|
|
MCP23X17_INTCAPA = 0x10,
|
|
|
|
MCP23X17_GPIOA = 0x12,
|
|
|
|
MCP23X17_OLATA = 0x14,
|
|
|
|
// B side
|
|
|
|
MCP23X17_IODIRB = 0x01,
|
|
|
|
MCP23X17_IPOLB = 0x03,
|
|
|
|
MCP23X17_GPINTENB = 0x05,
|
|
|
|
MCP23X17_DEFVALB = 0x07,
|
|
|
|
MCP23X17_INTCONB = 0x09,
|
|
|
|
MCP23X17_IOCONB = 0x0B,
|
|
|
|
MCP23X17_GPPUB = 0x0D,
|
|
|
|
MCP23X17_INTFB = 0x0F,
|
|
|
|
MCP23X17_INTCAPB = 0x11,
|
|
|
|
MCP23X17_GPIOB = 0x13,
|
|
|
|
MCP23X17_OLATB = 0x15,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum MCP23XInterruptMode { MCP23XXX_NO_INTERRUPT, MCP23XXX_CHANGE, MCP23XXX_RISING, MCP23XXX_FALLING };
|
|
|
|
|
|
|
|
enum MCP23XInterfaces { MCP23X_I2C, MCP23X_SPI };
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
uint8_t olata;
|
|
|
|
uint8_t olatb;
|
|
|
|
uint8_t address;
|
|
|
|
uint8_t interface;
|
2024-03-18 09:18:14 +00:00
|
|
|
uint8_t pins; // 8 (MCP23x08) or 16 (MCP23x17)
|
2023-02-28 16:54:43 +00:00
|
|
|
int8_t pin_cs;
|
2023-03-02 10:24:54 +00:00
|
|
|
int8_t pin_int;
|
2023-02-28 16:54:43 +00:00
|
|
|
} tMcp23xDevice;
|
|
|
|
|
|
|
|
struct MCP230 {
|
|
|
|
tMcp23xDevice device[MCP23XXX_MAX_DEVICES];
|
|
|
|
uint32_t relay_inverted;
|
|
|
|
uint32_t button_inverted;
|
|
|
|
uint8_t chip;
|
|
|
|
uint8_t max_devices;
|
|
|
|
uint8_t max_pins;
|
|
|
|
uint8_t relay_max;
|
|
|
|
uint8_t relay_offset;
|
|
|
|
uint8_t button_max;
|
|
|
|
uint8_t switch_max;
|
|
|
|
int8_t button_offset;
|
|
|
|
int8_t switch_offset;
|
2023-03-26 10:39:30 +01:00
|
|
|
bool base;
|
2023-03-02 10:24:54 +00:00
|
|
|
bool interrupt;
|
2023-02-28 16:54:43 +00:00
|
|
|
} Mcp23x;
|
|
|
|
|
|
|
|
uint16_t *Mcp23x_gpio_pin = nullptr;
|
|
|
|
|
|
|
|
/*********************************************************************************************\
|
|
|
|
* MCP23x17 - SPI and I2C
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
|
|
#ifdef USE_SPI
|
|
|
|
void MCP23xEnable(void) {
|
2023-03-03 09:44:35 +00:00
|
|
|
SPI.beginTransaction(SPISettings(MCP23XXX_SPI_CLOCK, MSBFIRST, SPI_MODE0));
|
2023-02-28 16:54:43 +00:00
|
|
|
digitalWrite(Mcp23x.device[Mcp23x.chip].pin_cs, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MCP23xDisable(void) {
|
|
|
|
SPI.endTransaction();
|
|
|
|
digitalWrite(Mcp23x.device[Mcp23x.chip].pin_cs, 1);
|
|
|
|
}
|
|
|
|
#endif
|
2023-03-01 15:52:34 +00:00
|
|
|
|
|
|
|
void MCP23xDumpRegs(void) {
|
|
|
|
uint8_t data[22];
|
|
|
|
for (Mcp23x.chip = 0; Mcp23x.chip < Mcp23x.max_devices; Mcp23x.chip++) {
|
2023-03-03 09:44:35 +00:00
|
|
|
uint32_t data_size = sizeof(data);
|
|
|
|
if (8 == Mcp23x.device[Mcp23x.chip].pins) { data_size /= 2; }
|
2023-03-01 15:52:34 +00:00
|
|
|
#ifdef USE_SPI
|
|
|
|
if (MCP23X_SPI == Mcp23x.device[Mcp23x.chip].interface) {
|
|
|
|
MCP23xEnable();
|
|
|
|
SPI.transfer(Mcp23x.device[Mcp23x.chip].address | 1);
|
|
|
|
SPI.transfer(0);
|
2023-03-03 09:44:35 +00:00
|
|
|
for (uint32_t i = 0; i < data_size; i++) {
|
2023-03-01 15:52:34 +00:00
|
|
|
data[i] = SPI.transfer(0xFF);
|
|
|
|
}
|
|
|
|
MCP23xDisable();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef USE_I2C
|
|
|
|
if (MCP23X_I2C == Mcp23x.device[Mcp23x.chip].interface) {
|
2023-03-03 09:44:35 +00:00
|
|
|
I2cReadBuffer(Mcp23x.device[Mcp23x.chip].address, 0, data, data_size);
|
2023-03-01 15:52:34 +00:00
|
|
|
}
|
|
|
|
#endif
|
2023-03-03 09:44:35 +00:00
|
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Intf %d, Address %02X, Regs %*_H"), Mcp23x.device[Mcp23x.chip].interface, Mcp23x.device[Mcp23x.chip].address, data_size, data);
|
2023-03-01 15:52:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-28 16:54:43 +00:00
|
|
|
uint32_t MCP23xRead16(uint8_t reg) {
|
|
|
|
// Read 16-bit registers: (regb << 8) | rega
|
|
|
|
uint32_t value = 0;
|
|
|
|
#ifdef USE_SPI
|
|
|
|
if (MCP23X_SPI == Mcp23x.device[Mcp23x.chip].interface) {
|
|
|
|
MCP23xEnable();
|
|
|
|
SPI.transfer(Mcp23x.device[Mcp23x.chip].address | 1);
|
|
|
|
SPI.transfer(reg);
|
|
|
|
value = SPI.transfer(0xFF); // RegA
|
|
|
|
value |= (SPI.transfer(0xFF) << 8); // RegB
|
|
|
|
MCP23xDisable();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef USE_I2C
|
|
|
|
if (MCP23X_I2C == Mcp23x.device[Mcp23x.chip].interface) {
|
|
|
|
value = I2cRead16LE(Mcp23x.device[Mcp23x.chip].address, reg);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t MCP23xRead(uint8_t reg) {
|
|
|
|
uint32_t value = 0;
|
|
|
|
#ifdef USE_SPI
|
|
|
|
if (MCP23X_SPI == Mcp23x.device[Mcp23x.chip].interface) {
|
|
|
|
MCP23xEnable();
|
|
|
|
SPI.transfer(Mcp23x.device[Mcp23x.chip].address | 1);
|
|
|
|
SPI.transfer(reg);
|
|
|
|
value = SPI.transfer(0xFF);
|
|
|
|
MCP23xDisable();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef USE_I2C
|
|
|
|
if (MCP23X_I2C == Mcp23x.device[Mcp23x.chip].interface) {
|
|
|
|
value = I2cRead8(Mcp23x.device[Mcp23x.chip].address, reg);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MCP23xValidRead(uint8_t reg, uint8_t *data) {
|
|
|
|
#ifdef USE_SPI
|
|
|
|
if (MCP23X_SPI == Mcp23x.device[Mcp23x.chip].interface) {
|
|
|
|
MCP23xEnable();
|
|
|
|
SPI.transfer(Mcp23x.device[Mcp23x.chip].address | 1);
|
|
|
|
SPI.transfer(reg);
|
|
|
|
*data = SPI.transfer(0xFF);
|
|
|
|
MCP23xDisable();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif
|
2023-03-01 15:52:34 +00:00
|
|
|
#ifdef USE_I2C
|
2023-02-28 16:54:43 +00:00
|
|
|
if (MCP23X_I2C == Mcp23x.device[Mcp23x.chip].interface) {
|
|
|
|
return I2cValidRead8(data, Mcp23x.device[Mcp23x.chip].address, reg);
|
|
|
|
}
|
|
|
|
return false;
|
2023-03-01 15:52:34 +00:00
|
|
|
#endif
|
2023-02-28 16:54:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MCP23xWrite(uint8_t reg, uint8_t value) {
|
|
|
|
#ifdef USE_SPI
|
|
|
|
if (MCP23X_SPI == Mcp23x.device[Mcp23x.chip].interface) {
|
|
|
|
MCP23xEnable();
|
|
|
|
SPI.transfer(Mcp23x.device[Mcp23x.chip].address);
|
|
|
|
SPI.transfer(reg);
|
|
|
|
SPI.transfer(value);
|
|
|
|
MCP23xDisable();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef USE_I2C
|
|
|
|
if (MCP23X_I2C == Mcp23x.device[Mcp23x.chip].interface) {
|
|
|
|
I2cWrite8(Mcp23x.device[Mcp23x.chip].address, reg, value);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************/
|
|
|
|
|
|
|
|
void MCP23xUpdate(uint8_t pin, bool pin_value, uint8_t reg_addr) {
|
|
|
|
// pin = 0 - 15
|
|
|
|
uint8_t bit = pin % 8;
|
|
|
|
uint8_t reg_value = 0;
|
|
|
|
if (reg_addr == MCP23X17_OLATA) {
|
|
|
|
reg_value = Mcp23x.device[Mcp23x.chip].olata;
|
2023-03-03 09:44:35 +00:00
|
|
|
if (8 == Mcp23x.device[Mcp23x.chip].pins) {
|
|
|
|
reg_addr = MCP23X08_OLAT;
|
|
|
|
}
|
2023-02-28 16:54:43 +00:00
|
|
|
} else if (reg_addr == MCP23X17_OLATB) {
|
|
|
|
reg_value = Mcp23x.device[Mcp23x.chip].olatb;
|
|
|
|
} else {
|
|
|
|
reg_value = MCP23xRead(reg_addr);
|
|
|
|
}
|
|
|
|
if (pin_value) {
|
|
|
|
reg_value |= 1 << bit;
|
|
|
|
} else {
|
|
|
|
reg_value &= ~(1 << bit);
|
|
|
|
}
|
|
|
|
MCP23xWrite(reg_addr, reg_value);
|
2023-03-03 09:44:35 +00:00
|
|
|
if ((8 == Mcp23x.device[Mcp23x.chip].pins) && (reg_addr == MCP23X08_OLAT)) {
|
|
|
|
reg_addr = MCP23X17_OLATA;
|
|
|
|
}
|
2023-02-28 16:54:43 +00:00
|
|
|
if (reg_addr == MCP23X17_OLATA) {
|
|
|
|
Mcp23x.device[Mcp23x.chip].olata = reg_value;
|
|
|
|
} else if (reg_addr == MCP23X17_OLATB) {
|
|
|
|
Mcp23x.device[Mcp23x.chip].olatb = reg_value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************/
|
|
|
|
|
2023-03-03 09:44:35 +00:00
|
|
|
uint32_t MCP23xSetChip(uint8_t pin) {
|
2024-03-18 09:18:14 +00:00
|
|
|
// Calculate chip based on number of pins per chip. 8 for MCP23x08, 16 for MCP23x17
|
2023-03-03 09:44:35 +00:00
|
|
|
// pin 0 - 63
|
|
|
|
for (Mcp23x.chip = 0; Mcp23x.chip < Mcp23x.max_devices; Mcp23x.chip++) {
|
|
|
|
if (Mcp23x.device[Mcp23x.chip].pins > pin) { break; }
|
|
|
|
pin -= Mcp23x.device[Mcp23x.chip].pins;
|
|
|
|
}
|
|
|
|
return pin; // relative pin number within chip (0 ... 7 or 0 ... 15)
|
|
|
|
}
|
|
|
|
|
2023-02-28 16:54:43 +00:00
|
|
|
void MCP23xPinMode(uint8_t pin, uint8_t flags) {
|
2023-03-03 09:44:35 +00:00
|
|
|
// pin 0 - 63
|
|
|
|
pin = MCP23xSetChip(pin);
|
|
|
|
uint8_t iodir;
|
|
|
|
uint8_t gppu;
|
|
|
|
if (8 == Mcp23x.device[Mcp23x.chip].pins) {
|
|
|
|
iodir = MCP23X08_IODIR;
|
|
|
|
gppu = MCP23X08_GPPU;
|
|
|
|
} else {
|
|
|
|
iodir = pin < 8 ? MCP23X17_IODIRA : MCP23X17_IODIRB;
|
|
|
|
gppu = pin < 8 ? MCP23X17_GPPUA : MCP23X17_GPPUB;
|
|
|
|
}
|
2023-02-28 16:54:43 +00:00
|
|
|
switch (flags) {
|