0
\$\begingroup\$

I declare global variables to read the sensor MPU9250.

typedef struct MPU_data{
    //Accelerometer
    int16_t ax;
    int16_t ay;
    int16_t az;
    //Gyroscope
    int16_t gx;
    int16_t gy;
    int16_t gz;
    //magnetometer
    int16_t mx;
    int16_t my;
    int16_t mz;
}MPU_data;
MPU_data mpu;

int16_t mpu_array[9];

In the main loop, I read data

while(true)
{
    uint8_t Buf[14];
    I2C_Read(MPU9250_ADDRESS,0x3B,Buf,14);

    // Create 16 bits values from 8 bits data

    // Accelerometer
    int16_t ax=-(Buf[0]<<8 | Buf[1]);
    int16_t ay=-(Buf[2]<<8 | Buf[3]);
    int16_t az=Buf[4]<<8 | Buf[5];

    // Gyroscope
    int16_t gx=-(Buf[8]<<8 | Buf[9]);
    int16_t gy=-(Buf[10]<<8 | Buf[11]);
    int16_t gz=Buf[12]<<8 | Buf[13];

    uint8_t ST1;
    do
    {
        I2C_Read(MAG_ADDRESS,0x02,&ST1,1);
    }
    while (!(ST1&0x01));

    // Read magnetometer data
    uint8_t Mag[7];
    I2C_Read(MAG_ADDRESS,0x03,Mag,7);

    // Create 16 bits values from 8 bits data

    // Magnetometer
    int16_t mx=-(Mag[3]<<8 | Mag[2]); mx += 200;
    int16_t my=-(Mag[1]<<8 | Mag[0]); my -= 70;
    int16_t mz=-(Mag[5]<<8 | Mag[4]); mz -= 700;

    uint8_t mpu_data[48];
    sprintf(mpu_data, "%i, %i, %i; %i, %i, %i; %i, %i, %i;\n", ax,ay,az,gx,gy,gz,mx,my,mz);
    USART_Send_t(mpu_data);

I get the normal data

3454, -79, -7349; -85, -26, -41; -268, -200, 218;
3450, -76, -7355; -88, -27, -45; -258, -188, 216;
3440, -63, -7364; -84, -26, -43; -259, -195, 219;
3441, -70, -7355; -85, -23, -40; -259, -199, 219;

If I write to global variables, I get a different behavior.

uint8_t Buf[14];
I2C_Read(MPU9250_ADDRESS,0x3B,Buf,14);

// Create 16 bits values from 8 bits data

// Accelerometer
mpu.ax=-(Buf[0]<<8 | Buf[1]);
mpu.ay=-(Buf[2]<<8 | Buf[3]);
mpu.az=Buf[4]<<8 | Buf[5];

// Gyroscope
mpu.gx=-(Buf[8]<<8 | Buf[9]);
mpu.gy=-(Buf[10]<<8 | Buf[11]);
mpu.gz=Buf[12]<<8 | Buf[13];

uint8_t ST1;
do
{
    I2C_Read(MAG_ADDRESS,0x02,&ST1,1);
}
while (!(ST1&0x01));

// Read magnetometer data
uint8_t Mag[7];
I2C_Read(MAG_ADDRESS,0x03,Mag,7);

// Create 16 bits values from 8 bits data

// Magnetometer
mpu.mx=-(Mag[3]<<8 | Mag[2]); mpu.mx += 200;
mpu.my=-(Mag[1]<<8 | Mag[0]); mpu.my -= 70;
mpu.mz=-(Mag[5]<<8 | Mag[4]); mpu.mz -= 700;

uint8_t mpu_data[48];
sprintf(mpu_data, "%i, %i, %i; %i, %i, %i; %i, %i, %i;\n", mpu.ax,mpu.ay,mpu.az,mpu.gx,mpu.gy,mpu.gz,mpu.mx,mpu.my,mpu.mz);
USART_Send_t(mpu_data);

Result

3370, -512, 0; 0, -23, 0; -68, -270, -472;
3369, -513, 0; 0, -24, 0; -68, -270, -472;
3365, -510, 0; 0, -24, 0; -68, -270, -472;
3359, -508, 0; 0, -23, 0; -62, -262, -476;

What am I doing wrong?

EDIT 1 (Another strange behavior)

Like I said, I created a function. In the comments, I noted a place that causes strange behavior.

inline void MPU_ReadAll(){
    uint8_t Buf[14];
    I2C_Read(MPU9250_ADDRESS,0x3B,Buf,14);

    // Create 16 bits values from 8 bits data

    // Accelerometer
    mpu_array[0]=-(Buf[0]<<8 | Buf[1]);
    mpu_array[1]=-(Buf[2]<<8 | Buf[3]);
    mpu_array[2]=Buf[4]<<8 | Buf[5];

    // Gyroscope
    mpu_array[3]=-(Buf[8]<<8 | Buf[9]);
    mpu_array[4]=-(Buf[10]<<8 | Buf[11]);
    mpu_array[5]=Buf[12]<<8 | Buf[13];
    uint8_t ST1;
    do
    {
        I2C_Read(MAG_ADDRESS,0x02,&ST1,1);
    }
    while (!(ST1&0x01));

    // Read magnetometer data
    uint8_t Mag[7];
    I2C_Read(MAG_ADDRESS,0x03,Mag,7);

    // Create 16 bits values from 8 bits data

    // Magnetometer
    mpu_array[6]=-(Mag[3]<<8 | Mag[2]); mpu_array[6] += 200;
    mpu_array[7]=-(Mag[1]<<8 | Mag[0]); mpu_array[7] -= 70;
    mpu_array[8]=-(Mag[5]<<8 | Mag[4]); mpu_array[8] -= 700;

    //If I delete the comments here, the program will work fine. 
    //If the data sending is commented out, the program gets stuck in this function.
    //However, I get zeros, as I wrote above.
    /*
    uint8_t mpu_data[48];
    sprintf(mpu_data, "%i, %i, %i; %i, %i, %i; %i, %i, %i;\n", mpu_array[0],mpu_array[1],mpu_array[2],mpu_array[3],mpu_array[4],mpu_array[5],mpu_array[6],mpu_array[7],mpu_array[8]);
    USART_Send_t(mpu_data);
    */
}
\$\endgroup\$
  • \$\begingroup\$ First thing you must find out where exactly it freezes in your code. \$\endgroup\$ – Anonymous Apr 2 '18 at 17:20
  • 1
    \$\begingroup\$ Is there a reason your MPU_data members are signed, but your local variable versions are unsigned? \$\endgroup\$ – The Photon Apr 2 '18 at 17:29
  • \$\begingroup\$ @Anonymous sorry, I completed the post \$\endgroup\$ – Marat Gareev Apr 2 '18 at 17:34
  • 1
    \$\begingroup\$ @ChrisStratton, I see what they're doing. But they should probably be doing it the same way in the local-variable case and the global-struct-member case if they want to get the same result. (Never mind that printf doesn't care if the argument is signed or unsigned, it just interprets the bits however you tell it to in the format string). \$\endgroup\$ – The Photon Apr 2 '18 at 18:18
  • 1
    \$\begingroup\$ There's undefined behavior all over the place, so anything can happen. \$\endgroup\$ – Lundin Apr 5 '18 at 13:04
