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.

TM4C123GE6PM: Getting zero values from MPU6050

Part Number: TM4C123GE6PM

Hello,

I'm using the code below to communicate between my launchpad and an MPU6050.

I've gone a lone way debugging it to the point that the I2C communication works correctly on the hardware level in an infinite "while (1)" loop.

On my oscilloscope - I see the SDA & SCL lines toggling with the correct address (0x68) being sent and the sensor acknowledging it

Unfortunately, 

The payload is always zero.

I see the 0x00 bytes returned from the sensor on the oscilloscope as well as in Code Composer's debug menu.

////////////////////////////////////////////////////////////////////////////////////////////////////
#include <stdbool.h>
#include <stdint.h>
#include "driverlib/debug.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/pwm.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "inc/hw_gpio.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"

////////////////////////////////////////////////////////////////////////////////////////////////////
// My I2C defines & includes
#include "driverlib/i2c.h"
#include "driverlib/interrupt.h"
#include "inc/hw_i2c.h"
#include "inc/hw_ints.h"
#include "sensorlib/i2cm_drv.h"
#include "sensorlib/mpu6050.h"
#include "sensorlib/hw_mpu6050.h"
#define DEBUG

////////////////////////////////////////////////////////////////////////////////////////////////////
#include "definitions_mpu6050.h"
tI2CMInstance g_sI2CInst; // I2C master driver structure. "tI2CMInstance" is defined in the i2cm_drv.h file.
tMPU6050 g_sMPU6050Inst; // MPU6050 sensor driver structure. "tMPU6050" is defined in the mpu6050.h file.
volatile unsigned long g_vui8DataFlag; // Data ready flag
volatile unsigned long g_vui8ErrorFlag; // Error flag

void
ISL29023I2CIntHandler(void)
{
I2CMIntHandler(&g_sI2CInst);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
int main(void)
{

// Configure the clock
SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);

////////////////////////////////////////////////////////////////////////////////////////////////////
// My I2C initialization code

// Enable GPIO peripheral that contains I2C 2
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);

// Enable I2C module 2
SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C2);

// Configure the pin muxing for I2C2 functions on port E4 and E5.
GPIOPinConfigure(GPIO_PE4_I2C2SCL);
GPIOPinConfigure(GPIO_PE5_I2C2SDA);

// Select the I2C function for these pins.
GPIOPinTypeI2CSCL(GPIO_PORTE_BASE, GPIO_PIN_4);
GPIOPinTypeI2C(GPIO_PORTE_BASE, GPIO_PIN_5);

IntMasterEnable();

I2CMInit(&g_sI2CInst, I2C2_BASE, INT_I2C2, 0xFF, 0xFF, SysCtlClockGet());
SysCtlDelay(SysCtlClockGet() / 3);

float fAccel[3], fGyro[3];

// USER_MPU6050Callback will modify the g_bMPU6050Done variable to true if MPU6050Init succeeds.
g_bMPU6050Done = false;

// Initialize the MPU6050
MPU6050Init(&g_sMPU6050Inst, &g_sI2CInst, MPU6050_I2C_ADDRESS, USER_MPU6050Callback, &g_sMPU6050Inst);

while(!g_bMPU6050Done) { }

// USER_MPU6050Callback will modify the g_bMPU6050Done variable to true if MPU6050ReadModifyWrite succeeds.
g_bMPU6050Done = false;

// Configure the MPU6050 for +/- 4 g accelerometer range.
MPU6050ReadModifyWrite(&g_sMPU6050Inst, MPU6050_O_ACCEL_CONFIG, ~MPU6050_ACCEL_CONFIG_AFS_SEL_M, MPU6050_ACCEL_CONFIG_AFS_SEL_4G, USER_MPU6050Callback, &g_sMPU6050Inst);

while(!g_bMPU6050Done) { }

