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

Other Parts Discussed in Thread: MSP430G2553, MSP430F5529

Hi, I am trying to implement this communication with TI's I2C hardware:

Master S1 AD+W RA S2 AD+R Nack P
Slave ACK1 ACK2 ACK3 Data

where Master is the MSP430g2553, and slave is the MPU 6050.

S is start, P is stop conditions. AD is MPU address, R read bit, W write bit, RA is the register address in the MPU I want to read. Data is the value at RA.

What I don't understand is how to watch for the second acknowledge ACK2, so that I can begin with the next start condition S2. Can someone explain the process?

Thanks, Kevin

 

  • You don't write which MSP you're using. If it an MSP with an USCI module, then the USCi does most of the work for you. YOu tell it the 7 bit address of the slave (without the R/W bit!), set the UCTXSTT bit and the USCi writes the start condition and the address byte. In the meantime RXIFG bit tells you (or calls an ISR) that you can write the first data byte (in this case the register address) to TXBUF and once the start byte was written and there is a first data byte in TXBUF, UCTXSTT clears and either sending of the RA byte begins or UCNACKIFG is set (and RA is discarded). At this point, when RXIFG tells you that RXBUF is free for hte next byte to be sent, you can simply clear UCTR and set UCTXSTT once more When it clears again, the second start byte has been sent and either the USCI is receiving the register content (telling you with RXIFG when the byte arrived) or UCNACKIFG tells you that the slave didn't respond. (some slaves do not respond for some time after receiving a config change or after an operation has been initiated)

    There is no need to check ACK2 at all, except if you want to know whether the slave might have rejected your RA (e.g. because it was an illegal register value, some slaves do this).

  • I did mention that it was an msp430g2553, but whatever.

    Thanks a lot for the help, that pushed me in the right direction anyways. I managed to hack it out with no interrupts and just MCU cycle delays and doing what you said while watching on an oscilloscope. I'm going to do a more stable program rewrite now that I got something tho!

  • hi kevin,

    i'm trying to implement your same code but i have some problem about setting of r/w condition in address byte and about sending stop condition.

    do you success in communicating with mpu6050?

    thanks

    here my code:

    #include <msp430.h>

    #define I2CWRITE 0;

    unsigned short I2CMode = 0;

    unsigned short MPURegAddress = 0x1B;
    unsigned short MPURegValue = 0;


    unsigned char *PTxData; // Pointer to TX data
    unsigned char TXByteCtr;
    const unsigned char TxData[] = // Table of data to transmit
    {
    0x1B,
    0x18
    };

    unsigned char *PRxData; // Pointer to RX data
    unsigned char RXByteCtr;
    volatile unsigned char RxBuffer[128]; // Allocate 128 byte of RAM

    int main(void)
    {
    WDTCTL = WDTPW + WDTHOLD; // Stop WDT
    P1SEL |= BIT6 + BIT7; // Assign I2C pins to USCI_B0
    P1SEL2|= BIT6 + BIT7; // 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 = 0x68; // Slave Address is 048h
    UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
    IE2 |= UCB0TXIE + UCB0RXIE; // Enable TX and RX interrupt

    while (1)
    {
    I2CMode = I2CWRITE;
    PTxData = (unsigned char *)TxData; // TX array start address
    TXByteCtr = sizeof TxData; // Load TX byte counter
    while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
    UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
    __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts
    // Remain in LPM0 until all data
    // is TX'd

    __delay_cycles(100);

    I2CMode = 1; // Send with restart condition
    PTxData = (unsigned char *)TxData; // TX array start address
    while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
    UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
    __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts
    // Remain in LPM0 until all data
    // is TX'd
    I2CMode = 2;
    PRxData = (unsigned char *)RxBuffer; // TX array start address
    RXByteCtr = 1;
    UCB0CTL1 &= ~UCTR; // clear UCTR bit to select read mode
    UCB0CTL1 |= UCTXSTT; // I2C TX, start condition
    __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts

    __delay_cycles(100);
    }
    }


    //------------------------------------------------------------------------------
    // The USCIAB0TX_ISR is structured such that it can be used to transmit any
    // number of bytes by pre-loading TXByteCtr with the byte count. Also, TXData
    // points to the next byte to transmit.
    //------------------------------------------------------------------------------
    #pragma vector = USCIAB0TX_VECTOR
    __interrupt void USCIAB0TX_ISR(void)
    {
    switch (I2CMode)
    {
    case 0 : // simple write: msp430 sends all bytes in TxData
    if (TXByteCtr) // Check TX byte counter
    {
    UCB0TXBUF = *PTxData++; // Load TX buffer
    TXByteCtr--; // Decrement TX byte counter
    }
    else
    {
    UCB0CTL1 |= UCTXSTP; // I2C stop condition
    IFG2 &= ~UCB0TXIFG; // Clear USCI_B0 TX int flag
    __bic_SR_register_on_exit(CPUOFF); // Exit LPM0
    }
    break;

    case 1 : //msp sends register address to mpu
    UCB0TXBUF = *PTxData; // Load TX buffer
    IFG2 &= ~UCB0TXIFG; // Clear USCI_B0 TX int flag
    __bic_SR_register_on_exit(CPUOFF); // Exit LPM0
    break;

    case 2: //after restart condition msp reads byte sended
    if(RXByteCtr)
    {
    *PRxData++ = UCB0RXBUF; // Move RX data to address PRxData
    RXByteCtr--;
    }
    else
    {
    UCB0CTL1 |= UCTXSTP; // I2C stop condition
    IFG2 &= ~UCB0TXIFG; // Clear USCI_B0 TX int flag
    __bic_SR_register_on_exit(CPUOFF); // Exit LPM0
    }

    break;
    }

    }

     

  • Hi Daniele, I did indeed get my I2C working with the MPU6050. I don't have the time to personally go through your code, but here is what I am currently using. I hope you can find help from this. I did not use the other power modes, and my Read routine needs some upgrades on where it puts the result, but I'm 80% sure it is working. Use of breakpoints and the scope can help immensely in debugging. I do notice some issues where sometimes I have to unplug and then plug the launchpad back in to reset the I2C peripheral. Other parts of the solution were to put 4.7k pull up resistors on SDA and SCL, as well as removing the jumper on P1.6 that goes to the green LED because use of the LED messes up the SCL.

    Daniele Roppoli1 said:
    i'm trying to implement your same code but i have some problem about setting of r/w condition in address byte and about sending stop condition.

    EDIT: I should mention in answer to your question if you are doing this in hardware, then the R/W bit will be automatically sent after the SA depending on whether the UCTR is high or low in UCB0CTL1. I'm not 100% clear on the Stop condition, but you essentially raise the Stop condition bit while you are in the process of Transmitting or Receiving, and then it will generate the correct ending to the Read or Write Sequence.

    #include "I2C.h"
    
    char buff_status = 0;
    
    char I2c_Tx;
    char SAdd;
    char SIAdd;
    char SASent;
    char SIASent;
    char Register;
    char ByteSent;
    char ByteRecd;
    char Result;
    char Result2;
    char RepStart;
    char Seq_ReadTWriteF;
    char *ptr_Rx_byte;
    signed char I2cByteCnt;
    
    char first_time;
    
    
    
    void init_Clk(void){
      
      // Set up DCOCLK and thus SMCLK for 8 MHz (no dividers)
      BCSCTL1 = CALBC1_8MHZ; 
      DCOCTL = CALDCO_8MHZ;
      
    }
    
    void Cycle_delay(int cycles_sq){
      int i;
      for(i=0; i<cycles_sq; i++){
        for(i=0; i<cycles_sq; i++){ 
        }
      }
      
    }
    
    void ScopeTag(char length){
      P1OUT ^= 1;
      Cycle_delay(length);
      P1OUT ^= 1;
    }
    
    /*///////////////////////////////////
                     ISRs
    *////////////////////////////////////
    
    
    // USCIAB0RX Interrupt Service Routine
    #pragma vector = USCIAB0RX_VECTOR
    __interrupt void USCIAB0RX_ISR(void)
    {
      ScopeTag(10);
    }
    
      
      
    // USCIAB0TX Interrupt Service Routine
    #pragma vector = USCIAB0TX_VECTOR
    __interrupt void USCIAB0TX_ISR(void){
        
      if(Seq_ReadTWriteF){
        // Single Byte Read Sequence
        
        if(SASent == False && SIASent == False){
          //Then first Interrupt so SA has been sent
          //  and you can load the buffer:      
          SASent = True;
          UCB0TXBUF = SIAdd;
          
        }else if(SASent == True && SIASent == False){
          //Then next interrupt so SA, and SIA are sent
          //  so start the Rx section
          UCB0I2CSA = SAdd;
          UCB0CTL1 &= ~UCTR;    
          UCB0CTL1 |= UCTXSTT;
          SIASent = True;
          SASent = False;
          
        }
        
        if(IFG2 & UCB0RXIFG){
          //If Rx flag is raised, (raised twice - I think once when ready, once when done)
          //  Then read Rxd byte and raise stop condition flag.
          if(first_time == True){
            Result = UCB0RXBUF;
          }else{
            Result2 = UCB0RXBUF;
          }
          first_time = False;
          UCB0CTL1 |= UCTXSTP;
          ByteRecd = True;
          ScopeTag(1);        
        }
        
      
      }else{
        // Single Byte Write Sequence
        
        if(SASent == False && SIASent == False){
          //Then first Interrupt so SA has been sent
          //  and you can load the buffer:      
          SASent = True;
          UCB0TXBUF = SIAdd;
          
        }else if(SASent == True && SIASent == False){
          //Then next interrupt so SA, and SIA are sent
          //  so start the Tx section
          //UCB0I2CSA = SAdd;
          //UCB0CTL1 |= UCTR;    
          //UCB0CTL1 |= UCTXSTT;
          SIASent = True;
          UCB0TXBUF = I2c_Tx;
          //SASent = False;
          
        }else if(SASent == True && SIASent == True){
          UCB0CTL1 |= UCTXSTP;
          ByteSent = True;
          ScopeTag(4);
        }    
        
      } 
        
      IFG2 &= ~(UCB0TXIFG + UCB0RXIFG);
      
    }
    
    /*///////////////////////////////////
                 I2C Functions
    *////////////////////////////////////
    
    void Write8Bits(char my_SA, char my_SIA, char my_data, char my_start, char my_length){
        //      010 my_data: value to write
        // 76543210 bit numbers
        //    xxx   args: my_start=4, my_length=3 (start from right + 1, length to right)
        // 00011100 mask byte
        // 10101111 original value (sample)
        // 10100011 original & ~mask
        // 10101011 masked | value
      
      ReadI2C(my_SA, my_SIA);
      char prev_data = Result;
      
      //if (prev_data != 0) {
        
            char mask = ((1 << my_length) - 1) << (my_start - my_length + 1);
            // shift data into correct position
            my_data <<= (my_start - my_length + 1); 
            // zero all non-important bits in data
            my_data &= mask; 
            // zero all important bits in existing word
            prev_data &= ~(mask); 
            // combine data with existing word
            prev_data |= my_data; 
            
            WriteI2C(my_SA, my_SIA, prev_data);
      //} else {
            // do nothing;
      //}
      
      
    }
      
    // Single Byte Read Sequence:
    //Master  S  AD+W     RA     DATA     P
    //Slave           ACK    ACK      ACK 
    void WriteI2C(char my_SA, char my_SIA, char my_Data){
        
      SAdd = my_SA;
      SIAdd = my_SIA;
      I2c_Tx = my_Data;
      
      Cycle_delay(200);
      
      Seq_ReadTWriteF = False;
      ByteSent = False;
      SASent = False;
      SIASent = False;
      first_time = True;    
      
      // Begin Tx
        P1OUT &= ~BIT0;
        ScopeTag(1);
        
        UCB0I2CSA = SAdd;
        UCB0CTL1 |= UCTR;
        UCB0CTL1 |= UCTXSTT;           
      
      // Begin Rx
        //wait for byte to be Rxd    
        while(ByteSent == False || (UCB0CTL1 & UCTXSTP) ){
        }
        
        ScopeTag(1); 
      
    }
    
    // Single Byte Read Sequence:
    //Master  S  AD+W     RA     S  AD+R           NACK  P
    //Slave           ACK    ACK         ACK  DATA
    
    void ReadI2C(char my_SA, char my_SIA)
    {
    
      SAdd = my_SA;
      SIAdd = my_SIA;
    
      Cycle_delay(200);
    
      Seq_ReadTWriteF = True;
      ByteRecd = False;
      SASent = False;
      SIASent = False;
      first_time = True;    
    
      // Begin Tx
        P1OUT &= ~BIT0;
        ScopeTag(1);
        
        UCB0I2CSA = SAdd;
        UCB0CTL1 |= UCTR;
        UCB0CTL1 |= UCTXSTT;           
    
      // Begin Rx
        //wait for byte to be Rxd    
        while(ByteRecd == False || (UCB0CTL1 & UCTXSTP) ){
        }
        ScopeTag(1); 
        
        Result *= 1;
        
    }
    
    //NOTE: Initializing or Reconfiguring the USCI Module
    //The recommended USCI initialization or reconfiguration process is:
    //1. Set UCSWRST (BIS.B #UCSWRST,&UCxCTL1)
    //2. Initialize all USCI registers with UCSWRST=1 (including UCxCTL1)
    //3. Configure ports.
    //4. Clear UCSWRST via software (BIC.B #UCSWRST,&UCxCTL1)
    //5. Enable interrupts (optional) via UCxRXIE and/or UCxTXIE
    
    void init_I2C(){
      
      //1. 
      //    Hold Reset
      UCB0CTL1 |= UCSWRST;
      
      //2. 
      //    Master, I2C Mode, Synchronous;
      //    SMCLK = DCOCLK = 8 Mhz, Transmitter;
      //    USCI Clock = SMCLK / 80 = 100 kHz;
      UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;
      UCB0CTL1 = UCSSEL1 + UCSSEL0 + UCTR + UCSWRST;
      UCB0BR0  = 77;
      UCB0BR1  = 0;
      
      //3.
      //    Select P1.6 and P1.7 for I2C mode.
      //P1DIR = from USCI
      P1SEL  = BIT6 + BIT7;
      P1SEL2 = BIT6 + BIT7;
      
      //4.
      //    Release Reset
      UCB0CTL1 &= ~UCSWRST;
      
      //5.
      //    Enable Interrupts
      UCB0I2CIE = UCNACKIE;
      IFG2 &= ~(UCB0TXIFG + UCB0RXIFG);
      IE2 |= UCB0TXIE + UCB0RXIE; 
      __enable_interrupt();
      //__bis_SR_register(GIE);
    }
    

  • thank you very mutch! i have now a working code to check my error!
    I really don't need to use low power mode; i used a not working code from internet and i tried to correct it but this is first time i'm using I2C on MSP430 MCU.

    About your issue, i notice that if you don't complete communication with MPU it rests in an unknown state and it' doesn't free the bus.

    bye

    Daniele

  • Hi kevin,I am currently trying to interface the msp430 with mpu6050 and I am very much new to microcontroller programming.It would be very much helpful if you could provide me with a proper working code to read the raw values from the mpu6050.I am also using msp430g2553 and I have a deadline for my project which is due next week.So it would be really helpful if you could help me with this.My mail id is arvinasokan@gmail.com

  • can anyone please provide me the compiled code of interfacing msp430f5529 with mpu6050?

    It is very urgent..my email id is deepakkeswani03@gmail.com

**Attention** This is a public forum