This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Launchpad MSP430G2231 + I/O expander TI PCF8574

Other Parts Discussed in Thread: PCF8574, MSP430G2231

Hi,

I have a TI PCF8574 I/O expander (http://search.digikey.com/scripts/DkSearch/dksus.dll?Detail&name=296-13109-5-ND) connected to MSP430 Launchpad (MSP430G2231) and having trouble in I2C communication with the expander using the attached code (not mine, used from some example).

Wiring:

SCL = P1.6

SDA = P1.7

A0, A1, A2 = GND

VCC = VCC

I/O expander I2C address is 0x20 (100000b) -> thus 0x40 is written to I2C bus to enable write mode (R/W is low)

The IO expander seems to ACK the address byte but there is a NACK (bolded line in I2C_State 8) for data byte and all pins remain HIGH.

What I would like to do is to set P0 to output/HIGH and all the other pins to output/LOW.

What am I doing wrong?

char MST_Data = 0x01;                  // Variable for transmitted data
char SLV_Addr = 0x40;                 
int I2C_State = 0;                            // State variable

void initialize(void)
    {
WDTCTL = WDTPW + WDTHOLD;            // Stop watchdog
  if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF)
  {
    while(1);                          // If calibration constants erased
                                       // do not load, trap CPU!!
  }
  BCSCTL1 = CALBC1_1MHZ;               // Set DCO
  DCOCTL = CALDCO_1MHZ;

  P1OUT = 0xC0;                        // P1.6 & P1.7 Pullups, others to 0
  P1REN |= 0xC0;                       // P1.6 & P1.7 Pullups
  P1DIR = 0xFF;                        // Unused pins as outputs 
   
  USICTL0 = USIPE6+USIPE7+USIMST+USISWRST; // Port & USI mode setup
  USICTL1 = USII2C+USIIE;              // Enable I2C mode & USI interrupt
  USICKCTL = USIDIV_3+USISSEL_2+USICKPL;
  USICNT |= USIIFGCC;                  // Disable automatic clear control
  USICTL0 &= ~USISWRST;                // Enable USI
  USICTL1 &= ~USIIFG;                  // Clear pending flag
  _EINT();
      }

 

void main(void)
    {   
    initialize();

 while(1)
  {
    USICTL1 |= USIIFG;                 // Set flag and start communication

//    LPM0;                              // CPU off, await USI interrupt
 //   _NOP();                            // Used for IAR
//    for (i = 0; i < 5000; i++);        // Dummy delay between communication cycles
  }

    }

 

/******************************************************
// USI interrupt service routine
******************************************************/
#pragma vector = USI_VECTOR
__interrupt void USI_TXRX (void)
{
  switch(I2C_State)
    {
      case 0: // Generate Start Condition & send address to slave
//              P1OUT |= 0x01;           // LED on: sequence start
              USISRL = 0x00;           // Generate Start Condition...
              USICTL0 |= USIGE+USIOE;
              USICTL0 &= ~USIGE;

              USISRL = SLV_Addr;       // ... and transmit address, R/W = 0
              USICNT = (USICNT & 0xE0) + 0x08; // Bit counter = 8, TX Address
              I2C_State = 2;           // Go to next state: receive address (N)Ack
              break;

      case 2: // Receive Address Ack/Nack bit
              USICTL0 &= ~USIOE;       // SDA = input
              USICNT |= 0x01;          // Bit counter = 1, receive (N)Ack bit
              I2C_State = 4;           // Go to next state: check (N)Ack
              break;

      case 4: // Process Address Ack/Nack & handle data TX
              USICTL0 |= USIOE;        // SDA = output
              if (USISRL & 0x01)       // If Nack received...
              { // Send stop...
                USISRL = 0x00;
                USICNT |=  0x01;       // Bit counter = 1, SCL high, SDA low
                I2C_State = 10;        // Go to next state: generate Stop
                P1OUT |= 0x01;         // Turn on LED: error
              }
              else
              { // Ack received, TX data to slave...
                USISRL = MST_Data;     // Load data byte
                USICNT |=  0x08;       // Bit counter = 8, start TX
                I2C_State = 6;         // Go to next state: receive data (N)Ack
//                P1OUT &= ~0x01;        // Turn off LED
              }
              break;

      case 6: // Receive Data Ack/Nack bit
              USICTL0 &= ~USIOE;       // SDA = input
            
              USICNT |= 0x01;          // Bit counter = 1, receive (N)Ack bit

              I2C_State = 8;           // Go to next state: check (N)Ack
              break;

      case 8: // Process Data Ack/Nack & send Stop
              USICTL0 |= USIOE;
              if (USISRL & 0x01)       // If Nack received...
                {
                P1OUT |= 0x01;         // Turn on LED: error
                }
              else                     // Ack received
              {
                P1OUT &= ~0x01;        // Turn off LED
              }
              // Send stop...
              USISRL = 0x00;
              USICNT |=  0x01;         // Bit counter = 1, SCL high, SDA low
              I2C_State = 10;          // Go to next state: generate Stop
              break;

      case 10:// Generate Stop Condition
              USISRL = 0x0FF;          // USISRL = 1 to release SDA
              USICTL0 |= USIGE;        // Transparent latch enabled
              USICTL0 &= ~(USIGE+USIOE);// Latch/SDA output disabled
              I2C_State = 0;           // Reset state machine for next transmission
  //            LPM0_EXIT;               // Exit active for next transfer
              break;
    }

  USICTL1 &= ~USIIFG;                  // Clear pending flag
}

 

  • Got it working.. Removed jumper J5/P1.6 and took the original code into use.

     

  • Jukka,

    really cool! I always enjoy seeing LaunchPad working with yet another TI product :).

    Once the project is complete, care to share the details with us?

    We have a growing # of projects on the LaunchPad wiki site, and would like to see more.

    ~Dung

  • Jukka Oravasaari said:
    P1OUT = 0xC0;                        // P1.6 & P1.7 Pullups, others to 0

    This is dangerous and won't normally work. For most MSP devices except very few, the internal pullups are only active if the port pin is configured as input. Since I2C configures it as output temporarily, teh pullups are deactivated the very moment they are needed.

    For the LaunchPad, it works, but this is not portable to other MSPs.

    In any case, the value of the internal pullups might be as high as 50kOhm (typ. 35, min 20), which may be far too high for a reliable I2C line pullup, which is usually 10k.

    P.s.: the 8574 is a nice device for cheap I/O expansion. I once wrote a 'driver' that supported I2C through the cassette interface of the C64. Together with a basic extension that supported 8*8574 as well as the 8583 RTC and the 8 bit ADC/DAC module. And I put support for these into my 'TEXL' compiler for the C64 GEOS. There was an 8-lane slot car racing installation controlled by a C64 through these I2C devices. Ahh, sweet memories :) Nice to see some oldies still reappear after over 20 years.

**Attention** This is a public forum