1
\$\begingroup\$

There's really just 2 possible causes here: either stack overflow or undefined behavior bugs.

Using sprintf on a lousy AVR is kind of like letting loose an elephant in a porcelain store. You could have a stack overflow, in which case the problem is the program as whole and no specific line.

Also, be wary of the weird Harvard architecture. When you move variables to file scope, you might paradoxically need to use more stack, since the compiler will have to generate all manner of strange "access data" code. Exactly what it does, I don't know. Disassemble and have a look. This is an AVR-only problem that you won't have on less exotic CPUs.

Now as it happens, you also do have undefined behavior all over the place.

On a 8 bit MCU such as AVR, code like my_uint8_t << 8 invokes undefined behavior if the uint8_t contains values larger than 0x7F, because of implicit integer promotion. You end up with bit shifting data into the sign bits of a 16 bit int type which is signed. Boom. GCC has an aggressive optimizer, so it might start making all kinds of strange assumptions. (I just answered a very similar question here).

The sound thing to do is to remove all implicit promotions from the code. There's several possible ways to do this:

// messy one-liner
mpu.ax= -(int16_t) ((uint16_t)Buf[0]<<8 | (uint16_t)Buf[1]);

or

// readable version
uint16_t tmp = (uint16_t)Buf[0]<<8 | (uint16_t)Buf[1];
mpu.ax = -(int16_t)tmp;
\$\endgroup\$
  • \$\begingroup\$ And this is one of many reasons why programmers in general and beginners in particular should stop using 8 bit CPUs. It is much harder and requires much more skill to program C for 8-bitters than for 32 bit CPUs. \$\endgroup\$ – Lundin Apr 5 '18 at 12:58
  • \$\begingroup\$ @MaratGareev Out of curiousity, did any of the above fixes solve the problem or does it persist? \$\endgroup\$ – Lundin Apr 9 '18 at 14:29
  • \$\begingroup\$ Unfortunately, it was not possible to check, because I have already started to redo the entire program. Still, the main problem was in I2C, I'm working incorrectly with this bus. The symptoms of the hang of the program are the same as here avrfreaks.net/forum/… \$\endgroup\$ – Marat Gareev Apr 9 '18 at 23:12
0
\$\begingroup\$

So, the problem of such inadequate behavior was the optimization of the compiler. By default it was Optimize (-O1), I changed it to None (-O0). Now the program behaves more predictably. Also did this

volatile int16_t mpu_array[9] = {[0 ... 8] = 0};

But still with these changes and the included optimization, the program hangs.

\$\endgroup\$
  • 3
    \$\begingroup\$ "So, the problem of such inadequate behavior was the optimization of the compiler." - Possible but unlikely. More likely: your program is subtly wrong (perhaps invoking Undefined Behaviour), and the optimization settings happen to (un)mask the buggy behaviour. \$\endgroup\$ – marcelm Apr 3 '18 at 13:26
  • \$\begingroup\$ @marcelm I'll try to analyze the code in detail. But I do not see at close range a difference between these options int16_t ax = .. and int16_t a[9]; a[0] = .... The difference in the testimony is present. With the optimization disabled, there is no difference, everything works as it should. \$\endgroup\$ – Marat Gareev Apr 3 '18 at 13:41
  • \$\begingroup\$ If you make those globals volatile, you might be able to re-enable the optimization. \$\endgroup\$ – evildemonic Apr 3 '18 at 14:46
  • \$\begingroup\$ @evildemonic I apologize, I forgot to write that I already did it. But still with these changes and the included optimization, the program hangs. \$\endgroup\$ – Marat Gareev Apr 3 '18 at 15:00

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.