This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CCS/MSP430F5505: MSP430F5505 i2c master with MSP430G2553 slave BUSY failure

Part Number: MSP430F5505
Other Parts Discussed in Thread: MSP430G2553

Tool/software: Code Composer Studio

Hello,

The i2c master is getting stuck on the bus busy between the MSP430F5505 i2c master connected to an MSP430G2553 i2c slave.

How to replicate the issue: 

  1. Power down both boards.
  2. Power up the MSP430G2553 i2c slave device.
  3. Power up the MSP430F5505 i2c master device.
  4. Reset the MSP430G2553 or sometimes leave it running and after a while, it will fail.

The MSP430F5505 is only transmitting a single byte, it is using msp430_driverlib_2_91_13_01.

See below a good packet:

When a fault occurs the following happens:

The last packet:

At this point, the clock remains low and the device gets stuck on bus busy.

In the documentation, it specifies that the bus busy bit is set on the start condition on the START condition and removed on the END condition it is removed. I have tried timeouts and sending again but the clock stays low and again it will fail.

See below the hardware design of the MSP430F5505:

The slave is using the MSP-EXP43-G2 with the debugger patch pins disconnected and LED2 patch pin removed. I have tested the voltages both ends at 3.3v and 0v.

Another concern I have is the clock sometimes does the following:

Master i2c code:

uint8_t transmitData = 0x01;

void hw_i2c_master_init(void)
{
// Configure Pins for I2C
//Set P4.1 and P4.2 as Secondary Module Function Input.

receiveBufferPointer = &receiveBuffer;

/*
* Select Port 4
* Set Pin 1, 2 to input Secondary Module Function
*/
GPIO_setAsPeripheralModuleFunctionInputPin(
GPIO_PORT_P4,
GPIO_PIN1 + GPIO_PIN2
);

//Initialize Master
USCI_B_I2C_initMasterParam param = {0};
param.selectClockSource = USCI_B_I2C_CLOCKSOURCE_SMCLK;
param.i2cClk = UCS_getSMCLK();
param.dataRate = USCI_B_I2C_SET_DATA_RATE_100KBPS;
USCI_B_I2C_initMaster(USCI_B1_BASE, &param);

//Specify slave address
USCI_B_I2C_setSlaveAddress(USCI_B1_BASE,
SLAVE_ADDRESS
);

//Set receive mode
USCI_B_I2C_setMode(USCI_B1_BASE, USCI_B_I2C_TRANSMIT_MODE);

//Enable I2C Module to start operations
USCI_B_I2C_enable(USCI_B1_BASE);

//Enable master Receive interrupt
USCI_B_I2C_clearInterrupt(USCI_B1_BASE, USCI_B_I2C_TRANSMIT_INTERRUPT);
USCI_B_I2C_enableInterrupt(USCI_B1_BASE, USCI_B_I2C_TRANSMIT_INTERRUPT);
}

Then every 100ms the following is called:

void i2c_send_byte(uint8_t txData)
{
unsigned timeout = 10;

if(!USCI_B_I2C_isBusBusy(USCI_B1_BASE)) {
//Send single byte data.
USCI_B_I2C_masterSendSingleByteWithTimeout(USCI_B1_BASE,
transmitData, timeout
);
}
//Delay until transmission completes
while (timeout--) {
if(!USCI_B_I2C_isBusBusy(USCI_B1_BASE)) {
break;
}
}
}

The i2c slave code:

2844.main.c
#include <msp430.h> 
#include <stdint.h>

#define SLAVE_ADDR  0x48

#define MAX_BUFFER_SIZE     20

void initClockTo16MHz()
{
    if (CALBC1_16MHZ==0xFF)                  // If calibration constant erased
    {
        while(1);                               // do not load, trap CPU!!
    }
    DCOCTL = 0;                               // Select lowest DCOx and MODx settings
    BCSCTL1 = CALBC1_16MHZ;                    // Set DCO
    DCOCTL = CALDCO_16MHZ;
}

#define TIMERA_CCR0_BASE 2000

void timerA_init(void)
{
    //Set up Timer A register TACTL
    TACTL = 0x0000;     //start from all all clear
    TACTL |= BIT9;      //use SMCLK as the source
    TACTL |= BIT7;      //use prescaler 8
    TACTL |= BIT6;
    TACTL |= BIT4;      //count up to TACCR0(set below)
    TACTL |= BIT1;      //enable the interrupt
    TACTL &=~ BIT0;     //clear all pending interrupts
    TACCR0 = TIMERA_CCR0_BASE;      //use 2000 for 16 mhz
    TACCTL0 = CCIE;     //compare, capture interrupt enable
}

