Fix MCP23XXX driver open-drain interrupt

This commit is contained in:
Theo Arends 2024-12-13 17:20:36 +01:00
parent ab5208d216
commit 53e2fab34c
1 changed files with 45 additions and 32 deletions

View File

@ -151,6 +151,20 @@ typedef struct {
int8_t pin_int; int8_t pin_int;
} tMcp23xDevice; } tMcp23xDevice;
typedef union { // Restricted by MISRA-C Rule 18.4 but so useful...
uint8_t reg; // Allow bit manipulation using template IOCON
struct {
uint8_t spare0 : 1; // 0 Unimplemented
uint8_t INTPOL : 1; // (0) INT pin active-low. (1) active-high
uint8_t ODR : 1; // (0) INT pin active driver output. (1) Open-drain output (overrides INTPOL)
uint8_t HAEN : 1; // (1) Hardware Address enabled (MCS23S17 only)
uint8_t DISSLW : 1; // (0) SDA output slew rate disabled
uint8_t SEQOP : 1; // 0 Sequential operation enabled, address pointer increments
uint8_t MIRROR : 1; // (1) INT pins are internally connected
uint8_t BANK : 1; // 0 Registers are in the same bank (addresses are sequential) (MCS23x17 only)
};
} tIOCON;
struct MCP230 { struct MCP230 {
tMcp23xDevice device[MCP23XXX_MAX_DEVICES]; tMcp23xDevice device[MCP23XXX_MAX_DEVICES];
uint32_t relay_inverted; uint32_t relay_inverted;
@ -162,7 +176,7 @@ struct MCP230 {
uint8_t relay_offset; uint8_t relay_offset;
uint8_t button_max; uint8_t button_max;
uint8_t switch_max; uint8_t switch_max;
uint8_t iocon; tIOCON iocon;
int8_t button_offset; int8_t button_offset;
int8_t switch_offset; int8_t switch_offset;
bool base; bool base;
@ -464,6 +478,15 @@ uint32_t MCP23xGetPin(uint32_t lpin) {
/*********************************************************************************************/ /*********************************************************************************************/
bool MCP23xAddItem(uint8_t &item) {
if (item >= MAX_RELAYS_SET) {
AddLog(LOG_LEVEL_INFO, PSTR("MCP: Max reached"));
return false;
}
item++;
return true;
}
String MCP23xTemplateLoadFile(void) { String MCP23xTemplateLoadFile(void) {
String mcptmplt = ""; String mcptmplt = "";
#ifdef USE_UFILESYS #ifdef USE_UFILESYS
@ -514,44 +537,36 @@ bool MCP23xLoadTemplate(void) {
if (!val) { break; } if (!val) { break; }
uint16_t mpin = val.getUInt(); uint16_t mpin = val.getUInt();
if (mpin) { // Above GPIO_NONE if (mpin) { // Above GPIO_NONE
if ((mpin >= AGPIO(GPIO_SWT1)) && (mpin < (AGPIO(GPIO_SWT1) + MAX_SWITCHES_SET))) { if ((mpin >= AGPIO(GPIO_SWT1)) && (mpin < (AGPIO(GPIO_SWT1) + MAX_SWITCHES_SET)) && MCP23xAddItem(Mcp23x.switch_max)) {
Mcp23x.switch_max++;
MCP23xSetPinModes(pin, INPUT_PULLUP); MCP23xSetPinModes(pin, INPUT_PULLUP);
} }
else if ((mpin >= AGPIO(GPIO_SWT1_NP)) && (mpin < (AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES_SET))) { else if ((mpin >= AGPIO(GPIO_SWT1_NP)) && (mpin < (AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES_SET)) && MCP23xAddItem(Mcp23x.switch_max)) {
mpin -= (AGPIO(GPIO_SWT1_NP) - AGPIO(GPIO_SWT1)); mpin -= (AGPIO(GPIO_SWT1_NP) - AGPIO(GPIO_SWT1));
Mcp23x.switch_max++;
MCP23xSetPinModes(pin, INPUT); MCP23xSetPinModes(pin, INPUT);
} }
else if ((mpin >= AGPIO(GPIO_KEY1)) && (mpin < (AGPIO(GPIO_KEY1) + MAX_KEYS_SET))) { else if ((mpin >= AGPIO(GPIO_KEY1)) && (mpin < (AGPIO(GPIO_KEY1) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) {
Mcp23x.button_max++;
MCP23xSetPinModes(pin, INPUT_PULLUP); MCP23xSetPinModes(pin, INPUT_PULLUP);
} }
else if ((mpin >= AGPIO(GPIO_KEY1_NP)) && (mpin < (AGPIO(GPIO_KEY1_NP) + MAX_KEYS_SET))) { else if ((mpin >= AGPIO(GPIO_KEY1_NP)) && (mpin < (AGPIO(GPIO_KEY1_NP) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) {
mpin -= (AGPIO(GPIO_KEY1_NP) - AGPIO(GPIO_KEY1)); mpin -= (AGPIO(GPIO_KEY1_NP) - AGPIO(GPIO_KEY1));
Mcp23x.button_max++;
MCP23xSetPinModes(pin, INPUT); MCP23xSetPinModes(pin, INPUT);
} }
else if ((mpin >= AGPIO(GPIO_KEY1_INV)) && (mpin < (AGPIO(GPIO_KEY1_INV) + MAX_KEYS_SET))) { else if ((mpin >= AGPIO(GPIO_KEY1_INV)) && (mpin < (AGPIO(GPIO_KEY1_INV) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) {
bitSet(Mcp23x.button_inverted, mpin - AGPIO(GPIO_KEY1_INV)); bitSet(Mcp23x.button_inverted, mpin - AGPIO(GPIO_KEY1_INV));
mpin -= (AGPIO(GPIO_KEY1_INV) - AGPIO(GPIO_KEY1)); mpin -= (AGPIO(GPIO_KEY1_INV) - AGPIO(GPIO_KEY1));
Mcp23x.button_max++;
MCP23xSetPinModes(pin, INPUT_PULLUP); MCP23xSetPinModes(pin, INPUT_PULLUP);
} }
else if ((mpin >= AGPIO(GPIO_KEY1_INV_NP)) && (mpin < (AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS_SET))) { else if ((mpin >= AGPIO(GPIO_KEY1_INV_NP)) && (mpin < (AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) {
bitSet(Mcp23x.button_inverted, mpin - AGPIO(GPIO_KEY1_INV_NP)); bitSet(Mcp23x.button_inverted, mpin - AGPIO(GPIO_KEY1_INV_NP));
mpin -= (AGPIO(GPIO_KEY1_INV_NP) - AGPIO(GPIO_KEY1)); mpin -= (AGPIO(GPIO_KEY1_INV_NP) - AGPIO(GPIO_KEY1));
Mcp23x.button_max++;
MCP23xSetPinModes(pin, INPUT); MCP23xSetPinModes(pin, INPUT);
} }
else if ((mpin >= AGPIO(GPIO_REL1)) && (mpin < (AGPIO(GPIO_REL1) + MAX_RELAYS_SET))) { else if ((mpin >= AGPIO(GPIO_REL1)) && (mpin < (AGPIO(GPIO_REL1) + MAX_RELAYS_SET)) && MCP23xAddItem(Mcp23x.relay_max)) {
Mcp23x.relay_max++;
MCP23xPinMode(pin, OUTPUT); MCP23xPinMode(pin, OUTPUT);
} }
else if ((mpin >= AGPIO(GPIO_REL1_INV)) && (mpin < (AGPIO(GPIO_REL1_INV) + MAX_RELAYS_SET))) { else if ((mpin >= AGPIO(GPIO_REL1_INV)) && (mpin < (AGPIO(GPIO_REL1_INV) + MAX_RELAYS_SET)) && MCP23xAddItem(Mcp23x.relay_max)) {
bitSet(Mcp23x.relay_inverted, mpin - AGPIO(GPIO_REL1_INV)); bitSet(Mcp23x.relay_inverted, mpin - AGPIO(GPIO_REL1_INV));
mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1)); mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1));
Mcp23x.relay_max++;
MCP23xPinMode(pin, OUTPUT); MCP23xPinMode(pin, OUTPUT);
} }
else if (mpin == AGPIO(GPIO_OUTPUT_HI)) { else if (mpin == AGPIO(GPIO_OUTPUT_HI)) {
@ -565,14 +580,9 @@ bool MCP23xLoadTemplate(void) {
else { mpin = 0; } else { mpin = 0; }
Mcp23x_gpio_pin[pin] = mpin; Mcp23x_gpio_pin[pin] = mpin;
} }
if ((Mcp23x.switch_max >= MAX_SWITCHES_SET) ||
(Mcp23x.button_max >= MAX_KEYS_SET) ||
(Mcp23x.relay_max >= MAX_RELAYS_SET)) {
AddLog(LOG_LEVEL_INFO, PSTR("MCP: Max reached (S%d/B%d/R%d)"), Mcp23x.switch_max, Mcp23x.button_max, Mcp23x.relay_max);
break;
}
} }
Mcp23x.max_pins = pin; // Max number of configured pins Mcp23x.max_pins = pin; // Max number of configured pins
AddLog(LOG_LEVEL_INFO, PSTR("MCP: Pins used %d (S%d/B%d/R%d)"), Mcp23x.max_pins, Mcp23x.switch_max, Mcp23x.button_max, Mcp23x.relay_max);
} }
// AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Pins %d, Mcp23x_gpio_pin %*_V"), Mcp23x.max_pins, Mcp23x.max_pins, (uint8_t*)Mcp23x_gpio_pin); // AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Pins %d, Mcp23x_gpio_pin %*_V"), Mcp23x.max_pins, Mcp23x.max_pins, (uint8_t*)Mcp23x_gpio_pin);
@ -591,7 +601,7 @@ uint32_t MCP23xTemplateGpio(void) {
JsonParserToken val = root[PSTR(D_JSON_IOCON)]; JsonParserToken val = root[PSTR(D_JSON_IOCON)];
if (val) { if (val) {
Mcp23x.iocon = val.getUInt() & 0x5E; // Only allow 0 MIRROR 0 DISSLW HAEN ODR INTPOL 0 Mcp23x.iocon.reg = val.getUInt() & 0x5E; // Only allow 0 MIRROR 0 DISSLW HAEN ODR INTPOL 0
} }
JsonParserArray arr = root[PSTR(D_JSON_GPIO)]; JsonParserArray arr = root[PSTR(D_JSON_GPIO)];
if (arr.isArray()) { if (arr.isArray()) {
@ -601,7 +611,7 @@ uint32_t MCP23xTemplateGpio(void) {
} }
void MCP23xModuleInit(void) { void MCP23xModuleInit(void) {
Mcp23x.iocon = 0b01011000; // Default 0x58 = Enable INT mirror, Disable Slew rate, HAEN pins for addressing Mcp23x.iocon.reg = 0b01011000; // Default 0x58 = Enable INT mirror, Disable Slew rate, HAEN pins for addressing
int32_t pins_needed = MCP23xTemplateGpio(); int32_t pins_needed = MCP23xTemplateGpio();
if (!pins_needed) { if (!pins_needed) {
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MCP: Invalid template")); AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MCP: Invalid template"));
@ -618,7 +628,8 @@ void MCP23xModuleInit(void) {
#endif #endif
while ((Mcp23x.max_devices < MCP23XXX_MAX_DEVICES) && PinUsed(GPIO_MCP23SXX_CS, Mcp23x.max_devices)) { while ((Mcp23x.max_devices < MCP23XXX_MAX_DEVICES) && PinUsed(GPIO_MCP23SXX_CS, Mcp23x.max_devices)) {
Mcp23x.chip = Mcp23x.max_devices; Mcp23x.chip = Mcp23x.max_devices;
Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, Mcp23x.chip)) ? Pin(GPIO_MCP23XXX_INT, Mcp23x.chip) : -1; uint32_t pin_int = (Mcp23x.iocon.ODR) ? 0 : Mcp23x.chip; // INT pins are open-drain outputs and supposedly connected together to one GPIO
Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, pin_int)) ? Pin(GPIO_MCP23XXX_INT, pin_int) : -1;
Mcp23x.device[Mcp23x.chip].pin_cs = Pin(GPIO_MCP23SXX_CS, Mcp23x.max_devices); Mcp23x.device[Mcp23x.chip].pin_cs = Pin(GPIO_MCP23SXX_CS, Mcp23x.max_devices);
digitalWrite(Mcp23x.device[Mcp23x.chip].pin_cs, 1); digitalWrite(Mcp23x.device[Mcp23x.chip].pin_cs, 1);
pinMode(Mcp23x.device[Mcp23x.chip].pin_cs, OUTPUT); pinMode(Mcp23x.device[Mcp23x.chip].pin_cs, OUTPUT);
@ -632,13 +643,13 @@ void MCP23xModuleInit(void) {
AddLog(LOG_LEVEL_INFO, PSTR("SPI: MCP23S08 found at CS%d"), Mcp23x.chip +1); AddLog(LOG_LEVEL_INFO, PSTR("SPI: MCP23S08 found at CS%d"), Mcp23x.chip +1);
Mcp23x.device[Mcp23x.chip].pins = 8; Mcp23x.device[Mcp23x.chip].pins = 8;
// MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Slew rate disabled, HAEN pins for addressing // MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Slew rate disabled, HAEN pins for addressing
MCP23xWrite(MCP23X08_IOCON, Mcp23x.iocon & 0x3E); MCP23xWrite(MCP23X08_IOCON, Mcp23x.iocon.reg & 0x3E);
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X08_OLAT); Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X08_OLAT);
} else if (0x80 == buffer) { // MCP23S17 } else if (0x80 == buffer) { // MCP23S17
AddLog(LOG_LEVEL_INFO, PSTR("SPI: MCP23S17 found at CS%d"), Mcp23x.chip +1); AddLog(LOG_LEVEL_INFO, PSTR("SPI: MCP23S17 found at CS%d"), Mcp23x.chip +1);
Mcp23x.device[Mcp23x.chip].pins = 16; Mcp23x.device[Mcp23x.chip].pins = 16;
// MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing // MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing
MCP23xWrite(MCP23X17_IOCONA, Mcp23x.iocon); MCP23xWrite(MCP23X17_IOCONA, Mcp23x.iocon.reg);
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X17_OLATA); Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X17_OLATA);
Mcp23x.device[Mcp23x.chip].olatb = MCP23xRead(MCP23X17_OLATB); Mcp23x.device[Mcp23x.chip].olatb = MCP23xRead(MCP23X17_OLATB);
} }
@ -655,8 +666,9 @@ void MCP23xModuleInit(void) {
uint8_t mcp23xxx_address = MCP23XXX_ADDR_START; uint8_t mcp23xxx_address = MCP23XXX_ADDR_START;
while ((Mcp23x.max_devices < MCP23XXX_MAX_DEVICES) && (mcp23xxx_address < MCP23XXX_ADDR_END)) { while ((Mcp23x.max_devices < MCP23XXX_MAX_DEVICES) && (mcp23xxx_address < MCP23XXX_ADDR_END)) {
Mcp23x.chip = Mcp23x.max_devices; Mcp23x.chip = Mcp23x.max_devices;
uint32_t pin_int = (Mcp23x.iocon.ODR) ? 0 : Mcp23x.chip; // INT pins are open-drain outputs and supposedly connected together to one GPIO
if (I2cSetDevice(mcp23xxx_address)) { if (I2cSetDevice(mcp23xxx_address)) {
Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, Mcp23x.chip)) ? Pin(GPIO_MCP23XXX_INT, Mcp23x.chip) : -1; Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, pin_int)) ? Pin(GPIO_MCP23XXX_INT, pin_int) : -1;
Mcp23x.device[Mcp23x.chip].interface = MCP23X_I2C; Mcp23x.device[Mcp23x.chip].interface = MCP23X_I2C;
Mcp23x.device[Mcp23x.chip].address = mcp23xxx_address; Mcp23x.device[Mcp23x.chip].address = mcp23xxx_address;
@ -667,7 +679,7 @@ void MCP23xModuleInit(void) {
I2cSetActiveFound(mcp23xxx_address, "MCP23008"); I2cSetActiveFound(mcp23xxx_address, "MCP23008");
Mcp23x.device[Mcp23x.chip].pins = 8; Mcp23x.device[Mcp23x.chip].pins = 8;
// MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Slew rate disabled, HAEN pins for addressing // MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Slew rate disabled, HAEN pins for addressing
MCP23xWrite(MCP23X08_IOCON, Mcp23x.iocon & 0x3E); MCP23xWrite(MCP23X08_IOCON, Mcp23x.iocon.reg & 0x3E);
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X08_OLAT); Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X08_OLAT);
Mcp23x.max_devices++; Mcp23x.max_devices++;
} }
@ -676,7 +688,7 @@ void MCP23xModuleInit(void) {
Mcp23x.device[Mcp23x.chip].pins = 16; Mcp23x.device[Mcp23x.chip].pins = 16;
MCP23xWrite(MCP23X08_IOCON, 0x00); // Reset bank mode to 0 (MCP23X17_GPINTENB) MCP23xWrite(MCP23X08_IOCON, 0x00); // Reset bank mode to 0 (MCP23X17_GPINTENB)
// MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing // MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing
MCP23xWrite(MCP23X17_IOCONA, Mcp23x.iocon); MCP23xWrite(MCP23X17_IOCONA, Mcp23x.iocon.reg);
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X17_OLATA); Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X17_OLATA);
Mcp23x.device[Mcp23x.chip].olatb = MCP23xRead(MCP23X17_OLATB); Mcp23x.device[Mcp23x.chip].olatb = MCP23xRead(MCP23X17_OLATB);
Mcp23x.max_devices++; Mcp23x.max_devices++;
@ -761,6 +773,7 @@ void MCP23xInit(void) {
} else { } else {
gpio = MCP23xRead16(MCP23X17_GPIOA); // Clear MCP23x17 interrupt gpio = MCP23xRead16(MCP23X17_GPIOA); // Clear MCP23x17 interrupt
} }
if (Mcp23x.iocon.ODR && Mcp23x.chip) { continue; }
pinMode(Mcp23x.device[Mcp23x.chip].pin_int, INPUT_PULLUP); pinMode(Mcp23x.device[Mcp23x.chip].pin_int, INPUT_PULLUP);
attachInterrupt(Mcp23x.device[Mcp23x.chip].pin_int, MCP23xInputIsr, CHANGE); attachInterrupt(Mcp23x.device[Mcp23x.chip].pin_int, MCP23xInputIsr, CHANGE);
} }