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.

Interfacing eeprom via I2C

Other Parts Discussed in Thread: MSP430F6638, MSP430F67791

Hi all,

I have problem with interfacing eeprom via I2C.

I used MSP430f6638 and eeprom 24LC512.

I tried to do as document Sla208a (MSP430F2xx and external EEPROM 24xx128), but when I read data from eeprom, it always receives 0xFF.


#include "msp430.h"
#include "common.h"
#include "i2c_eeprom.h"
#include "hal.h"

#define MAXPAGEWRITE 64

int PtrTransmit;
unsigned char I2CBufferArray[66];
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

                                                                                                     // Recommended initialization steps of I2C module as shown in User Guide:
UCB1CTL1 |= UCSWRST;                                                     // Enable SW reset
UCB1CTL0 = UCMST + UCMODE_3 + UCSYNC;            // I2C Master, synchronous mode
UCB1CTL1 = UCSSEL_2 + UCTR + UCSWRST;             // Use SMCLK, TX mode, keep SW reset
UCB1BR0 = SCL_CLOCK_DIV;                                           // fSCL = SMCLK/12 = ~100kHz
UCB1BR1 = 0;
UCB1I2CSA = eeprom_i2c_address;                                 // define Slave Address
                                                                                                    // In this case the Slave Address
                                                                                                    // defines the control byte that is
                                                                                                    // sent to the EEPROM.
UCB1I2COA = 0x48;                                                               // own address.
UCB1CTL1 &= ~UCSWRST;                                                // Clear SW reset, resume operation

if (UCB1STAT & 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
};
}

/*---------------------------------------------------------------------------*/
// Description:
// Initialization of the I2C Module for Write operation.
/*---------------------------------------------------------------------------*/


void I2CWriteInit(void)
{
UCB1CTL1 |= UCTR;                                                               // UCTR=1 => Transmit Mode (R/W bit = 0)
UCB1IFG &= UCTXIFG;
UCB1IE &= ~UCRXIE;                                                             // disable Receive ready interrupt
UCB1IE |= UCTXIE;                                                                 // enable Transmit ready interrupt
}

/*----------------------------------------------------------------------------*/
// Description:
// Initialization of the I2C Module for Read operation.
/*----------------------------------------------------------------------------*/


void I2CReadInit(void)
{
UCB1CTL1 &= ~UCTR;                                                           // UCTR=0 => Receive Mode (R/W bit = 1)
UCB1IFG &= ~UCRXIFG;
UCB1IE &= ~UCTXIE;                                                              // disable Transmit ready interrupt
UCB1IE |= UCRXIE;                                                                 // 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 (UCB1STAT & 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();
UCB1CTL1 |= UCTXSTT;                                        // start condition generation
// => I2C communication is started
__bis_SR_register(LPM0_bits + GIE);                  // Enter LPM0 w/ interrupts
UCB1CTL1 |= UCTXSTP;                                        // I2C stop condition
while(UCB1CTL1 & UCTXSTP);                             // Ensure stop condition got sent
}

/*----------------------------------------------------------------------------*/
// Description:
// Page Write Operation. The communication via the I2C bus with an EEPROM
// (24xx65) is realized. A data byte is written into a user defined address.
/*----------------------------------------------------------------------------*/