void initGPIO()
{
    P1DIR |= BIT4; // Set pin -> Output
    P1IE &= ~BIT4;   // Disable interrupts for pin

    P1DIR |= BIT5; // Set pin -> Output
    P1IE &= ~BIT5;   // Disable interrupts for pin

    P1SEL |= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
    P1SEL2|= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
}

void i2c_init()
{
    UCB0CTL1 |= UCSWRST;                      // Enable SW reset
    UCB0CTL0 = UCMODE_3 + UCSYNC;             // I2C Slave, synchronous mode
    UCB0I2COA = 0x48;                         // Own Address is 048h
    UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
    UCB0I2CIE |= UCSTPIE + UCSTTIE;           // Enable STT and STP interrupt
    IE2 |= UCB0RXIE;                          // Enable RX interrupt
}


int main(void)
{
    WDTCTL = WDTPW | WDTHOLD;   // Stop watchdog timer

    initClockTo16MHz();

    initGPIO();

    timerA_init();

    i2c_init();

    __bis_SR_register(LPM0_bits + GIE);
    return 0;
}

typedef enum I2C_ModeEnum{
    IDLE_MODE,
    NACK_MODE,
    TX_REG_ADDRESS_MODE,
    RX_REG_ADDRESS_MODE,
    TX_DATA_MODE,
    RX_DATA_MODE,
    SWITCH_TO_RX_MODE,
    SWITHC_TO_TX_MODE,
    TIMEOUT_MODE
} I2C_Mode;

I2C_Mode SlaveMode = RX_REG_ADDRESS_MODE;

#pragma vector = USCIAB0TX_VECTOR
__interrupt void USCIAB0TX_ISR(void)
{
  P1OUT ^= BIT5;

  if (IFG2 & UCB0RXIFG)                 // Receive Data Interrupt
  {
      uint8_t rx_val = UCB0RXBUF;

      switch (SlaveMode)
      {
          case (RX_REG_ADDRESS_MODE):
              SlaveMode = RX_DATA_MODE;
              break;
          case (RX_DATA_MODE):
              SlaveMode = RX_REG_ADDRESS_MODE;
              //IE2 &= ~UCB0RXIE;                       // Disable RX interrupt
              //IE2 |= UCB0TXIE;                        // Enable TX interrupt
              break;
          default:
              _no_operation();
              break;
      }
  }
  else if (IFG2 & UCB0TXIFG)            // Transmit Data Interrupt
  {
      UCB0TXBUF = 0x02;

      SlaveMode = RX_REG_ADDRESS_MODE;

      //IE2 &= ~(UCB0TXIE);
      //IE2 |= UCB0RXIE;                          // Enable RX interrupt
  }
}

//------------------------------------------------------------------------------
// The USCI_B0 state ISR is used to wake up the CPU from LPM0 in order to do
// processing in the main program after data has been transmitted. LPM0 is
// only exit in case of a (re-)start or stop condition when actual data
// was transmitted.
//------------------------------------------------------------------------------
#pragma vector = USCIAB0RX_VECTOR
__interrupt void USCIAB0RX_ISR(void)
{
    P1OUT ^= BIT4;

    if (UCB0STAT & UCSTPIFG)                        //Stop or NACK Interrupt
    {
        UCB0STAT &=
            ~(UCSTTIFG + UCSTPIFG + UCNACKIFG);     //Clear START/STOP/NACK Flags
    }
    if (UCB0STAT & UCSTTIFG)
    {
        UCB0STAT &= ~(UCSTTIFG);                    //Clear START Flags
    }
}

#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer_A(void)
{
    //clear the timer interrupt
    TACTL &=~ BIT0;
}

1) How should the recovery work?

2) What can cause this fault and how can It be avoided?

Many thanks,

