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: MPU6050 SCL line stays low after MPU6050Init

Part Number: TM4C123GE6PM

Hello,

I use the following code to try to communicate with the MPU6050 sensor:

////////////////////////////////////////////////////////////////////////////////////////////////////
#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)
{

    // 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_GPIOD);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);

    // Enable I2C module 2
//    SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C3);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C2);

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

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

    IntMasterEnable();

//    I2CMInit(&g_sI2CInst, I2C3_BASE, INT_I2C3, 0xFF, 0xFF, SysCtlClockGet());
    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, 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]);
    }
}

I run the code step by step using F6 and get to the MPU6050Init function.

When this function is called I see (using an oscilloscope) the SDA and SCL I2C lines toggle correctly and the MPU6050 acknowledge to address 0x68. 

However, when the transaction is finished the MPU6050 holds the SCL line low and doesn't release it.

According to the I2C specifications - this signifies that the device is serving an interrupt. Only in my case it continues forever.

When I proceed with F6 to the MPU6050ReadModifyWrite function is called and completed - but then when I press F6 again the code gets stuck in the while(!g_bMPU6050Done) { } condition.

Is it possible that MPU6050Init doesn't release the device from reset  ?

Similarly to what mentioned in this post:

e2e.ti.com/.../315587

 

  • Hi,

      Please check the MPU6050 datasheet under what circumstances will it hold the SCL low. It is legal for the I2C slave devices to hold the SCL low as a means of wait state. However, it should release the SCL after it is ready to take commands from the master. 

      Is MPU6050 a dedicated device you have connected to the MCU? The BOOSTXL-SENSHUB sensor hub booster pack has the MPU9150 for which there is a TivaWare example for it. 

  • Hi,

    As I wrote, the SCL being held low signifies that the device is handling an interrupt...

    But this lasts forever...

    I don't have the SENSHUB - just the MPU6050. Which is also part of Tivaware sensorlib.

  • Hi,
    What do you mean according to the SCL specification the SCL being held low signifies the device is handling an interrupt? Where do you read that?

    Do you have the logic analyzer to capture the SDA and SCL bus transactions? If yes, can you show it?
  • The yellow channel is SCL.
    The blue one is SDA.

    It's a little hard to see, in the middle of the waveform - you can spot the blue signal pulled low, thus acknowledging the transaction.
    At the end, you can see that the blue line rises to a stable '1'while the SCL is continued to be pulled low by the MPU6050 (when I view the waveform a long time afterwards - it's still low).
  • I have some comments. You wrote:

       MPU6050ReadModifyWrite(&g_sMPU6050Inst, MPU6050_O_ACCEL_CONFIG, ~MPU6050_ACCEL_CONFIG_AFS_SEL_M, MPU6050_ACCEL_CONFIG_AFS_SEL_4G, USER_MPU6050Callback, &g_sMPU6050Inst);

    • First of all, I will suggest you check the returned value after calling MPU6050ReadModifyWrite(). It will return false if the operation is not successful. 
    • The last argument to pass to the API is supposed to be the callback data. However, you pass &g_sMPU6050Inst. Any reason why this is?
    • There is an MPU6050 example in section 15.3 in the the sensorlib user's guide. Please reference this example and compare to your code. 

  • Thanks for the response.
    Please look carefully at the example - the last argument isn't the callback data but a NULL argument.
    I changed it to &g_sMPU6050Inst because this is what I saw in the Launchpad's Workshop Lab 14 for the ISL29023 sensor (also I2C) - other than that my function is the same.
    Anyways, I changed it to "0" as in the sensorlib example and still there's no change.

    Once I call "MPU6050ReadModifyWrite" with F6 the code hangs.
    When I pause it - I see it's trapped in a while (1) loop inside FaultISR.
  • Hi,

     I will suggest you single step your code using F5 (step into) instead of F6 (step over) to find out which line of code caused the fault. F6 execute the entire function and you don't know what is going on within the function. A lot of time it may be related to the amount of stack you allocated. If you didn't reserve enough stack during the linker time then you can get a stack overflow. Check the SP register in the CPU and see what it indicates. The stack pointer starts at 0x20000000 and if you see the SP  below this value then you can get a stack overflow fault. This is not to say you are really getting a stack overflow fault. To find out what caused the fault, here is one app note that will help.

    I will also suggest you start with increasing the stack size to a larger value. Not sure what you have currently. if you have reserved like 256 bytes then change to 2048 just to see if it helps. If the stack size increase doesn't fix the problem then refer to the app note for diagnosing the faults.

  • I tried to play with the stack size and encountered some VERY strange behavior.
    The original value was: 100
    At first I changed the size to 512 like in the picture and nothing good came out of that...the program got stuck at MPU6050Init (even earlier than before).
    Then I changed the stack size to to 90 - this got the program to get stuck at the same point as with 100 (at the ReadModifyWrite function - which is further than I got with 512).

    Afterwards, I changed it to 150 - with this value ReadModifyWrite works correctly.
    I ran ~10 tests per stack size (100, 90, 512, 150) and the point at which the program gets stuck is 100% consistent.

    As if there's a stack size "sweet-spot" and it's 150 (!!!!!!!!!)
    What can cause such an erratic behavior ?
  • Some more information - regarding the I2C SCL line being pulled low and staying there.
    It's not the MPU6050 that's pulling the SCL low - it's the ARM !!

    I did the following test:
    1. Called the MPU6050Init function (to cause the SCL low condition).
    2. Disconnected the SCL hookup wire between the ARM and the MPU6050.
    3. Measured the SCL voltage on the MPU6050 side - it was 3.3V (non-pull).
    4. Measured the SCL voltage on the ARM side - it was 0V.

    So,
    We have erratic behavior characterized by a stack size "sweet spot" (150 - at which the code doesn't freeze where it usually does).
    And we also have the ARM pulling the SCL low (regardless of stack size) after finishing the MPU6050Init transaction.

    I'm really clueless here...
    Perhaps it has something to do with the fact that some variables are declared outside of the main function ??
  • Hi,
    First of all, I don't know why 512 stack size does not work while 150 will work. It does not make sense to me. I will suggest you use 1024. But if you think 150 will work all the time then stick with it. If you started with 90 then it is really too small the stack size to use for some of the APIs.

    You said after the MPU6050Init is called the SCL will stay low. However, when your code continues to the ReadModifyWrite the transaction will still continue, is this correct?
  • "You said after the MPU6050Init is called the SCL will stay low. However, when your code continues to the ReadModifyWrite the transaction will still continue, is this correct?"

    Yes - but only when the stack size is configured to 150.

    "But if you think 150 will work all the time then stick with it"

    I'm not trying to hide dirt under the rug just to make my program run at any cost...

    I really want to understand why my program is sensitive to stack size changes. Did you look into the code? Does it look OK ?

    Do you want me to upload the whole project ?

    I'm really clueless here...

  • When you have a complicated API such that it calls functions and some of these functions can subsequently call other functions, each function call is a context switch for which the current context (i.e. registers) need to be pushed onto the stack. If you declare local variable inside a function then that will also use the stack. The amount of stack that is needed is application dependent. Some tools such as TI-RTOS's ROV can analyze the real usage of the stack compared to the amount you allocated. The program will crash when the program uses more than the allocated stack as indicated as a stack overflow fault. Again, how much stack is needed is application dependent. As far as I know, all TivaWare examples use at least 256 bytes for small examples. Some more complicated examples reserve 512 or more. 90 as you used initially is just too small especially you are using the sensorlib APIs. Here are some links talking about stack estimation.
    stackoverflow.com/.../stack-size-estimation.
    embeddedgurus.com/.../


    So with enough stack (150 you later use) you said the ReadModifyWrite will work after the MPU6050Init . You observe the SCL being low when you F6 over the MPU6050Init and then stop the CPU, correct? This means the master is still owning the I2C bus. It is waiting to continue the next transaction. I think this is a normal behavior. Once the ReadModifyWrite is called it will continue with the next transaction. Perhaps I should ask the question in another way. If you don't put any breakpoint or single-step and just let it run free, does it proceed to the end and if the master has sent all the intended data to the slave? If yes, then I think it is working properly.
  • But why doesn't it work when I change the stack size to a larger value -512 for example ?
  • Did you free-run your code exactly the same way with different stack sizes? I can't understand why a larger stack size will fault while a smaller one does not. If the program crashes with 512 stack size then use the app note I refer to debug the source of the fault. Perhaps with 512 stack size it faults for a different reason, not stack overflow.

    Also in the articles I sent, you can preload the memory with some known patterns and then run your code. You will then observe the memory content where the stack is allocated to see what have been changed and you can get some idea on how much stack is really used compared to what is allocated.
  • I'll get the chance to test it in a few days.

    Is it possible that the CPU will show different behavior when I free run it instead of step by step ?

  • It is possible depending when you breakpoint in your code and if interrupt comes or not.
  • Good news.

    I changed the stack size to 1024 and ran the code free without breakpoints.

    I see I2C transactions on the oscilloscope so it's no longer stuck...

    So the problem (if there was any) is solved by making the stack larger as you suggested - thanks!

    But I still don't understand why my code breaks when I run it step by step using F6.

    Can you suggest a specific scenario that will induce such a bug.

  • When you do F6 you are in single stepping mode. Each time you hit F6, the CPU runs some code and then halts. The CPU can be halted for hours until you hit F6 again. This will be a different operating environment compared to CPU in free run mode where interrupts can occur in which more context switching are happening. The context switches will involve pushing/saving the contexts to the stack which will use additional stack memory.
  • I understand the difference - but my case counters this logic.
    From your explanation we can conclude that free running the code is much more demanding from the CPU and I agree.
    And this is why I'd expect more bugs to occur when free running it...

    But this isn't my case - In my case free running the code doesn't cause an error while using F6 does.
  • shai kon said:
    But this isn't my case - In my case free running the code doesn't cause an error while using F6 does.

    I think the code requires handling an interrupt to be able to run.

    The default CCS behaviour is to disable interrupts while stepping, but not when free running. That can cause stepping through code which requires interrupts to "hang".

    If you untick the option to "Disable interrupt while source stepping" (see Program/Memory Load Options) does that allow the code to run when source stepping?

  • Thanks!
    I unchecked it and it works now when source stepping.
    Is there a good reason to disable interrupts while debugging ?
  • shai kon said:
    Is there a good reason to disable interrupts while debugging ?

    If you had an enabled interrupt such as a timer then when trying to step with enabled interrupts you could find the program keeps stepping into the interrupt handler (see the reply from Britta in Compiler/MSP430G2955: GIE is disabled automatically when step debugging - while the referenced thread is for a MSP430 device believe the same is true for a Cortex-M4 based Tiva device).

  • The following post is a continuation of this one:
    e2e.ti.com/.../2741848

    Please share your inputs.