PCF8574. Expansor I2C de 8 bits de puertos GPIO digitales de entrada y salida.

publicado en: Portada | 12

Cuando un proyecto crece en complejidad no es raro encontrarse con que hacen falta más puertos de entrada-salida de los que hay disponibles en el microcontrolador seleccionado inicialmente. La solución puede ser optar por otro modelo de MCU de la misma familia pero que disponga de más patillas o añadir al proyecto un integrado, como el PCF8574, que aporte los GPIO que falten y se comunique con el µC con un bus I2C (que el bus que utiliza el PCF8574) o un bus SPI, con los que se puede conectar al microcontrolador varios dispositivos de manera simultánea.

El PCF8574 es muy popular porque, además de ser barato, es sencillo tanto incluirlo en un circuito como usarlo desde un programa. Se presenta en varios encapsulados, incluyendo DIP, por lo que es ideal para hacer prototipos y pruebas antes de implementar el integrado en el proyecto definitivo.

Expansor I2C digital GPIO PCF8574

La conexión del PCF8574 es muy sencilla. Por el lado del microcontrolador se conecta al bus I2C y a un pin que soporte una interrupción. Las tres líneas, las dos del bus y la de la interrupción, deben contar con las correspondientes resistencias pull-up.

La parte fija de la dirección IC del PCF8574 es 0b0100AAA (0x32 | AAA) y la del PCF8574A es 0b0100AAA0 (0x64 | AAA). Los tres dígitos representados con AAA se configuran con las patillas A0 a A2.
. Conectándolas a masa (a nivel bajo), como en el esquema de conexión del ejemplo de abajo, se consigue la dirección base 0b0100000 (0x32), o 0b0100AAA0 (0x64) en el caso del PCF8574A.

Expansor de ES I2C de 8 bits PCF8574. Circuito de aplicación

El PCF8574 dispone de resistencias pull-up internas en los pin de entrada y salida P0 a P7 por lo que al inicio de su funcionamiento (al alimentarlo) están a nivel alto.

La alimentación del PCF8574 está en el intervalo de 2,5 V a 6 V, así que puede usarse con un rango muy amplio de µC. Como no ofrece a la salida una corriente muy alta, es conveniente conectar las salidas del integrado con un driver (un simple transistor en corte o saturación puede servir) o, por ejemplo para usar LED, activarlos a nivel bajo y no alimentarlos con la salida del PCF8574.

PCF8574 pinout patillaje expansor puerto entrada salida GPIO I2C 8 bits

Aunque es muy sencillo incorporar la versión DIP a un prototipo o usarla para pruebas, también existen módulos, como el de la imagen de abajo, que incluyen el PCF8574A, las resistencias del bus I2C (aunque normalmente no la de la interrupción) y recursos para configurar la dirección del dispositivo y encadenar módulos al estilo daisy chain.

Expansor digital de GPIO pin E-S I2C de 8 bits PCF8574

El PCF8574 puede manejarse desde una placa Arduino usando la librería Wire en el programa con el que se explote. Para usarlo en modo escritura basta con enviar el valor del estado de los 8 bits que representan el puerto. La lectura puede hacerse de dos modos: que el programa tome la iniciativa para realizar el muestreo (por ejemplo cada cierto intervalo de tiempo) o que se realice la lectura del estado sólo cuando se produzca un cambio, del que el integrado avisa al microcontrolador con la interrupción.

En el siguiente código se muestra un ejemplo de cómo usar la salida del PCF8574. El funcionamiento consiste, básicamente, en enviar el código que representa el nivel de las patillas a la dirección I2C del integrado. Para hacer esta operación con la librería Wire de Arduino hay que activar las comunicaciones con Wire.begin() (que no será necesario invocar más que una vez), acceder a la dirección del PCF8574 o del PCF8574A con Wire.beginTransmission(), enviar el valor con Wire.write() y liberar el bus I2C con Wire.endTransmission().

El ejemplo de abajo es el caso más sencillo de lectura del PCF8574, simplemente se consulta el estado cada cierto intervalo de tiempo y se envía a la consola serie (como ejemplo de uso). Las principales diferencias con el código del ejemplo anterior consisten en que se solicitan los datos con Wire.requestFrom() y se cargan del bus con Wire.read().

La parte menos ortodoxa del código anterior consiste en leer el bus I2C sin saber si se habrán recibido ya los datos o no. Para hacerlo de forma más correcta se puede usar Wire.available() para saber cuántos bytes de datos se han recibido en la última consulta.

El formato más básico, que suele ser suficiente, consiste en consultar los datos recibidos y operar con ellos desde una estructura if() para evitar leerlos si no están disponibles.