Jon

  • Are you fairly certain it's the slave that's hanging the bus? Does it clear up if you disconnect the slave?

    You're enabling TXIE (USCI_B_I2C_TRANSMIT_INTERRUPT) but I don't see an ISR for it. If you're using the SingleByte suite you don't need TXIE, and probably shouldn't use it.

    timeout=10 is pretty short. That is a count of a spin loop, so estimate maybe 5-10usec/count. The timeout exit doesn't look very graceful. I suggest maybe 100-1000 instead.

  • We believe the master is hanging the bus as it gets stuck on the master bus busy status register. By adding some resistors in series we proved it was the master pulling the line down.

    In regards to the transmit interrupt on the master, it has never gone into the interrupt, the code was there from when I was testing sending multiple bytes. I have now commented out enabling interrupt and still have the same problem.

    I have increased the timeout to 1000, still the same problem.

    void hw_i2c_master_init(void)
    {
        // Configure Pins for I2C
        //Set P4.1 and P4.2 as Secondary Module Function Input.
    
        receiveBufferPointer = &receiveBuffer;
    
        /*
        * Select Port 4
        * Set Pin 1, 2 to input Secondary Module Function
        */
        GPIO_setAsPeripheralModuleFunctionInputPin(
            GPIO_PORT_P4,
            GPIO_PIN1 + GPIO_PIN2
        );
    
        //Initialize Master
        USCI_B_I2C_initMasterParam param = {0};
        param.selectClockSource = USCI_B_I2C_CLOCKSOURCE_SMCLK;
        param.i2cClk = UCS_getSMCLK();
        param.dataRate = USCI_B_I2C_SET_DATA_RATE_100KBPS;
        USCI_B_I2C_initMaster(USCI_B1_BASE, &param);
    }
    
    void i2c_send_byte(uint8_t txData)
    {
        unsigned timeout = 1000;
    
        //Specify slave address
        USCI_B_I2C_setSlaveAddress(USCI_B1_BASE,
          SLAVE_ADDRESS
          );
    
        USCI_B_I2C_setMode(USCI_B1_BASE, USCI_B_I2C_TRANSMIT_MODE);
    
        //Enable I2C Module to start operations
        USCI_B_I2C_enable(USCI_B1_BASE);
    
        //USCI_B_I2C_clearInterrupt(USCI_B1_BASE, USCI_B_I2C_TRANSMIT_INTERRUPT);
        //USCI_B_I2C_enableInterrupt(USCI_B1_BASE, USCI_B_I2C_TRANSMIT_INTERRUPT);
    
        if(!USCI_B_I2C_isBusBusy(USCI_B1_BASE)) {
            //Send single byte data.
            USCI_B_I2C_masterSendSingleByteWithTimeout(USCI_B1_BASE,
                transmitData, timeout
             );
        }
        //Delay until transmission completes
        while (timeout--) {
            if(!USCI_B_I2C_isBusBusy(USCI_B1_BASE)) {
                break;
            }
        }
    
        USCI_B_I2C_setMode(USCI_B1_BASE, USCI_B_I2C_RECEIVE_MODE);
    
        //USCI_B_I2C_clearInterrupt(USCI_B1_BASE, USCI_B_I2C_RECEIVE_INTERRUPT);
        //USCI_B_I2C_enableInterrupt(USCI_B1_BASE, USCI_B_I2C_RECEIVE_INTERRUPT);
    }

  • I have just loaded up my development kit for the master. Opposed to using the same i2c port I decided I would use the one in your example and noticed the pins in the example do not match the pins on the datasheet.

    #define GPIO_PIN0                                                      (0x0001)
    #define GPIO_PIN1                                                      (0x0002)
    #define GPIO_PIN2                                                      (0x0004)
    
    
        GPIO_setAsPeripheralModuleFunctionInputPin(
            GPIO_PORT_P3,
            GPIO_PIN1 + GPIO_PIN2
            );
    

  • To eliminate our hardware I have set up the same test using TI MSP-TS430RGC64USB dev kit and the results were the same. I did have to correct the port pins to 0 and 1 as per my previous post which proved the examples provided are incorrect.

    2781.i2c_slave.c
    #include <msp430.h> 
    #include <stdint.h>
    
    #define SLAVE_ADDR  0x48
    
    #define MAX_BUFFER_SIZE     20
    
    void initClockTo16MHz()
    {
        if (CALBC1_16MHZ==0xFF)                  // If calibration constant erased
        {
            while(1);                               // do not load, trap CPU!!
        }
        DCOCTL = 0;                               // Select lowest DCOx and MODx settings
        BCSCTL1 = CALBC1_16MHZ;                    // Set DCO
        DCOCTL = CALDCO_16MHZ;
    }
    
    #define TIMERA_CCR0_BASE 2000
    
    void timerA_init(void)
    {
        //Set up Timer A register TACTL
        TACTL = 0x0000;     //start from all all clear
        TACTL |= BIT9;      //use SMCLK as the source
        TACTL |= BIT7;      //use prescaler 8
        TACTL |= BIT6;
        TACTL |= BIT4;      //count up to TACCR0(set below)
        TACTL |= BIT1;      //enable the interrupt
        TACTL &=~ BIT0;     //clear all pending interrupts
        TACCR0 = TIMERA_CCR0_BASE;      //use 2000 for 16 mhz
        TACCTL0 = CCIE;     //compare, capture interrupt enable
    }
    
    void initGPIO()
    {
        P1DIR |= BIT4; // Set pin -> Output
        P1IE &= ~BIT4;   // Disable interrupts for pin
    
        P1DIR |= BIT5; // Set pin -> Output
        P1IE &= ~BIT5;   // Disable interrupts for pin
    
        P1SEL |= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
        P1SEL2|= BIT6 + BIT7;                     // Assign I2C pins to USCI_B0
    }
    
    void i2c_init()
    {
        UCB0CTL1 |= UCSWRST;                      // Enable SW reset
        UCB0CTL0 = UCMODE_3 + UCSYNC;             // I2C Slave, synchronous mode
        UCB0I2COA = 0x48;                         // Own Address is 048h
        UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
        UCB0I2CIE |= UCSTPIE + UCSTTIE;           // Enable STT and STP interrupt
        IE2 |= UCB0RXIE;                          // Enable RX interrupt
    }
    
    
    int main(void)
    {
        WDTCTL = WDTPW | WDTHOLD;   // Stop watchdog timer
    
        initClockTo16MHz();
    
        initGPIO();
    
        timerA_init();
    
        i2c_init();
    
        __bis_SR_register(LPM0_bits + GIE);
        return 0;
    }
    
    typedef enum I2C_ModeEnum{
        IDLE_MODE,
        NACK_MODE,
        TX_REG_ADDRESS_MODE,
        RX_REG_ADDRESS_MODE,
        TX_DATA_MODE,
        RX_DATA_MODE,
        SWITCH_TO_RX_MODE,
        SWITHC_TO_TX_MODE,
        TIMEOUT_MODE
    } I2C_Mode;
    
    I2C_Mode SlaveMode = RX_REG_ADDRESS_MODE;
    
    #pragma vector = USCIAB0TX_VECTOR
    __interrupt void USCIAB0TX_ISR(void)
    {
      P1OUT ^= BIT5;
    
      if (IFG2 & UCB0RXIFG)                 // Receive Data Interrupt
      {
          uint8_t rx_val = UCB0RXBUF;
    
          switch (SlaveMode)
          {
              case (RX_REG_ADDRESS_MODE):
                  SlaveMode = RX_DATA_MODE;
                  break;
              case (RX_DATA_MODE):
                  SlaveMode = RX_REG_ADDRESS_MODE;
                  //IE2 &= ~UCB0RXIE;                       // Disable RX interrupt
                  //IE2 |= UCB0TXIE;                        // Enable TX interrupt
                  break;
              default:
                  _no_operation();
                  break;
          }
      }
      else if (IFG2 & UCB0TXIFG)            // Transmit Data Interrupt
      {
          UCB0TXBUF = 0x02;
    
          SlaveMode = RX_REG_ADDRESS_MODE;
    
          //IE2 &= ~(UCB0TXIE);
          //IE2 |= UCB0RXIE;                          // Enable RX interrupt
      }
    }
    
    //------------------------------------------------------------------------------
    // The USCI_B0 state ISR is used to wake up the CPU from LPM0 in order to do
    // processing in the main program after data has been transmitted. LPM0 is
    // only exit in case of a (re-)start or stop condition when actual data
    // was transmitted.
    //------------------------------------------------------------------------------
    #pragma vector = USCIAB0RX_VECTOR
    __interrupt void USCIAB0RX_ISR(void)
    {
        P1OUT ^= BIT4;
    
        if (UCB0STAT & UCSTPIFG)                        //Stop or NACK Interrupt
        {
            UCB0STAT &=
                ~(UCSTTIFG + UCSTPIFG + UCNACKIFG);     //Clear START/STOP/NACK Flags
        }
        if (UCB0STAT & UCSTTIFG)
        {
            UCB0STAT &= ~(UCSTTIFG);                    //Clear START Flags
        }
    }
    
    #pragma vector = TIMER0_A0_VECTOR
    __interrupt void Timer_A(void)
    {
        //clear the timer interrupt
        TACTL &=~ BIT0;
    }
    
    

    3157.i2c_master.c
    /* --COPYRIGHT--,BSD
     * Copyright (c) 2017, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     * --/COPYRIGHT--*/
    #include "MSP430F5xx_6xx/driverlib.h"
    
    //*****************************************************************************
    //! This example shows how to configure the I2C module as a master for
    //! single byte transmission in interrupt mode. The address of the slave
    //! module that the master is communicating with also set in this example.
    //!
    //! This example uses the following peripherals and I/O signals.  You must
    //! review these and change as needed for your own board:
    //! - I2C peripheral
    //! - GPIO Port peripheral (for I2C pins)
    //! - SCL2
    //! - SDA
    //!
    //! This example uses the following interrupt handlers.  To use this example
    //! in your own application you must add these interrupt handlers to your
    //! vector table.
    //! - USCI_B0_VECTOR.
    //!
    //
    //*****************************************************************************
    //*****************************************************************************
    //
    //Set the address for slave module. This is a 7-bit address sent in the
    //following format:
    //[A6:A5:A4:A3:A2:A1:A0:RS]
    //
    //A zero in the "RS" position of the first byte means that the master
    //transmits (sends) data to the selected slave, and a one in this position
    //means that the master receives data from the slave.
    //
    //*****************************************************************************
    #define SLAVE_ADDRESS 0x48
    
    uint8_t transmitData;
    
    void i2c_send_byte(uint8_t txData)
    {
        unsigned timeout = 1000;
    
        if(!USCI_B_I2C_isBusBusy(USCI_B0_BASE)) {
            //Send single byte data.
            USCI_B_I2C_masterSendSingleByteWithTimeout(USCI_B0_BASE,
                transmitData, timeout
             );
        }
    
        //Delay until transmission completes
        while (timeout--) {
            if(!USCI_B_I2C_isBusBusy(USCI_B0_BASE)) {
                break;
            }
        }
    }
    
    void main (void)
    {
        //Stop WDT
        WDT_A_hold(WDT_A_BASE);
    
        GPIO_setAsOutputPin(
            GPIO_PORT_P4,
            GPIO_PIN7
        );
    
        //Assign I2C pins to USCI_B0
        GPIO_setAsPeripheralModuleFunctionInputPin(
            GPIO_PORT_P3,
            GPIO_PIN0 + GPIO_PIN1
            );
    
        //Initialize transmit data packet
        transmitData = 0x01;
    
        //Initialize Master
        USCI_B_I2C_initMasterParam param = {0};
        param.selectClockSource = USCI_B_I2C_CLOCKSOURCE_SMCLK;
        param.i2cClk = UCS_getSMCLK();
        param.dataRate = USCI_B_I2C_SET_DATA_RATE_400KBPS;
        USCI_B_I2C_initMaster(USCI_B0_BASE, &param);
    
        //Specify slave address
        USCI_B_I2C_setSlaveAddress(USCI_B0_BASE,
            SLAVE_ADDRESS
            );
    
        //Set in transmit mode
        USCI_B_I2C_setMode(USCI_B0_BASE,
            USCI_B_I2C_TRANSMIT_MODE
            );
    
        //Enable I2C Module to start operations
        USCI_B_I2C_enable(USCI_B0_BASE);
    
        //Enable TX interrupt
        USCI_B_I2C_clearInterrupt(USCI_B0_BASE,
            USCI_B_I2C_TRANSMIT_INTERRUPT
            );
        USCI_B_I2C_enableInterrupt(USCI_B0_BASE,
            USCI_B_I2C_TRANSMIT_INTERRUPT
            );
    
        while (1)
        {
            GPIO_toggleOutputOnPin(
                    GPIO_PORT_P4,
                    GPIO_PIN7
                );
    
            //Send single byte data.
            i2c_send_byte(
                transmitData
                );
    
            //Delay between each transaction
            __delay_cycles(50);
    
            //Increment transmit data counter
            transmitData++;
        }
    }
    
    //******************************************************************************
    //
    //This is the USCI_B0 interrupt vector service routine.
    //
    //******************************************************************************
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=USCI_B0_VECTOR
    __interrupt
    #elif defined(__GNUC__)
    __attribute__((interrupt(USCI_B0_VECTOR)))
    #endif
    void USCI_B0_ISR (void)
    {
        switch (__even_in_range(UCB0IV,12)){
            //Vector 12: Transmit buffer empty - TXIF
            case USCI_I2C_UCTXIFG:
            {
                __no_operation();
                break;
            }
            default:  break;
        }
    }
    
    

  • I have added an i2c RTC to the master and this works how expected. When I add the MSP430G2553 slave it will work several times then break.

    Slave replying

    Slave breaking

  • It sure sounds like the slave (G2553) is hanging the bus, rather than the master (F5505). What code is it running?

    [Edit: To answer your original question: If a slave hangs the bus, the master doesn't have much recourse. The I2C spec (UM10204, Rev 6) Sec 3.1.16 pretty much says "Uh, we hope you have control over the slave Reset pin".]

**Attention** This is a public forum