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.

TI I2C Example Problems

Other Parts Discussed in Thread: MSP430F5438A

Hey Folks,

Some of you may remember I just had a problem bit-banging an i2c interface to an eeprom, Microchip
24FC1025. That problem remains unsolved, with IAR and myself continuing to work on it, but right now, I am
trying to interface with the eeprom using the TI built-in I2C USCIB2. I have been fighting to make a very
simple (I thought) TI example titled "MSP430x54x_uscib0_i2c_08". I have minimally altered the example so I
can test it on our development board and view diagnostic msgs on hyperterminal on a PC. I can send the code
if you think it is needed.

The example program takes an array of 5 unsigned chars, sets the pointer to the start of the array and
the byte counter to sizeof the array. I then reset a write protect pin and call the start condition with
UCTR set. I can tell from msgs embedded in the ISR that the isr executed 5 times. I am viewing output of
clk and data on a scope. The patterns seem normal, and the slave address is sent and it appears that I
receive an ack because the data line remains low.

The problem is that I see no scope patterns indicating data bytes have been sent. Can someone please
suggest what to try next? This is the isr:


//------------------------------------------------------------------------------
// The USCIAB0TX_ISR is structured such that it can be used to transmit any
// number of bytes by pre-loading TXByteCtr with the byte count. Also, TXData
// points to the next byte to transmit.
//------------------------------------------------------------------------------
#pragma vector = USCI_B2_VECTOR
__interrupt void USCI_B2_ISR(void)
{
switch(__even_in_range(UCB2IV,12))
{
case 0: break; // Vector 0: No interrupts
case 2: break; // Vector 2: ALIFG
case 4: break; // Vector 4: NACKIFG
case 6: break; // Vector 6: STTIFG
case 8: break; // Vector 8: STPIFG
case 10: break; // Vector 10: RXIFG
case 12: // Vector 12: TXIFG


if (TXByteCtr) // Check TX byte counter
{
#if 1 // print out??
sprintf(outBuf, "ISR::%X", *PTxData);
TxBuffer(outBuf, 10, 1);
#endif // print out??

UCB2TXBUF = *PTxData++; // Load TX buffer
TXByteCtr--; // Decrement TX byte counter
}
else
{
UCB2CTL1 |= UCTXSTP; // I2C stop condition
UCB2IFG &= ~UCTXIFG; // Clear USCI_B2 TX int flag
__bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
}
default: break;
}
}