void EEPROM_PageWrite(unsigned int StartAddress, unsigned char * Data, unsigned int Size)
{
volatile unsigned int i = 0;
volatile unsigned char counterI2cBuffer;
unsigned char adr_hi;
unsigned char adr_lo;
unsigned int currentAddress = StartAddress;
unsigned int currentSize = Size;
unsigned int bufferPtr = 0;
unsigned char moreDataToRead = 1;

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

// Execute until no more data in Data buffer
while(moreDataToRead)
{
adr_hi = currentAddress >> 8;                                          // calculate high byte
adr_lo = currentAddress & 0xFF;                                      // and low byte of address

                                                                                                // Chop data down to 64-byte packets to be transmitted at a time
                                                                                                // Maintain pointer of current startaddress
if(currentSize > MAXPAGEWRITE)
{
bufferPtr = bufferPtr + MAXPAGEWRITE;
counterI2cBuffer = MAXPAGEWRITE - 1;
PtrTransmit = MAXPAGEWRITE + 1; // set I2CBufferArray Pointer
currentSize = currentSize - MAXPAGEWRITE;
currentAddress = currentAddress + MAXPAGEWRITE;

// Get start address
I2CBufferArray[MAXPAGEWRITE + 1] = adr_hi; // High byte address.
I2CBufferArray[MAXPAGEWRITE] = adr_lo; // Low byte address.
}
else
{
bufferPtr = bufferPtr + currentSize;
counterI2cBuffer = currentSize - 1;
PtrTransmit = currentSize + 1; // set I2CBufferArray Pointer.
moreDataToRead = 0;
currentAddress = currentAddress + currentSize;

// Get start address
I2CBufferArray[currentSize + 1] = adr_hi; // High byte address.
I2CBufferArray[currentSize] = adr_lo; // Low byte address.
}

// Copy data to I2CBufferArray
unsigned char temp;
for(i ; i < bufferPtr ; i++)
{
temp = Data[i]; // Required or else IAR throws a
// warning [Pa082]
I2CBufferArray[counterI2cBuffer] = temp;
counterI2cBuffer--;
}

I2CWriteInit();
UCB1CTL1 |= UCTXSTT; // start condition generation
// => I2C communication is started
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0 w/ interrupts
UCB1CTL1 |= UCTXSTP; // I2C stop condition
while(UCB1CTL1 & UCTXSTP); // Ensure stop condition got sent

EEPROM_AckPolling(); // Ensure data is written in EEPROM
}
}

/*----------------------------------------------------------------------------*/
// 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(UCB1STAT & UCBUSY); // wait until I2C module has
// finished all operations
I2CReadInit();

