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.

MSP430FR5994: I2C EEPROM with MSP430FR5994

Part Number: MSP430FR5994
Other Parts Discussed in Thread: MSP430F5529

Hello.

I am working with MSP430FR5994 and I2C EEPROM. I have some difficulties to migrate the I2Croutines.c that works in parts.

After writing to the EEPROM, excessive battery consumption occurs, something that does not happen when reading the EEPROM.

Using another MSP430F5529, both writing and reading work correctly without changing battery consumption.

Could anyone guide me or if they know the MSP430FR5994 registers to correct the attached code?

I appreciate the colaboration of everyone!

MSP430FR5994 I2C Pin Configuration
////////////////////////////////////////////////////////////////////////////////
//         Definição da Porta P5 como Serial I2C das Memórias EEPROMS         //
////////////////////////////////////////////////////////////////////////////////
P5OUT = 0;                                                                   // Reseta Todos os Pinos da Porta P5
P5DIR = 0xFF;                                                                // Define Todos os Pinos da Porta P5 como Saídas
P5OUT &=~ 0xFF;                                                              // Inicializa Todos os Pinos da Porta P5 com Nível Lógico "0" Baixo
P5SEL0 |= BIT0 | BIT1;                                                       // Seleciona o Pino da Porta P5.0 UCB1SDA e P5.1 UCB1SCL como Barramento I2C                                                   
P5SEL1 &= ~(BIT0 | BIT1);                                                    // Seleciona Módulo Primário Conforme Tabela I/O Function Selection
// Configuração do Barramento I2C das Memórias EEPROMs
UCB1CTLW0 = UCSWRST;                                                         // Enable SW reset
UCB1CTLW0 |= UCMODE_3 | UCMST | UCSSEL__SMCLK;                               // I2C Master, synchronous mode
UCB1BRW = 160;                                                               // fSCL = SMCLK/160 = 100 kHz
UCB1CTLW1 |= 0x40;                                                           // Use SMCLK, TX mode, keep SW reset
UCB1CTLW1 |= UCASTP_2;
UCB1CTLW0 &= ~UCSWRST;                                                       // Clear SW reset, resume operation
//UCB1IE |= UCNACKIE + UCALIE + UCSTPIE + UCSTTIE + UCTXIE0 + UCRXIE0 | UCBCNTIE | USCI_I2C_UCCLTOIFG; // Interrupts Enable
UCB1IE |= UCNACKIE + UCTXIE0 + UCRXIE0; // Interrupts Enable
  
   
I2Croutines.h

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

#include "I2Croutines.h"
#include "delay.h"

#define     MAXPAGEWRITE   33

int PtrTransmit;
char I2CBufferArray[66];
char I2CBuffer;

/*----------------------------------------------------------------------------*/
// Description:
//   Initialization of the I2C Module
/*----------------------------------------------------------------------------*/
void InitI2C(char eeprom_i2c_address)
{
  while (UCB1STAT & UCBUSY);                                                    // wait until I2C module has
                                                                                // finished all operations.
  // Direciona para o Endereço de Memória EEPROM Correto para o Uso
  UCB1I2CSA  = eeprom_i2c_address;                                              // define Slave Address
                                                                                // In this case the Slave Address
                                                                                // defines the control byte that is
                                                                                // sent to the EEPROM.
}

