Shop OBEX P1 Docs P2 Docs Learn Events
Unsure in converting raw gyro data to deg/s ( ITG-3200 ) — Parallax Forums

Unsure in converting raw gyro data to deg/s ( ITG-3200 )

noellimnoellim Posts: 10
edited 2015-06-13 23:43 in Propeller 1
Hi All,

I am using the sparkfun 9 DOF sensor stick(https://www.sparkfun.com/products/10724). I have written a basic i2c code to extract raw data from the gyros of the sensor stick. I went on to read the data sheet(https://www.sparkfun.com/datasheets/Sensors/Gyro/PS-ITG-3200-00-01.4.pdf) which tells me to divide the the raw data with gyro sensitivity which is 14.375 to get the result in degrees per second(LSB/s). I do not know if this is correct? I am getting values close to x=0,y=2,z=-1 when the sensor is stationary.
#include "simpletools.h"                      // Include simple tools


const char SCL = 3;                          //i2c pins
const char SDA = 4;
                     
const char gyrosAddr = 0x68;
int datReg_gyr = 0x1D;

float GYRO_SENSITIVITY=14.375;      //from data sheet

i2c *ADCSbus;

void Gyro_setup()
{
  unsigned char Read_back[]={0};
  unsigned char DLPF = 0x16;                           //Register 22- DLPF,FullScale
  i2c_in(ADCSbus,gyrosAddr,0x00,1,Read_back,1);        //Test Device ID
  print("Gyros ID is : %x \n",Read_back[0]);
  Read_back[0]=0x19;
  i2c_out(ADCSbus,gyrosAddr,DLPF,1,&Read_back[0],1);   //0x19 is written to set FS_SEL to proper operation 
                                                       // DLPF_CFG to a random mode(1 in this case-I dono about this)
  i2c_in(ADCSbus,gyrosAddr,DLPF,1,Read_back,1);
  print("Read from DLPF is : %x \n",Read_back[0]);
}

int main()                                    
{ 
     int16_t gx16, gy16, gz16;
     int8_t data_gyr[6];

   ADCSbus = i2c_newbus(SCL, SDA, 0);

  Gyro_setup();

while(1)
  {
    memset(data_gyr, 0, 6);
    i2c_in(ADCSbus,gyrosAddr,datReg_gyr,1,data_gyr,6);
    gx16 = (data_gyr[0] << 8) | data_gyr[1];
    gy16 = (data_gyr[2] << 8) | data_gyr[3];
    gz16 = (data_gyr[4] << 8) | data_gyr[5];
    float gfx16 = gx16/GYRO_SENSITIVITY;
    float gfy16 = gy16/GYRO_SENSITIVITY;
    float gfz16 = gz16/GYRO_SENSITIVITY;
    
    print("%c%d,%d,%d,%d%c\n",HOME,gfx16,gfy16,gfz16,timer,CLREOL);  //gyr only
}

Comments

  • noellimnoellim Posts: 10
    edited 2015-06-10 11:35
    I did something similar for the accelerometer(ADXL 345) by multiplying 0.0039 in the code. I am not sure if this is correct as well.

    My values when the sensor is stationary are as follows:

    afx16=0,afy16=0,afz16=-22

    float ACC_SENSITIVITY=0.0039;               //considering +/-2g resolution=> 3.9mg/LSB
    
        memset(data_acc, 0, 6);
        i2c_in(ADCSbus, acc_addr, datReg_acc, 1, data_acc, 6);
        ax16 = (data_acc[0]<< 8) | data_acc[1];
        ay16 = (data_acc[2]<< 8) | data_acc[3];
        az16 = (data_acc[4]<< 8) | data_acc[5];
        float afx16=ax16*ACC_SENSITIVITY;          //converting from raw data to g(taking into consideration resolution of reading) 
        float afy16=ay16*ACC_SENSITIVITY;
        float afz16=az16*ACC_SENSITIVITY;
    
    
  • JasonDorieJasonDorie Posts: 1,930
    edited 2015-06-10 18:10
    The 14.375 is correct (I've used this gyro). The gyro will very likely have an offset when stationary that you'll need to compensate for. Take 16 readings over the course of a second, take the average of them, then subtract the result of that from all the readings you take from then on. That offset value may also change under different temperature conditions (commonly called temperature drift).

    The way to tell for sure if it's correct would be to put the gyro on a turntable or a servo where you have control of the rate of spin. Or you you could try integrating the numbers - IE, add up all the readings, but scaled by the amount of elapsed time (in seconds) between readings. Then you could rotate the board and see if the summation value tracks well. For example, if you rotate the board 90 degrees around the Z axis, the summed Z value should be very close to 90.

    Your accelerometer readings don't look right. An accelerometer at rest should read very close to 1.0 G in the Z axis, zero in X and Y (assuming it's upright and level). Before scaling, the values should be close to 0x, 0y, and 256z.
  • noellimnoellim Posts: 10
    edited 2015-06-10 20:41
    Thanks for your reply.

    For both the acceleromenter and gyroscope the readings are in 16bit format which is provided by the sensor stick. The raw accelerometer value that I get is therefore x=0(+-10) y=0(+-10) z=6000(+-50).

    I will only get 0x,0y,256z only if the raw data is 8 bit . Am I correct?
  • SRLMSRLM Posts: 5,045
    edited 2015-06-11 00:44
    For the accelerometer, I'm referencing from page 27 of the datasheet: http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf

    What is the register 0x31 justify bit set as? If it's set to 1, then you'll need to shift with sign extension.

    To clarify, the number is a 13 bit number with either sign extension or left justification, depending on that bit.

    Second, do you have the read order correct? From the datasheet for register 0x31:

    "The output data is twos complement, with DATAx0 as the least significant byte and DATAx1 as the most significant byte, where x represent X, Y, or Z."

    So I think your code should be
    ax16 = (data_acc[1]<< 8) | data_acc[0];
    
  • noellimnoellim Posts: 10
    edited 2015-06-11 08:52
    Hi ,

    I did the configuration for register 0x31 as 0x0D. Then the MSB and LSB is also swapped. The changes are as below.

    At Acc_setup:
     i2c_out(ADCSbus, acc_addr, 0x31, 1, &regdataformet[0], 1);   //change data formet 
     i2c_in(ADCSbus, acc_addr, 0x31, 1, Read_back, 1);   //0x1D should be written to reg 0x31
     print("Read from reg 31: %x \n", Read_back[0]);
    

    At main Function:
    memset(data_acc, 0, 6);
        i2c_in(ADCSbus, acc_addr, datReg_acc, 1, data_acc, 6);
        ax16 = (data_acc[1]<< 8) | data_acc[0];
        ay16 = (data_acc[3]<< 8) | data_acc[2];
        az16 = (data_acc[5]<< 8) | data_acc[4];
        float afx16=ax16*ACC_SENSITIVITY;          //converting from raw data to g(taking into consideration resolution of reading) 
        float afy16=ay16*ACC_SENSITIVITY;
        float afz16=az16*ACC_SENSITIVITY;
    

    The values I get when the now when the sensor is stationary is as follows:
    afx16=320, afy16=-670, afz16=7428

    It still seems similar to my values that I got previously?
  • SRLMSRLM Posts: 5,045
    edited 2015-06-11 11:08
    0x0D is 0b00001101. So, you're setting the full resolution to 1, justify to 1 and range to 01 (+-4g). You probably wanted 0x08 (0b00001000) which gives you full resolution, justification, and +-2g.

    Second, when you give us example values, it's much more useful to get the a*16 type values, rather than the af*16 values. The float conversion and multiplication mangles the usefulness of the numbers for debugging.
  • noellimnoellim Posts: 10
    edited 2015-06-11 11:45
    I have changed to 0x08 as you said. Now I am getting data which is more stable but I am not sure if it is the correct data.

    The raw data is :

    ax16= 5, ay16= -13, az16= -23

    This values are when the sensor is stationary.
  • JasonDorieJasonDorie Posts: 1,930
    edited 2015-06-11 12:45
    You wrote:
    For both the acceleromenter and gyroscope the readings are in 16bit format which is provided by the sensor stick. The raw accelerometer value that I get is therefore x=0(+-10) y=0(+-10) z=6000(+-50). I will only get 0x,0y,256z only if the raw data is 8 bit . Am I correct?

    Not quite. Like SLRM said, it's much easier for us to see what's going on if you report the data coming from the sensor first. Once you have that right, then worry about scaling it. In general, when working with a new device or system, you start very simple, verify it works, then add complexity a little at a time. If you start with 5 things that *might* not be working, you'll spend a long time guessing.

    An accelerometer at rest will read a constant "acecleration" due to gravity. Assuming you have your sensor reasonably flat and level, you'll most likely see that reading on the Z axis. So if the accelerometer gives 3.9mg per LSB, that means 1g (1000mg) should be outputting (1000mg / 3.9mg), or 256.41 as a raw sensor reading. Since the sensor can't output fractions, it's 256. X and Y should be reading pretty close to zero.

    That's going to depend on the other settings SLRM mentioned, which are justification and resolution. If you look on page 4 of the datasheet, one of the entries in the table is "sensitivity". That shows you how many LSB's per G you should see, minimum, typical, and maximum. So a 1G reading in Z should give you one of the numbers you see in the "typical" column of the table, depending on how you have the chip configured. In "full resolution" mode or +/-2g, the number is 256.
  • noellimnoellim Posts: 10
    edited 2015-06-11 13:00
    The last post that I posted is the raw data values.
    memset(data_acc, 0, 6);
        i2c_in(ADCSbus, acc_addr, datReg_acc, 1, data_acc, 6);
        ax16 = (data_acc[1]<< 8) | data_acc[0];
        ay16 = (data_acc[3]<< 8) | data_acc[2];
        az16 = (data_acc[5]<< 8) | data_acc[4];
    

    the raw values from the sensor when it is flat on the table are ax16= 5, ay16= -13, az16= -23
    This values are without any scaling
  • noellimnoellim Posts: 10
    edited 2015-06-11 13:11
    This is the code that I am using:
    #include "simpletools.h"                      // Include simple tools
    #include "mstimer.h"                          // Include timing library
    
    const char SCL = 3;                          //i2c pins
    const char SDA = 4;
    
    const char acc_addr = 0x53;
    
    char i, timer_start;                                  //timer declarations
    int timer;
    
    i2c *ADCSbus;
    
    void Acc_setup()
    {
      unsigned char Read_back[]={0,0};                    // place var definitions at top of function
      unsigned char regs[] = {0x00, 0x08};  
      unsigned char reg2[] = {0x84};  
      unsigned char regdataformat[] = {0x08};
      
      i2c_in(ADCSbus, acc_addr, 0x00, 1, Read_back, 1);   
      print("Accel ID is: %x \n", Read_back[0]);          //should be 0xE5
    
      i2c_out(ADCSbus, acc_addr, 0x2D, 1, &regs[1], 1);   //write 0x2D
      i2c_in(ADCSbus, acc_addr, 0x2D, 1, Read_back, 1);
      print("Read from reg 2D: %x \n", Read_back[0]);     //to check if 0x08 is written onto reg 0x2D
      
      i2c_out(ADCSbus, acc_addr, 0x38, 1, &reg2[0], 1);   //change FIFO to stream and set D2 to 1
      i2c_in(ADCSbus, acc_addr, 0x38, 1, Read_back, 1);   //0x84 should be written to reg 0x38
      print("Read from reg 38: %x \n", Read_back[0]);
      
      i2c_out(ADCSbus, acc_addr, 0x31, 1, &regdataformat[0], 1);   //change data formet 
      i2c_in(ADCSbus, acc_addr, 0x31, 1, Read_back, 1);   //0x1D should be written to reg 0x31
      print("Read from reg 31: %x \n", Read_back[0]);
    }
    int main()                                    
    { 
        int16_t ax16, ay16, az16;                         //16 bit variables
        Acc_setup();
        timer_start=mstime_start();
      while(1)
      {
        memset(data_acc, 0, 6);
        i2c_in(ADCSbus, acc_addr, datReg_acc, 1, data_acc, 6);
        ax16 = (data_acc[1]<< 8) | data_acc[0];
        ay16 = (data_acc[3]<< 8) | data_acc[2];
        az16 = (data_acc[5]<< 8) | data_acc[4];
        timer=mstime_get();
        print("%c%d,%d,%d,%d%c\n",HOME, ax16,ay16,az16,timer,CLREOL); //acc only
        pause(50);
        high(26);
      }
    }
    

    Let me know if I am missing anything!
  • SRLMSRLM Posts: 5,045
    edited 2015-06-11 23:14
    I'm not sure what the problem is at this point. My suggestion first is to get the binary values of the data_acc array, and see if you can figure out what's being received and how it's reconstructed. There may be something we're missing.

    If that doesn't work, I usually start debugging with a logic analyzer. I use a Saleae Logic16, and it usually makes things very clear by seeing what's happening on the wire.
  • noellimnoellim Posts: 10
    edited 2015-06-12 08:56
    I tried to output the raw data from my accelerometer using this code.
    print("%c %d,%d,%d,%d,%d,%d\n",HOME,data_acc[0],data_acc[1],data_acc[2],data_acc[3],data_acc[4],data_acc[5]);
    

    the raw data seems to be fluctuating even when I am not moving the sensor. It seems to be fluctuating in multiples of 32. Should I divide the data by 32 ? Is there some problem with the code?

    This is the raw data from using the print as above:

    -96,1,32,-1,32,29
    -96,1,0,-1,32,29
    -64,1,0,-1,64,29
    -96,1,0,-1,64,29
    -128,1,64,-1,64,29
    -64,1,32,-1,64,29
    -96,1,0,-1,64,29
    -96,1,32,-1,64,29
    -96,1,32,-1,64,29
    -64,1,32,-1,64,29
    -96,1,32,-1,32,29
    -96,1,32,-1,32,29
    -96,1,0,-1,64,29
    -96,1,0,-1,64,29
    -96,1,64,-1,64,29
    -96,1,32,-1,32,29
    -96,1,-32,-2,64,29
    -128,1,0,-1,32,29
    -96,1,32,-1,32,29
    -96,1,32,-1,64,29
    -128,1,32,-1,32,29
    -96,1,32,-1,96,29
    -64,1,32,-1,32,29
    -96,1,32,-1,96,29
    -96,1,32,-1,64,29
    -96,1,64,-1,64,29
    -64,1,0,-1,64,29
    -128,1,32,-1,32,29
    -96,1,32,-1,32,29
    -96,1,32,-1,64,29
    -96,1,32,-1,32,29
  • noellimnoellim Posts: 10
    edited 2015-06-12 09:14
    I tried to the code on combinig the MSB and LSB to 16 bit data and then divided the value by 32.
    The value I got was ax16=-7 , ay16=5 , az16=234 when the sensor is stationary on the table. This value seems to be near/similar to x0, y0, z256 as said earlier.
    I went on to multiply this value with 0.0039 and the I got results as follows:
    afx16,afy16,afz16
    0.06,-0.02,0.91
    0.06,-0.02,0.91
    0.07,-0.02,0.90
    0.07,-0.02,0.91
    0.07,-0.05,0.90
    0.07,-0.05,0.90
    0.07,-0.05,0.91
    0.07,-0.01,0.91
    0.07,-0.02,0.91
    0.07,-0.05,0.91
    0.07,-0.05,0.91
    0.07,-0.05,0.91
    0.07,-0.02,0.91
    0.07,-0.02,0.91
    0.07,-0.05,0.91
    0.07,-0.05,0.90
    0.07,-0.02,0.91

    Am I doing it correctly?

    This is the code that i used:
        i2c_in(ADCSbus, acc_addr, datReg_acc, 1, data_acc, 6);
        ax16 = (data_acc[1]<< 8) | data_acc[0];
        ay16 = (data_acc[3]<< 8) | data_acc[2];
        az16 = (data_acc[5]<< 8) | data_acc[4];
        ax16=ax16/32;
        ay16=ay16/32;
        az16=az16/32;
        float afx16=ax16*ACC_SENSITIVITY;          //converting from raw data to g(taking into consideration resolution of reading) 
        float afy16=ay16*ACC_SENSITIVITY;
        float afz16=az16*ACC_SENSITIVITY;
    
  • SRLMSRLM Posts: 5,045
    edited 2015-06-12 15:40
    Interesting.

    First off, the values look to be correct. Next up is to back out why they are correct.

    Diving by 32 is equivalent to shift right by 5 (value >> 5), so that implies that the value is left justified instead of right justified. Secondly, shifting right by 5 puts the value at 11 bits, which is full resolution for the +- 4g range (see page 4 of the datasheet).

    It seems that register 0x31 is being set incorrectly, with a value of 0bNNNN0101.
  • noellimnoellim Posts: 10
    edited 2015-06-13 07:50
    I have now set the register 31 to 0x09 which is full resolution, right justification, +-4g.

    I now get data from ax16=13 , ay16=-12 , az16=-22 (raw data without any multiplication or division) when the sensor is stationary.

    What should I do with this? It doesn't seem to be near x0 y0 z255. Is there anything else wrong?
  • SRLMSRLM Posts: 5,045
    edited 2015-06-13 23:43
    I'm not sure what's wrong. My suspicion is that the control register is not being set correctly, but that's just a hunch. At this point I don't have anything further.
Sign In or Register to comment.