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.

HELP - MSP430F5438 Interfaced with HMC6343 via I2C - ALWAYS NACK!!!!!!!!!!!!! SEE OSCILLOSCOPE PLOT

Other Parts Discussed in Thread: MSP430F5438

CIDE: Code Composer v4.2.1
MICROCONTROLLER: MSP430F5438
SENSOR: Honeywell HMC6343 (Digital Compass)

GOAL: To interface the MSP430F5438 to the HMC6343 using I2C.

Upon sending a start condition, slave address (32), and R/W bit (0), we receive a NACK from the slave.  We are running out of ideas for solving this issue.... We wrote our own code from scratch and sampled TI's code with no success.

Currently we are trying to get an AK bit from the slave using TI's sample code below....but we always get a NACK.  Why doesn't this work?!?!?!?

Also, see oscilloscope plot and circuit diagram below.

Any help is much appreciated :)

//******************************************************************************
//  MSP430F54x Demo - USCI_B0 I2C Master TX single bytes to MSP430 Slave
//
//  Description: This demo connects two MSP430's via the I2C bus. The master
//  transmits to the slave. This is the master code. It continuously
//  transmits 00h, 01h, ..., 0ffh and demonstrates how to implement an I2C
//  master transmitter sending a single byte using the USCI_B0 TX interrupt.
//  ACLK = n/a, MCLK = SMCLK = BRCLK = default DCO = ~1.045MHz
//
//                                /|\  /|\
//                MSP430F5438     10k  10k     MSP430F5438
//                   slave         |    |         master
//             -----------------   |    |   -----------------
//           -|XIN  P3.1/UCB0SDA|<-|----+->|P3.1/UCB0SDA  XIN|-
//            |                 |  |       |                 |
//           -|XOUT             |  |       |             XOUT|-
//            |     P3.2/UCB0SCL|<-+------>|P3.2/UCB0SCL     |
//            |                 |          |                 |
//
//   M Smertneck / W. Goh
//   Texas Instruments Inc.
//   September 2008
//   Built with CCE Version: 3.2.2 and IAR Embedded Workbench Version: 4.11B
//******************************************************************************

#include "msp430x54x.h"

unsigned char TXData;
unsigned char TXByteCtr;

void main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  P3SEL |= 0x06;                            // Assign I2C pins to USCI_B0
  UCB0CTL1 |= UCSWRST;                      // Enable SW reset
  UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;     // I2C Master, synchronous mode
  UCB0CTL1 = UCSSEL_2 + UCSWRST;            // Use SMCLK, keep SW reset
  UCB0BR0 = 12;                             // fSCL = SMCLK/12 = ~100kHz
  UCB0BR1 = 0;
  UCB0I2CSA = 0x32;                         // Slave Address is 032h
  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
  UCB0IE |= UCTXIE;                         // Enable TX interrupt

  TXData = 0x01;                            // Holds TX data

  while (1)
  {
    TXByteCtr = 1;                          // Load TX byte counter

    while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent
    UCB0CTL1 |= UCTR + UCTXSTT;             // I2C TX, start condition
   
    __bis_SR_register(LPM0_bits + GIE);     // Enter LPM0 w/ interrupts
    __no_operation();                       // Remain in LPM0 until all data
                                            // is TX'd
                                             
    TXData++;                               // Increment data byte
  }
}

