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.

CCS/EK-TM4C123GXL: Problems interfacing Tiva to MPU6050 Accelerometer/Gyroscope

Part Number: EK-TM4C123GXL

Tool/software: Code Composer Studio

Hello folks,

I'm trying to read the sensor values from the MPU6050 module, but sometimes the code hangs (forcing me to power cycle the Tiva, reseting is futile) or reads wrong values (appears to be consistent, but not what it should be).

I've already tried using Arduino and the sensor works perfectly with the Uno. I've also interfaced the sensor with an ATmega328P on a breadboard using 3V3 to see if it's the voltage that was causing the problem, but it worked there also. So no problems working with 3V3.

Since the sensorlib poorly explains how to read from MPU6050 (not trying to be mean here, but there are problems including the files, adapting the I²C code to use the I2CM, etc), I've decided to make the readings myself using the I²C the "traditional way", reading/writing from/to registers manually.

Here is my code:

#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_i2c.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/i2c.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "uartstdio.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"

#define SLAVE_ADDRESS           0x68

volatile int32_t g_ACXH;
volatile int32_t g_ACXL;
volatile int32_t g_ACX;

volatile int32_t g_ACYH;
volatile int32_t g_ACYL;
volatile int32_t g_ACY;

volatile int32_t g_ACZH;
volatile int32_t g_ACZL;
volatile int32_t g_ACZ;

volatile int32_t g_TempH;
volatile int32_t g_TempL;
volatile int32_t g_Temp;
volatile int32_t g_temperature = 0;

volatile int32_t g_GCXH;
volatile int32_t g_GCXL;
volatile int32_t g_GCX;

volatile int32_t g_GCYH;
volatile int32_t g_GCYL;
volatile int32_t g_GCY;

volatile int32_t g_GCZH;
volatile int32_t g_GCZL;
volatile int32_t g_GCZ;

//init the UART console baud 115200
inline void InitConsole(void)
{
    //
    // Enable GPIO port A which is used for UART0 pins.
    // TODO: change this to whichever GPIO port you are using.
    //
    MAP_SysCtlPeripheralDisable(SYSCTL_PERIPH_GPIOA);
    MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_GPIOA);
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    while(!MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOA));

    //
    // Configure the pin muxing for UART0 functions on port A0 and A1.
    // This step is not necessary if your part does not support pin muxing.
    // TODO: change this to select the port/pin you are using.
    //
    MAP_GPIOPinConfigure(GPIO_PA0_U0RX);
    MAP_GPIOPinConfigure(GPIO_PA1_U0TX);

    //
    // Enable UART0 so that we can configure the clock.
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

    //
    // Use the internal 16MHz oscillator as the UART clock source.
    //
    MAP_UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);

    //
    // Select the alternate (UART) function for these pins.
    // TODO: change this to select the port/pin you are using.
    //
    MAP_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    //
    // Initialize the UART for console I/O.
    //
    UARTStdioConfig(0, 115200, 16000000);
}