Aunque, en principio, el método anterior funciona, ya que el tiempo que tarda en ejecutarse la consulta es suficiente para que los datos hayan podido transmitirse, lo más correcto sería esperar hasta que lleguen los datos solicitados o hasta que pase un intervalo de tiempo prefijado (timeout) después del cual se podría suponer que se ha producido algún tipo de error y no llegarán esos datos.

Muestrear el estado del puerto del PCF8574 cada cierto periodo de tiempo o cuando lo determinen ciertas condiciones del estado del sistema (por ejemplo, la lectura de otras entradas) es perfectamente funcional, pero en algunas situaciones hay que responder a determinada lectura, cuando lleguen nuevos datos, lo antes posible, no cuando el intervalo de muestreo lo determine. Para esos casos lo más eficaz es utilizar una interrupción hardware, que en el PCF8574 se activa con cada cambio en el estado del puerto, es decir, con cada nueva lectura.

Las diferentes placas Arduino tienen asignaciones de patillas asociadas a interrupciones por hardware un poco diferentes.

Para asignar las interrupciones se usa attachInterrupt() y para desactivarlas detachInterrupt(). La asignación de la interrupción necesita que se indique:

  • el número de interrupción, que se puede obtener con digitalPinToInterrupt() indicándole el número del pin (en lugar del número de interrupción, que podría dar lugar a confusión por el modelo de placa),
  • la función que se invoca cuando se genera la interrupción y
  • el estado que se detecta, que puede ser LOW, CHANGE, RISING o FALLING según se considere el nivel bajo, un cambio en el pin de la interrupción, el flanco de ascenso o el flanco de descenso, respectivamente.

Conforme a lo anterior, el código de lectura del PCF8574 que lee su estado solamente cuando se genera una interrupción podría quedar como el del siguiente ejemplo:

En el ejemplo anterior se usa la variable global lectura_pendiente como un indicador de que se ha producido un cambio en el puerto. La función que gestiona la interrupción se encarga del mínimo proceso necesario y el código de lectura queda dentro del bucle de forma análoga a los anteriores ejemplos.

Al utilizar interrupciones, en lugar de muestrear «manualmente» el estado del puerto, será muy importante atender a las condiciones en las que se lanza la interrupción, para evitar efectos como los rebotes en contactos. La solución puede ser hardware (con un condensador o con una puerta lógica) o software, modificando el código para no actuar si el periodo entre cambios de estado del puerto no se produce en determinado intervalo.

Víctor Ventura

Desarrollando aplicaciones para la web conocí el potencial de internet de las cosas, encontré la excusa perfecta para satisfacer la inquietud de aprender electrónica que había tenido desde siempre. Ahora puedo darme el gusto de programar las cosas que yo mismo diseño y fabrico.

Más entradas - Página web

Sígueme:
TwitterLinkedIn

Seguir Víctor Ventura:

Programador multimedia y web + IoT. Mejor con software libre.

Desarrollando aplicaciones para la web conocí el potencial de internet de las cosas, encontré la excusa perfecta para satisfacer la inquietud de aprender electrónica que había tenido desde siempre. Ahora puedo darme el gusto de programar las cosas que yo mismo diseño y fabrico.

