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.

MSP430F5529: MSP430F5529 I2C slave random hang while in interrupt

Part Number: MSP430F5529
Other Parts Discussed in Thread: LM75A

Hi, I'm using a custom board with msp430f5529. The msp has multiple i2c slave devices (single on-board LM75A and two off-board LM75A connected via LTC4315f bus buffer).

The MSP itself is also a slave for Raspberry Pi4. I2c master and slave are USCI_B0 and B1 correspondingly.

The issue is: Raspberry launches a cyclic (while true) command, asking, for example, an off-board LM75A. System works properly for some time (like 5 cycles or 6 minutes non-stop, it's not fixed) and then it hangs. Host waits for 10 sec timeout, but msp hangs busy.

I2C_master code for msp is copied from MSP430F55xx_usci_i2c_standard_master.c from official library.

Master Init code below. clock is sourced from SMCLK:

void I2C_init()
{
    //initClockTo16MHz();
    
    I2C_init_GPIO();

    UCB0CTL1 |= UCSWRST;                      // Enable SW reset
    UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;     // I2C Master, synchronous mode
    UCB0CTL1 = UCSSEL_2 + UCSWRST;            // Use SMCLK, keep SW reset
    UCB0BR0 = 10;//160;              //1.01/10 ~100khz      // fSCL = SMCLK/160 = ~100kHz
    UCB0BR1 = 0;
    UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
    UCB0IE |= UCNACKIE;
    
}

I2C slave is custom:

void initGPIO()
{
    //I2C Pins
    P4SEL |= BIT1 | BIT2;
}

void initI2C()
{
    UCB1CTL1 |= UCSWRST;                      // Enable SW reset
    UCB1CTL0 = UCMODE_3 + UCSYNC;             // I2C Master, synchronous mode
    UCB1CTL1 = UCSSEL_2 + UCSWRST;            // Use SMCLK, keep SW reset
    UCB1I2COA = SLAVE_ADDR;                   // Own Address
    UCB1BR0 = 1;//160;
    UCB1BR1 = 0;
    UCB1CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
    UCB1IE |= (UCRXIE | UCTXIE| UCSTPIE | UCSTTIE | UCALIE | UCNACKIE);
}

int I2C_slave_init(void) {
    initGPIO();
    initI2C();
    return 0;
}


//******************************************************************************
// I2C Interrupt ***************************************************************
//******************************************************************************

enum i2c_slave_stage{WAITING_FOR_START, RECEIVE_REG_ADDR, 
WAITING_FOR_DATA_OR_RESTART, TRANSMIT_DATA, WAITING_FOR_STOP}; 
enum i2c_slave_stage stage = WAITING_FOR_START;

void read_i2c_data() {
  uint8_t temp = UCB1RXBUF;
  if (stage == RECEIVE_REG_ADDR) {
    i2c_offset = (temp > sizeof(i2c_interface_struct))? sizeof(i2c_interface_struct) : temp;
    stage = WAITING_FOR_DATA_OR_RESTART;
  } else if (stage == WAITING_FOR_DATA_OR_RESTART) {
    (*(((uint8_t*)(&i2c_interface_struct))+i2c_offset)) = temp;
    stage = WAITING_FOR_STOP;
  } else {
    // generate NAK
    UCB1CTL1 |= UCTXNACK;
  }
}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=USCI_B1_VECTOR
__interrupt void USCI_B1_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B1_VECTOR))) USCI_B1_ISR (void)
#else
#error Compiler not supported!
#endif
{
  volatile uint8_t temp;
  
  switch(__even_in_range(UCB1IV,0xC))
  {
    case USCI_NONE:
      break;                             // Vector 0 - no interrupt
    case USCI_I2C_UCALIFG:
      break;                            // Interrupt Vector: I2C Mode: UCALIFG
    case USCI_I2C_UCNACKIFG:
      break;                            // Interrupt Vector: I2C Mode: UCNACKIFG
    case USCI_I2C_UCSTTIFG:   
      if (stage == WAITING_FOR_DATA_OR_RESTART)
        stage = TRANSMIT_DATA;
      else
        stage = RECEIVE_REG_ADDR;
      break;                            // Interrupt Vector: I2C Mode: UCSTTIFG
    case USCI_I2C_UCSTPIFG:
      // Fix: STOP in write transaction could come before DATA
      if (UCB1IFG & UCRXIFG) {
        //(*(((uint8_t*)(&i2c_interface_struct))+i2c_offset)) = UCB1RXBUF;
        read_i2c_data();
      }
      i2c_offset=0;
      stage = WAITING_FOR_START;
      break;                            // Interrupt Vector: I2C Mode: UCSTPIFG
    case USCI_I2C_UCRXIFG:
      read_i2c_data();
      break;                            // Interrupt Vector: I2C Mode: UCRXIFG
    case USCI_I2C_UCTXIFG:
      // Fix: Don't check stage==TRANSMIT_DATA (reSTART in write transaction could come before regAddr)
      UCB1TXBUF = *( ((uint8_t*)(&i2c_interface_struct)) + i2c_offset);
      while (UCB1IFG & UCTXIFG);
      break;                          // Interrupt Vector: I2C Mode: UCTXIFG
    default: break;
  }
}