//main program
int main(void)
{
    //set system clock to 80 MHz
    MAP_SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);

    //init the UART
    InitConsole();

    //clears screen
    UARTprintf("\033[2J");
    //move cursor to position 0,0
    UARTprintf("\033[0;0H");

    //init the I2C0 and GPIOB
    MAP_SysCtlPeripheralDisable(SYSCTL_PERIPH_I2C0);
    MAP_SysCtlPeripheralDisable(SYSCTL_PERIPH_GPIOB);
    MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);
    MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_GPIOB);
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    while(!MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_I2C0) || !MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB));

    //configure PB2 to SCL and PB3 SDA
    MAP_GPIOPinConfigure(GPIO_PB2_I2C0SCL);
    MAP_GPIOPinConfigure(GPIO_PB3_I2C0SDA);
    MAP_GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);
    MAP_GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);

    //init I2C Master with 100kbps (if true would be 400kbps)
    MAP_I2CMasterInitExpClk(I2C0_BASE, MAP_SysCtlClockGet(), true);

    //sets slave adress, and master is reading from slave (writing if false)
    MAP_I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, false);

    //initialize the MPU
    I2CMasterDataPut(I2C0_BASE, 0x6B);
    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
    while(I2CMasterBusy(I2C0_BASE));

    I2CMasterDataPut(I2C0_BASE, 0x00);
    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
    while(I2CMasterBusy(I2C0_BASE));

    for(;;)
    {
        //request reading from register 0x3B
        I2CMasterDataPut(I2C0_BASE, 0x3B);
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
        while(I2CMasterBusy(I2C0_BASE));

        //master is reading from slave
        MAP_I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, true);

        //start reading registers
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
        while(I2CMasterBusy(I2C0_BASE));

        //shift the "higher" bits
        g_ACXH = MAP_I2CMasterDataGet(I2C0_BASE) << 8;

        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
        while(I2CMasterBusy(I2C0_BASE));

        g_ACXL = MAP_I2CMasterDataGet(I2C0_BASE);

        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
        while(I2CMasterBusy(I2C0_BASE));

        g_ACYH = MAP_I2CMasterDataGet(I2C0_BASE) << 8;

        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
        while(I2CMasterBusy(I2C0_BASE));

        g_ACYL = MAP_I2CMasterDataGet(I2C0_BASE);

        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
        while(I2CMasterBusy(I2C0_BASE));

        g_ACZH = MAP_I2CMasterDataGet(I2C0_BASE) << 8;

        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
        while(I2CMasterBusy(I2C0_BASE));

        g_ACZL = MAP_I2CMasterDataGet(I2C0_BASE);

        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
        while(I2CMasterBusy(I2C0_BASE));

        g_TempH = MAP_I2CMasterDataGet(I2C0_BASE) << 8;

        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
        while(I2CMasterBusy(I2C0_BASE));

        g_TempL = MAP_I2CMasterDataGet(I2C0_BASE);

        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
        while(I2CMasterBusy(I2C0_BASE));

        g_GCXH = MAP_I2CMasterDataGet(I2C0_BASE) << 8;

        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
        while(I2CMasterBusy(I2C0_BASE));

        g_GCXL = MAP_I2CMasterDataGet(I2C0_BASE);

        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
        while(I2CMasterBusy(I2C0_BASE));

        g_GCYH = MAP_I2CMasterDataGet(I2C0_BASE) << 8;

        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
        while(I2CMasterBusy(I2C0_BASE));

        g_GCYL = MAP_I2CMasterDataGet(I2C0_BASE);

        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
        while(I2CMasterBusy(I2C0_BASE));

        g_GCZH = MAP_I2CMasterDataGet(I2C0_BASE) << 8;

        //finish reading registers
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
        while(I2CMasterBusy(I2C0_BASE));

        g_GCZL = MAP_I2CMasterDataGet(I2C0_BASE);

        //join the two 8 bit variables into one 16 bit
        g_ACX   = g_ACXH    |   g_ACXL;
        g_ACY   = g_ACYH    |   g_ACYL;
        g_ACZ   = g_ACZH    |   g_ACZL;
        g_Temp  = g_TempH   |   g_TempL;
        g_GCX   = g_GCXH    |   g_GCXL;
        g_GCY   = g_GCYH    |   g_GCYL;
        g_GCZ   = g_GCZH    |   g_GCZL;

        //master is writing to slave
        MAP_I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, false);

        //convert to temperature
        g_temperature = (g_Temp/340.00f)+36.53f;

        UARTprintf("ACX: \t%d\n", g_ACX);
        UARTprintf("ACY: \t%d\n", g_ACY);
        UARTprintf("ACZ: \t%d\n", g_ACZ);
        UARTprintf("Temp: \t%d\n", g_temperature);
        UARTprintf("GCX: \t%d\n", g_GCX);
        UARTprintf("GCY: \t%d\n", g_GCY);
        UARTprintf("GCZ: \t%d\n", g_GCZ);

        //move cursor to position 0,0
        UARTprintf("\033[0;0H");

        MAP_SysCtlDelay(MAP_SysCtlClockGet()/3*0.01);
    }
}

The steps I took to make the readings on the MPU6050:

  • Initialized the GPIO, I²C and UART
  • Initialized the I²C master writing mode 400kbps
  • Initialized the MPU writing 0x00 to register 0x6B
  • Sent write request to address 0x3B
  • Changed I²C to read mode
  • Read the registers and place the contents to the variables
  • Merged the variables making 16bitvariable = 8bitvariable_H << 8 | 8bitvariable_L
  • Calculated the temperature according to equation shown on MPU's register map page 30 
  • Printed the variables
  • Changed the I²C to write, send write request to address 0x3B and do the readings all over again 

