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.

MSP430g2553 I2C with PCF8574 bus extender

Other Parts Discussed in Thread: MSP430G2553, MSP430F2619

I'm using a PCF8574N as a bus extender for MSP430g2553. I'm using the bus extenders to reduce the number of pins needed to drive an LED matrix.

When I step through my interrupt, the code works and I get exactly the right output... but if I just let it run, it does not. So, this must be some kind of timing issue. I'm using TI's I2C master library (http://www.ti.com/lit/an/slaa382/slaa382.pdf). The buffer is loading with values and outputting properly... and everything is perfect when I step through.

This is driving me a little insane. All help is very much appreciated!

Note: the driving circuit works flawlessly without bus extenders hooked up - so no problems there.

//******************************************************************************
//   MSP430 USCI I2C Transmitter and Receiver
//
//  Description: This code configures the MSP430's USCI module as
//  I2C master capable of transmitting and receiving bytes.
//
//  ***THIS IS THE MASTER CODE***
//
//                    Master                   
//                 MSP430F2619             
//             -----------------          
//         /|\|              XIN|-   
//          | |                 |     
//          --|RST          XOUT|-    
//            |                 |        
//            |                 |        
//            |                 |       
//            |         SDA/P1.7|------->10k-----|
//            |         SCL/P1.6|------->10k-----|
//
// Note: External pull-ups are needed for SDA & SCL
//
// Uli Kretzschmar
// Texas Instruments Deutschland GmbH
// November 2007
// Built with IAR Embedded Workbench Version: 3.42A
//******************************************************************************
#include "msp430g2553.h"                        // device specific header
//#include "msp430x22x4.h"
//#include "msp430x23x0.h"
//#include "msp430xG46x.h"
// ...                                         // more devices are possible

#include "TI_USCI_I2C_master.h"

signed char byteCtr;
unsigned char *TI_receive_field;
unsigned char *TI_transmit_field;

//------------------------------------------------------------------------------
// void TI_USCI_I2C_receiveinit(unsigned char slave_address,
//                              unsigned char prescale)
//
// This function initializes the USCI module for master-receive operation.
//
// IN:   unsigned char slave_address   =>  Slave Address
//       unsigned char prescale        =>  SCL clock adjustment
//-----------------------------------------------------------------------------
void TI_USCI_I2C_receiveinit(unsigned char slave_address,
                             unsigned char prescale){
  P1SEL |= SDA_PIN + SCL_PIN;                 // Assign I2C pins to USCI_B0
  P1SEL2 |= SDA_PIN + SCL_PIN;
  UCB0CTL1 = UCSWRST;                        // Enable SW reset
  UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;       // I2C Master, synchronous mode
  UCB0CTL1 = UCSSEL_2 + UCSWRST;              // Use SMCLK, keep SW reset
  UCB0BR0 = prescale;                         // set prescaler
  UCB0BR1 = 0;
  UCB0I2CSA = slave_address;                  // set slave address
  UCB0CTL1 &= ~UCSWRST;                       // Clear SW reset, resume operation
  UCB0I2CIE = UCNACKIE;
  IE2 = UCB0RXIE;                            // Enable RX interrupt
}

//------------------------------------------------------------------------------
// void TI_USCI_I2C_transmitinit(unsigned char slave_address,
//                               unsigned char prescale)
//
// This function initializes the USCI module for master-transmit operation.
//
// IN:   unsigned char slave_address   =>  Slave Address
//       unsigned char prescale        =>  SCL clock adjustment
//------------------------------------------------------------------------------
void TI_USCI_I2C_transmitinit(unsigned char slave_address,
                          unsigned char prescale){
  P1SEL |= SDA_PIN + SCL_PIN;                 // Assign I2C pins to USCI_B0
  P1SEL2 |= SDA_PIN + SCL_PIN;
  UCB0CTL1 = UCSWRST;                        // Enable SW reset
  UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;       // I2C Master, synchronous mode
  UCB0CTL1 = UCSSEL_2 + UCSWRST;              // Use SMCLK, keep SW reset
  UCB0BR0 = prescale;                         // set prescaler
  UCB0BR1 = 0;
  UCB0I2CSA = slave_address;                  // Set slave address
  UCB0CTL1 &= ~UCSWRST;                       // Clear SW reset, resume operation
  UCB0I2CIE = UCNACKIE;
  IE2 = UCB0TXIE;                            // Enable TX ready interrupt
}

//------------------------------------------------------------------------------
// void TI_USCI_I2C_receive(unsigned char byteCount, unsigned char *field)
//
// This function is used to start an I2C commuincation in master-receiver mode.
//
// IN:   unsigned char byteCount  =>  number of bytes that should be read
//       unsigned char *field     =>  array variable used to store received data
//------------------------------------------------------------------------------
void TI_USCI_I2C_receive(unsigned char byteCount, unsigned char *field){
  TI_receive_field = field;
  if ( byteCount == 1 ){
    byteCtr = 0 ;
    __disable_interrupt();
    UCB0CTL1 |= UCTXSTT;                      // I2C start condition
    while (UCB0CTL1 & UCTXSTT);               // Start condition sent?
    UCB0CTL1 |= UCTXSTP;                      // I2C stop condition
    __enable_interrupt();
  } else if ( byteCount > 1 ) {
    byteCtr = byteCount - 2 ;
    UCB0CTL1 |= UCTXSTT;                      // I2C start condition
  } else
    while (1);                                // illegal parameter
}

//------------------------------------------------------------------------------
// void TI_USCI_I2C_transmit(unsigned char byteCount, unsigned char *field)
//
// This function is used to start an I2C commuincation in master-transmit mode.
//
// IN:   unsigned char byteCount  =>  number of bytes that should be transmitted
//       unsigned char *field     =>  array variable. Its content will be sent.
//------------------------------------------------------------------------------
void TI_USCI_I2C_transmit(unsigned char byteCount, unsigned char *field){
  TI_transmit_field = field;
  byteCtr = byteCount;
  UCB0CTL1 |= UCTR + UCTXSTT;                 // I2C TX, start condition
}

//------------------------------------------------------------------------------
// unsigned char TI_USCI_I2C_slave_present(unsigned char slave_address)
//
// This function is used to look for a slave address on the I2C bus.  
//
// IN:   unsigned char slave_address  =>  Slave Address
// OUT:  unsigned char                =>  0: address was not found,
//                                        1: address found
//------------------------------------------------------------------------------
unsigned char TI_USCI_I2C_slave_present(unsigned char slave_address){
  unsigned char ie2_bak, slaveadr_bak, ucb0i2cie, returnValue;
  ucb0i2cie = UCB0I2CIE;                      // restore old UCB0I2CIE
  ie2_bak = IE2;                              // store IE2 register
  slaveadr_bak = UCB0I2CSA;                   // store old slave address
  UCB0I2CIE &= ~ UCNACKIE;                    // no NACK interrupt
  UCB0I2CSA = slave_address;                  // set slave address
  IE2 &= ~(UCB0TXIE + UCB0RXIE);              // no RX or TX interrupts
  __disable_interrupt();
  UCB0CTL1 |= UCTR + UCTXSTT + UCTXSTP;       // I2C TX, start condition
  while (UCB0CTL1 & UCTXSTP);                 // wait for STOP condition
 
  returnValue = !(UCB0STAT & UCNACKIFG);
  __enable_interrupt();
  IE2 = ie2_bak;                              // restore IE2
  UCB0I2CSA = slaveadr_bak;                   // restore old slave address
  UCB0I2CIE = ucb0i2cie;                      // restore old UCB0CTL1
  return returnValue;                         // return whether or not
                                              // a NACK occured
}

//------------------------------------------------------------------------------
// unsigned char TI_USCI_I2C_notready()
//
// This function is used to check if there is commuincation in progress.
//
// OUT:  unsigned char  =>  0: I2C bus is idle,
//                          1: communication is in progress
//------------------------------------------------------------------------------
unsigned char TI_USCI_I2C_notready(){
  return (UCB0STAT & UCBBUSY);
}


#pragma vector = USCIAB0RX_VECTOR
__interrupt void USCIAB0RX_ISR(void)
{
  if (UCB0STAT & UCNACKIFG){            // send STOP if slave sends NACK
    UCB0CTL1 |= UCTXSTP;
    UCB0STAT &= ~UCNACKIFG;
  }

}


#pragma vector = USCIAB0TX_VECTOR
__interrupt void USCIAB0TX_ISR(void)
{
  if (IFG2 & UCB0RXIFG){
    if ( byteCtr == 0 ){
      UCB0CTL1 |= UCTXSTP;                    // I2C stop condition
      *TI_receive_field = UCB0RXBUF;
      TI_receive_field++;
    }
    else {
      *TI_receive_field = UCB0RXBUF;
      TI_receive_field++;
      byteCtr--;
    }
  }
  else {
    if (byteCtr == 0){
      UCB0CTL1 |= UCTXSTP;                    // I2C stop condition
      IFG2 &= ~UCB0TXIFG;                     // Clear USCI_B0 TX int flag
    }
    else {
      UCB0TXBUF = *TI_transmit_field;
      //__delay_cycles(10);
      TI_transmit_field++;
      byteCtr--;
    }
  }
}

My main and call of transmit:

void main(void) {

    IDX = (unsigned char*) malloc(sizeof(unsigned char));

    WDTCTL = WDTPW + WDTHOLD;                // Stop watchdog timer
    BCSCTL1 = CALBC1_16MHZ;
    DCOCTL = CALDCO_16MHZ;

    P2SEL = 0x00;

    P1DIR = 0xFF;
    P2DIR = 0xFF;

    _EINT();

    TI_USCI_I2C_transmitinit(0x20, 200);   // slave address, prescaler

        writesingle(0, 0xC0);

}

void writesingle (unsigned char addr, unsigned char num) {
        WRMO_OUT = 0x30;    // Write and Mode HIGH --- KEEP THEM THIS WAY
        *IDX = 0x30;
        temp = *IDX;
        while ( TI_USCI_I2C_notready() ); // wait for bus to clear
        TI_USCI_I2C_transmit(1, IDX);
        *IDX = *IDX | addr;
        while ( TI_USCI_I2C_notready() );
        TI_USCI_I2C_transmit(1, IDX);
        WRMO_OUT = 0x20;    // Pulse WRITE - Write LOW
        WRMO_OUT = 0x30;    // Pulse WRITE - Write HIGH
        WRMO_OUT = 0x10;    // Mode LOW --- KEEP IT THIS WAY
        WRMO_OUT = 0x00;    // Write LOW --- KEEP IT THIS WAY
        *IDX = num;
        while ( TI_USCI_I2C_notready() );
        TI_USCI_I2C_transmit(1, IDX);
        WRMO_OUT = 0x10;    // Pulse WRITE - Write HIGH
}

  • Kaitlin Fish-Stuhl said:
    void writesingle (unsigned char addr, unsigned char num) {
            WRMO_OUT = 0x30;    // Write and Mode HIGH --- KEEP THEM THIS WAY
            *IDX = 0x30;
            temp = *IDX;
            while ( TI_USCI_I2C_notready() ); // wait for bus to clear
            TI_USCI_I2C_transmit(1, IDX);
            *IDX = *IDX | addr;
            while ( TI_USCI_I2C_notready() );
            TI_USCI_I2C_transmit(1, IDX);
            WRMO_OUT = 0x20;    // Pulse WRITE - Write LOW
            WRMO_OUT = 0x30;    // Pulse WRITE - Write HIGH
            WRMO_OUT = 0x10;    // Mode LOW --- KEEP IT THIS WAY
            WRMO_OUT = 0x00;    // Write LOW --- KEEP IT THIS WAY
            *IDX = num;
            while ( TI_USCI_I2C_notready() );
            TI_USCI_I2C_transmit(1, IDX);
            WRMO_OUT = 0x10;    // Pulse WRITE - Write HIGH
    }

    What does WRMO_OUT control, and do changes to WRMO_OUT need to be synchronized to the I2C activity?

    From looking at TI_USCI_I2C_transmit it initiates a I2C transmit but doesn't wait for the I2C write to complete before returning. i.e. WRMO_OUT may change while an I2C transmit is in progress.

  • !! That has to be it, then... WRMO_OUT cannot change while the transmit is in progress and have the driver still work.

    WRMO_OUT controls the mode/write pins of the LED driver.

  • Got it working thanks to your advice. THANK YOU so much. I knew it had to be WRMO changing prematurely but didn't know why that was happening.

**Attention** This is a public forum