//------------------------------------------------------------------------------
// The USCIAB0_ISR is structured such that it can be used to transmit any
// number of bytes by pre-loading TXByteCtr with the byte count.
//------------------------------------------------------------------------------
#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: 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
    {
      UCB0TXBUF = TXData;
      UCB0IE &= ~UCTXIE;                  // Load TX buffer
      TXByteCtr--;                          // Decrement TX byte counter
    }
    else
    {
      UCB0CTL1 |= UCTXSTP;                  // I2C stop condition
      UCB0IFG &= ~UCTXIFG;                  // Clear USCI_B0 TX int flag
      UCB0IE &= ~UCTXIE;
      __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
    }
    break;
  default: break;
  }
}

  • Hi Nick,

    I'm not sure this post will be helpful for you, but please take a look at http://electronix.ru/forum/lofiversion/index.php/t70411.html

    This is a russian forum, the same problem with HMC5843 (also a I2C compass) is described here. In brief (if you're not familiar with russian :) ) :

    1)The author doesn't want to use interrupts.

    2)He say that he has NACK on 9th clock while writing to HMC.

    3)The solution for him was to write:

    ...

      UCB0CTL1 |= UCTXSTT | UCTR; // Send START, SLAVE ADDR with WRITE

    ... (see the source)

    instead of

    ...

    UCB0CTL1 |= UCTXSTT | UCTR; // Send START, SLAVE ADDR with WRITE
     while (UCB0CTL1 & UCTXSTT); //<-- THIS LINE IS WRONG

    ...

    He provides working source:


    #define NACK_TX -1
    #define NACK_RX -2

    int I2C_Read(unsigned char addr, unsigned char *data, int cnt){
    int result,i;

    result = 0;

    UCB0CTL1 |= UCTXSTT | UCTR; // Send START, SLAVE ADDR with WRITE
    // while (UCB0CTL1 & UCTXSTT);


    while (!(IFG2 & UCB0TXIFG)) if (UCB0STAT & UCNACKIFG){
    result = NACK_TX;
    goto STOP;
    }
    UCB0TXBUF = addr;

    while (!(IFG2 & UCB0TXIFG)) if (UCB0STAT & UCNACKIFG){
    result = NACK_TX;
    goto STOP;
    }

    UCB0CTL1 &= ~UCTR;
    UCB0CTL1 |= UCTXSTT; // Send START, SLAVE ADDR with WRITE

    for (i=0;i<cnt;i++){

    while (!(IFG2 & UCB0RXIFG)) if (UCB0STAT & UCNACKIFG){
    result = NACK_RX;
    goto STOP;
    }

    data[i] = UCB0RXBUF;

    }//for

    STOP:
    UCB0CTL1 |= UCTXSTP;
    while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent

    if (IFG2 & UCB0RXIFG) UCB0RXBUF;

    return result;

    }//I2C_Read

    As I'm unfamiliar with HMCxxxx, I can't say exactly that this solution is acceptable for you, but take a look at it.

    Regards,

    Ilia V. Davidov

  • First, why are ~95% of all tags you have selected totally unrelated to your problem? Did you just check every tag you could find? It might get you more attention by putting you on everyones list, but rather decreases the chance that anyone scanning the tags will answer.

    Now to your problem: I'm surprised by your scope reading: the idle state of the clock line should be high and not low.

    Nick Gentry said:
    UCB0I2CSA = 0x64;                         // Slave Address is 048h

    Is it 48h (=0x48) or is it 0x64? And if it is0c64, is it really 0x62 or is it rather 0x32? The slave address does not contain the R/W bit. This is added from the UCTR bit when sending the start sequence. Note that thsi bit is added physically as 8th bit to the 7 bit address and not just numerically added to an 8 bit address.
    Many datasheets give the device address as even 8 bit address, which is used 1:1 for read operations and '1' has to be added numerically for a write operation.
    The MSP, however, requires the upper 7 bit of this address as slave address and adds the R/W bit as a separate 8th bit.
    It's clearely written in teh users guide, but many people seem to miss this important info.

    Nick Gentry said:
    UCB0CTL1 |= UCTR + UCTXSTT;             // I2C TX, start condition
        __bis_SR_register(LPM0_bits + GIE);     // Enter LPM0 w/ interrupts

    This combinations makes no sense. Either you wait for the start sequence to be clear or you enter LPM and let the ISRs handle everything.
    If the ISRs handle everything, then it is necessary to check for UCNACKIFG too in case the slave didn't respond (else you will not wake up ever), yet your main code doesn't check this too.

    Nick Gentry said:
          UCB0IE &= ~UCTXIE;                  // Load TX buffer

    This disables teh interrupt rather than clearing the interrupt flag (whcih has been already cleared by the previous write to TXBUF. Once the interrupt is disabled, you won't enter the ISR anymore. THis also means that after pushing the first byte into TXBUF, no more bytes are pushed, no end of the transfer is detected and therefore the LPM is never exited.
    Since the first interrupt happens the moment you set UCSTT (and is discarded when the slave doe snot respond), the TX interrupt is already disabled when you enter LPM. Sleep well and forever, MSP (until the next reset).

  • Thank you for your replies! :)

    Jens: I have have modified the tags, thanks for your input. Regarding the slave address, it is supposed to be 32h. I was doing some testing and forgot to change the code back to 32h (0b110010) before posting.

    I am still looking over your comments and doing some experimenting. Will send update soon.

  • Hi Jens,

    To clarify, we did set the slave address to be 32h in the slave address register as shown in the oscilloscope plot. I'm slightly confused about the addition of the R/W bit. The line, UCB0CTL1 |= UCTR + UCTXSTT; does it for us, doesn't it? The idle state of the clock being high is something I completely missed and can't explain why it's inverted. We changed the pull up values to 1 KOhm from 10 KOhm to get a better signal for the clock. With the 10 Kohm pull ups, the clock looked more like a capacitor charging and discharging (o-scope plot was taken using 10K pullups). Do you think this is ok? I'll check for the UCNACKIFG too, should i do this by enabling the interrupt for a NACK?

    I have a few more questions as well. The MSP430F5438 goes into a transmit interrupt after the start condition is sent and after I load the TXBUF with data, the program just lives in the interrupt and never leaves it. I don't know what we should do about that. Also, when I step through the line   UCB0CTL1 |= UCTR + UCTXSTT, I watch the UCB0CTL1 and the UCTXSTT bit is never set. Is this supposed to happen? Do you know what's wrong? We'd greatly appreciate your input.

    Also, to debug, I just sent a repeated start condition without enabling interrupts or doing anything else. The slave never sends back an ACK. Also, thanks for pointing out the sleep mode condition (I've never implemented it before). I'm just not going to use it for now. So, would just executing _EINT() before the beginning of the loop be sufficient and then we could ignore that entire line altogether?

    Thanks,

    Nick

  • Any idea why the clock signal seems to be inverted? We are using 10Kohm pullup resistors to the +3.3V line.  How is this possible?  See oscilloscope plot above.

     

    Thanks!!

  • Nick Gentry said:
    Any idea why the clock signal seems to be inverted?

    It isn't inverted. The protocol allows data changes only during low clock signal (except for start/stop condition). And this is what happens. All data changes happen at/after the falling edge of the clock, and only the start/stop happens after the rising edge/on high level. That's okay. It's only during the idle time where the clock should be high but isn't.

  • I am trying to do the same but my code is not working. Can you please guide me with some help.

  • #include "msp430.h" volatile unsigned char receivedata[10]; unsigned char transmitcmd = 0x50; volatile unsigned char txready = 1; unsigned char *ptr; unsigned char counter; void transmit() { UCB3CTL1 |= UCSWRST; UCB3I2CSA = 0X32; UCB3CTL1 &= ~UCSWRST; UCB3IE |= UCTXIE + UCNACKIE; while (UCB3CTL1 & UCTXSTP); UCB3CTL1 |= UCTXSTT + UCTR; _EINT(); } void receive() { UCB3CTL1 |= UCSWRST; UCB3I2CSA = 0X33; UCB3CTL1 &= ~UCSWRST; UCB3IE |= UCRXIE; UCB3CTL1 &= ~UCTR; while(UCB3CTL1 & UCTXSTP); /* Stop has got send */ UCB3CTL1 |= UCTXSTT; /* Generate start condition in master mode */ __delay_cycles(80); _EINT(); } void main() { WDTCTL = WDTPW + WDTHOLD; /* holding watchdog timer */ P1DIR = 0X03; P1OUT = 0X01; P10SEL = 0X06; /* selecting the I2C pin for USCI_B3 */ UCB3CTL1 = UCSWRST; /* software reset enable. Held in reset state */ UCB3CTL0 |= UCMST + UCMODE_3 + UCSYNC; /* Configuring into I2C Master mode in synchronous state */ UCB3CTL1 |= UCSSEL_3 + UCSWRST; /* selecting the clock source to SMCLK */ UCB3BR0 = 12; /* Bit clock prescalar */ UCB3BR1 = 0; /* Bit clock prescalar (UCB3BR0 + UCB3BR1*256) */ UCB3I2CSA = 0X32; /* Slave Address 048h */ UCB3CTL1 &= ~UCSWRST; /* USCI reset released for operation */ UCB3IE |= UCRXIE + UCTXIE; /* Transmit Interrupt Enable */ _EINT(); while(1) { txready = 1; ptr = (unsigned char*)receivedata; counter = 6; transmit(); __delay_cycles(1000); receive(); __delay_cycles(200000); } } #pragma vector = USCI_B3_VECTOR /* Vector for USCI_B3 Interrupts */ __interrupt void abc() { switch(__even_in_range(UCB3IV,12)) /* USCI interruot vector register value */ { case 2: case 4: case 6: case 8:break; case 10: counter--; if(counter) /* Moving the received value to a variable */ { *ptr = UCB3RXBUF; ptr++; if(counter==1) UCB3CTL1 |= UCTXSTP; } else *ptr = UCB3RXBUF; break; case 12: if(txready) /* If data is ready for transmission */ { UCB3TXBUF = transmitcmd; /* Move the data into transmit buffer */ txready--; P1OUT ^=0X01; } else { UCB3CTL1 |= UCTXSTP; /* Generate the STOP Condition */ UCB3IFG &= ~UCTXIFG; /* Clearing the transmit interrupt flag */ } break; } }

**Attention** This is a public forum