UCB1CTL1 |= UCTXSTT; // I2C start condition
while(UCB1CTL1 & UCTXSTT); // Start condition sent?
UCB1CTL1 |= UCTXSTP; // I2C stop condition
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0 w/ interrupts
while(UCB1CTL1 & 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 (UCB1STAT & 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; // set I2CBufferArray Pointer

// Write Address first
I2CWriteInit();
UCB1CTL1 |= UCTXSTT; // start condition generation
// => I2C communication is started
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0 w/ interrupts

// Read Data byte
I2CReadInit();

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

/*----------------------------------------------------------------------------*/
// Description:
// Sequential Read Operation. Data is read from the EEPROM in a sequential
// form from the parameter address as a starting point. Specify the size to
// be read and populate to a Data buffer.
/*----------------------------------------------------------------------------*/
void EEPROM_SequentialRead(unsigned int Address , unsigned char * Data , unsigned int Size)
{
unsigned char adr_hi;
unsigned char adr_lo;
unsigned int counterSize;

while (UCB1STAT & 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; // set I2CBufferArray Pointer

// Write Address first
I2CWriteInit();
UCB1CTL1 |= UCTXSTT; // start condition generation
// => I2C communication is started
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0 w/ interrupts

// Read Data byte
I2CReadInit();

UCB1CTL1 |= UCTXSTT; // I2C start condition
while(UCB1CTL1 & UCTXSTT); // Start condition sent?

for(counterSize = 0 ; counterSize < Size ; counterSize++)
{
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0 w/ interrupts
Data[counterSize] = I2CBuffer;
}
UCB1CTL1 |= UCTXSTP; // I2C stop condition
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0 w/ interrupts
while(UCB1CTL1 & UCTXSTP); // Ensure stop condition got sent
}

/*----------------------------------------------------------------------------*/
// 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 (UCB1STAT & UCBUSY); // wait until I2C module has
// finished all operations
do
{
UCB1STAT = 0x00; // clear I2C interrupt flags
UCB1CTL1 |= UCTR; // I2CTRX=1 => Transmit Mode (R/W bit = 0)
UCB1CTL1 &= ~UCTXSTT;
UCB1CTL1 |= UCTXSTT; // start condition is generated
while(UCB1CTL1 & UCTXSTT) // wait till I2CSTT bit was cleared
{
if(!(UCB1STAT & UCNACKIFG)) // Break out if ACK received
break;
}
UCB1CTL1 |= UCTXSTP; // stop condition is generated after
// slave address was sent => I2C communication is started
while (UCB1CTL1 & UCTXSTP); // wait till stop bit is reset
__delay_cycles(500); // Software delay
}while(UCB1STAT & UCNACKIFG);
}

/*---------------------------------------------------------------------------*/
/* Interrupt Service Routines */
/* Note that the Compiler version is checked in the following code and */
/* depending of the Compiler Version the correct Interrupt Service */
/* Routine definition is used. */
#if __VER__ < 200
interrupt [USCI_B1_VECTOR] void TX_ISR_I2C(void)
#else
#pragma vector=USCI_B1_VECTOR
__interrupt void TX_ISR_I2C(void)
#endif
{
if(UCB1IFG & UCTXIFG)
{
handleLed(LED0, LED_TOGGLE);
UCB1TXBUF = I2CBufferArray[PtrTransmit];// Load TX buffer
PtrTransmit--; // Decrement TX byte counter
if(PtrTransmit < 0)
{
while(!(UCB1IFG & UCTXIFG));
UCB1IE &= ~UCTXIE; // disable interrupts.
UCB1IFG &= ~UCTXIFG; // Clear USCI_B0 TX int flag
__bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
}
}
else if(UCB1IFG & UCRXIFG)
{
handleLed(LED1, LED_TOGGLE);
I2CBuffer = UCB1RXBUF; // store received data in buffer
__bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
}
}

  • The main is here:

    #include "msp430.h"

    #include "i2c_eeprom.h"

    #define SlaveAddress 0x50   

    unsigned char read_val[150];
    unsigned char write_val[150];
    unsigned int address;

    int main(void)
    {
    unsigned int i;

    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer

    InitI2C(SlaveAddress); 


    EEPROM_ByteWrite(0x0000,0x12);
    EEPROM_AckPolling(); // Wait for EEPROM write cycle
    // completion
    EEPROM_ByteWrite(0x0001,0x34);
    EEPROM_AckPolling(); // Wait for EEPROM write cycle
    // completion
    EEPROM_ByteWrite(0x0002,0x56);
    EEPROM_AckPolling(); // Wait for EEPROM write cycle
    // completion
    EEPROM_ByteWrite(0x0003,0x78);
    EEPROM_AckPolling(); // Wait for EEPROM write cycle
    // completion
    EEPROM_ByteWrite(0x0004,0x9A);
    EEPROM_AckPolling(); // Wait for EEPROM write cycle
    // completion
    EEPROM_ByteWrite(0x0005,0xBC);
    EEPROM_AckPolling(); // Wait for EEPROM write cycle
    // completion
    EEPROM_ByteWrite(0x0007,14);
    EEPROM_AckPolling(); // Wait for EEPROM write cycle
    // completion
    read_val[0] = EEPROM_RandomRead(0x0000); // Read from address 0x0000
    read_val[1] = EEPROM_CurrentAddressRead();// Read from address 0x0001
    read_val[2] = EEPROM_CurrentAddressRead();// Read from address 0x0002
    read_val[3] = EEPROM_CurrentAddressRead();// Read from address 0x0003
    read_val[4] = EEPROM_CurrentAddressRead();// Read from address 0x0004
    read_val[5] = EEPROM_CurrentAddressRead();// Read from address 0x0005


    // Fill write_val array with counter values
    for(i = 0 ; i <= sizeof(write_val) ; i++)
    {
    write_val[i] = i;
    }

    address = 0x0000; // Set starting address at 0
    // Write a sequence of data array
    EEPROM_PageWrite(address , write_val , sizeof(write_val));
    // Read out a sequence of data from EEPROM
    EEPROM_SequentialRead(address, read_val , sizeof(read_val));

    __bis_SR_register(LPM4);
    __no_operation();
    }

  • I know that this is an old thread but still I am facing the same problem so I thought of posting in the same thread. Have you got this working? I have tried the same code and I also get the same response i.e I always receive 0xFF...

    I am using MSP430F67791 and USCIB0 .

  • Hi Imran Arshad,

     As per the code you have used you need to check whether you are getting ack from the slave,  debug yourself in interrupt function by putting break points, also confirm you have connected your slave device through pull up resistors.

  • Thanks for the reply... How to check for Ack from slave? I have connected my SDA and SCL lines using a 10k Pull Up resistors. This is how I am testing the code... In the main I am doing.

    InitI2C(SlaveAddress);  //0x50
    EEPROM_ByteWrite(0x0022,0x12);
    EEPROM_AckPolling(); // Wait for EEPROM write cycle
    read_val[0] = EEPROM_RandomRead(0x0022); // Read from address 0x0000

    I have put breakpoint in the ISR and this is what I am getting. Each line tells what was the value of (__even_in_range(UCB0IV, 30) for each breakpoint in sequence.

    USCI_I2C_UCTXIFG0   //Transmit 0x00

    USCI_I2C_UCTXIFG0   //Transmit 0x22

    USCI_I2C_UCTXIFG0   //Transmit 0x12

    USCI_I2C_UCSTPIFG  

    USCI_I2C_UCTXIFG0  //Don't know why this one got called.

    USCI_I2C_UCSTPIFG

    USCI_I2C_UCTXIFG0  //Transmit 0x00 for read

    USCI_I2C_UCTXIFG0  //Transmit 0x22 for read

    USCI_I2C_UCSTPIFG

    USCI_I2C_UCRXIFG0  //Receive Event Fired and in the UCB0RXBUF I got 0xff

    USCI_I2C_UCSTPIFG

  • After the start byte is sent (which will in transmit mode only happen after the first byte to transmit has been written to TXBUF), the USCI will check the ACK answer from the addressed slave. Then TXSTP will clear and the USCI will either set UCNACKIFG to indicate a NACK (no answer) form the slave, or start sending the byte in TXBUF (and request for the next byte to send, with TXIFG).

    Btw: in transmit mode, UCTXSTP has to be set on TX interrupt if nothing more is to send. Not after you just wrote the last byte to TXBUF.
    In receive mode, it needs to be set before the last byte has been received (or else another byte will be received)
    Timing the chain of actions on a double-buffered send/receive is a bit tricky. :)

  • Thanks for the reply... Although I can't get most of it as I am a newbie to MSP and EEPROM programming.

    I have hooked up a scope on the SCL and SDA lines to see what is happening there....

    Here is my test code with the above mentioned functions...

    InitI2C(SlaveAddress);
    EEPROM_ByteWrite(0x00AA,0x41);
    EEPROM_AckPolling(); // Wait for EEPROM write cycle

    read_val[0] = EEPROM_RandomRead(0x00AA); // Read from address 0x00AA

    Here is what I get on the scope.... I have taken two pics as in a single picture the waves were too small and not readable...

    Picture 1:

    I can see the Start Condition ( SDA High to Low when SCL is high)

    After the start condition I can see the Start Byte (Address)... I can see it sends out 10100000. It is also expected as the slave address is 0x50 which 1010000 ad last 0 is added for Write operation.

    After this I can see a small spike which I think is the ACK as the controller sets the SDA open and it tries to go to 3.3V (Due to pull Up) but the slave (EEPROM) forces it down which is ACK....

    After the spike I can see 0x00 send as the higher byte of Address. Again this is what is expected..

    Again an ACK is received and after that the Lower byte of address is transmitted. I can see it as 10101010 which is 0xAA which is again correct.

    But what happens after that I don't know... Shouldn't there be 0x41 (The data byte) on the line after the ACK?

    What we can see is that the SDA and SCL are both up for a short time and then the start byte (0x50) is send again. Also I don't know what happens in the second picture as well.... Will you kindly look at my pictures for some time and I am pretty sure you will be able to point out the mistake very soon.....

    Picture 1:

    [url=https://flic.kr/p/p4qZvJ][img]https://farm4.staticflickr.com/3837/15135031312_30f56d55d8_h.jpg[/img][/url][url=https://flic.kr/p/p4qZvJ]L-1[/url] by [url=https://www.flickr.com/people/64034875@N05/]imarshad[/url], on Flickr

    Picture 2:

    [url=https://flic.kr/p/p4cEwg][img]https://farm6.staticflickr.com/5564/15132430971_3b109e5d9f_h.jpg[/img][/url][url=https://flic.kr/p/p4cEwg]L-2[/url] by [url=https://www.flickr.com/people/64034875@N05/]imarshad[/url], on Flickr

  • Thanks for the reply... Although I can't get most of it as I am a newbie to MSP and EEPROM programming.

    I have hooked up a scope on the SCL and SDA lines to see what is happening there....

    Here is my test code with the above mentioned functions...

    InitI2C(SlaveAddress);
    EEPROM_ByteWrite(0x00AA,0x41);
    EEPROM_AckPolling(); // Wait for EEPROM write cycle

    read_val[0] = EEPROM_RandomRead(0x00AA); // Read from address 0x00AA

    Here is what I get on the scope.... I have taken two pics as in a single picture the waves were too small and not readable...

    Picture 1:

    I can see the Start Condition ( SDA High to Low when SCL is high)

    After the start condition I can see the Start Byte (Address)... I can see it sends out 10100000. It is also expected as the slave address is 0x50 which 1010000 ad last 0 is added for Write operation.

    After this I can see a small spike which I think is the ACK as the controller sets the SDA open and it tries to go to 3.3V (Due to pull Up) but the slave (EEPROM) forces it down which is ACK....

    After the spike I can see 0x00 send as the higher byte of Address. Again this is what is expected..

    Again an ACK is received and after that the Lower byte of address is transmitted. I can see it as 10101010 which is 0xAA which is again correct.

    But what happens after that I don't know... Shouldn't there be 0x41 (The data byte) on the line after the ACK?

    What we can see is that the SDA and SCL are both up for a short time and then the start byte (0x50) is send again. Also I don't know what happens in the second picture as well.... Will you kindly look at my pictures for some time and I am pretty sure you will be able to point out the mistake very soon.....

    Picture 1:

    <a href="https://www.flickr.com/photos/64034875@N05/15135031312" title="L-1 by imarshad, on Flickr"><img src="https://farm4.staticflickr.com/3837/15135031312_30f56d55d8_h.jpg" width="1600" height="1198" alt="L-1"></a>

    Picture 2:

    <a href="https://www.flickr.com/photos/64034875@N05/15132430971" title="L-2 by imarshad, on Flickr"><img src="https://farm6.staticflickr.com/5564/15132430971_3b109e5d9f_h.jpg" width="1600" height="1195" alt="L-2"></a>

  • I don't know why the flickrimages posted above are not working... Here is the link

    Picture 1:

    https://www.flickr.com/photos/64034875@N05/15135031312

    Picture 2:

    https://www.flickr.com/photos/64034875@N05/15132430971

  • Hmmm, I’d agree that after the address, there should be the data byte. Sure that the slave wants the high byte of the address first?
    Honestly, the x- resolution of the images isn’t good enough, so I have problems to really figure out what data point belongs to which clock position. At least in the browser.
    Also, the images show that your signal rise times are quite long. I’d suggest a stronger pull-up on SDA and SCL, to give a cleaner signal (you don’t know the trigger point of the slave inputs.
    For a starter, I would also chose a lower I2C clock speed, it makes analyzing the signal easier.

    For your ISR:
    You shouldn’t clear TXIFG, you already cleared TXIE, that’s enough.
    Also, you shouldn’t wait in your ISR for the last byte being sent. This is a waste of energy.

    Rewrite it like this:
    if(PtrTransmit >=0)
    {
      UCB1TXBUF = I2CBufferArrray[PtrTransmit--];
    }
    else
    {
      UCB1IE&=~UCTXIE;
      UCB1CTL1|= UCTXSTP;
      __bic_SR_register_on_exit(LPM0_bits);
    }

    This way, you’ll get another interrupt right when the last byte has state sending. You then can set UCTXSTP and exit LPM.
    Main will still waste ~10 I2C clock cycles busy-waiting for UCTXSTP to clear, but you’re not wasting another 9 clock cycles in the ISR. :)

    Checking the flow chart, it seems that UCTXSTP must be set when the last byte has started sending, so maybe there is a race condition if UTXSTP is set right after TXIFG requests the next byte. The additional interrupt latency maybe solves this problem.
    Well, maybe I’m too missing something in the code.

    One thing I noticed: you don’t handle a NACK, so if the slave doesn’t respond, your whole code is stalled.

**Attention** This is a public forum