Unsure in converting raw gyro data to deg/s ( ITG-3200 )
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.
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
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;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.
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?
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
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, ®dataformet[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?
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.
The raw data is :
ax16= 5, ay16= -13, az16= -23
This values are when the sensor is stationary.
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.
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
#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, ®s[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, ®2[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, ®dataformat[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!
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.
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
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;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.
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?