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.

CCS/MSP430F6720: Problem communicating with EEPROM 24xx128 via I2C

Part Number: MSP430F6720

Tool/software: Code Composer Studio

Hi,

I am trying to port slaa208a from MSP430F1/2XXX to MSP430F67XX. I was able to get the code work to some degree but are having some weird problems. Reading from EEPROm seems OK, but writing to EEPROM always has problems:

1) Writing 256 byte one by one takes 10s of seconds.

2) A lot of those bytes are not successfully written into eeprom. When read back from those addresses, many of them are 0xFF, the default value.

My code are attached below. How should I go about troubleshooting?

Thanks in advance.

i2ceeprom.h

#define I2C_PORT_SEL  P2SEL
#define I2C_PORT_OUT  P2OUT
#define I2C_PORT_REN  P2REN
#define I2C_PORT_DIR  P2DIR
#define SDA_PIN       BIT1                  // UCB0SDA pin
#define SCL_PIN       BIT0                  // UCB0SCL pin
#define SCL_CLOCK_DIV 0x0050                // SCL clock divider

void InitI2C(unsigned char eeprom_i2c_address);
void EEPROM_ByteWrite(unsigned int Address , unsigned char Data);
//void EEPROM_PageWrite(unsigned char StartAddress , unsigned char * Data , unsigned char Size);
unsigned char EEPROM_RandomRead(unsigned int Address);
unsigned char EEPROM_CurrentAddressRead(void);
//void EEPROM_SequentialRead(unsigned char Address , unsigned char * Data , unsigned char Size);
void EEPROM_AckPolling(void);
void resetI2C(void);

i2ceeprom.c

#include "msp430.h"
#include "i2ceeprom.h"

#define     MAXPAGEWRITE   8

int PtrTransmit;
unsigned char I2CBufferArray[16];
unsigned char I2CBuffer;

/*----------------------------------------------------------------------------*/
// Description:
//   Initialization of the I2C Module
/*----------------------------------------------------------------------------*/
void InitI2C(unsigned char eeprom_i2c_address)
{
  I2C_PORT_SEL |= SDA_PIN | SCL_PIN;        // Assign I2C pins to USCI_B0

  // Setup eUSCI_B0
  UCB0CTLW0 |= UCSWRST;                             // Enable SW reset
  UCB0CTLW0 |= UCMST | UCMODE_3 | UCSSEL__SMCLK | UCSYNC;         // I2C Master, use SMCLK

  //UCB0BRW = SCL_CLOCK_DIV;                  // fSCL = SMCLK/40 = ~100kHz
  UCB0BRW_L = 40;                                   // fSCL = SMCLK/12 = ~100kHz
  UCB0BRW_H = 0;
  UCB0I2CSA = eeprom_i2c_address;           // Slave Address
  UCB0CTLW0 &= ~UCSWRST;                    // Clear SW reset, resume operation

  if (UCB0STATW & UCBBUSY)                   // test if bus to be free
  {                                         // otherwise a manual Clock on is generated
    I2C_PORT_SEL &= ~SCL_PIN;               // Select Port function for SCL
    I2C_PORT_OUT &= ~SCL_PIN;               //
    I2C_PORT_DIR |= SCL_PIN;                // drive SCL low
    I2C_PORT_SEL |= SDA_PIN | SCL_PIN;      // select module function for the
                                            // used I2C pins
  };
}

void resetI2C()
{
	I2C_PORT_SEL &= ~(SDA_PIN + SCL_PIN);      // Assign I2C pins to GPIO
	I2C_PORT_DIR &= ~(SDA_PIN + SCL_PIN); 		 // Set I2C pins to input
	I2C_PORT_REN &= ~(SDA_PIN + SCL_PIN);		   // No pull-up or pulldown resistor
}

/*---------------------------------------------------------------------------*/
// Description:
//   Initialization of the I2C Module for Write operation.
/*---------------------------------------------------------------------------*/
void I2CWriteInit(void)
{
	UCB0CTLW0 |= UCTR;                         // UCTR=1 => Transmit Mode (R/W bit = 0)
	UCB0IFG  &= ~UCTXIFG0;
	UCB0IE &= ~UCRXIE0;                         // disable Receive ready interrupt
	UCB0IE |= UCTXIE0;                          // enable Transmit ready interrupt
}

/*----------------------------------------------------------------------------*/
// Description:
//   Initialization of the I2C Module for Read operation.
/*----------------------------------------------------------------------------*/
void I2CReadInit(void)
{
  UCB0CTLW0 &= ~UCTR;                        // UCTR=0 => Receive Mode (R/W bit = 1)
  UCB0IFG &= ~UCRXIFG0;
  UCB0IE &= ~UCTXIE0;                         // disable Transmit ready interrupt
  UCB0IE |= UCRXIE0;                          // enable Receive ready interrupt
}