Things that decreased the hangups: changed the way I was reading the variables. Instead of shifting 8 bits left on the "H" registers later in the code, I did it right in the I2CMasterDataGet() call. It only hangs occasionally when I don't insert a delay in the loop.

Output on Arduino:

Output on Tiva:

What's causing this strange behavior?

  • Perhaps some (or multiple) of the following may assist:

    • if your Slave I2C device allows - drop down to the "more forgiving" 100Kbps  (always best to start conservatively)
    • have you installed the "requisite" EXTERNAL (~10K pull-ups) @ SCL & SDA?    (MCU internal ones exceed 50K - not advisable)
    • Scope Caps - especially revealing 2 or so "Successful Data Reads" - followed by  "Unsuccessful Data Reads" - w/each "cap" as "identical  as possible" (enabling eased recognition)  - should highlight differences - is that not so?
    • is your "cable/wire attachment" between each MCU board and the Slave - identical?   You must "minimize any/all controllable differences."    Use the (same) cable.
    • does this issue "continue" across a 2nd "123 LPad?"     Testing just one board - which may have a flaw - "spins YOUR wheels" - and those of hapless helpers
    • scope caps - "AT the Sensor's I2C Pins" under "brand A" and your '123 - would prove helpful
    • describe the power source - under "brand A" and then under your '123 - might "borderline voltage/current" cause your dilemma?    Assume nothing.
    • remove "any other code" running on your '123 board.   (To include the UART code - if possible ... UART "eats" much time - always suspect in such cases)

    Real measurements - current (today/tomorrow) measurements - are required!   (NO reliance on what you "believe" was measured - months past - is acceptable - that's UNFAIR to your helper crüe!)

  • My options are limited for now, but I'll try to at least borrow another Launchpad.

    Dropping down to 100Kbps changed nothing. The MPU module includes a 2.2kohm pull-ups. The cables I use is always the same ones, so no worries here.

    Sigh...if I only had a oscilloscope...but I bought a logic analyser (the cheapos that are on eBay, based on 8050 MCUs), I hope in the future it'll be useful. The only thing that is making me mad is the ridiculous, slow and low quality postal service that we have here in Brazil. Will also try to make readings on the university's oscilloscopes when have the time.

    The power source is the USB provided by my PC (USB2.0 port). Disabled UART and verified in the CCS debug section, but the values remains odd...
  • Helder Sales said:
    trying to read the sensor values ... but  sometimes the code hangs

    While you (quickly) note - "Changing to a 75% reduced bit rate, "Changes Nothing" - under the circumstances you've provided (sometimes) your comment IS "unexpected!"    Most always - when the issue occurs irregularly (sometimes) - the issue reveals as,  "Timing Related."     I don't know how you could have "tested/verified" your issue (so quickly) - when the fault is described as only occuring (sometimes!)    

    Your language in describing the cable interconnect -  "always the same ONES" - does not clearly note the use of the "Identical or Same Cable" (SINGULAR)  removed from the working system - and  (then) attached to your '123 board.   Cables are responsible for MANY System Failures - and/or intermittent operation - which is why the, "Elimination of cable variables" - proves SO important.

    Your scope captures - duplicated as much as is possible - and then presented here - in a neatly (and precisely)  "vertically offset" - yet (otherwise)  "overlaid & aligned"  fashion - displaying (both) success & failure - would prove of great value.    (Note the "attempt at precision" - w/in that past sentence's composition - your analysis & reporting - must strive for its equal...)

    Had you tried enforcing more delays - between Command-Writes - to your sensor?    It is suspected that your ARM "runs rings" around the lesser MCU - and may violate the "required set-up time" of your sensor.   Your adding of delays (almost everywhere) while unpleasant - may prove insightful.

    The reduced interconnect - offered up by the various Serial Interfaces - is compelling - but DOES force a greater demand - upon your "Test & Diagnostics."

    Minus those neatly overlaid - comparative scope caps - we "helper candidates" are, "much in the dark" - and limited in diagnosis...

  • cb1_mobile said:
    Most always - when the issue occurs irregularly (sometimes) - the issue reveals as,  "Timing Related."     I don't know how you could have "tested/verified" your issue (so quickly) - when the fault is described as only occuring (sometimes!)

    Resetting the Tiva multiple times occasionally gave me hangups (and to correct that, only power-cycling the board). I'll try to think as "Timing related" issues when problems tend to occur irregularly, that's a nice hint you gave me.

    cb1_mobile said:
    Your language in describing the cable interconnect -  "always the same ONES" - does not clearly note the use of the "Identical or Same Cable" (SINGULAR)  removed from the working system - and  (then) attached to your '123 board.   Cables are responsible for MANY System Failures - and/or intermittent operation - which is why the, "Elimination of cable variables" - proves SO important.

    Sorry, I intended to say the same cable. One letter changed everything eh?

    cb1_mobile said:
    Had you tried enforcing more delays - between Command-Writes - to your sensor?    It is suspected that your ARM "runs rings" around the lesser MCU - and may violate the "required set-up time" of your sensor.   Your adding of delays (almost everywhere) while unpleasant - may prove insightful.

    That actually made the hangups go away (thanks!). I guess I2CMasterBusy() call isn't enough, got to study I²C more in this aspect. Not sure how I would determine the minimum time required (since the datasheet said it supports both 100/400Kbps, I assumed I wouldn't need to insert some delays).

    cb1_mobile said:
    The reduced interconnect - offered up by the various Serial Interfaces - is compelling - but DOES force a greater demand - upon your "Test & Diagnostics."

    Yup, everything has a price to pay. Also, speaking of price, these modules are dirty cheap, so that's why I bought them (I know sometimes the cheapest things aren't the best, but I was pratically forced to buy the cheapos).

    As for the readings, the problem was that I2CMasterDataGet() returns a unsigned integer. Even if I place into a signed integer, I need to cast it again to convert into a signed value. Missed that completely, because I coded based on what I learned from using PCF8074A, and in that time I didn't use signed ints. I just want to understand why I should cast it again, that seems odd (same oddity as UART printing 65000 in a signed int).

  • Helder Sales said:
    That actually made the hangups go away (thanks!). I guess I2CMasterBusy() call  isn't enough

    You may benefit by reviewing the source code presented by "I2CMasterBusy" - and see if it, "Tests for a proper response - from the Slave."    If it does not - then there IS some chance - that the Slave is being, "Prompted for Data" and cannot always, "Meet your demand."

    Many are curious as to your usage of this sensor.    It IS reported that the,  "Girls from Ipanema" (who ARE tall & lovely) have been (repeatedly) "buzzed."   (perhaps by your Drone - wrestling w/the MPU6050?)

  • Well, it would be funny to presence that! Maybe one day I'll use it on drones, for "science"!

    Satisfying your (and readers) curiosity, these will be (hopefully) employed on a Mini-Baja Prototype, as a part of some University extension project.

    I plan to use a small bunch of these sensors. 4 of them I'm hoping to get some impact data from the suspensions, maybe something more with the gyroscopes. One will be measuring the lateral acceleration from the stress-bar located in the rear. Finally, the last one I'll measure the vibration from the pilot's seat when the engine is in idle (was requested to read this, for evaluating pilot's comfort...).

    Why I chose to use MPU6050 and not ADXL335 or another one? Well, can't say no for a US$1 sensor on eBay (that also has a gyroscope integrated), specially when money is low.

    Bonus - here some photos of our last two prototype vehicles I took in two competitions that is run by Society of Automotive Engineers (SAE):

  • Some here (never moi) may have to "reconsider" their sense that you were "Over-flying" Ipanema's beach - in search of, "Natural Wonders." (true story - once I got w/in 20 feet of Brazil's "Adriana Lima" - during a "downtown Chicago event" and, "Lost the power of speech" (drooled "even more" than normal) - even at that 20' distance...)

    While I "understand the "pull" of "cost-down" - many such electronic devices - sourced as you describe - are "Fallouts" and/or even "counterfeit" - price alone DOES impart HIGH (sometimes VERY HIGH) RISK.