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.

I2C communication issue with magnetometer HMC5883L: Firmware stuck into USCI_B0 Interrupt Subroutine

Other Parts Discussed in Thread: CC430F5137

Hello everybody,

I am using a CC430F5137 for a wireless sensor.

I interface the CC430 with a Honeywell magnetometer (HMC5883L) using an I2C bus. The magnetometer is read every 2 seconds using I2C bus. The system is located on the ground into a plastic case and works fine for weeks but randomly gets stuck and stops accessing to the magnetometer via I2C.

After many attempts I managed to understand that the system is quite sensitive to moisture because, as soon as it is exposed to a source of humidity suddenly the CC430 gets stuck into the I2C interrupt subroutine.

Particularly the CC430 enters the USCI_B0 interrupt subroutine and gets stuck on the instruction at line 10:

#pragma vector = USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
{
  switch(__even_in_range(UCB0IV,12))
  {
  case  0: break;                           // Vector  0: No interrupts
  case  2: break;                           // Vector  2: ALIFG
  case  4: 		                            // Vector  4: NACKIFG
	I2C_failure = 0x01;		//sensor_failure is due to SoC to I2C peripheral communication error
	__bic_SR_register_on_exit(LPM1_bits); // Exit active CPU
	break;
  case  6: break;                           // Vector  6: STTIFG
  case  8: break;                           // Vector  8: STPIFG
  case 10:                                  // Vector 10: RXIFG
	RXByteCtr--;                            // Decrement RX byte counter
	if (RXByteCtr)
	{
	  *PRxData++ = UCB0RXBUF;               // Move RX data to address PRxData
	  if (RXByteCtr == 1)                   // Only one byte left?
		UCB0CTL1 |= UCTXSTP;                // Generate I2C stop condition
	}
	else
	{
	  *PRxData = UCB0RXBUF;                 // Move final RX data to PRxData
	  __bic_SR_register_on_exit(LPM1_bits); // Exit active CPU
	}
	I2C_failure = 0;						// NO I2C sensor_failure
	break;
  case 12:                                  // Vector 12: TXIFG
    if (TXByteCtr)                          // Check TX byte counter
    {
      UCB0TXBUF = *PTxData++;               // Load TX buffer
      TXByteCtr--;                          // Decrement TX byte counter
    }
    else
    {
      UCB0CTL1 |= UCTXSTP;                  // I2C stop condition
      UCB0IFG &= ~UCTXIFG;                  // Clear USCI_B0 TX int flag
      __bic_SR_register_on_exit(LPM1_bits); // Exit LPM0
    }
	I2C_failure = 0;				// NO I2C sensor_failure
	break;
  default: break;
  }
}

The problem is that the CC430 never exits from the interrupt and gets stuck on the line 10 instruction without signalling the NACK situation.

The only way to restore the system is with a strong reset.

The other strange this is that if I place a breakpoint on thisinstruction, the Debugger never stops there and looks running but, as soon as I pause it, the code execution results stuck on that instruction.

The questions are three:

  1. Are there known issues regarding the I2C usage in humidity conditions? How can I avoid them? Is there any workaround?
  2. Why does the CC430 get stuck on that instruction? Is there any known workaround?
  3. Why does the breakpoint doesn't work even if, when I pause, the Firmware results stuck on that instruction?

In order to be more detailed I also want to underline that the CC430 gets stuck on that instruction after having entered a first time the interrupt subroutine for loading the first byte to be sent in the UCB0RXBUF buffer (instruction 32). The UCTXSTT bit, furthermore, gets never reset (as it should be after I2C address sending) and firmware gets stuck on the line 10 instruction.

Waiting for your reply,

Best regards,

Claudio

  • The only reason I can think of why the code could be stuck in the __bic_SR_register_on_exit line is that you already have a  stack overflow and this instruction causes a write to some vital register.

    However, I rather think that you are subject to code optimization and re-arrangement. The compiler might have joined the three __bic instructions in your ISR into one, and the debugger shows you the first when breaking, while you actually are at one of the others. From the program counter at the break moment, the debugger cannot know. You should disable optimizations when debugging. Also, a look at the assembly view may shed some light on it.
    I hope that I2C_failure (and all other global vars used in the ISR) is declared as volatile, or else main might not notice the changes done in the interrupt (by looking at  a local register copy of that variable, which of course won’t change)

    I2C is quite robust. However, on noisy environment as well as on bus topologies with rather high parasitic and line capacitances, stronger pull-up resistors (down to 1k) are required. Same for high I2C bus speeds (100-400kHz). Look at the waveforms with a scope.

**Attention** This is a public forum