/*----------------------------------------------------------------------------*/
// Description:
//   Byte Write Operation. The communication via the I2C bus with an EEPROM
//   (2465) is realized. A data byte is written into a user defined address.
/*----------------------------------------------------------------------------*/
void EEPROM_ByteWrite(unsigned int Address, unsigned char Data)
{
  unsigned char adr_hi;
  unsigned char adr_lo;

  while (UCB0STATW & UCBUSY);                // wait until I2C module has finished all operations.

  adr_hi = Address >> 8;                    // calculate high byte
  adr_lo = Address & 0xFF;                  // and low byte of address

  I2CBufferArray[2] = adr_hi;               // Low byte address.
  I2CBufferArray[1] = adr_lo;               // High byte address.
  I2CBufferArray[0] = Data;
  PtrTransmit = 2;                          // set I2CBufferArray Pointer

  I2CWriteInit();
  UCB0CTLW0 |= UCTR | UCTXSTT;              // start condition generation I2C communication is started

  __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0 w/ interrupts
  UCB0CTLW0 |= UCTXSTP;                      // I2C stop condition
  while(UCB0CTLW0 & UCTXSTP);                // Ensure stop condition got sent
}
/*----------------------------------------------------------------------------*/
// Description:
//   Current Address Read Operation. Data is read from the EEPROM. The current
//   address from the EEPROM is used.
/*----------------------------------------------------------------------------*/
unsigned char EEPROM_CurrentAddressRead(void)
{
  while(UCB0STATW & UCBUSY);                 // wait until I2C module has
                                            // finished all operations
  I2CReadInit();

  UCB0CTLW0 |= UCTXSTT;                      // I2C start condition
  while(UCB0CTLW0 & UCTXSTT);                // Start condition sent?
  UCB0CTLW0 |= UCTXSTP;                      // I2C stop condition
  __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0 w/ interrupts
  while(UCB0CTLW0 & UCTXSTP);                // Ensure stop condition got sent
  return I2CBuffer;
}

/*----------------------------------------------------------------------------*/
// Description:
//   Random Read Operation. Data is read from the EEPROM. The EEPROM
//   address is defined with the parameter Address.
/*----------------------------------------------------------------------------*/
unsigned char EEPROM_RandomRead(unsigned int Address)
{
  unsigned char adr_hi;
  unsigned char adr_lo;

  while (UCB0STATW & UCBUSY);                // wait until I2C module has finished all operations

  adr_hi = Address >> 8;                    // calculate high byte
  adr_lo = Address & 0xFF;                  // and low byte of address

  I2CBufferArray[1] = adr_hi;               // store single bytes that have to
  I2CBufferArray[0] = adr_lo;               // be sent in the I2CBuffer.
  PtrTransmit = 1;

  // Write Address first
  I2CWriteInit();
  UCB0CTLW0 |= UCTXSTT;                      // start condition generation => I2C communication is started
  __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0 w/ interrupts
  // Read Data byte
  I2CReadInit();
  UCB0CTLW0 |= UCTXSTT;                      // I2C start condition
  while(UCB0CTLW0 & UCTXSTT);                // Start condition sent?
  UCB0CTLW0 |= UCTXSTP;                      // I2C stop condition
  __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0 w/ interrupts
  while(UCB0CTLW0 & UCTXSTP);                // Ensure stop condition got sent
  return I2CBuffer;
}
/*----------------------------------------------------------------------------*/
// Description:
//   Acknowledge Polling. The EEPROM will not acknowledge if a write cycle is
//   in progress. It can be used to determine when a write cycle is completed.
/*----------------------------------------------------------------------------*/
void EEPROM_AckPolling(void)
{
  while (UCB0STATW & UCBUSY);                // wait until I2C module has
                                            // finished all operations
  do
  {
    UCB0STATW = 0x00;                        // clear I2C interrupt flags
    UCB0CTLW0 |= UCTR;                       // I2CTRX=1 => Transmit Mode (R/W bit = 0)
    UCB0CTLW0 &= ~UCTXSTT;
    UCB0CTLW0 |= UCTXSTT;                    // start condition is generated
    while(UCB0CTLW0 & UCTXSTT)               // wait till I2CSTT bit was cleared
    {
      if(!(UCNACKIFG & UCB0STATW))           // Break out if ACK received
        break;
    }
    UCB0CTLW0 |= UCTXSTP;                    // stop condition is generated after
                                            // slave address was sent => I2C communication is started
    while (UCB0CTLW0 & UCTXSTP);             // wait till stop bit is reset
    __delay_cycles(500);                    // Software delay
  }while(UCNACKIFG & UCB0STATW);
}

