Lectura MPU-6050 con MSP430g2553 usando I2C

He estado buscando bibliotecas o documentaciones que me ayuden a leer y escribir en el acell + gyro breakout MPU-6050 de InvenSense. Logré leerlo con las librerías de arduino hechas por Jeff Rowberg ( Link ), pero ahora quiero usar la TI MSP430g2553 (con CCS o IAR). Tengo la intención de hacer una buena biblioteca y algunos ejemplos para compartirlo, ya que no puedo encontrarlo tan fácilmente como lo encontré para Arduino y PIC.

El código que tengo hasta ahora parece funcionar, pero no puedo estar seguro. La prueba más básica es leer el registro WHO_AM_I, que devuelve la dirección del esclavo (0x68). El código que utilicé se basa en este tema , que se basa en una documentación de TI.

Mi código es muy simple:

Incluye:

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

Prototipos:

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

Principal:

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) {}
}

Funciones:

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
}

El problema es: solo puedo obtener los datos si trato de leer dos veces. Es decir, solo puedo obtener el valor del registro si escribo la línea

         who_am_i = Receive(MPU6050_RA_WHO_AM_I);

dos veces. (Y obtengo el valor esperado).

Puede ser una tontería, pero creo que esto sucede porque no entendí algo que podría conducir a un mal funcionamiento total del código completo. Por lo tanto, me gustaría resolver o comprender este problema antes de continuar con mi Código. Muchas gracias.

¿Qué recibes en realidad? ¿Podrías pegar tus resultados reales? ¿De cuántos bytes son los datos que intentas leer?
¿Por qué estás habilitando las interrupciones? ¿Por qué el NACK?
Estoy tratando de leer datos de un solo byte y recibo 0x00 cuando ocurre el error y 0x68 (que es el valor esperado) cuando fue exitoso. La respuesta de @SomeHardwareGuy resolvió el problema.
@CL, el NACK se envía para informar al dispositivo esclavo que el maestro no quiere más bytes, como se describe al final de la página 10 de este documento . Estoy bastante seguro de que tendré que suprimirlo cuando esté leyendo los datos de aceleración y giroscopio, ya que están compuestos por 2 bytes. La interrupción no es necesaria, de hecho.

Respuestas (2)

Es posible que no esté esperando correctamente a que se llene el búfer rx. En su función Recibir, después de su línea:

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

intente agregar un:

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

O alguna variación de eso, y ver si ayuda.

Otra adición. Incluso si aplica la corrección sugerida por @SomeHardwareGuy, leerá el valor del búfer anterior si lee continuamente. El búfer debe leerse después de aplicar la condición de parada. La secuencia correcta es la siguiente:

    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;

}