The code hangs on line 88 (  while (UCB1IFG & UCTXIFG);  )  *didn't figure out how highlight works*

Below's the code for sensor check. First the line is established via buffer, then I read sensor, then I close the bus.

if(LTC4315_bus_enable(LTC4315_I2C_ADDRESS, LTC4315_BUS2)){ // 41 - ltc
    if(LM75A_read_temperature1(&temp)){
        temp_storage.device2_temperature_storage = (uint32_t)temp;
    }
}
LTC4315_bus_disable(LTC4315_I2C_ADDRESS, LTC4315_BUS2);

The hang is caught both with and without debugger being connected. Sometimes I caught debugger stuck on __bis_SR_register(LPM0_bits + GIE); string of I2C_Master_WriteReg, unable to close the LTC line, but the debugger said that UCB1_ISR is in use (which is slave). So I wonder, wether it can be that there is a conflict between UCB0_ISR and UCB1_ISR? Or is it smth else that I just don't see?

Maybe it's important: system works in while(1) loop in main. there are also UART, SPI and RTC_A working. Also, I tried to remove LPM0 from I2C Master, but then I failed to do read operations with LTC and couldn't open the link.

I appreciate your help.

  • What is line 88 (spin waiting for TXIFG=0) attempting to do? My first thought is that it shouldn't exist.

    You've just fed TXBUF, so TXIFG has gone to 0 before that line. At that moment either:

    1) TXIFG=0 (as expected) in which case the test is superfluous.

    2) You somehow manage to catch the tail-end of TXIFG becoming =0 (I'm not sure this is possible), in which case the test is superfluous.

    3) TXIFG went back to 1 in the meantime (through some happenstance) in which case you won't feed TXBUF again so it will stay =1 and your ISR is stuck forever..

    Summary: Try removing line 88.

  • Already tried that. Completely removing this line just breaks the system - it hangs on the previous line where I fill the buffer, not being able to complete even first transction.

  • The previous line (87) is an assignment "UCB1TXBUF=". I don't know how it could hang on that line. I wonder if the debugger is creating an illusion of some sort.

  • First, Thank you for the idea of removing the check.

    Seems I was "lucky" yesterday - deleting this checkreally hangs the system on line 87, but not on the first transaction. My guess was that MSP actually needs some time after buffer assignment, so I used some duct tape and inserted a __delay_cycles(100); where that check was.

    This seems to fix the issue, but a delay inside an interrupt.. The nature of this error still seems unclean, and the need to check the stability of system after every upcoming change in I2C is a way too high price for a quick solution.

    Any ideas what might cause it or some ways to fix?

  • As far as I know TXIFG goes to 0 immediately on writing TXBUF, though it may come back on very soon if the shift register is (was) empty.

    How do you determine that it's hanging in line 87?

  • Debug pause when msp stops responding.

    Here's what USCI registers look like then. It's like 5-7 seconds after connection loss. If I wait more, e.g. a minute, Disassembly just points to some unreachable memory

  • Qualified help is still appreciated!

    Turns out, the delay actually does nothing. I suspect there may be some interference between UCB0 and UCB1 interrupts, but got no clues what to do with it.

  • I don't have an answer. I spent some time last weekend trying to replicate this condition, but I was using a very simple Slave rather than your code. Some observations:

    1) It is possible, particularly under load, for the slave ISR to be presented with multiple IFGs, and the IV priority doesn't necessarily reflect chronology. You might e.g. see STTIFG, STPIFG, and TXIFG all at once, but that wouldn't be the order they arrived in. I checked some cases, but I still wonder if there's some combination that would confuse your state machine.

    2) TXNACK is a funny thing -- it's only used in certain circumstances (RXIFG only maybe?) and if set at the "wrong" time it kind of "hangs around" until later. Given (1) this seems a possibility.

    3) I'm still a little unclear on your precise symptom: I know of no mechanism for a simple assignment to hang, so I suppose it's looping through the ISR triggering TXIFG repeatedly. But your register display shows STTIFG, which is higher priority than TXIFG, so it should have been seen first. (This could also be a debugger illusion.)

    The first thing I would try is to slow down the master as a diagnostic. This would reduce the probability of (1) at least.

**Attention** This is a public forum