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.

EEPROM sometimes goes wrong and reads garbage!

The following code is for EEPROM that I used in my MSP code.

InitI2C(SlaveAddress);

EEPROM_ByteWrite(0x0001, '&');

val_read[0] = EEPROM_RandomRead(0x0000);

where EEPROM_ByteWrite is as follows.

void EEPROM_ByteWrite(unsigned int Address, unsigned char Data)
{
  unsigned char adr_hi;
  unsigned char adr_lo;
  
  I2CBuffer = '0';

  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 = 1;                          // 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
}

And EEPROM_RandomRead() is as follows.

unsigned char EEPROM_RandomRead(unsigned int Address)
{
  unsigned char adr_hi;
  unsigned char adr_lo;
  
  I2CBuffer = '0';

  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;               // store single bytes that have to
  I2CBufferArray[1] = 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
  UCB1CTL1 |= UCTXSTP;
  while(UCB1CTL1 & UCTXSTP);                // Ensure stop condition got sent
  return I2CBuffer;
}

And  InitI2C() as follows.

void InitI2C(unsigned char eeprom_i2c_address)
{
  I2C_PORT_SEL |= SDA_PIN + SCL_PIN;        // Assign I2C pins to USCI_B1

  // Recommended initialisation 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 = 0x01A5;                       // 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
  };
}

So basically when I read back is sometimes 0x26 which is '&' , sometimes 0x01, and sometimes 0x00!!!  Do I have timing issue?!
Having said this, I do not have any issue with this in an standalone program with only EEPROM code. As long as I implement this code to my firmware for the MSP, it starts to act weird and after a while it crashes down!
So, I am sure something in the code interrupts my EEPROM code!
What do you think?

  • I guess I have conflict between interrupt vector in calibration with EEPROM!
  • Hello CaEngineer,

    Can you post you ISR code as well? Unless you are using I2C connected to multiple devices and interupt yourself before finishing a transaction, Id o not think you are having a timing issue. I don't know what EEPROM you are using but I would check to see if it supports clock stretching. Another thing to check is your pullup values to see if they are strong enough for your board layout. If your transactions are not clean and square enough, it could cause issues.

    also, form the code above, it seems you are writing to address 0x0001 in the EEPROM, but reading from 0x0000. I would check this in your code and ensure you are reading from the same address you are writing to.

    Regards,
    JH
  • Here is the code that I use for both communication and I2C.

    ISR(USCIAB1TX, serial_tx_interrupt1)
    {
        UCA1TXBUF = tx_msg[1].buf.uint8[tx_msg[1].ptr++];
        if (tx_msg[1].ptr >= tx_msg[1].len)
        {
            /* Stop transmission */
            UC1IE &= ~UCA1TXIE;
            tx_msg[1].ptr = 0;
            tx_msg[1].len = 0;
        }
        
        if(UCB1TXIFG &  UC1IFG)
      {
        UCB1TXBUF = I2CBufferArray[PtrTransmit];// Load TX buffer
        PtrTransmit--;                          // Decrement TX byte counter
        if(PtrTransmit < 0)
        {
          while(!( UC1IFG & UCB1TXIFG));
           UC1IE &= ~UCB1TXIE;                     // disable interrupts.
           UC1IFG &= ~UCB1TXIFG;                   // Clear USCI_B1 TX int flag
          __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
        }
      }
      else if(UCB1RXIFG &  UC1IFG)
      {
        I2CBuffer = UCB1RXBUF;                  // store received data in buffer
        __bic_SR_register_on_exit(LPM0_bits);   // Exit LPM0
      }
        
        
    }

    I checked today and I realized that if I just stop the communication and put the EEPROM interrupt I still have problem and I still read sometimes the character/s that I don't want and it gets worst and it is crashed! (somewhere it is stopped!)

    Here is only the ISR for EEPROM.

    /*---------------------------------------------------------------------------*/
    /*  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 [USCIAB1TX_VECTOR] void TX_ISR_I2C(void)
    #else
        #pragma vector=USCIAB1TX_VECTOR
     __interrupt void TX_ISR_I2C(void)
    #endif
    {
      if(UCB1TXIFG &  UC1IFG)
      {
        UCB1TXBUF = I2CBufferArray[PtrTransmit];// Load TX buffer
        PtrTransmit--;                          // Decrement TX byte counter
        if(PtrTransmit < 0)
        {
          while(!( UC1IFG & UCB1TXIFG));
           UC1IE &= ~UCB1TXIE;                     // disable interrupts.
           UC1IFG &= ~UCB1TXIFG;                   // Clear USCI_B1 TX int flag
          __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
        }
      }
      else if(UCB1RXIFG &  UC1IFG)
      {
        I2CBuffer = UCB1RXBUF;                  // store received data in buffer
        __bic_SR_register_on_exit(LPM0_bits);   // Exit LPM0
      }
    }

    Regarding the EEPROM, we used the following.

    www.onsemi.com/.../CAT24C03-D.PDF

    But the address, I am reading from the right address. You need to write into the address 0x0001 and read of from one bit less which is 0x0000. And I believe that I have right pull-up resistors because the standalone code is working fine.

    So, as it is crashed after a while, I believe my interrupts interrupt the code and mess with EEPROM!

    But I don't know how to put my EEPROM functions in order not to crash!

    Any idea?

  • Here is the thing. With the following code, I have not the code crashed but it is reading from the bit which is the neighboring bit and &!
    So, sometimes & and sometimes P!

    InitI2C(SlaveAddress); EEPROM_ByteWrite(0x0003, '&'); EEPROM_AckPolling(); read_val[0] = EEPROM_RandomRead(0x0002);

    How can I get rid of P?!

  • And not to mention that sometimes '.' which I don't know where it comes from!

    And now no '.' I changed the address I have two following characters.

  • Now it is crashed again!
  • CaEnginner,

    I'm still seeing you are writing to a address "X" in the eeprom, but reading from address "X-1" of the eeprom. Shouldn't' you read and write to same address in eeprom to expect the same answer?

    Regards,
    JH
  • No! I need to read from one address less! I found out my problem! I had defined my saving register as unsigned! I remove that and now it is working!

    But I believe I still need to think a way how to communicate with the calibration part. Because EEPROM and calibration share the same Interrupt vector!

    What do you think?

  • CaEngineer,

    I'm glad you were able to solve your issue. As for your other question, it is okt hat they share the same interrupt vector for its just I2C transactions. If you want to ensure that you are not initiating a transaction with say the calibration part while you are in the middle of an exchange with the EEPROM, then a flag check to a variable of your choosing at the beginning of each transaction might solve your issue.

    Regards,
    JH
  • JH,

    I used switches in order to communicate with the EEPROM and interrupt the calibration program. However, a better solution might be needed. Speaking of the flag checking, sounds like a plan but they already have flags to use and check for sending and receiving. So, switches solutions are safer. But I still need to see what we will next!

**Attention** This is a public forum