IMU problem - I2C

Hey guys,

I am still trying to wrap my head around I2C - I always screw something up. Please bare with me - it very well be a stupid noob mistake. So here we go:

I use the Adafruit 10dof sensor (Adafruit 10-DOF IMU Breakout - L3GD20H + LSM303 + BMP180 : ID 1604 : $29.95 : Adafruit Industries, Unique & fun DIY electronics and kits) but at the moment I am just using the L3GD20H sensor. I used this datashet http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/DM00036465.pdfw.st.com/resource/en/datasheet/l3gd20.pdf

My code:

#include <Wire.h>

#define GYRO_ADDRESS 0x6B
#define GYRO_SENSIBILITY500 (0.0175F)

void setup()
{
    Serial.begin(9600);
    Wire.begin();

    initGyro();
}

void loop()
{
    getRotation();
    delay(2000);
}

void I2C_write(byte slaveAddress, byte registerAddress, byte data)
{
    Wire.beginTransmission(slaveAddress);
    Wire.write(registerAddress);
    Wire.write(data);
    if(Wire.endTransmission()!=0)
      Serial.println("Writing Error!");
}

long I2C_read(byte slaveAddress, byte registerAddress, byte numberOfBytes)
{
    Wire.beginTransmission(slaveAddress);
    Wire.write(registerAddress);
    if(Wire.endTransmission()!=0)
      Serial.println("Reading Error!");
    Wire.requestFrom(slaveAddress, numberOfBytes);

    while(Wire.available() < numberOfBytes);
    long temp = 0;
    for(int i=1; i<=numberOfBytes; i++)
    {
      temp += (long)Wire.read()<<(8*(numberOfBytes - i));
    }
    return temp;
}

inline void initGyro()
{
    Serial.println("Initialising Gyro");
    I2C_write(GYRO_ADDRESS, 0x20, 0x00);
    I2C_write(GYRO_ADDRESS, 0x39, 0b00000000);
    I2C_write(GYRO_ADDRESS, 0x23, 0b00010000);
    I2C_write(GYRO_ADDRESS, 0x20, 0x0F);
}

inline void getRotation()
{
    double temp = I2C_read(GYRO_ADDRESS, 0x28, 2);
    temp *= GYRO_SENSIBILITY500;
    Serial.print(temp);
    Serial.println(" degrees/second");
}

Now my problem is that I get completely wrong readings. Like 800 to 900 degrees per second when the sensor is laying flat on the table. I just really don't know where my mistake is. Every kind of help is highly appreciated!!!

Gyros generally output an "offset" when held stationary, and it will be different on each axis. Calibration is required, which removes the offset after averaging some number of stationary readings, before useful measurements can be made.

Of course, there could be other problems with your setup and code.

jremington:
Gyros generally output an "offset" when held stationary, and it will be different on each axis. Calibration is required[...]

Thank you for reminding me of that but there must be something else that is wrong...

I2C_write(GYRO_ADDRESS, 0x23, 0b00010000);

sets the gyro to 500dps so I have no freaking idea how I can get readings of 800dps...

 I2C_write(GYRO_ADDRESS, 0x39, 0b00000000);

I don't see a register 0x39 in the data sheet. http://www.st.com/content/ccc/resource/technical/document/datasheet/43/37/e3/06/b0/bf/48/bd/DM00036465.pdf/files/DM00036465.pdf/jcr:content/translations/en.DM00036465.pdf

7.10 OUT_X_L (28h), OUT_X_H (29h)
X-axis angular rate data. The value is expressed as two’s complement.

I think you have your high and low bytes switched around when you read an recombine them.

inline void getRotation()
{
    double temp = I2C_read(GYRO_ADDRESS, 0x28, 2);
    temp *= GYRO_SENSIBILITY500;
    Serial.print(temp);
    Serial.println(" degrees/second");
}
long I2C_read(byte slaveAddress, byte registerAddress, byte numberOfBytes)
{
    Wire.beginTransmission(slaveAddress);
    Wire.write(registerAddress);
    if(Wire.endTransmission()!=0)
      Serial.println("Reading Error!");
    Wire.requestFrom(slaveAddress, numberOfBytes);

    while(Wire.available() < numberOfBytes);
    long temp = 0;
    for(int i=1; i<=numberOfBytes; i++)
    {
      temp += (long)Wire.read()<<(8*(numberOfBytes - i));
    }
    return temp;
}

cattledog:
I think you have your high and low bytes switched around when you read an recombine them.