while(1)
{
// USER_MPU6050Callback will modify the g_bMPU6050Done variable to true if MPU6050DataRead succeeds.
g_bMPU6050Done = false;

// Request another reading from the MPU6050.
MPU6050DataRead(&g_sMPU6050Inst, USER_MPU6050Callback, &g_sMPU6050Inst);

while(!g_bMPU6050Done) { }

// Get the new accelerometer and gyroscope readings.
MPU6050DataAccelGetFloat(&g_sMPU6050Inst, &fAccel[0], &fAccel[1], &fAccel[2]);
MPU6050DataGyroGetFloat(&g_sMPU6050Inst, &fGyro[0], &fGyro[1], &fGyro[2]);
}
}

  • I don't have the MPU6050 hardware so I cannot debug this for you, but in line 73, you are passing a pointer to the MPU6050 instance when the routine is calling for a pointer to callback data. I suspect you should really have a 0 (null pointer) there (you did not provide the source of your USER_MPU6050Callback function). Same thing in lines 81 and 91.
  • You mean it should be:

    MPU6050Init(&g_sMPU6050Inst, &g_sI2CInst, MPU6050_I2C_ADDRESS, USER_MPU6050Callback, 0);


    Instead of:

    MPU6050Init(&g_sMPU6050Inst, &g_sI2CInst, MPU6050_I2C_ADDRESS, USER_MPU6050Callback, &g_sMPU6050Inst);

    ?

    And this is my source-code for USER_MPU6050Callback:

    void USER_MPU6050Callback(void *pvCallbackData, uint_fast8_t ui8Status) 
    { 
    // See if an error occurred. 
    if(ui8Status != I2CM_STATUS_SUCCESS) 
    { 
    // An error occurred, so handle it here if required. 
    }
    g_bMPU6050Done = true;
    }

  • Yes, but I suspect that is not the only problem. Your callback routine does not even use that value.
  • "Yes, but I suspect that is not the only problem. Your callback routine does not even use that value."

    Doesn't use what value ?
  • The pointer to the callback data. The last argument of the MPU6050Init function.
  • I didn't use 0 in the first place because this is exactly how lab14 in the workshop does that with ISL29023Init...

    ISL29023Init(&g_sISL29023Inst, &g_sI2CInst, ISL29023_I2C_ADDRESS,ISL29023AppCallback, &g_sISL29023Inst);

    Can you explain why the author choose to use &g_sISL29023Inst instead of NULL ?

  • Sorry, I cannot. That parameter is not used in the callback function of lab14 either.
  • I just tested my code with a slowed down version of SCL (100KHz).
    The sensor responds absolutely the same way as with 400KHz - it shows signs of life by acknowledging the data but sends back a payload that's all 0s.

    I've seen this post:
    e2e.ti.com/.../315587

    Is it possible that the problem hasn't been solved and my code doesn't initialize the sensor correctly ?

    Can you suggest something I can try to verify it ?

  • I tested the MPU6050 with an Arduino board.
    It works perfectly...so clearly nothing is wrong with the sensor or my setup.

    It must be the code...
  • Try this simple code I posted in another thread. You may need to change the I2C port, as my hardware used I2C3.
    e2e.ti.com/.../742217
  • Thanks.

    I'll try it. I had some progress by myself since.

    I tried to access the WHO_AM_I register of the MPU6050 using this code:

    uint8_t who_am_i_register ;
    MPU6050Read(&g_sMPU6050Inst, MPU6050_O_WHO_AM_I , &who_am_i_register, 1 ,USER_MPU6050Callback , 0);

    The device responded correctly with 0x68

    We're almost there...

  • The hardware I have is a MPU-9150 instead of a MPU-6050, but the register map seems to be the same. On my board it is attached to I2C3 with the INT pin connected to GPIOB pin 2. I can see a similar behavior of reading all zero values after reset. What seems to work for me is to add a polling of register 0x6B looking for a 0x40 before continuing with the rest of the configuration. I cannot find this in the TDK-InvenSense documentation, but I followed the I2C bus with a logic analyzer on a working example. I wrote an example using TivaWare, but not the sensor library. My goal was to make readable code. It does pure polling, no interrupts and no callback functions. I have attached the Code Composer project. I hope this helps.

    /cfs-file/__key/communityserver-discussions-components-files/908/MPU6050_2D00_2.zip

  • I managed to solve the problem!

    As I suspected the problem was in the example code...

    After calling "MPU6050Init" as follows:

    MPU6050Init(&g_sMPU6050Inst, &g_sI2CInst, MPU6050_I2C_ADDRESS, USER_MPU6050Callback, 0);

    I read register PWR_MGMT_1 (address 0x6B).

    The result was 0x40 - I.E bit #6 remained high after initialization - effectively keeping the device sleeping.

    I really think no one bothered to check that the example code (as posted in the spmu371d document) really works.

    Anyway,

    I'd like to share my working code - any comments are appreciated:

    ////////////////////////////////////////////////////////////////////////////////////////////////////
    #include <stdbool.h>
    #include <stdint.h>
    #include "driverlib/debug.h"
    #include "driverlib/gpio.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/pwm.h"
    #include "driverlib/rom.h"
    #include "driverlib/sysctl.h"
    #include "inc/hw_gpio.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include <stddef.h> // For using the NULL pointer
    
    #define PWM_FREQUENCY 55
    
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    // My I2C defines & includes
    #include "driverlib/i2c.h"
    #include "driverlib/interrupt.h"
    #include "inc/hw_i2c.h"
    #include "inc/hw_ints.h"
    #include "sensorlib/i2cm_drv.h"
    #include "sensorlib/mpu6050.h"
    #include "sensorlib/hw_mpu6050.h"
    #define DEBUG
    
    
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    // My UART defines & includes
    #include "driverlib/uart.h"
    #include "utils/uartstdio.h"
    
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    #include "definitions_mpu6050.h"
    tI2CMInstance g_sI2CInst; // I2C master driver structure. "tI2CMInstance" is defined in the i2cm_drv.h file.
    tMPU6050 g_sMPU6050Inst; // MPU6050 sensor driver structure. "tMPU6050" is defined in the mpu6050.h file.
    volatile unsigned long g_vui8DataFlag; // Data ready flag
    volatile unsigned long g_vui8ErrorFlag; // Error flag
    
    void
    ISL29023I2CIntHandler(void)
    {
    I2CMIntHandler(&g_sI2CInst);
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    int main(void)
    {
        float fAccel[3], fGyro[3];
        uint8_t register_mpu6050_o_pwr_mgmt_1 ; // Used for debug.
        uint8_t register_mpu6050_o_who_am_i ; // Used for debug.
    
        // Configure the clock
        SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
    
    ////////////////////////////////////////////////////////////////////////////////////////////////////
        // My I2C initialization code
    
        // Enable GPIO peripheral that contains I2C 2
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    
        // Enable I2C module 2
        SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C2);
    
        // Configure the pin muxing for I2C2 functions on port E4 and E5.
        GPIOPinConfigure(GPIO_PE4_I2C2SCL);
        GPIOPinConfigure(GPIO_PE5_I2C2SDA);
    
        // Select the I2C function for these pins.
        GPIOPinTypeI2CSCL(GPIO_PORTE_BASE, GPIO_PIN_4);
        GPIOPinTypeI2C(GPIO_PORTE_BASE, GPIO_PIN_5);
    
        IntMasterEnable();
    
        USER_I2CMInit(&g_sI2CInst, I2C2_BASE, INT_I2C2, 0xFF, 0xFF, SysCtlClockGet()); // Set SCL to 100KHz instead of the original 400KHz.
        //I2CMInit(&g_sI2CInst, I2C2_BASE, INT_I2C2, 0xFF, 0xFF, SysCtlClockGet());
    
        SysCtlDelay(SysCtlClockGet() / 3);
    
        // USER_MPU6050Callback will modify the g_bMPU6050Done variable to true if the functions that use it succeed.
    
        // Initialize the MPU6050
        g_bMPU6050Done = false;
        MPU6050Init(&g_sMPU6050Inst, &g_sI2CInst, MPU6050_I2C_ADDRESS, USER_MPU6050Callback, 0);
        while(!g_bMPU6050Done) { }
        SysCtlDelay(SysCtlClockGet() / 3);
    
        g_bMPU6050Done = false;
        MPU6050Read(&g_sMPU6050Inst, MPU6050_O_WHO_AM_I , &register_mpu6050_o_who_am_i, 1 ,USER_MPU6050Callback , 0);
        while(!g_bMPU6050Done) { }
        SysCtlDelay(SysCtlClockGet() / 3);
    
        g_bMPU6050Done = false;
        MPU6050Read(&g_sMPU6050Inst, MPU6050_O_PWR_MGMT_1 , &register_mpu6050_o_pwr_mgmt_1, 1 ,USER_MPU6050Callback , 0); // Someone forgot to take the device out of sleep.
        while(!g_bMPU6050Done) { }
        SysCtlDelay(SysCtlClockGet() / 3);
    
        g_bMPU6050Done = false;
        MPU6050Write(&g_sMPU6050Inst, MPU6050_O_PWR_MGMT_1 , 0x00 , 1 , USER_MPU6050Callback , 0);
        while(!g_bMPU6050Done) { }
        SysCtlDelay(SysCtlClockGet() / 3);
    
        g_bMPU6050Done = false;
        MPU6050Read(&g_sMPU6050Inst, MPU6050_O_PWR_MGMT_1 , &register_mpu6050_o_pwr_mgmt_1 , 1 ,USER_MPU6050Callback , 0);
        while(!g_bMPU6050Done) { }
        SysCtlDelay(SysCtlClockGet() / 3);
    
        g_bMPU6050Done = false;
        // Configure the MPU6050 for +/- 4 g accelerometer range.
        MPU6050ReadModifyWrite(&g_sMPU6050Inst, MPU6050_O_ACCEL_CONFIG, ~MPU6050_ACCEL_CONFIG_AFS_SEL_M, MPU6050_ACCEL_CONFIG_AFS_SEL_4G, USER_MPU6050Callback, 0);
        while(!g_bMPU6050Done) { }
    
        int counter = 0 ;
        while(1)
        {
            g_bMPU6050Done = false;
    
            while ( counter < 100000 )
            {
                counter ++ ;
            }
            counter = 0 ;
            MPU6050DataRead(&g_sMPU6050Inst, USER_MPU6050Callback, 0);
    
            while(!g_bMPU6050Done) { }
    
            // Get the new accelerometer and gyroscope readings.
            MPU6050DataAccelGetFloat(&g_sMPU6050Inst, &fAccel[0], &fAccel[1], &fAccel[2]);
            MPU6050DataGyroGetFloat(&g_sMPU6050Inst, &fGyro[0], &fGyro[1], &fGyro[2]);
        }
    }
    

  • Sorry Bob.

    I didn't see your post from 2:59 before posting mine on 3:28...

    I've read your code -  I see that you take the device out of sleep on the same write operation that you configure the PLL with the X axis gyroscope:

        // Use PLL with X axis gyroscope reference
        MPU6050WriteRegister(0x6B, 0x01); // This also clears bit #6 ?

    Am I correct ?

    If so - do you agree that the example code from the sesorlib never does that ?

  • I agree there is an issue. I am still trying to figure out if the problem is the example code or the sensor library MPU6050Init routine. I also think it may be a bit more complicated. It looks like after resetting the MPU6050, it takes some time. In your code you solve that by just waiting 1/3 of a second. In my example and in MPU6050Init() it polls the configuration register until the SLEEP bit is set. Then it clears the sleep bit and configures the clock source.

    The MPU9150 is the same as the MPU6050 with an integrated compass. That example works just fine. I see differences in the initialization routine. I am still working my way through it.

  • If it helps,
    I just tested my code with "SysCtlDelay(SysCtlClockGet() / 3);" omitted everywhere - and it still works well.

    IMO the (one and only) issue here is that MPU6050Init function doesn't clear bit #6 from register 0x6B - regardless of how long you wait or poll...