2
\$\begingroup\$

I´ve been searching for libraries or documentations for helping me reading from and writing to the acell + gyro breakout MPU-6050 from InvenSense. I managed to read it with the arduino's libraries made by Jeff Rowberg (Link), but now I want to use the TI MSP430g2553 (with the CCS or IAR). I intend to make a good library and some examples in order to share it, since I can`t find it as easily as I found for Arduino and PIC.

The code I have so far seems to work, but I cant be certain. The most basic test is to read the WHO_AM_I register, which returns the slave address (0x68). The code I used is based on this topic, which is based on a TI documentation.

My code is very simple:

Includes:

#include <msp430g2553.h>
#include "MPU6050.h"

Prototypes:

void init_I2C(void);
int i2c_notready(void);
char Receive(char);
void Transmit(char, char);

Main:

int main(void) {

         volatile char who_am_i;
         char test;

         WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
         BCSCTL1 = CALBC1_1MHZ;                    // Set DCO to 1Mhz
         DCOCTL = CALDCO_1MHZ;
         P1SEL |= BIT6 + BIT7;       // Assign I2C pins to USCI_B0
         P1SEL2 |= BIT6 + BIT7;       // Assign I2C pins to USCI_B0

         init_I2C();    // initialize i2c
         __delay_cycles(10000);

         while ( i2c_notready() );       // wait for bus to be free
         __delay_cycles(10000);

         who_am_i = Receive(MPU6050_RA_WHO_AM_I);

         while(1) {}
}

Functions:

void init_I2C(void) {
          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;                             // fSCL = 1Mhz/10 = ~100kHz
          UCB0BR1 = 0;
          UCB0I2CSA = MPU6050_DEFAULT_ADDRESS;
          UCB0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
          IE2 |= UCB0RXIE + UCB0TXIE;    // Enable RX and TX interrupt
}


int i2c_notready(){
        if(UCB0STAT & UCBBUSY) return 1;
        else return 0;
}


char Receive(char registerAddr){
        char receivedByte;
        while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent
        UCB0CTL1 |= UCTR + UCTXSTT;             // I2C start condition with UCTR flag for transmit
        while((IFG2 & UCB0TXIFG) == 0);     //UCB0TXIFG is set immidiately
        UCB0TXBUF = registerAddr;           //write registerAddr in TX buffer
        while((IFG2 & UCB0TXIFG) == 0);     // wait until TX buffer is empty and transmitted
        UCB0CTL1 &= ~UCTR ;                // Clear I2C TX flag for receive
        UCB0CTL1 |= UCTXSTT + UCTXNACK;    // I2C start condition with NACK for single byte reading
        while (UCB0CTL1 & UCTXSTT);             // Start condition sent? RXBuffer full?
        receivedByte = UCB0RXBUF;
        UCB0CTL1 |= UCTXSTP;                    // I2C stop condition
        return receivedByte;
}


void Transmit(char registerAddr, char data){
    while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent
    UCB0CTL1 |= UCTR + UCTXSTT;             // I2C start condition with UCTR flag for transmit
    while((IFG2 & UCB0TXIFG) == 0);         //UCB0TXIFG is set immidiately
    UCB0TXBUF = registerAddr;               //write registerAddr in TX buffer
    while((IFG2 & UCB0TXIFG) == 0);         // wait until TX buffer is empty and transmitted
    UCB0TXBUF = data;                       //Write data in register
    while((IFG2 & UCB0TXIFG) == 0);         // wait until TX buffer is empty and transmitted
    UCB0CTL1 |= UCTXSTP;                    // I2C stop condition
    IFG2 &= ~UCB0TXIFG;                     // Clear TX interrupt flag
}

The problem is: I can only get the data if I try to read twice. That is, I can only get the register value if I write the line

         who_am_i = Receive(MPU6050_RA_WHO_AM_I);

twice. (And I do get the expected value).

It may be silly, but I think this is happening becouse I misunderstood something that could lead to total misfunction of the complete code. Therefore I would like to solve or understand this problem before geting any further on my Code. Thank you very much.

\$\endgroup\$
  • \$\begingroup\$ What do you actually receive? Could you paste your actual results? How many bytes is the data you're trying to read? \$\endgroup\$ – scld Mar 19 '15 at 1:14
  • \$\begingroup\$ Why are you enabling the interrupts? Any why the NACK? \$\endgroup\$ – CL. Mar 19 '15 at 9:12
  • \$\begingroup\$ I´m trying to read a single byte data and I receive 0x00 when the error occurs and 0x68 (which is the expected value) when it was successful. The Answer from @SomeHardwareGuy solved the problem. \$\endgroup\$ – Nilo nilold Mar 24 '15 at 16:15
  • \$\begingroup\$ @CL, the NACK is sent to inform the slave device that the master doesn´t want further bytes, as described at the end of the 10th page of this document. I´m pretty sure I will have to suppress it when I´m reading the accel and gyro datas, as they`re composed of 2 bytes. The interrupt is not necessary, indeed. \$\endgroup\$ – Nilo nilold Mar 24 '15 at 16:19
1
\$\begingroup\$

You might not be waiting properly for the rx buffer to fill. In your Receive function, after your line:

while (UCB0CTL1 & UCTXSTT);             // Start condition sent? RXBuffer full?

try adding a:

while(UCB0RXIFG != (IFG2 & UCB0RXIFG)); //keep checking for recv ready flag

Or some variation of that, and see if it helps.

\$\endgroup\$
0
\$\begingroup\$

Another addition. Even if you apply the fix suggested by @SomeHardwareGuy, you will read the previous buffer value if you read continuously. The buffer should be read after stop condition is applied. The correct sequence is as follows:

    uint8_t Receive(uint8_t registerAddr){
    int8_t receivedByte;
    while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent
    UCB0CTL1 |= UCTR + UCTXSTT;             // I2C start condition with UCTR flag for transmit
    while((IFG2 & UCB0TXIFG) == 0);     //UCB0TXIFG is set immidiately
    UCB0TXBUF = registerAddr;           //write registerAddr in TX buffer
    while((IFG2 & UCB0TXIFG) == 0);     // wait until TX buffer is empty and transmitted
    UCB0CTL1 &= ~UCTR ;                // Clear I2C TX flag for receive
    UCB0CTL1 |= UCTXSTT + UCTXNACK;    // I2C start condition with NACK for single byte reading
    while (UCB0CTL1 & UCTXSTT);             // Start condition sent? RXBuffer full?
    UCB0CTL1 |= UCTXSTP;                    // I2C stop condition
    while(UCB0RXIFG != (IFG2 & UCB0RXIFG)); //keep checking for recv ready flag
    receivedByte = UCB0RXBUF;
    return receivedByte;

}

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.