El MCP23S17 permite controlar un puerto de 16 bits o 2 puertos de 8 bits de entrada-salida genéricos (GPIO) gestionándolos desde un microcontrolador por medio del bus SPI. Existe un integrado similar, el MCP23017, que usa comunicaciones I2C, cuyo funcionamiento es equiparable excepto por el tipo de bus que utiliza y la velocidad de transmisión de datos que implica: mientras que el MCP23017 (I2C) puede funcionar a 100 KHz, 400 KHz o 1,7 MHz, el MCP23S17 (SPI) del que trata este texto funciona hasta a 10 MHz.
Para avisar al microcontrolador de un cambio de estado en los puertos, el MCP23S17 dispone de dos interrupciones INTA para el puerto de 8 bits A e INTB para el puerto de 8 bits B. Estas interrupciones son configurables en señal (activa en nivel alto o bajo), en activación asociada (como una operación OR sobre ambas interrupciones) o independiente y en modo: activación por cualquier cambio o cuando sea diferente de un valor preconfigurado (el del registro DEFVAL).
El MCP23S17 puede funcionar según dos tipos de acceso. El modo byte, que no incrementa la dirección al terminar la operación, por lo que sirve para realizar accesos sucesivos a la misma dirección, y el modo secuencial, que incrementa la dirección automáticamente, por lo que sirve para alternar las direcciones automáticamente después de cada acceso.
La implementación hardware del MCP23S17 es muy sencilla, los únicos componentes pasivos que necesita para su funcionamiento son las resistencias para establecer los niveles alto o bajo por defecto. Como además existe versión DIP, es muy cómodo para aplicarlo en montajes de prueba como componente auxiliar.
En el siguiente esquema se muestran los bloques de conexión del MCP23S17. Las patillas A0, A1 y A2 establecen los tres penúltimos bits de la dirección en el bus SPI. Como la señal de reset se establece a nivel bajo, es necesario conectarla a una resistencia pull-up. La alimentación del integrado puede estar comprendida entre 1V8V y 5V5.
Tanto la configuración como las operaciones de lectura y escritura sobre el MCP23S17 se realizan accediendo a los diferentes registros organizados en pares para facilitar la configuración del puerto A y B. Excepto en el caso de IOCON, que es el registro de configuración de entrada salida principal, las direcciones de los registros dependen del bit BANK del registro IOCON. Cuando se establece el bit IOCON.BANK a nivel bajo las direcciones de los registros para el puerto A y B se alternan: primero un registro que configura el puerto A y luego el de configuración equivalente para el puerto B. Si el bit IOCON.BANK vale 1, todos los registros del puerto A se sitúan al principio y luego se encuentran todos los del puerto B. Para evitar errores que generen direcciones incorrectas, no debe cambiarse IOCON.BANK cuando el modo de direccionamiento es secuencial porque genera automáticamente una nueva dirección, que en este caso podría no ser válida, después de cada operación.
dirección | registro | funcionamiento | |
BANK=0 | BANK=1 | ||
0x00 | 0x00 | IODIRA | Configura el modo como entrada o como salida. Los bits a nivel alto indican que el GPIO del puerto correspondiente (A o B) es de entrada y los bits que estén a nivel bajo en el registro indican que el GPIO es de salida. |
0x01 | 0x10 | IODIRB | |
0x02 | 0x01 | IPOLA | Establece la polaridad del bit del puerto. Cuando el bit está a nivel alto, el pin correspondiente GPIO invierte su valor; cuando el bit está a nivel bajo la polaridad es la normal y un valor alto en un pin corresponde con una señal a nivel alto y uno bajo a un nivel bajo. |
0x03 | 0x11 | IPOLB | |
0x04 | 0x02 | GPINTENA | Sirve para establecer los bits que producen la activación de la interrupción. Los bits de GPINTENA-GPINTENB a nivel alto influyen en el disparo de la interrupción y los que estén a nivel bajo no afectan a la generación de las interrupciones. |
0x05 | 0x12 | GPINTENB | |
0x06 | 0x03 | DEFVALA | Almacena el valor del puerto para no lanzar la interrupción si INTCON está activo. Si al comparar el estado actual del puerto el valor es diferente del configurado en DEFVALA-DEFVALB y los bits de INTCONA-INTCONB indican que su valor debe tenerse en cuenta, se dispara la interrupción. |
0x07 | 0x13 | DEFVALB | |
0x08 | 0x04 | INTCONA | Establece los bits del puerto que se comparan con DEFVAL para activar la interrupción cuando sean diferentes (los que estén a nivel alto). Los bits de INTCONA-INTCONB cuyo valor sea cero no se consideran al comparar el estado actual del puerto con el valor almacenado en DEFVALA-DEFVALB para disparar la interrupción. |
0x09 | 0x14 | INTCONB | |
0x0a | 0x05 | IOCON | Ambos registros contienen la misma información y si se cambia el valor de uno también cambia el valor de otro. Para evitar confusiones no se les nombra como IOCONA-IOCONB sino que se hace referencia a ambos como IOCON. Bits que determinan la configuración de entrada salida:
|
0x0b | 0x15 | IOCON | |
0x0c | 0x06 | GPPUA | Sirve para controlar las resistencias pull-up internas de los puertos. Cuando se establece a nivel alto un bit de entrada de un puerto (un pin) se activa la resistencia interna de 100 kΩ. |
0x0d | 0x16 | GPPUB | |
0x0e | 0x07 | INTFA | Es un indicador de los bits que han producido una interrupción. El registro establece a nivel alto los bits por los cuales se genera la interrupción. Es de solo-lectura, no de escritura. |
0x0f | 0x17 | INTFB | |
0x10 | 0x08 | INTCAPA | Contiene el estado de cada puerto en el momento de producirse una interrupción (la «captura» del estado del puerto) y sirve para poder analizarlo permitiendo que la entrada cambie a otro estado mientras tanto. |
0x11 | 0x18 | INTCAPB | |
0x12 | 0x09 | GPIOA | Cuando se accede para leer devuelve el valor del puerto de entrada pero si se accede para escribir tiene el mismo efecto que OLAT: escribir en el latch de salida, es decir, no accede directamente al puerto de salida sino a través del latch de salida. |
0x13 | 0x19 | GPIOB | |
0x14 | 0x0a | OLATA | Da acceso al latch de salida del puerto correspondiente. Leer OLAT no accede directamente al puerto. Escribir en GPIO o en OLAT modifica el latch de salida que posteriormente modificará el puerto (la patilla) de salida. |
0x15 | 0x1a | OLATB |
El proceso para utilizar el MCP23S17 desde un microcontrolador es muy simple, consiste en establecer las comunicaciones SPI, preparar la configuración base e ir intercambiando información (recibiendo el estado o solicitando un estado) ya sea por las condiciones que impone el programa, incluyendo otras lecturas externas o control del tiempo, o por las interrupciones que genere el MCP23S17.
A nivel de hardware, para la implementación más simple no es necesario ningún componente pasivo, basta con conectar la alimentación y la patilla de reset a nivel alto. Si hubiera varios MCP23S17 en la misma línea de comunicaciones SPI habría que establecer diferentes direcciones para cada uno de ellos con las patillas A0, A1 y A2 a nivel alto o bajo.
Para usarlo desde Arduino se puede trabajar con la librería SPI. El proceso de explotación es el habitual:
- preparar la librería con
SPI.begin()
- establecer el pin CS (SS) como salida con
pinMode()
- ponerlo a nivel alto con
digitalWrite()
para desactivar el bus SPI al inicio - para cada escritura ponerlo a nivel bajo con
digitalWrite()
- enviar la dirección, el registro y el valor con
SPI.transfer()
- liberar las comunicaciones volviendo a poner CS (SS) a nivel alto con
digitalWrite()