Hi I've just started using the MMA8562 sensor in a project where I want to calculate my position as time goes by.
I want to do this by using a gyro (FXAS21002C) and the accelerometer.
I would like to get the 3 axis acc values with 12.5Hz sample rate and after correcting with the gyro values double integrate to find my position in x and y as time goes by.
So far the setup is stationary on my desk.
If the PCB is flat I get good results: x and y is near zero and I see the gravity in the z channel (value of around 16600) , and samples come in with 12.5Hz
But if I lift the PCB edge a few (5-10) degrees the z component disapears - so I get all three values near zero - gravity disapeared???
The MMA8562 can then measure with or without the gravity acceleration and it can change from second to second??
If I put the PCB on the side I can get the gravity in the X direction - but again the value can then disappear without warning.
These are the commands I use to intialize the MMA8562:
IIC_WriteReg(0x1d, 0x2b, 0x40); //Reset accelerometer
__delay_ms(10);
IIC_WriteReg(0x1d, 0x2b, 0x00); //remove reset - maybe not nessesary
__delay_ms(1);
IIC_WriteReg(0x1d, 0x0e, 0x00); //select 2g range
__delay_ms(1);
IIC_WriteReg(0x1d, 0x2b, 0x12); //High resolution
__delay_ms(1);
IIC_WriteReg(0x1d, 0x2c, 0x02); //Interrupt active high
__delay_ms(1);
IIC_WriteReg(0x1d, 0x2d, 0x01); //Interrupt on Data Ready
__delay_ms(1);
IIC_WriteReg(0x1d, 0x2e, 0x01); //Data ready comes out on Int1 pin
__delay_ms(1);
IIC_WriteReg(0x1d, 0x2a, 0x29); //Sample at 12.5Hz and activate
Is there some more registers I need to program to just get the raw accelerations?
Here is an example where I have rotated the PCB up to vertical - I write out every 25 samples and move the board very slowly
ACC X: -80 Y: -32 Z: 16672 first the board i laying flat on the desk
ACC X: -80 Y: -32 Z: 16672
ACC X: -96 Y: -16 Z: 16672
ACC X: -80 Y: -48 Z: 16672
ACC X: -80 Y: -32 Z: 16672
ACC X: -96 Y: -32 Z: 16688
ACC X: -80 Y: -32 Z: 16688 Now I start lifting the PCB
ACC X: 1552 Y: -32 Z: -32 Some of the gravity is now in the X channel, but where is the Z component?
ACC X: -112 Y: -64 Z: -48 Now all is gone
ACC X: 6176 Y: -48 Z: 15376 This is what I would expect
ACC X: 6192 Y: -32 Z: -32 There goes the Z component again
ACC X: 9760 Y: 304 Z: -16 --
ACC X: -112 Y: -112 Z: -128 all gone!
ACC X: -64 Y: 16 Z: -64
ACC X: -96 Y: -64 Z: -112
ACC X: 15648 Y: -32 Z: 3856 Here We are at almost vartical - all good
ACC X: -128 Y: -64 Z: -48 all gone
ACC X: -96 Y: -64 Z: 3344 Now we got Z back but where is X?
ACC X: -16 Y: -96 Z: 2928
ACC X: 15872 Y: -96 Z: -80 Here's X but no Z
Any help would be greatly appreciated.
Morten
Solved! Go to Solution.
Hi Morten,
Sorry for the delay in getting back yo you.
Looking at your snapshots, it seems that your burst read is correct and the X,Y and Z data are read at the right time (STATUS reg = 0x0F). However, since the problem is random, we need to record more samples. It would also be useful to monitor the interrupt pins.
It may be the sprintf() function causing a timing problem. I am afraid we really need to see what is going on the I2C bus and interrupt pins and compare that with your debug output.
Regards,
Tomas
Hi Morten,
It seems that you are not converting properly the raw data from accelerometer registers 0x01 – 0x06 to signed 12-bit values. You should use something like this:
// Signed 12-bit accelerometer data
Xout_12_bit = ((short) (OUT_X_MSB<<8 | OUT_X_LSB)) >> 4; // Compute 12-bit X-axis acceleration output value
Yout_12_bit = ((short) (OUT_Y_MSB<<8 | OUT_Y_LSB)) >> 4; // Compute 12-bit Y-axis acceleration output value
Zout_12_bit = ((short) (OUT_Z_MSB<<8 | OUT_Z_LSB)) >> 4; // Compute 12-bit Z-axis acceleration output value
Is this what you are doing, or something very close anyway?
You might also find useful my complete example code for the MMA8652FC.
Let me know whether or not this helps, or if you have any other questions.
Regards,
Tomas
PS: If my answer helps to solve your question, please mark it as "Correct". Thank you.
Hi Tomas,
I have now made sure my ReadRegs routine works by reading the control registers 1-5 in all 9 million times and checking that I get back what I programmed - not one false value received.
So it must be the mma8652 that is giving strange xyz values dependent on the angle that gravity enters the chip.
I have also brought up my second development board and that behaves the same way as I described 6 hours ago.
Any help would be greatly apreciated.
Morten
Hi Morten,
First of all, the way I am converting the raw data from registers 0x01 – 0x06 to 12-bit signed values is correct. In addition to your procedure, I am shifting the complete 16-bit signed value (OUT_X_MSB<<8 | OUT_X_LSB) 4 bits to right to get the 12-bit signed values, not only the LSB register as you stated.
However, your problem does not seem to be related to the data conversion as it happens randomly and even with the device sitting stationary on your desk. It sounds to me more like a timing issue or something like that. Do you have a logic analyzer to check the timing and data on the bus? What board and MCU are you actually using?
Regards,
Tomas
Hi Tomas,
Have you had a chance to look at the timing shots I posted on october second?
Hoping for a solution
Morten
Hi Morten,
Sorry for the delay in getting back yo you.
Looking at your snapshots, it seems that your burst read is correct and the X,Y and Z data are read at the right time (STATUS reg = 0x0F). However, since the problem is random, we need to record more samples. It would also be useful to monitor the interrupt pins.
It may be the sprintf() function causing a timing problem. I am afraid we really need to see what is going on the I2C bus and interrupt pins and compare that with your debug output.
Regards,
Tomas
Hi Tomas,
I found out what was wrong - it was my code - I used a buffer to receive my data declared as:
char buf[7];
When I started to make traces for you with good and bad samples I changed my code to only run single reads on demand and output extra debug information.
When looking at that it was obvious what was wrong:
ACC X: -7 Y: 20 Z: 1040
S:ffff X:00 ff90 Y:01 40 Z:41 00
In the last line I dump all 7 bytes received, the status byte and 3 times 2 byte acceleration bytes.
See the status byte is written as ffff and the least significant X acceleration byte is ff90 - so the C compiler sign extended the bytes for me before I added in the least significant byte!
So after changing to this buffer:
unsigned char buf[7];
Everything works perfectly.
Thanks Morten
Hi Tomas,
You are quite right, of cause - I missed your parenteses before the shift right operation.
I have a 2 channel storage scope and will try to capture - but I'm afraid I just broke one of the pull-up resistors off trying to get it.
Right now my system does not work at all.
Anyway it is our own surface mount PCB.
It uses the Microchip MCU dsPIC33 running at 70MHz and the I2C bus is about 4 cm's long and the pull-ups are 4,7 kohm
I'm running the bus at 400 kHz using the build-in I2C circuit.
On the bus are the MCU, an MMA8652 accelerometer, an FXAS21002C gyro and the MAG3110 compass
Okay, I switched to my backup board, here's the trace of reading 7 registers starting at adr 0 from the MMA8652
https://dl.dropboxusercontent.com/u/2651504/whole.png
And here's zoom'ed a bit more
https://dl.dropboxusercontent.com/u/2651504/zoom.png
If I need to zoom somewhere else let me know.
Best regards Morten
Hi Tomas,
I have now tried to work with all my I2C devices, and I get strange values from all of them.
I have tried to lower my bitrate on the bus - I'm now running 250kHz
Here is a trace of the whole of a 7 byte read from adr 0 and up
Here I've zoomed in on the first part of the transfer
And here's further zoomed on the last bit of the zoom above
Here's how I've set up the different devices:
#define aAcce 0x1d
#define aGyro 0x20
#define aComp 0x0e
//registers in the MM8652 acceleromter
#define aDaCfg 0x0e
#define aCtrl1 0x2a
#define aCtrl2 0x2b
#define aCtrl3 0x2c
#define aCtrl4 0x2d
#define aCtrl5 0x2e
//registers in the FXA21002C Gyro
#define gCtrl0 0x0d
#define gCtrl1 0x13
#define gCtrl2 0x14
//registers in the MAG3110 Magnetometer
#define cCtrl1 0x10
#define cCtrl2 0x11
//disable I2C devices
IIC_WriteReg(aComp, cCtrl1, 0x00); //stop magnetometer
IIC_WriteReg(aAcce, aCtrl1, 0x00); //stop accelerometer
IIC_WriteReg(aGyro, gCtrl1, 0x00); //stop gyro
//reset i2Cdevices
IIC_WriteReg(aComp, cCtrl2, 0x10); //reset magnetometer
IIC_WriteReg(aAcce, aCtrl2, 0x40); //Reset accelerometer
IIC_WriteReg(aGyro, gCtrl1, 0x40); //Reset gyro
__delay_ms(10);
//Setup MMA8652 Accelerometer
IIC_WriteReg(aAcce, aDaCfg, 0x00); //select 2g range
IIC_WriteReg(aAcce, aCtrl2, 0x02); //High resolution
IIC_WriteReg(aAcce, aCtrl3, 0x02); //Interrupt active high
IIC_WriteReg(aAcce, aCtrl4, 0x01); //Interrupt on Data Ready
IIC_WriteReg(aAcce, aCtrl5, 0x01); //Data ready comes out on Int1 pin
IIC_WriteReg(aAcce, aCtrl1, 0x29); //Sample at 12.5Hz and activate
//Setup Gyro FXAS21002C
IIC_WriteReg(aGyro, gCtrl0, 0x03); //250 degrees per second
IIC_WriteReg(aGyro, gCtrl2, 0x0e); //Interrupt at data ready on pin 1 active high
IIC_WriteReg(aGyro, gCtrl1, 0x1f); //Sample at 12.Hz and activate
//setup MAG3110 Magnetometer
IIC_WriteReg(aComp, cCtrl2, 0x80); //run continous
IIC_WriteReg(aComp, cCtrl1, 0x19); //Sample at 10Hz and activate
This is my code to get the acceleration values, I now use you code Tomas so I place the values in the low 12 bits of a signed integer
I then check that the sum of the absolute acceration values are at least 900 - othervise the MM8652 has dropped measuring gravity
- and check that I can read the control registers correctly.
int X,Y,Z;
int AccTid=0, GyroTid=0, MagTid=0;
unsigned long g=0, z=0, i2cgood=0, i2cbad=0;
while(1) {
if (AccDataRdy) {
char buf[7];
IIC_ReadRegs(aAcce, 0, 7, buf);
X = (buf[1]<<8 | buf[2])>>4;
Y = (buf[3]<<8 | buf[4])>>4;
Z = (buf[5]<<8 | buf[6])>>4;
int tot;
tot = abs(X) + abs(Y)+ abs(Z);
if (tot < 900) z++; else g++;
if (CheckRegs) {
//Now check read all written I2C registers
IIC_ReadRegs(aAcce, aCtrl1, 7, buf); //First MMA8652 accelerometer
if (buf[0] == 0x29) i2cgood++; else i2cbad++; //Check CTRL_REG1
if (buf[1] == 0x02) i2cgood++; else i2cbad++; //Check CTRL_REG2
if (buf[2] == 0x02) i2cgood++; else i2cbad++; //Check CTRL_REG3
if (buf[3] == 0x01) i2cgood++; else i2cbad++; //Check CTRL_REG4
if (buf[4] == 0x01) i2cgood++; else i2cbad++; //Check CTRL_REG5
if (IIC_ReadReg(aAcce, aDaCfg) == 0x00) i2cgood++; else i2cbad++; //Check XYZ_DATA_CFG
IIC_ReadRegs(aGyro, gCtrl1, 2, buf); //Next Gyro FXAS21002C
if (buf[0] == 0x1f) i2cgood++; else i2cbad++; //Check CTRL_REG1
if (buf[1] == 0x0e) i2cgood++; else i2cbad++; //Check CTRL_REG2
if (IIC_ReadReg(aGyro, gCtrl0) == 0x03) i2cgood++; else i2cbad++; //Check CTRL_REG0
IIC_ReadRegs(aComp, cCtrl1, 2, buf); //Last MAG3110 Magnetometer
if (buf[0] == 0x19) i2cgood++; else i2cbad++; //Check CTRL_REG1
//CTRL_REG2 can't be read back - always reads as 0
// if (buf[1] == 0x80) i2cgood++; else i2cbad++; //Check CTRL_REG2
}
AccTid++;
if (AccTid >= 25) {
AccTid=0;
while (!DebugDone);
sprintf(DebugInfo,"ACC X:%6d Y:%6d Z:%6d -g: %lu +g: %lu -r: %lu +r: %lu\r\n", X, Y, Z, z,g, i2cbad, i2cgood);
// z=g=0;
SendDebugInfo(DebugInfo);
}
}
Here's an example of my debug output:
ACC X: -2 Y: -1 Z: 1043 -g: 5364 +g: 51286 -r: 0 +r: 566500
ACC X: -2 Y: -5 Z: -1 -g: 5368 +g: 51307 -r: 0 +r: 566750
ACC X: -2 Y: -2 Z: 1042 -g: 5370 +g: 51330 -r: 0 +r: 567000
ACC X: -1 Y: -2 Z: 1042 -g: 5370 +g: 51355 -r: 0 +r: 567250
ACC X: -2 Y: -3 Z: 1042 -g: 5374 +g: 51376 -r: 0 +r: 567500
ACC X: -2 Y: -3 Z: 1042 -g: 5376 +g: 51399 -r: 0 +r: 567750
ACC X: -3 Y: -2 Z: 1043 -g: 5376 +g: 51424 -r: 0 +r: 568000
ACC X: -3 Y: -3 Z: -1 -g: 5378 +g: 51447 -r: 0 +r: 568250
ACC X: 16 Y: -2 Z: 1041 -g: 5381 +g: 51469 -r: 0 +r: 568500
ACC X: -4 Y: -2 Z: 1041 -g: 5386 +g: 51489 -r: 0 +r: 568750
ACC X: -3 Y: -4 Z: 1040 -g: 5387 +g: 51513 -r: 0 +r: 569000
As you can fom this test - horizontal gavity is dropped around 10% of the samples
-g (without gravity) is 5387 samples and good samples is 51513
Half a million control registers have been read and all the reads returned the expected values.
If I tilt the board about 10 degrees about 40% of the samples has no gravity
ACC X: 176 Y: -3 Z: 1025 -g: 19377 +g: 30511 -r: 0 +r: 498880
ACC X: 176 Y: -3 Z: 1026 -g: 19381 +g: 30532 -r: 0 +r: 499130
ACC X: -3 Y: -7 Z: -2 -g: 19387 +g: 30551 -r: 0 +r: 499380
ACC X: 177 Y: -4 Z: 1024 -g: 19397 +g: 30566 -r: 0 +r: 499630
ACC X: 177 Y: -3 Z: 1024 -g: 19407 +g: 30581 -r: 0 +r: 499880
I hope this is enough information for you to pinpoint what I'm doing wrong.
I have attached the log files - one line for every 25 samples.
The gyro seems to drift a lot - I've made code that averages the first 16 samples of each channel and I then subtract that from the raw samples - but a simple integation (summation) of the samples still drift - If I then redo my offset it starts to drift in other directions
We are talking about 100,000 LSB in around 10 minutes at 12.5 Hz samplerate
The compass is pointing south (I believe) and returns
X jumping between either 0 or 2000
Y relatively stable at 800-1000
Z around zero
But I can read the control registers from both devices - very strange
Morten
Hi Thomas,
Thanks for your quick reply. We seem to setup the same registers ( I believe I had found your very nice link before :smileyhappy:)
I did not show how I convert the samples. Here how I do it:
int X,Y,Z; //16 bits signed ints
char buf[7];
IIC_ReadRegs(0x1d, 0, 7, buf); //reads 7 auto incrementing bytes starting from address 0
//buf[0] contains the status byte, typically 0x07 for new xyz data
X = buf[1]<<8 | buf[2];
Y = buf[3]<<8 | buf[4];
Z = buf[5]<<8 | buf[6];
I do believe your code is actually wrong because you shift the LSB down 4 places.
The Spec pdf says the data is placed in the upper 4 bits of OUT_X_LSB and the lower 4 bits are zero
So my code places the 12 bits in the top part of the 16 bits integer, where your code places it like this:
hhhhhhhh0000llll
You can also see below that my samples comes out as multiples of 16 because the low 4 bits are zero.
But that aside my problem is that laying flat on the table sometimes the data I get back is wrong.
Here is some output where I have adjusted the PCB to get minimum x and y values, I've now added to the end of the line how many of the samples where abs(x)+abs(y)+abs(z) is below 15500 i.e. where gravity has disappeared and the last number is the number of samples with gravity (remember I only dump a line for every 25th sample)
ACC X: -32 Y: 16 Z: 16640 0 25
ACC X: -48 Y: 16 Z: 16640 3 22
ACC X: -16 Y: 16 Z: 16656 1 24
ACC X: -32 Y: 16 Z: -16 1 24
ACC X: -16 Y: 16 Z: 16640 0 25
ACC X: -32 Y: 16 Z: 16640 2 23
ACC X: -32 Y: 0 Z: 16640 2 23
ACC X: -32 Y: 16 Z: 16640 0 25
ACC X: -32 Y: 32 Z: 16640 0 25
So you see that I had 9 out of 225 samples where the result is wrong, the chip does not detect the gravity (line 4)
Now if i tilt the PCB a little bit - so X begins to point down I get these very nice results
ACC X: 2576 Y: -80 Z: 16416 0 25
ACC X: 2592 Y: -80 Z: 16416 0 25
ACC X: 2592 Y: -80 Z: 16416 0 25
ACC X: 2592 Y: -96 Z: 16432 0 25
ACC X: 2592 Y: -96 Z: 16416 0 25
ACC X: 2592 Y: -80 Z: 16416 0 25
ACC X: 2592 Y: -96 Z: 16416 0 25
ACC X: 2592 Y: -80 Z: 16416 0 25
See how I have ~0.16g in the X direction and the Z has deminished a bit.
Now I tilt it just a little bit more, and see what happens:
ACC X: -128 Y: -96 Z: -32 25 0
ACC X: -128 Y: -96 Z: -32 25 0
ACC X: 2928 Y: -96 Z: -32 25 0
ACC X: -128 Y: -80 Z: -32 25 0
ACC X: -128 Y: -96 Z: -32 25 0
ACC X: 2928 Y: -80 Z: -32 25 0
ACC X: 2928 Y: -96 Z: -32 25 0
ACC X: -128 Y: -80 Z: -32 25 0
ACC X: -128 Y: -80 Z: -32 25 0
ACC X: -128 Y: -96 Z: -32 25 0
ACC X: -128 Y: -96 Z: -32 25 0
Sometimes I get the 0.175g in the X direction - but ALL the Z direction is gone!!
Do I have a bad chip? Or is there some other register that I need to disable - I thinking that the chip has some Portrait/Landscape things - but they should be disabled after reset as I read the Spec.