12 Respuestas

  1. Victor Rodriguez Vallejos

    Estimado,

    He leído tu apunte de PCF8574, me pareció:
    – MUY interesante.
    – Un desarrollo claro y directo.
    – Aclaratorio en su operatoria.

    En fin… excelente aporte, Gracias por el.

    Verificare tu nombre en otros canales para ver si hay mas de ello, yo soy Electrónico de profesión y desde siempre programador, así que creo tener la base suficiente para refutar lo poco que hable del tema por ti entregado.

    Atento a tus comentarios,

    Víctor Rodríguez

    NOTA: Si tienes canal Youtube o similar y tienes tiempo de lo das por favor, nuevamente gracias.

    • Víctor Ventura

      Hola, Víctor.

      Gracias por tus elogios, me alegra mucho que te haya gustado el artículo 🙂

      Por el momento, este blog es el único canal en el que publico sobre electrónica.

      Gracias por participar en polaridad.es ¡Hasta pronto!

  2. David

    Buenas Victor Ventura, muy explicativo tu aporte, muy bueno, estoy estudiando ing. electrónica y actualmente estoy trabajando con el pic18f4550… requiero leer los datos desde un teclado matricial usando el pcf8574… como hago para muestrear el estado de las salidas del PCF… que comando debo enviarle para que me proporcione los estados de la salida…. de antemano GRACIAS… espero sigas apoyándonos en esto de la programación.

    • Víctor Ventura

      Hola, David.

      La forma en la que se explota un dispositivo matricial suele ser (como en tu caso) independiente del MCU e incluso del driver.

      Para leer un teclado matricial hay que recorrer sus filas o columnas (por ejemplo, con un bucle for en C) estableciendo un nivel y recorrer sus columnas o filas (otro bucle for dentro del anterior) leyendo el estado que resulta.

      La elección sobre qué se establece y qué se lee (filas o columnas) normalmente es una elección arbitraria en un dispositivo genérico como entiendo que es tu teclado matricial.

      El nivel al que establece una fila o columna en el proceso de lectura debe ser el contrario del nivel por defecto, es decir, si en «estado de reposo» el valor devuelto es cero (por ejemplo, por disponer de resistencias pull-down) el nivel de prueba debe ser uno.

      Este patrón de trabajo (al que se suele hacer referencia por el nombre de «barrido») se utiliza muy habitualmente (tanto, que hay dispositivos que lo facilitan por hardware) así que lo mismo un día me animo a hacer un artículo con ejemplos de código de aplicación. Me temo que para poder dar ejemplos genéricos, que todo el mundo pueda seguir, lo más práctico es usar algo «alejado del metal» (un sistema operativo o algo como Arduino). La otra idea que me da tu comentario es publicar código para Pinguino, lo que permitiría probarlo en microcontroladores de la marca Microchip, como es el caso de tu PIC18F4550.

      Mucha suerte con tu proyecto y gracias por participar en polaridad.es.

  3. juan pablo rodriguez

    Hola, Soy JP Rodriguez y quería peguntarte como puedo conectar varios dispositivos e/s al pcf8574p, ya que en la red solo encuentro ejemplo con botones y led, y mi idea es leer un RTC y BMP085 y escribir en un nfr24l01.

    Gracias y saludos

    • Víctor Ventura

      Hola, JP.

      No sé qué RTC vas a usar, cómo se conecta al MCU, pero el BMP085 se conecta por I2C, así que el PCF8574 no es lo más adecuado, ya que su función es aportar al MCU más puertos genéricos.

      El PCF8574 es controlado por I2C, no controla dispositivos por I2C (a lo peor esa es tu confusión). Si vas a controlar un RTC y un sensor barométrico por I2C ¿Para qué quieres un PCF8574 si el MCU ya dispone de algún puerto I2C con el que hacerlo directamente?

      Los ejemplos que has encontrado son de entradas y salidas (digitales) genéricas y normalmente de un bit (pulsadores y LED) que es la finalidad normal para la que se utiliza un expansor de puertos de entrada salida genéricos (GPIO) como el PCF8574.

      Suponiendo que elijas un RTC como el DS3231 solamente necesitas un MCU (una placa Arduino, por ejemplo), el propio DS3231 que se controlará por I2C, el BMP085, que también se controlará por I2C y el nRF24L01, que se controla por SPI. No necesitas un PCF8574.

      Espero haberte entendido bien y que esto te ayude un poco. Gracias por participar en polaridad.es

  4. Henry Vilca

    Hola muy buen articulo, tengo una duda, si este dispositivo permite leer los estados de las salidas.
    me explico con un ejemplo » en un primer paso escribo un dato en el puerto, despues de eso es posible leer el dato del latch de salida?».

    un abrazo, Gracias y muy buen trabajo.

    • Víctor Ventura

      Hola, Henry.

      Hasta donde sé, desde I2C no se puede leer el estado de la salida, solamente de la entrada. Dependiendo del circuito que montes podría ser que al leer (la entrada) se obtenga el valor que antes has enviado (a la salida) pero no porque se lea la salida.

      Gracias por participar en polaridad.es

  5. Miguel Meléndez

    Excelente artículo Víctor, muy claros y detallados tus ejemplos de uso para este expansor. Voy a utilizarlo para un circuito temporizador multipuertos y me será de mucha utilidad la información que aquí nos has compartido.

    Muchas Gracias y Saludos!

  6. manuel castillo

    Hola Victor, muy educativo tu articulo. Ahora te presento un desafio.
    Necesito usarlo en un NODEMCU Lolin v3. Para conectar 8 mudulos reles, ademas de son sensores de temperatura en sonda compatibles con I2C y una mini pantalla oled, tambien I2C.
    El punto es que segun la temperatura, active o desactive alguno de los reles. Y a voluntad se puedan apagar o prender los otros reles. Obviamente, desde un servidor web.
    Que tal? Como seria el tratamiento para poder controlar cada elemento de los reles en forma independiente y debe ser lectura/escritura.

  7. Mario

    Hola Víctor, gracias por tus apuntes. Soy algo nuevo en esto y no me queda muy claro como identifico y declaro la salida o entrada en el modulo de expansión. Quiero decir, al igual que en arduino ponemos directamente para declarar una variable a un pin, aquí como se haría?
    Muchas gracias

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *