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

Other Parts Discussed in Thread: MSP430F5438

i2c start

 

hi everyone, I just started a i2c program to check if my slave is present (digital pot AD5254). I get an ACK ok but when i send a start condition it sends that slve address ok.

i2c1_start(); // This condition is always executed.

i2c1_write(0x00); // Sometimes it will send this, other times it skips it and automatically sends a stop.

i2c1_write(0x0ff);   // It rarely gets this far

i2c1_stop();

Here is my code.

#include "msp430f5438.h"

void i2c1_init(unsigned int slave_add, unsigned char fscl);

unsigned char i2c1_start(void);

void i2c1_stop(void);

unsigned char i2c1_write(unsigned char data);

 

void main (void){

i2c1_init(0x2c, 12); // Slave address

i2c1_start();

i2c1_write(0x00); // Register 0

i2c1_write(0x0ff); // Position ff

i2c1_stop();

__bis_SR_register(LPM0_bits + GIE);     // Enter LPM0, enable interrupts

return;

}

 

 

 

void i2c1_init(unsigned int slave_add, unsigned char fscl)

{

    /* Local Variables */

 

    /* Code */

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

   UCB0BR0 = fscl;                   // FSCL = SMCLK/fscl

   UCB0BR1 = 0;

   UCB0I2CSA = slave_add; // Slave Address is 02Ch 

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

   UCB0IE = UCTXIE + UCRXIE; // Enable TX & RX Interrupts

   __bis_SR_register(GIE); // Interrupts enabled

    return;

 

}//end of i2c1_init

 

 

unsigned char i2c1_start(void)

{

UCB0CTL1 |= (UCTR + UCTXSTT);             // Start condition sent?

    return 1;

}//unsigned char i2c1_start(void)

 

unsigned char i2c1_write(unsigned char data)

{

//while(UCB0STAT & UCBBUSY); //USCI busy?

UCB0TXBUF = data;

return 1;

}

 

void i2c1_stop(void)

{

UCB0CTL1 |= UCTXSTP;                    // I2C stop condition

    return;

}// void i2c1_stop(void)

 

 

#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:

   UCB0IFG &= ~UCNACKIFG;

   break;                           // Vector  4: NACKIFG

  case  6: break;                           // Vector  6: STTIFG

  case  8: break;                           // Vector  8: STPIFG

  case 10: // Vector 10: RXIFG

  

   break;                          

  case 12:                                  // Vector 12: TXIFG  

      UCB0IFG &= ~UCTXIFG; // Clear TX Flag

    break;

  default: break;

  }

}

Does anybody have an idea where i may be going wrong or what is causing this to happen.

Thanks for any help.

 

  • First, you should separate the words in your tag list by komma. As it is now, it is jsut one large tag that nobody will search or find. :)

    Then, well, I2C is a tricky thing. The hardware module does so many things for you that you can easily mess things up by not peroperly letting it make its job.

    First, your ISR does not do enything useful. You just ignore if an error is flagged (such as a NAK from the slave or no response at all) and do nothing useful when a byte is received or a byte to send is required. You could as well not enable the ISR as well and ignore the flags.

    Then you stuff bytes into TXBUF without checking whether the last byte has been sent already. USBBUSY isn't good for that, as it is set at START and cleared after a STOP. Only UCTXIFG will tell whether the TX buffer is ready for the next byte (and your ISR will manually clear it immediately, leaving your i2c1_write function in the dark.

    Also, while UCTXIFG is set, the USCI will NOT clock the ACK bit in, but hold the clock until either a new byte has been written into TXBUF or UCTXSTP has been set.

    If you set UCTXSTP before a byte has been loaded from TXBUF into the output shift register, this byte will NEVER be sent. a STOP condition is generated as soon as the byte that is currently shifted out is sent completely. This may even be the slave address itself.

    So if you start, write two bytes into TXBUF and then stop, it is a race condition whether you write the stop before or after the first data byte has started sending. The second one will not be output at all, unless your I2C clock is at least as fast as MCLK.

    Using an ISR is only useful if you make background transfers from a buffer. If you are writing data byte-by-byte in your main, do NOT use an ISR and do a busy-wait for the proper IFGs to time your output.

    It IS possible, to put all the I2C handling into an ISR. Then you'll need a buffer and a function that it called with 'send x bytes at buffer y to slave z' or 'receive x bytes from slave z into buffer x'. The function then will set up the transfer, put the CPU into sleep mode and wake up when the transfer is done (or broken). But unless you plan to do such things, stay away from ISRs for I2C. It won't do any good.

    ps: for SPI and UART, for everythign that gives or takes data, the same applies. Unless you use background transfers with buffers, don't use ISRs.

  • Thanks for the help guys. It was indeed timing issues that caused me to miss the data being sent. Also i think the interrupts were causing the data the be continually sent even when the program was executed. 

     

    Thanks again.

  • Hi TI employees,

    I like to know is it true that I could use MSP430 USCI_Bx to generate the attached image master SCL clock stretching signal (1.3.5.1 Clock Stretching) with a delay function to cancel a slave device from sleep ?

  • It seems that your slave goes into sleep and wakes up if a clock low of sufficient length is detected.
    If so, you can generate the LOW pulse by simply switching the SCL pin to I/O mode (configured for low output) and back, without any fancy USCI tricks.
    However, I don't remember such a sleep/wakeup to be part of the I2C specification, so more details are required.

    To answer your question literally: yes, you could. After all, generating a clock pulse depending on clock speed is one of the main functions of the USCI I2C hardware. Whether it is useful four your problem is another question. :)
  • Hi Jens-Michael Gross,

    i think i understood what are you trying to mean.
    1 / 100 KHz = 10 uSec or
    1 / 400 KHz = 2.5 uSec
    So, does that helpful to me ? i got it. seem like i have to take the first option simply switching the SCL pin to I/O mode and back.

    Yes, sleep / wakeup never be be part of the I2C specification.
    appreciate for the reply and suggestion.

  • One thing to add: the 'clock stretching' feature is meant to enlarge a clock pulse depending on events. Like waiting for you to write something to TXBUF before the transmission can proceed, or waiting for you to read the last received byte from RXBUF so it isn't overwritten by the next incoming byte.
    It cannot be used to deliberately generating a clock pulse of a desired length. Even though you of course can use a timer to time the _end_ of a clock stretching situation, you cannot _start_ it at will. It will only happen at certain situations. Situations in which you can't make use of it for your purpose.

**Attention** This is a public forum