Yeah you are right! I thought MSB was first. However, my code is still not working. I think something with my I2C_read function is up. I printed the two bytes I received and they turned out to be always the same...

Initialising Gyro
Byte #1: 75
Byte #2: 75

Byte #1: 203
Byte #2: 203

Byte #1: 183
Byte #2: 183

Byte #1: 193
Byte #2: 193

Byte #1: 192
Byte #2: 192

I think something with my I2C_read function is up

Try this less general version for now and see if it still prints out the same values for both bytes.

int I2C_read(byte slaveAddress, byte registerAddress, byte numberOfBytes)
{
    Wire.beginTransmission(slaveAddress);
    Wire.write(registerAddress);
    if(Wire.endTransmission()!=0)
      Serial.println("Reading Error!");
    Wire.requestFrom(slaveAddress, numberOfBytes); 
    //while(Wire.available() < numberOfBytes);
    //long temp = 0;
    int temp = 0;
    byte lsb = Wire.read();//0x28 register low byte
    Serial.println(lsb);
    byte msb = Wire.read();//0x29 register high byte
    Serial.println(msb);
    temp = msb << 8 | lsb; 
  
   // for(int i=1; i<=numberOfBytes; i++)
   // {
   //   temp += (long)Wire.read()<<(8*(numberOfBytes - i));
  //  }
    return temp;
}

cattledog:
Try this less general version for now and see if it still prints out the same values for both bytes.

Yeah still printing the same value for both bytes...

Initialising Gyro
189
189
-296.85
192
192
-283.36
193
193
-278.86
192
192
-283.36

189
189
-296.85

According to the data sheet, the value is expressed in two's compliment form. At least you are getting the math right in spite of the values read from the register being strange. :slight_smile:

189 = 10111101

189 <<8 + 189 = 10111101 10111101 which works out in 2's complement to -29685. Divide by 10000

I2C_write(GYRO_ADDRESS, 0x39, 0b00000000);

I am concerned about this. 0x38 is the largest value of an actual data register.

I know that some i2c devices like a DS1307 RTC and external eeproms will wrap register addresses when writing or reading addresses beyond the actual register number.

With your device, if there is register wrap behaviour, you may have written 0b00000000 to register 0x00 which is a reserved register with a nice warning in the data sheet

Registers marked as Reserved must not be changed. Writing to these registers may cause
permanent damage to the device.

If you continue to have issues with this device, you may want to consult adafruit customer service or ST to understand this issue.

cattledog:

I2C_write(GYRO_ADDRESS, 0x39, 0b00000000);

I am concerned about this. 0x38 is the largest value of an actual data register.

I have no freaking idea what I tried to achieve with this write command. I deleted it.

However, I found oomething very interesting: When I read the two bytes with two seperate read commands they come out differet from one another and even look as if they might be correct!

inline float getRotation()
{
    int temp = I2C_read(GYRO_ADDRESS, 0x29, 1) << 8 | I2C_read(GYRO_ADDRESS, 0x28, 1) ;
    Serial.println(temp*GYRO_SENSIBILITY500);
}

Putput:

0
203
3.55
0
211
3.69
0
208
3.64

I think the values aren't zero becuase I havn't callibrated the gyro yet. The question now is: Why da f**** does it behave like that?

In any case I want to thank you a thousand times for your help and that you answered so many times! (=

However, I found oomething very interesting: When I read the two bytes with two seperate read commands they come out differet from one another and even look as if they might be correct!

From the data sheet--information about multi byte reads

The I2C embedded in the L3GD20 behaves like a slave device and the following protocol
must be adhered to. After the start condition (ST) a slave address is sent, once a slave
acknowledge (SAK) has been returned, an 8-bit sub-address is transmitted: the 7 LSb
represent the actual register address while the MSb enables address auto-increment. If the
MSb of the SUB field is 1, the SUB (register address) will be automatically incremented to
allow multiple data read/write.

In order to read multiple bytes, it is necessary to assert the most significant bit of the subaddress
field. In other words, SUB(7) must be equal to ‘1’ while SUB(6-0) represents the
address of the first register to be read.

To start at register 0x28 with multi byte reads enabled I think you want to use register address 0xA8.
B10101000 which is 0x28 with a 1 in the seventh bit.

cattledog:
From the data sheet--information about multi byte reads

To start at register 0x28 with multi byte reads enabled I think you want to use register address 0xA8.
B10101000 which is 0x28 with a 1 in the seventh bit.

It was there the whole time... I have been starring at this datasheet for freaking hours - how embarrassing...

But THANK YOU even more! You are a saint (=