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.
This commit is contained in:
Michael Spahlinger 2016-06-27 08:11:54 -06:00 committed by Gregory Nutt
parent 43d7301255
commit 3d5e690977
3 changed files with 82 additions and 5 deletions

View File

@ -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

View File

@ -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. */

View File

@ -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 */