/*---------------------------------------------------------------------------*/
// 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, char Data)
{
  char adr_hi;
  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(LPM3_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, char * Data, unsigned int Size)
{
  volatile unsigned int i = 0;
  volatile char counterI2cBuffer;
  char adr_hi;
  char adr_lo;
  unsigned int currentAddress = StartAddress;
  unsigned int currentSize = Size;
  unsigned int bufferPtr = 0;
  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
    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(LPM3_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.
/*----------------------------------------------------------------------------*/
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(LPM3_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.
/*----------------------------------------------------------------------------*/
char EEPROM_RandomRead(unsigned int Address)
{
  char adr_hi;
  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(LPM3_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(LPM3_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 , char * Data , unsigned int Size)
{
  char adr_hi;
  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(LPM3_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(LPM3_bits + GIE);                                         // Enter LPM0 w/ interrupts
    Data[counterSize] = I2CBuffer;
  }
  UCB1CTL1 |= UCTXSTP;                                                          // I2C stop condition
  __bis_SR_register(LPM3_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(!(UCNACKIFG & UCB1STAT))                                               // 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_ms(5);                                                                // Aguarda 5 milisegundos
  }while(UCNACKIFG & UCB1STAT);
}

/*----------------------------------------------------------------------------*/
//  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 defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = EUSCI_B1_VECTOR
__interrupt void USCI_B1_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(EUSCI_B1_VECTOR))) USCI_B1_ISR (void)
#else
#error Compiler not supported!
#endif
{
   switch(__even_in_range(UCB1IV, USCI_I2C_UCBIT9IFG))
     {
        case USCI_NONE:          break;                                         // Vector 0: No interrupts
        case USCI_I2C_UCALIFG:   break;                                         // Vector 2: ALIFG
        case USCI_I2C_UCNACKIFG: break;                                         // Vector 4: NACKIFG
        case USCI_I2C_UCSTTIFG:  break;                                         // Vector 6: STTIFG
        case USCI_I2C_UCSTPIFG:  break;                                         // Vector 8: STPIFG
        case USCI_I2C_UCRXIFG3:  break;                                         // Vector 10: RXIFG3
        case USCI_I2C_UCTXIFG3:  break;                                         // Vector 12: TXIFG3
        case USCI_I2C_UCRXIFG2:  break;                                         // Vector 14: RXIFG2
        case USCI_I2C_UCTXIFG2:  break;                                         // Vector 16: TXIFG2
        case USCI_I2C_UCRXIFG1:  break;                                         // Vector 18: RXIFG1
        case USCI_I2C_UCTXIFG1:  break;                                         // Vector 20: TXIFG1
        case USCI_I2C_UCRXIFG0:                                                 // Vector 22: RXIFG0
                I2CBuffer = UCB1RXBUF;                                          // store received data in buffer
                __bic_SR_register_on_exit(LPM3_bits);                           // Exit LPM0
        break;
        case USCI_I2C_UCTXIFG0:  	                                        // Vector 24: TXIFG0
                UCB1TXBUF = I2CBufferArray[PtrTransmit];                        // Load TX buffer
                PtrTransmit--;                                                  // Decrement TX byte counter
                if(PtrTransmit < 0)
                 {
                    while (!(UCB1IFG & UCTXIFG));                               // USCI_B1 TX buffer ready?
                    UCB1IE &= ~UCTXIE;                                          // disable interrupts.
                    UCB1IFG &= ~UCTXIFG;                                        // Clear USCI_B1 TX int flag
                    __bic_SR_register_on_exit(LPM3_bits);                       // Exit LPM3
                }
        break;
        case USCI_I2C_UCBCNTIFG: break;                                         // Vector 26: BCNTIFG
        case USCI_I2C_UCCLTOIFG: break;                                         // Vector 28: clock low timeout
        case USCI_I2C_UCBIT9IFG: break;                                         // Vector 30: 9th bit
        default: break;
     }
}

Anderson Portela.

  • >  if(!(UCNACKIFG & UCB1STAT)) // Break out if ACK received

    This looks a bit odd, since UCNACKIFG is in UCB1IFG. Bit 5 in UCB1STAT (both USCI and EUSCI) is UCGC, which (I suppose) is always 0. (The test also seems backwards for ack-polling.)

    It may be that this works-by-accident, due to the 5ms delay (a fairly standard Twrite for an EEPROM).

    Is this code identical to what you were using for the F5529 (USCI)?

  • Hello Bruce.

    Yes, this code is identical to the one I use with F5529.

    Strange that it writes and reads normally, but in the FR5994 after writing, it generates an excessive current consumption of the battery around 600uA. If I don't write, I consume around 10uA.

    In the F5529 the consumption after writing and reading around 14uA.

  • Does the high-current condition continue forever after the first write, or is it temporary? 

    > UCB1CTLW1 |= UCASTP_2;

    You're setting UCASTP=2, but TBCNT is always 0. I've never tried this combination, and it's possible it has some anomalies associated with it (the UG is vague). Since you're not using the feature, I suggest you not request it. (This is a difference from the F5529.)

  • I think I found an early version of your code here:

    /cfs-file/__key/communityserver-discussions-components-files/166/I2Croutines.c
    From the comments, it was for an F2-series device, where UCNACKIFG is in UCBxSTAT [Ref F2 User Guide (SLAU144K) Table 17-7]. That's not the case for the F5 nor FR5 series.
    The test still doesn't look right since it supposes that a NACK appears immediately after Start, when it can't appear earlier than (1+8+1) I2C clocks (100us at 100kHz) later.
    [Edit: This appears to be the primordial version:
    The code is via a link in the PDF.]
  • Hello Bruce.

    Answering your previous question:

    Consumption is still excessive after writing, it returns to normal only after a reset of F5994.

    Yes, I used this code for F5529 and it worked great.

    I made some modifications to use it with the F5994, where I found the problems after writing the memory.

    P.S. Maybe I don't know how to configure the correct F5994 registers. This may be the issue!

    The funny thing is that when debugging I don't find any code crashes, especially in the NACK section.

    Here are some modifications where I noticed improvements, not sure about the correct configuration:

    ////////////////////////////////////////////////////////////////////////////////
    //         Definição da Porta P5 como Serial I2C das Memórias EEPROMS         //
    ////////////////////////////////////////////////////////////////////////////////
    P5OUT = 0;                                                                   // Reseta Todos os Pinos da Porta P5
    P5DIR = 0xFF;                                                                // Define Todos os Pinos da Porta P5 como Saídas
    P5OUT &=~ 0xFF;                                                              // Inicializa Todos os Pinos da Porta P5 com Nível Lógico "0" Baixo
    P5SEL0 |= BIT0 | BIT1;                                                       // Seleciona o Pino da Porta P5.0 UCB1SDA e P5.1 UCB1SCL como Barramento I2C                                                   
    P5SEL1 &= ~(BIT0 | BIT1);                                                    // Seleciona Módulo Primário Conforme Tabela I/O Function Selection
    // Configuração do Barramento I2C das Memórias EEPROMs
    UCB1CTLW0 = UCSWRST;                                                         // Enable SW reset
    UCB1CTLW0 |= UCMODE_3 | UCMST | UCSSEL__SMCLK;                               // I2C Master, synchronous mode
    UCB1BRW = 160;                                                               // fSCL = SMCLK/160 = 100 kHz
    //UCB1CTLW1 |= 0x40;                                                           // Use SMCLK, TX mode, keep SW reset
    //UCB1CTLW1 |= UCASTP_2;
    UCB1CTLW0 &= ~UCSWRST;                                                       // Clear SW reset, resume operation
    //UCB1IE |= UCNACKIE + UCALIE + UCSTPIE + UCSTTIE + UCTXIE0 + UCRXIE0 | UCBCNTIE | USCI_I2C_UCCLTOIFG; // Interrupts Enable
    UCB1IE |= UCNACKIE + UCTXIE0 + UCRXIE0; // Interrupts Enable
    
    
    
    #include "I2Croutines.h"
    #include "delay.h"
    
    #define     MAXPAGEWRITE   33
    
    int PtrTransmit;
    char I2CBufferArray[66];
    char I2CBuffer;
    
    /*----------------------------------------------------------------------------*/
    // Description:
    //   Initialization of the I2C Module
    /*----------------------------------------------------------------------------*/
    void InitI2C(char eeprom_i2c_address)
    {
      while (UCB1STATW & UCBUSY);                                                    // wait until I2C module has
                                                                                    // finished all operations.
      // Direciona para o Endereço de Memória EEPROM Correto para o Uso
      UCB1I2CSA  = eeprom_i2c_address;                                              // define Slave Address
                                                                                    // In this case the Slave Address
                                                                                    // defines the control byte that is
                                                                                    // sent to the EEPROM.
    }
    
    /*---------------------------------------------------------------------------*/
    // Description:
    //   Initialization of the I2C Module for Write operation.
    /*---------------------------------------------------------------------------*/
    void I2CWriteInit(void)
    {
      UCB1CTLW0 |= 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)
    {
      UCB1CTLW0 &= ~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, char Data)
    {
      char adr_hi;
      char adr_lo;
    
      while (UCB1STATW & 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();
      UCB1CTLW0 |= UCTXSTT;                                                          // start condition generation
                                                                                    // => I2C communication is started
      __bis_SR_register(LPM3_bits + GIE);                                           // Enter LPM0 w/ interrupts
      UCB1CTLW0 |= UCTXSTP;                                                          // I2C stop condition
      while(UCB1CTLW0 & 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, char * Data, unsigned int Size)
    {
      volatile unsigned int i = 0;
      volatile char counterI2cBuffer;
      char adr_hi;
      char adr_lo;
      unsigned int currentAddress = StartAddress;
      unsigned int currentSize = Size;
      unsigned int bufferPtr = 0;
      char moreDataToRead = 1;
    
      while (UCB1STATW & 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
        char temp;
        for(i ; i < bufferPtr ; i++)
        {
          temp = Data[i];                                                           // Required or else IAR throws a
                                                                                    // warning [Pa082]
          I2CBufferArray[counterI2cBuffer] = temp;
          counterI2cBuffer--;
        }
    
        I2CWriteInit();
        UCB1CTLW0 |= UCTXSTT;                                                        // start condition generation
                                                                                    // => I2C communication is started
        __bis_SR_register(LPM3_bits + GIE);                                         // Enter LPM0 w/ interrupts
        UCB1CTLW0 |= UCTXSTP;                                                        // I2C stop condition
        while(UCB1CTLW0 & 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.
    /*----------------------------------------------------------------------------*/
    char EEPROM_CurrentAddressRead(void)
    {
      while(UCB1STATW & UCBUSY);                                                     // wait until I2C module has
                                                                                    // finished all operations
      I2CReadInit();
    
      UCB1CTLW0 |= UCTXSTT;                                                          // I2C start condition
      while(UCB1CTLW0 & UCTXSTT);                                                    // Start condition sent?
      UCB1CTLW0 |= UCTXSTP;                                                          // I2C stop condition
      __bis_SR_register(LPM3_bits + GIE);                                           // Enter LPM0 w/ interrupts
      while(UCB1CTLW0 & 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.
    /*----------------------------------------------------------------------------*/
    char EEPROM_RandomRead(unsigned int Address)
    {
      char adr_hi;
      char adr_lo;
    
      while (UCB1STATW & 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();
      UCB1CTLW0 |= UCTXSTT;                                                          // start condition generation
                                                                                    // => I2C communication is started
      __bis_SR_register(LPM3_bits + GIE);                                           // Enter LPM0 w/ interrupts
    
      // Read Data byte
      I2CReadInit();
    
      UCB1CTLW0 |= UCTXSTT;                                                          // I2C start condition
      while(UCB1CTLW0 & UCTXSTT);                                                    // Start condition sent?
      UCB1CTLW0 |= UCTXSTP;                                                          // I2C stop condition
      __bis_SR_register(LPM3_bits + GIE);                                           // Enter LPM0 w/ interrupts
      while(UCB1CTLW0 & 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 , char * Data , unsigned int Size)
    {
      char adr_hi;
      char adr_lo;
      unsigned int counterSize;
    
      while (UCB1STATW & 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();
      UCB1CTLW0 |= UCTXSTT;                                                          // start condition generation
                                                                                    // => I2C communication is started
      __bis_SR_register(LPM3_bits + GIE);                                           // Enter LPM0 w/ interrupts
    
      // Read Data byte
      I2CReadInit();
    
      UCB1CTLW0 |= UCTXSTT;                                                          // I2C start condition
      while(UCB1CTLW0 & UCTXSTT);                                                    // Start condition sent?
    
      for(counterSize = 0 ; counterSize < Size ; counterSize++)
      {
        __bis_SR_register(LPM3_bits + GIE);                                         // Enter LPM0 w/ interrupts
        Data[counterSize] = I2CBuffer;
      }
      UCB1CTLW0 |= UCTXSTP;                                                          // I2C stop condition
      __bis_SR_register(LPM3_bits + GIE);                                           // Enter LPM0 w/ interrupts
      while(UCB1CTLW0 & 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 (UCB1STATW & UCBUSY);                                                    // wait until I2C module has
                                                                                    // finished all operations
      do
      {
        UCB1STATW = 0x00;                                                            // clear I2C interrupt flags
        UCB1CTLW0 |= UCTR;                                                           // I2CTRX=1 => Transmit Mode (R/W bit = 0)
        UCB1CTLW0 &= ~UCTXSTT;
        UCB1CTLW0 |= UCTXSTT;                                                        // start condition is generated
        while(UCB1CTLW0 & UCTXSTT)                                                   // wait till I2CSTT bit was cleared
        {
          if(!(UCNACKIFG & UCB1STATW))                                               // Break out if ACK received
            break;
        }
        UCB1CTLW0 |= UCTXSTP;                                                        // stop condition is generated after
                                                                                    // slave address was sent => I2C communication is started
        while (UCB1CTLW1 & UCTXSTP);                                                 // wait till stop bit is reset
        delay_ms(5);                                                                // Aguarda 5 milisegundos
      }while(UCNACKIFG & UCB1STATW);
    }
    
    /*----------------------------------------------------------------------------*/
    //  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 defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = EUSCI_B1_VECTOR
    __interrupt void USCI_B1_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(EUSCI_B1_VECTOR))) USCI_B1_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
       switch(__even_in_range(UCB1IV, USCI_I2C_UCBIT9IFG))
         {
            case USCI_NONE:          break;                                         // Vector 0: No interrupts
            case USCI_I2C_UCALIFG:   break;                                         // Vector 2: ALIFG
            case USCI_I2C_UCNACKIFG: break;                                         // Vector 4: NACKIFG
            case USCI_I2C_UCSTTIFG:  break;                                         // Vector 6: STTIFG
            case USCI_I2C_UCSTPIFG:  break;                                         // Vector 8: STPIFG
            case USCI_I2C_UCRXIFG3:  break;                                         // Vector 10: RXIFG3
            case USCI_I2C_UCTXIFG3:  break;                                         // Vector 12: TXIFG3
            case USCI_I2C_UCRXIFG2:  break;                                         // Vector 14: RXIFG2
            case USCI_I2C_UCTXIFG2:  break;                                         // Vector 16: TXIFG2
            case USCI_I2C_UCRXIFG1:  break;                                         // Vector 18: RXIFG1
            case USCI_I2C_UCTXIFG1:  break;                                         // Vector 20: TXIFG1
            case USCI_I2C_UCRXIFG0:                                                 // Vector 22: RXIFG0
                    I2CBuffer = UCB1RXBUF;                                          // store received data in buffer
                    __bic_SR_register_on_exit(LPM3_bits);                           // Exit LPM0
            break;
            case USCI_I2C_UCTXIFG0:  	                                        // Vector 24: TXIFG0
                    UCB1TXBUF = I2CBufferArray[PtrTransmit];                        // Load TX buffer
                    PtrTransmit--;                                                  // Decrement TX byte counter
                    if(PtrTransmit < 0)
                     {
                        while (!(UCB1IFG & UCTXIFG));                               // USCI_B1 TX buffer ready?
                        UCB1IE &= ~UCTXIE;                                          // disable interrupts.
                        UCB1IFG &= ~UCTXIFG;                                        // Clear USCI_B1 TX int flag
                        __bic_SR_register_on_exit(LPM3_bits);                       // Exit LPM3
                    }
            break;
            case USCI_I2C_UCBCNTIFG: break;                                         // Vector 26: BCNTIFG
            case USCI_I2C_UCCLTOIFG: break;                                         // Vector 28: clock low timeout
            case USCI_I2C_UCBIT9IFG: break;                                         // Vector 30: 9th bit
            default: break;
         }
    }

  • I see that you turned off UCASTP and UCCLTO. How much improvement did you see?

    1) The EUSCI and USCI work generally the same, but they are different implementations, so they might act different if "nudged" into undocumented conditions. Since (except for what I've mentioned) I don't see anything blatantly wrong with what you're doing, this is the kind of thing I'm looking for.

    2) I keep coming back to the ack polling function, since (a) it's used only after a write, not after a read (b) it isn't doing what it claims it is (c) it probably works-by-accident since it Always delays for 5ms [Twr] . Here's one experiment: Replace each call to this function with a bare "delay_ms(5)".

    Also, how does delay_ms() work? Is it possible it leaves something running that it shouldn't? (In the original, it was a __delay_cycles(500) which is self-contained.)

    3) Can you provide a part number for your EEPROM device? I found a datasheet for the (MCHP) 24LC65 [DS21073K] which [Table 1-2 Note (4)] says that Twr is 5ms for each 8 bytes, so for 32 bytes it would be 4*5=20ms. Maybe that's not true for your device. (I've worked with similar devices, but I don't have any here with me.)

    4) Another thing that nags at me: In many places, there is a check for UCBUSY, which isn't actually  defined in I2C mode. Instead, I2C mode defines a separate UCBBUSY. This was also true for the F2 series, and Mr Goh evidently succeeded, but it seems possible that UCBUSY does (undocumented) things but acts differently for the EUSCI vs the USCI. 

  • bruce,

    Information from EEPROM 24LC1025 and delay_ms() function code follow;

    Can you write the I2Croutines.c code the way you understand how it should be for me to test here with the F5994?

    delay.h
    
    // Example for 16MHz
    #define F_CPU 16000000UL
    
    #define F_CPU_NS (F_CPU/1000000000.0)
    #define F_CPU_US (F_CPU/1000000.0)
    #define F_CPU_MS (F_CPU/1000.0)
    #define F_CPU_S  (F_CPU/1.0)
    
    // Delay Macros (more accurate -- static delay required)
    /*
    #define delay_ns(__ns) \
      if((uint32_t) (F_CPU_NS * __ns) != F_CPU_NS * __ns)\
            __delay_cycles((uint32_t) ( F_CPU_NS * __ns)+1);\
      else __delay_cycles((uint32_t) ( F_CPU_NS * __ns))
    */
    #define delay_us(__us) \
      if((uint32_t) (F_CPU_US * __us) != F_CPU_US * __us)\
            __delay_cycles((uint32_t) ( F_CPU_US * __us)+1);\
      else __delay_cycles((uint32_t) ( F_CPU_US * __us))
    #define delay_ms(__ms) \
      if((uint32_t) (F_CPU_MS * __ms) != F_CPU_MS * __ms)\
            __delay_cycles((uint32_t) ( F_CPU_MS * __ms)+1);\
      else __delay_cycles((uint32_t) ( F_CPU_MS * __ms))
    /*
    #define delay_s(__s) \
      if((uint32_t) (F_CPU_S * __s) != F_CPU_S * __s)\
            __delay_cycles((uint32_t) ( F_CPU_S * __s)+1);\
      else __delay_cycles((uint32_t) ( F_CPU_S * __s))
    */

  • The 24LC1025 data sheet (DS20001941L) Table 1-2 indicates Twc=5ms (max), so (3) above isn't a concern.

    The delay_ms definition looks fine.

    How much improvement did you see from removing UCASTP and UCCLTO?

    Without a test case, I would be coding "blind", but I would start with:

    1) Replace all occurrences of UCBUSY with UCBBUSY

    2) Replace calls (I only see one) to EEPROM_AckPolling with delay_ms(5). If that shows a (positive) effect, I would then think about whether/how to re-implement it.

  • My congratulations Bruce!!!!

    How much improvement did you see from removing UCASTP and UCCLTO?

    Answering:
    Battery current draw became more stable at 8.9uA. Before it was oscillating between 20uA and 10uA.

    1) Replace all occurrences of UCBUSY with UCBBUSY

    Answering:
    With the use of UCBBUSY the consumption simply doubled to 1.650uA. I went back to using UCBUSY and the consumption is at 8.9uA.

    2) Replace calls (I only see one) to EEPROM_AckPolling with delay_ms(5). If that shows a (positive) effect, I would then think about whether/how to re-implement it.

    Answering:
    Solution to the problem is to replace the EEPROM_AckPolling function with a simple delay_ms(5). Resolved the problem of excessive current consumption after memory writing.

    I confess that I'm having a little trouble understanding the problem that occurred with the use of the EEPROM_AckPolling function. But for all intents and purposes, it's working great!

    Many thanks Bruce for your attention and especially solution!

  • I'm glad you succeeded. I also don't know what is the exact trouble with the ack-polling function; maybe someday if I have a test case I can experiment.

    As to UCBBUSY: My first thought is that the checks cost more current (I assume you meant 16.5uA not 1.65uA) since it is actually testing for something, i.e. sometimes the I2C really is busy at that moment and the UCBUSY test doesn't detect it. I won't argue with success, and maybe the tests don't really matter, but maybe it's something to put in your lab notebook in case of strange behavior in the future. 

**Attention** This is a public forum