Thanks,
Mike Raines

  • First, it is a bad idea to do things like TxBuffer() inside an ISR. Best case it is slow (and ISRs are fast interrupts of the main code, not parallel threads). Worst case, the transmit function uses interrupts by itself and either can’t finish (interrupts are disabled inside an ISR) or enable interrupts and cause a stack overflow due to unlimited interrupt nesting.

    Then, it is difficult to find a bug when only a fraction of the related code is posted. How do you init the transfer? The ISR looks fine, but maybe the startup is wrong.

  • Jens-Michael,

    Thanks for the reply.  I have made progress since my initial post, partially by taking your advice.  My current problem seems to contradict as it (seems ) that adding TxBuffer(....); causes the code to work.  Would you please take a look?

     I am attempting to write/read a Microchip 24FC1025 eeprom using the intrinsic i2c on a Texas Instruments MSP430F5438A chip.  This may be a mistake because I got no hits when searching the Microchip forum for msp430 or F5438.  Has anyone done this successfully and, if so, what are the pitfalls to watch out for? 

     

    It is my understanding that this eeprom has two slave addresses, one for each half of the memory.  The slave address for the lower half is 1010 000 (0x50) and for the upper half is 1010 100 (0x54).  Before reading/writing the eeprom I must insure I'm using the proper slave address.  This is an example of switching to block 1:

    //=======================================================================

    //=======================================================================

     

    void InitializeUcb2Block1(void)         // eeprom interface

     

      UCB2CTL1 |= UCSWRST;                          // Enable SW reset

      UCB2CTL0 = UCMST + UCMODE_3 + UCSYNC;         // I2C Master, synchronous mode

      UCB2CTL1 = UCSSEL_2 + UCSWRST;                // Use SMCLK, keep SW reset

      UCB2BR0 = 12;                                 // fSCL = SMCLK/12 = ~100kHz

      UCB2BR1 = 0;

      UCB2I2CSA = 0x50;                             // Slave Address is 0x50    1010 000

      UCB2CTL1 &= ~UCSWRST;                         // Clear SW reset, resume operation

      UCB2IE        |= UCTXIE;                      // Enable TX interrupt

      UCB2IE        |= UCRXIE;                      // enable rx interrupt

     

    }  // end function void InitializeUcb2ForI2cToEeprom(void)

     

    //=======================================================================

    //=======================================================================

     

    // ================== ensure slave address points to block 1   ==============

     

      if (UCB2I2CSA != 0x50)                  // if not block 1

      {

        InitializeUcb2Block1();

      }  // end if not block 1

     

      DelayMs(1);                   // test delay

     

    This seems to work fine.

     

    For writing, I first disable a write-protect pin P9.0 (set the pin low).  I place the high and low bytes of the address into elements 0 and 1 of an array TxData, followed by the data bytes to place in the eeprom starting at this address.  I then set a pointer used by the USCIB2 ISR (interrupt service routine) to the start of the array and another variable used by the isr, TXByteCtr, to the number of bytes to write.  I then turn the process over to the intrinsic TI I2C by executing: 

    UCB2CTL1      |= UCTR + UCTXSTT; and while(UCB2CTL1 & UCTXSTP);  The write-protect pin is enabled when the isr clocks out the last byte.

     

    For reading, I disable the write-protect pin just to send the address and follow the write procedure placing just the read address in the array and setting TXByteCtr to 2.  It is my understanding that the isr now sends the address and that this places the eeprom internal address pointer to the address I sent.  I then set a pointer used by the isr to a buffer to receive the read bytes, and I set another variable available to the isr to the number of bytes to read.  I then switch from write to read functionality by executing:               UCB2CTL1      &= ~UCTR;   and UCB2CTL1    |= UCTXSTT;

     

    This seems to work most of the time, but I’m worried about several anomolies.  Recently, I noticed that on a read, the isr only accesses every other byte.  It's like the memory pointer is incrementing by 2 instead of 1.  I discovered if I removed the three print statements from case 10 of the isr, the read worked normally.

     

    Just yesterday, while implementing this to several write/read to/from eeprom instances, I removed the three “print” statements from case 12 of the isr because my PC printout was getting crowded.  To my great surprise, the read operations stopped working.  I put the statements back, and the read operations work again. 

     

    To summarize, it did not concern me greatly that the extra isr statements in case 10 caused read problems, and I had no heartburn about removing them.  The (seeming) fact that the extra statements are necessary for case 12 to work is scary.  I do not like working with algorithms that are this touchy.

     

    Can someone please shed some light on this problem, and a possible solution?  This is the isr:

     

    //============================================================

    //============================================================

    #pragma vector = USCI_B2_VECTOR         // TI intrinsic i2c isr

    __interrupt void USCI_B2_ISR(void)

    {

      unsigned char iv = (unsigned char)UCB2IV;

    //  sprintf(outBuf, "UCB2IV is:  %X", iv);

    //  TxBuffer(outBuf, 30, 1);

       

    //  switch(__even_in_range(UCB2IV,12))

      switch(__even_in_range(iv,12))

      {

      case  0: break;                           // Vector  0: No interrupts

      case  2: break;                           // Vector  2: ALIFG

      case  4: break;                           // Vector  4: NACKIFG

      case  6: break;                           // Vector  6: STTIFG

      case  8: break;                           // Vector  8: STPIFG

      case 10:

       

    //    TxBuffer("case 10:", 10, 1);

        RXByteCtr --;                               // decrement rx byte counter

        if (RXByteCtr)                               // if bytes to read

        {

    //      sprintf(outBuf, "case 10:%X", UCB2RXBUF);

    //      TxBuffer(outBuf, 30, 1);

          *PRxData ++ = UCB2RXBUF;                  // move data byte to buffer

          if (RXByteCtr == 1)                       // if next-to-last byte

            UCB2CTL1        |= UCTXSTP;             // generate i2c stop condition

        }

        else

        {

    //      sprintf(outBuf, "case 10:%X", UCB2RXBUF);

    //      TxBuffer(outBuf, 30, 1);

          *PRxData = UCB2RXBUF;                     // move final byte to buffer

    //      __bic_SR_register_on_exit (LPM0_bits);    // exit with active cpu

    //    __bic_SR_register_on_exit (LPM3_bits);    // exit with active cpu      

        }  // end if bytes to read else

       

        break;                           // Vector 10: RXIFG

      case 12:                                  // Vector 12: TXIFG 

       

        TxBuffer("case 12:  ", 12, 0);

        if (TXByteCtr)                          // Check TX byte counter

        {

          sprintf(outBuf, "%X", *PTxData);

          TxBuffer(outBuf, 30, 1);

         

          UCB2TXBUF = *PTxData++;               // Load TX buffer if bytes to send

          TXByteCtr--;                          // Decrement TX byte counter

        }

        else

        {

          UCB2CTL1 |= UCTXSTP;                  // I2C stop condition

          UCB2IFG &= ~UCTXIFG;                  // Clear USCI_B0 TX int flag

          TxBuffer("***STOP***", 30, 1);

          P9OUT     |= BIT0;                        // write-protect on

         

    //      __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0

    //    __bic_SR_register_on_exit(LPM3_bits); // Exit LPM3     

        }  // end if bytes to send 

        

        break;

      default: break;

      }  // end switch

    }  // end function __interrupt void USCI_B2_ISR(void)

     

    //=======================================================================

    //=======================================================================

     

    Mike Raines

  • I think, the problem is in the transfer progress checking. (you only posted an excerpt of your code)

    After setting UCTXSTT, you can’t just wait for UCTXSTP to clear. It is still clear at this point. It will be set when the ISR has put the last byte on its way and will be cleared after the last byte (and the following stop condition) has been sent. But between the start and this last byte, some time will pass where it is clear too. So you need to wait first until the ISR has signaled that all bytes have been put on their way. At this point, UCTXSTP should be set and you can wait for it to clear again before you start the next transfer.

    Alternatively, you can check for UCBUSY, which will be set all the time from UCTXSTT being set to UCTXSTP being finally cleared.

     Besides this, you are right. The 24FC1025 acts as if it were two independent I2C slaves with half the capacity. And in fact, the chip was built to simulate a pair of 512MBit EEproms. It is even pin-compatible to the smaller variant, including the A2 input pin which was used to set the A2 bit of the slave address. So if you had a PCB with two of the smaller ones, you could mount this one to the socket with A2 tied to VCC and it would work without any firmware change in the master.
    So think of it as being two independent I2C devices which are sharing the same case.

    One more thing: Microchip has done a really good job confusing people by inventing their own nomenclature for their usage of I2C. Talking about ‘control byte’ that ‘selects’ the A2 address bit, and so on. Instead of simply stating the fact that it is two independent I2C slaves in one case. And the so-called ‘control byte’ being a simple standard I2C start byte that selects a slave.

  • Jenns_Michael,

         Thanks for the reply.  I think you are probably right about me using UCTXSTP incorrectly.  Would you please offer a suggestion on using UCBUSY?  I have attached a read and a write function from my code:

    //=============================================================================
    //=============================================================================

    int GetHLogNumber(void) //converting to ti intrinsic i2c
    {

    int hourlyLogNumber = 0x00;
    unsigned char hLogStoreL = 0x00; // making local
    unsigned char hLogStoreH = 0x00; // making local


    // ============== clear receive buffer ===========================

    for (int i=0; i<128; i++)
    {
    RxBuffer[i] = 0x00; // clear
    } // end for each byte in buffer

    DelayMs(idelayTimeTiEepromI2c); // test delay // shorter??

    // ================== ensure slave address points to block 1 ===============================

    if (UCB2I2CSA != 0x50) // if not block 1
    {
    InitializeUcb2Block1();
    } // end if not block 1


    // =================== load target eeprom address ======================================

    hLogStoreL = (hLogStore & 0xFF);
    hLogStoreH = (hLogStore >> 8);

    #if 1 // print out??
    sprintf(outBuf, "GetHLogNumber::Reading from address %X H %X L", hLogStoreH, hLogStoreL);
    TxBuffer(outBuf, 80, 1);
    #endif // print out??


    TxAddress[0] = hLogStoreH;
    TxAddress[1] = hLogStoreL;

    PTxData = (unsigned char*)TxAddress; // set pointer for isr
    TXByteCtr = 2; // 2 byte address only

    // =============== disable write protect and give control to isr =======================

    P9OUT &= ~(BIT0);
    UCB2CTL1 |= UCTR + UCTXSTT;
    //__bis_SR_register(LPM3_bits + GIE);
    //__no_operation();

    while(UCB2CTL1 & UCTXSTP);

    // =============== now read data from address pointed to ===========================

    PRxData = (unsigned char*)RxBuffer; // start of rx buffer
    RXByteCtr = 2; // read 1 integer
    UCB2CTL1 &= ~UCTR; // switch from tx to rx
    UCB2CTL1 |= UCTXSTT; // i2c start condition

    // __bis_SR_register (LPM3_bits + GIE); // enter lpm 0, enable interrupts
    // remain in lpm0 until all data
    // is rs'd
    // __no_operation(); // set breakpoint <here> and
    // read out the Rxbuffer

    // =================== now process received data ====================================


    DelayMs(idelayTimeTiEepromI2c); // needed for return from isr??????// was 5

    #if 1 // printout?

    sprintf(outBuf, "GetHLogNumber::byte1 == %d", RxBuffer[0]);
    TxBuffer(outBuf, 30, 1);

    sprintf(outBuf, "GetHLogNumber::byte2 == %d", RxBuffer[1]);
    TxBuffer(outBuf, 30, 1);

    #endif // printout?

    hourlyLogNumber = RxBuffer[0];
    hourlyLogNumber = hourlyLogNumber << 8;
    hourlyLogNumber += RxBuffer[1];

    #if 1 // printout?
    sprintf(outBuf, "GetHLogNumber::hourlyLogNumber == %d", hourlyLogNumber);
    TxBuffer(outBuf, 60, 1);
    #endif // printout?


    return hourlyLogNumber;
    } // end function int GetHLogNumber(void)

    //=============================================================================
    //=============================================================================

    //=============================================================================
    //=============================================================================

    void SetHLogNumber(int n) // converting to ti intrinsic i2c
    {
    unsigned char* nPtr = ((unsigned char*)&n) ;
    unsigned char hLogStoreH = 0x00; // making local
    unsigned char hLogStoreL = 0x00; // making local

    // ================== ensure slave address points to block 1 ===============================

    if (UCB2I2CSA != 0x50) // if not block 1
    {
    InitializeUcb2Block1();
    } // end if not block 1

    DelayMs(idelayTimeTiEepromI2c); // test delay // shorter

    // =================== load target eeprom address ======================================

    hLogStoreL = (hLogStore & 0xFF);
    hLogStoreH = (hLogStore >> 8);

    #if 1 // print out??
    sprintf(outBuf, "SetHLogNumber::Writing to address %X H %X L", hLogStoreH, hLogStoreL);
    TxBuffer(outBuf, 80, 1);
    #endif // print out??


    bytesToSend[0] = hLogStoreH;
    bytesToSend[1] = hLogStoreL;
    bytesToSend[2] = *(nPtr + 1);
    bytesToSend[3] = *(nPtr + 0);
    PTxData = (unsigned char*)bytesToSend; // set pointer for isr
    TXByteCtr = 4; // 2 byte address, 2 byte integer

    #if 1 // print out?
    for (int i=0; i<4; i++)
    {
    sprintf(outBuf, "SetHLogNumber:: sending byte[%d] == %X" , i, bytesToSend[i]);
    TxBuffer(outBuf, 70, 1);
    } // end for i
    #endif // print out?

    // =============== disable write protect and give control to isr =======================

    P9OUT &= ~(BIT0);
    UCB2CTL1 |= UCTR + UCTXSTT;
    // __bis_SR_register(LPM3_bits + GIE);
    // __no_operation();

    while(UCB2CTL1 & UCTXSTP);


    } // end function void SetHLogNumber(int n)

    //=============================================================================
    //=============================================================================

    Thanks,

    Mike Raines

    Hoffer Flow Controls, Inc

    252-331-1997 Ext 261

  • Jens-Michael,

         I'm wondering if the only reason my code works at all is the delays I pur in.  If I can figure out how to do it correctly, I believe the delays will no longer be necessary.. What do you think?

    Thanks,

    Mike Raines

  • Jenns-Michael,

         This is my isr function:


    //============================================================
    //============================================================
    #pragma vector = USCI_B2_VECTOR // TI intrinsic i2c isr
    __interrupt void USCI_B2_ISR(void)
    {
    unsigned char iv = (unsigned char)UCB2IV;
    // sprintf(outBuf, "UCB2IV is: %X", iv);
    // TxBuffer(outBuf, 30, 1);

    // switch(__even_in_range(UCB2IV,12))
    switch(__even_in_range(iv,12))
    {
    case 0: break; // Vector 0: No interrupts
    case 2: break; // Vector 2: ALIFG
    case 4: break; // Vector 4: NACKIFG
    case 6: break; // Vector 6: STTIFG
    case 8: break; // Vector 8: STPIFG
    case 10:

    // TxBuffer("case 10:", 10, 1);
    RXByteCtr --; // decrement rx byte counter
    if (RXByteCtr) // if bytes to read
    {
    // sprintf(outBuf, "case 10:%X", UCB2RXBUF);
    // TxBuffer(outBuf, 30, 1);
    *PRxData ++ = UCB2RXBUF; // move data byte to buffer
    if (RXByteCtr == 1) // if next-to-last byte
    UCB2CTL1 |= UCTXSTP; // generate i2c stop condition
    }
    else
    {
    // sprintf(outBuf, "case 10:%X", UCB2RXBUF);
    // TxBuffer(outBuf, 30, 1);
    *PRxData = UCB2RXBUF; // move final byte to buffer
    } // end if bytes to read else

    break; // Vector 10: RXIFG
    case 12: // Vector 12: TXIFG

    // TxBuffer("case 12: ", 12, 0);
    Delay15Us();
    if (TXByteCtr) // Check TX byte counter
    {
    // sprintf(outBuf, "%X", *PTxData);
    // TxBuffer(outBuf, 30, 1);
    UCB2TXBUF = *PTxData++; // Load TX buffer if bytes to send
    TXByteCtr--; // Decrement TX byte counter
    }
    else
    {
    UCB2CTL1 |= UCTXSTP; // I2C stop condition
    UCB2IFG &= ~UCTXIFG; // Clear USCI_B0 TX int flag
    // TxBuffer("***STOP***", 30, 1);
    DelayMs(delayTimeTiEepromI2c); // from Vito 5 max, 3 typical
    P9OUT |= BIT0; // write-protect on
    } // end if bytes to send

    break;
    default: break;
    } // end switch
    } // end function __interrupt void USCI_B2_ISR(void)

    //=======================================================================
    //=======================================================================

  • I noticed in your ISR RX code that if you just received the next-to-last byte, you read it, then immediately set UCTXSTP. However, the interrupt is triggered at the beginning of the ACK cycle of the next-to-last byte, while setting UCTXSTP must not take place before receiving the last byte has begun. Depending on whether you read RXBUF when the ACK cycle is already over (and is stretched until you do the read) or not, it may be that you’re stopping too early and won’t get the last byte. Doing the sprintf/TXBuffer before setting UCTXSTP might have prevented this.
    I’m not 100% sure whether this is really a problem, but according to the timing diagram in the users guide, it is.

    The TX part seems okay, but I wouldn’t do the write protect handling inside the ISR. In main code, after launching the transfer, main should wait for TX being done (UCTXSTP is set, or ISR wakes you from LPM) and then UCTXSTP is clear again. Then do any delay and set the write protect. The ISR doesn’t know which peripheral you addressed and therefore shouldn’t handle device-specific signals like the write protect. Also, delays inside an ISR are a sign of wrong design. (and you don’t need the 15µs delay at start of TX ISR too, due to double buffering, the interrupt comes long before the previous byte has done sending)

     UCBUSY is set when a start is detected on the bus (or you set UCTXSTT) and clears when a stop is detected (or UCTXSTP is cleared after sending a stop).

     Nowhere in your code I see you configuring the I2C port pins for module usage. maybe that's the reason you don't see a signal?
    Somewhere you need to do a P9SEL |= BIT1|BIT2; to switch the two I2C pins from GPIO to USCI mode.

**Attention** This is a public forum