I2C IO-Expander

From Nutwiki
Revision as of 17:02, 27 October 2016 by Harald (Talk | contribs) (1 revision imported)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Description

This text explains how to access an I2C PCF8574 expander.

PCF8574 short description

* works from 2.5 to 6V 
* selectable I2C address range from 0x20 to 0x27 for PCF8574 or from 0x38 to 0x3F for PCF8574A. Device Address can be configured by A0-A2 pins. Address pins can be simply shorted to power or ground line.
* very small current source (100uA) when output is set to HIGH (thus pull-ups are required if needed). It is better to use negative logic and drive outputs by selecting LOW state.

Prerequisites

Examples below was tested with NUT/OS 4.8.8.0 and Atmel AT91SAM7X256 processor. Both of software TWI (bit banging mode) and hardware TWI works with these expanders. Three PCF8574 expanders was used with LEDs connected between expander's IO ports and 3.3V source (with resistors of course). Expanders addresses were set to 0x20, 0x21 and 0x22.


Source Code

First expander is set to turn on all LEDs. Next two are used in input mode.

<source lang="c">

   #define I2C_TIMEOUT_MS          500
   #define I2C_SLA_PCF8574_BASE    0x20
   #define I2C_SLA_PCF8574A_BASE   0x38
   #define NUM_OF_PCFS             3
   unsigned long ulI2CSpeed;
   unsigned char ucRXBuf, ucTXBuf;
   unsigned char ucSlaveAddr;
   unsigned char ucBit;
   int iStatus;


   printf("Init I2C line\n");
   ulI2CSpeed = 100000;
   TwInit (0);
   if (-1==TwIOCtl (TWI_SETSPEED, &ulI2CSpeed))
   {
       printf ("I2C Clock set failed!\n");
   }
 for (;;) {
       printf ("Sleeping...\n");
       NutSleep(1000);
       for (ucSlaveAddr=I2C_SLA_PCF8574_BASE; ucSlaveAddr<(I2C_SLA_PCF8574_BASE+NUM_OF_PCFS); ucSlaveAddr++)
       {
           // set output state for each expander:
           switch (ucSlaveAddr)
           {
               case 0x20:  //first PCF
                   ucTXBuf = 0;    // turn on all LEDs connected between PCF and Vdd
                   break;
               case 0x21:
               case 0x22:
               default:
                   ucTXBuf = 0xFF; // all outputs to HIGH state
                   break;
           }
           printf ("Reading slave @0x%02X ... ", ucSlaveAddr);
           iStatus = TwMasterTransact(ucSlaveAddr, &ucTXBuf, sizeof(ucTXBuf), &ucRXBuf, sizeof(ucRXBuf), I2C_TIMEOUT_MS);
           if (iStatus==-1)
           {
               printf("timed out!\n");
           }
           else
           {
               printf ("Received %d bytes. Val=0x%02X", iStatus, ucRXBuf);
               printf (" bin=");
               for (ucBit=8; ucBit>0; ucBit--)
               {
                   printf ( "%d", 0!=(ucRXBuf & (1<<(ucBit-1))) );
               }
               printf ("\n");
           }
           NutSleep(200);
       }
   }

</source>

Walking light example:

<source lang="c">

   #define I2C_TIMEOUT_MS          500
   #define I2C_SLA_PCF8574_BASE    0x20
   unsigned long ulI2CSpeed;
   unsigned char ucRXBuf, ucTXBuf;
   unsigned char ucSlaveAddr;
   unsigned char ucBit;
   int iStatus;


   printf("Init I2C line\n");
   ulI2CSpeed = 100000;
   TwInit (0);
   if (-1==TwIOCtl (TWI_SETSPEED, &ulI2CSpeed))
   {
       printf ("I2C Clock set failed!\n");
   }
   ucBit = 0;
   unsigned char ucSlaveAddrOld;
   ucSlaveAddr = I2C_SLA_PCF8574_BASE + (ucBit/8);
   for (;;)
   {
       ucSlaveAddrOld = ucSlaveAddr;
       ucSlaveAddr = I2C_SLA_PCF8574_BASE + (ucBit/8);
       // expander address changed - switch off all ports on previous expander
       if (ucSlaveAddr!=ucSlaveAddrOld)
       {
           ucTXBuf = 0xFF;
           iStatus = TwMasterTransact(ucSlaveAddrOld, &ucTXBuf, sizeof(ucTXBuf), 0, 0, I2C_TIMEOUT_MS);
       }
       ucTXBuf = 0xFF & ~(1<<(ucBit%8));
       //printf ("ucBit=%02d ucTXBuf=0x%02X\n", ucBit, ucTXBuf);
       //printf ("Slave @0x%02X ... ", ucSlaveAddr);
       iStatus = TwMasterTransact(ucSlaveAddr, &ucTXBuf, sizeof(ucTXBuf), 0, 0, I2C_TIMEOUT_MS);
       if (iStatus==-1)
       {
           printf("timed out!\n");
       }
       if (++ucBit>23)
       {
           ucBit = 0;
       }
       NutSleep (20);
   }

</source>


See also