From 3d5e690977467be190f61c1eefeebb79c11a9545 Mon Sep 17 00:00:00 2001 From: Michael Spahlinger Date: Mon, 27 Jun 2016 08:11:54 -0600 Subject: [PATCH] Shadow-Mode: The output- and configuration registers of the IO-Expander are held in the microcontrollers memory and only written to the IO-Expander. This reduces bus traffic and is more error-proof than the normal read- modify-write operation. Retry Mode: If enabled and an error occurs while writing to the IO-Expander the current transmission is automatically repeated once. --- drivers/ioexpander/Kconfig | 20 ++++++++++++ drivers/ioexpander/pca9555.c | 61 ++++++++++++++++++++++++++++++++++-- drivers/ioexpander/pca9555.h | 6 ++-- 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/drivers/ioexpander/Kconfig b/drivers/ioexpander/Kconfig index fd6abd7715..de3b5d09a1 100644 --- a/drivers/ioexpander/Kconfig +++ b/drivers/ioexpander/Kconfig @@ -52,6 +52,26 @@ config IOEXPANDER_MULTIPIN This settings enable the definition of routines for optimized simultaneous access to multiple pins. +config IOEXPANDER_SHADOW_MODE + bool "Use Shadow Mode instead of Read-Modify-Write Operations" + default n + ---help--- + This setting enables a mode where the output and pin + configuration registers are held in RAM. + With this for example we do not need to read back the + output-register every time we want to change one pin. + We do instead change the bit in the internal register + and then just write this register to the IO-Expander. + This reduces bus traffic and eliminates the problem of + EMC-caused toggling of output pins. + +config IOEXPANDER_RETRY + bool "Retry to send commands and data at I2C communication errors" + default n + ---help--- + Retry to send commands and data if a I2C-communication + error occurs (eg. caused by EMC). + endif # IOEXPANDER config DEV_GPIO diff --git a/drivers/ioexpander/pca9555.c b/drivers/ioexpander/pca9555.c index c01f7c37a5..34000d07eb 100644 --- a/drivers/ioexpander/pca9555.c +++ b/drivers/ioexpander/pca9555.c @@ -223,11 +223,20 @@ static int pca9555_setbit(FAR struct pca9555_dev_s *pca, uint8_t addr, buf[0] = addr; +#ifdef CONFIG_IOEXPANDER_SHADOW_MODE + /* Get the shadowed register value */ + + buf[1] = pca->sreg[addr]; + +#else + /* Get the register value from the IO-Expander */ + ret = pca9555_writeread(pca, &buf[0], 1, &buf[1], 1); if (ret < 0) { return ret; } +#endif if (bitval) { @@ -238,7 +247,23 @@ static int pca9555_setbit(FAR struct pca9555_dev_s *pca, uint8_t addr, buf[1] &= ~(1 << pin); } - return pca9555_write(pca, buf, 2); +#ifdef CONFIG_IOEXPANDER_SHADOW_MODE + /* Save the new register value in the shadow register */ + + pca->sreg[addr] = buf[1]; +#endif + + ret = pca9555_write(pca, buf, 2); +#ifdef CONFIG_IOEXPANDER_RETRY + if (ret != OK) + { + /* Try again (only once) */ + + ret = pca9555_write(pca, buf, 2); + } +#endif + + return ret; } /**************************************************************************** @@ -271,6 +296,12 @@ static int pca9555_getbit(FAR struct pca9555_dev_s *pca, uint8_t addr, return ret; } +#ifdef CONFIG_IOEXPANDER_SHADOW_MODE + /* Save the new register value in the shadow register */ + + pca->sreg[addr] = buf; +#endif + *val = (buf >> pin) & 1; return OK; } @@ -417,6 +448,13 @@ static int pca9555_getmultibits(FAR struct pca9555_dev_s *pca, uint8_t addr, return ret; } +#ifdef CONFIG_IOEXPANDER_SHADOW_MODE + /* Save the new register value in the shadow register */ + + pca->sreg[addr] = buf[0]; + pca->sreg[addr+1] = buf[1]; +#endif + /* Read the requested bits */ for (i = 0; i < count; i++) @@ -465,15 +503,22 @@ static int pca9555_multiwritepin(FAR struct ioexpander_dev_s *dev, /* Start by reading both registers, whatever the pins to change. We could * attempt to read one port only if all pins were on the same port, but - * this would not save much. */ + * this would not save much. + */ +#ifndef CONFIG_IOEXPANDER_SHADOW_MODE ret = pca9555_writeread(pca, &addr, 1, &buf[1], 2); if (ret < 0) { - pca9555_unlock(pca); return ret; } +#else + /* In Shadow-Mode we "read" the pin status from the shadow registers */ + + buf[1] = pca->sreg[addr]; + buf[2] = pca->sreg[addr+1]; +#endif /* Apply the user defined changes */ @@ -505,6 +550,11 @@ static int pca9555_multiwritepin(FAR struct ioexpander_dev_s *dev, /* Now write back the new pins states */ buf[0] = addr; +#ifdef CONFIG_IOEXPANDER_SHADOW_MODE + /* Save the new register values in the shadow register */ + pca->sreg[addr] = buf[1]; + pca->sreg[addr+1] = buf[2]; +#endif ret = pca9555_write(pca, buf, 3); pca9555_unlock(pca); @@ -585,6 +635,11 @@ static void pca9555_irqworker(void *arg) ret = pca9555_writeread(pca, &addr, 1, buf, 2); if (ret == OK) { +#ifdef CONFIG_IOEXPANDER_SHADOW_MODE + /* Don't forget to update the shadow registers at this point */ + + pca->sreg[addr] = buf; +#endif bits = ((unsigned int)buf[0] << 8) | buf[1]; /* If signal PID is registered, enqueue signal. */ diff --git a/drivers/ioexpander/pca9555.h b/drivers/ioexpander/pca9555.h index 39435f23d6..7152a58bb1 100644 --- a/drivers/ioexpander/pca9555.h +++ b/drivers/ioexpander/pca9555.h @@ -61,6 +61,7 @@ /******************************************************************************************** * Pre-processor Definitions ********************************************************************************************/ + /* Configuration ****************************************************************************/ /* Prerequisites: * CONFIG_I2C @@ -118,11 +119,12 @@ struct pca9555_dev_s { struct ioexpander_dev_s dev; /* Nested structure to allow casting as public gpio * expander. */ - +#ifdef CONFIG_IOEXPANDER_SHADOW_MODE + uint8_t sreg[8]; /* Shadowed registers of the PCA9555 */ +#endif #ifdef CONFIG_PCA9555_MULTIPLE FAR struct pca9555_dev_s *flink; /* Supports a singly linked list of drivers */ #endif - FAR struct pca9555_config_s *config; /* Board configuration data */ FAR struct i2c_master_s *i2c; /* Saved I2C driver instance */ sem_t exclsem; /* Mutual exclusion */