//------------------------------------------------------------------------------
// The USCIAB0_ISR is structured such that it can be used to transmit any
// number of bytes by pre-loading TXByteCtr with the byte count.
//------------------------------------------------------------------------------
// USCI_B0 interrupt service routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
#else
#error Compiler not supported!
#endif
{
    switch (__even_in_range(UCB0IV, 30))
    {
        case USCI_NONE: break;                        	// No interrupts
        case USCI_I2C_UCALIFG: break;                 	// ALIFG
        case USCI_I2C_UCNACKIFG: break;               	// NACKIFG
        case USCI_I2C_UCSTTIFG: break;                	// STTIFG
        case USCI_I2C_UCSTPIFG: break;                	// STPIFG
        case USCI_I2C_UCRXIFG3: break;                	// RXIFG3
        case USCI_I2C_UCTXIFG3: break;                	// TXIFG3
        case USCI_I2C_UCRXIFG2: break;                	// RXIFG2
        case USCI_I2C_UCTXIFG2: break;                	// TXIFG2
        case USCI_I2C_UCRXIFG1: break;                	// RXIFG1
        case USCI_I2C_UCTXIFG1: break;                	// TXIFG1
        case USCI_I2C_UCRXIFG0: 						            // RXIFG0
            I2CBuffer = UCB0RXBUF;                  	  // store received data in buffer
            __bic_SR_register_on_exit(LPM0_bits);   	  // Exit LPM0
        	break;
        case USCI_I2C_UCTXIFG0:                       	// TXIFG0
            UCB0TXBUF = I2CBufferArray[PtrTransmit];	  // Load TX buffer
            PtrTransmit--;                          	  // Decrement TX byte counter
            if(PtrTransmit < 0)
            {
              while(!(UCB0IFG & UCTXIFG0));
              UCB0IE &= ~UCTXIE0;                     	// disable interrupts.
              UCB0IFG &= ~UCTXIFG0;                   	// Clear USCI_B0 TX int flag
              __bic_SR_register_on_exit(LPM0_bits); 	  // Exit LPM0
            }
            break;
        case USCI_I2C_UCBCNTIFG: break;               	// CNTIFG
        case USCI_I2C_UCCLTOIFG: break;               	// LTOIFG
        case USCI_I2C_UCBIT9IFG: break;               	// BIT9IFG
        default: break;
    }
}

main.c

	//WDTCTL = WDTPW | WDTHOLD;										// Stop watchdog timer	sysInit(); 
	WDTCTL = WDTPW | WDTSSEL__ACLK | WDTIS_3;						// Enable watchdog timer: ACLK, 4min and 16 seconds
	initSystem();  // Init i2c UART and other
    int eepromAddr = 0;
    char eepromCont = 0;

    for(eepromAddr = 0; eepromAddr < 256; eepromAddr++) {
    	EEPROM_ByteWrite(eepromAddr, ++eepromCont);
    	EEPROM_AckPolling();
    	WDTCTL = WDTPW | WDTSSEL__ACLK | WDTIS_3 | WDTCNTCL;
    }

    eepromAddr = 0;
    while (1) {
        eepromCont = EEPROM_RandomRead(eepromAddr);
        eepromAddr++;
        if(eepromAddr > 256) {
            eepromAddr = 0;
        }
        while (!(UCA1IFG&UCTXIFG)); // USCI_A0 TX buffer ready?
        UCA1TXBUF = eepromAddr;
        while (!(UCA1IFG&UCTXIFG)); // USCI_A0 TX buffer ready?
        UCA1TXBUF = eepromCont;
        while (!(UCA1IFG&UCTXIFG)); // USCI_A0 TX buffer ready?
        UCA1TXBUF = 0x0D;
        WDTCTL = WDTPW | WDTSSEL__ACLK | WDTIS_3 | WDTCNTCL;
        __bis_SR_register(LPM3_bits | GIE);
    }

  • Hi Zhiyong,

    There are no current device errata that could be involved and I do not see a direct reason as to why your code would not work but have noticed that you are porting a USCI example to eUSCI. The slight differences between these two modules could be causing an issue, I would recommend looking into the SLAA522 App Report: www.ti.com/.../slaa522a.pdf

    You should also reference the MSP430F672x I2C code examples and discern if there are any noticeable differences. Otherwise I would suggest that you use an oscilloscope or logic analyzer to view the I2C lines and see if the data being sent to the EEPROM is as intended.

    Regards,
    Ryan
  • Hi Ryan,

    thanks for your reply and pointers. So far the problem is isolated to the acknowledge_polling function. If I replace the polling function with a hard coded 5 ms deplay, then writing is no more a problem.

    Regarding SLAA522, I believe it needs some update. On page 3, it states that UCNACKIFG is not automatically cleared upon receiving start condition in eUCSI, but per MSP430F67XX user manual, this is no longer correct.

    Regards,
    Zhiyong
  • Hi Zhiyong,

    I'm glad to hear that you found a solution to the issue. Both documents state that UCNACKIFG is cleared by a start condition in the eUSCI. A start condition is never received in master mode.

    Regards,
    Ryan

**Attention** This is a public forum