Hi I am using MMA8451Q accelerometer and its library in PE with code warrior. using MMA1_GetX(); function I am extracting accelerometer's value, so for 1g I am getting 16383 and for -1g I am getting -16383 whereas in the self calibration application note ie AN4069 I found the following information
So according to this 1g value should be 8192 and for -1g value should around -8192 and while extracting raw data from the function, I am getting values from 0 to 65,536 which I suppose it should be around 0 to 16384 . Please help me to understand how this is happening ? and also my values are fluctuating abruptly at same place i.e the reading is varying around +/-100 values when placed on a still surface. I changed the mode from 2g to 4g and 8g but the results were same, I also used inbuilt noise cancellation feature but that also didn't help.
When I went to the code of the library, I found for raw x data the following code
In this code the buffer values are being shifted and hence we are getting 14 bit data so I was wandering how it is happening? So I wanted to know how these bits are being shifted to take a form of 14 bits? May be instead of leaving 2 MSB we are filling that MSB and keeping 2 LSB as floating? Due to the huge fluctuation in the values I think this may be the reason. but I dont know actually how it is happening so I am not sure of it so please correct me where I am wrong. and how to stablise the accelerometer values?
Thanks and regards
Amit Kumar
Hello again,
I have finally done some tests using the PE component for the accelerometer. In order to convert raw data to the correct 14-bit signed (2's complement) number, you have to do this:
In the beginning of main(), declare the variables:
unsigned short raw;
short converted;
Then in the main(), you will get X axis data:
raw = MMA1_MeasureGetRawX();
converted = ((short) raw) / 4; // Typecast raw value to signed 16-bit, and shifts 2 bite to the right without losing signal
Then you will have the signed 16-bit data on "converted" variable. If you want accel in g units, just follow Hui_Ma equation, but in this case the 1/4 factor was already included in "converted" calculation:
g = converted / res;
where "res" is 4096 for +/-2g, 2048 for +/-4g, 1024 for +/-8g.
Good luck!
Antonio
Thanks Antonio and Hui for the help I have done this conversion and it is working fine but still I am getting noise and on that I am still working, I have tried the low noise, high resolution but it didn't worked for me . So still struck with that. Any advice regarding the same ?
Thanks and Regards
Amit Kumar
Some more info, including the example code that demonstrates how to achieve the best noise performance can be found here.
Regards,
Tomas
Hello,
It is normal for accelerometers to have some noise on measurements. The accelerometer has an embedded high-pass filter, but we would need a low-pass one. My suggestion is to read accel values and place them on a software FIFO Buffer (not the internal accelerometer FIFO, which is used for special functions). Then you may average values in a moving window. For example, you read 10 samples and place them in a vector (positions 0-9), and average them. When you read next value, you place result on vector's position 0, and average the 10 values again, so now you actually have averages of samples 1-10. At next sample, you replace vector's position 1 with the new value, and average the 10 values again (samples 2-11), and so on. Averaging multiple samples should reduce noise. Here is a possible C code for that, but keep in mind that I have not tested it.
At the beginning of main:
unsigned short raw;
short converted, samples[10], averaged;
int sum = 0;
char k, p = 0;
In the main function:
for(k = 0; k < 10; k++) { // Fills in the first 10 samples
raw = MMA1_MeasureGetRawX();
converted = ((short) raw) / 4; // Typecast raw value to signed 16-bit, and shifts 2 bite to the right without losing signal
samples[k] = converted;
sum += converted;
}
averaged = sum / 10; // Average of 10 first samples
// Do whatever you want to do with the averaged sample
for(;;) { // Supposing it is an infinite loop
raw = MMA1_MeasureGetRawX();
converted = ((short) raw) / 4;
samples[p] = converted;
p++;
sum = 0;
for(k = 0; k < 10; k++) {
sum += converted;
}
averaged = sum / 10;
// Do whatever you want to do with the averaged sample
}
This is the basic idea. Actually you may optimize the averaging process after the first averaged sample. Instead of summing all values again, you just subtract the oldest sample from "sum", and add the newest one to it, and finally divide it by 10 to "average":
for(;;) { // Supposing it is an infinite loop
raw = MMA1_MeasureGetRawX();
converted = ((short) raw) / 4;
sum -= samples[p];
sum += converted;
samples[p] = converted;
p++;
averaged = sum / 10;
// Do whatever you want to do with the averaged sample
}
Good luck!
Antonio
"It is normal for accelerometers to have some noise on measurements."
Yes. Also consider the environment.
I was once evaluating a 16-bit accel. At first I thought it was complete junk because it was so noisy.
I eventually figured out it was sensing my typing on the keyboard several feet away.
As the number of bits start to go up you have to consider things like trucks driving by and the air conditioner on the roof running.
A Median filter may be more effective. It will remove impulse noise that a averaging filter will not.
First run read 13 samples of the acel., sort them, then take the middle value. Add each new reading then sort. Take middle value repeat each sample.
Must be odd number of samples or average the middle to values if even number of samples. See the link above.
Agreed, Median filter is probably more efficient. Moving average filter is simpler, that's why I suggested it in the first place. Anyway, median filter algorithm is also fast enough for the expected data rate.
Antonio
Hi Kumar,
At first, thanks Antonio for the answer.
The MMA8451Q measured acceleration data is stored in the OUT_X_MSB, OUT_X_LSB, OUT_Y_MSB, OUT_Y_LSB, OUT_Z_MSB, and OUT_Z_LSB registers as 2’s complement 14-bit numbers.
The most significant 8-bits of each axis are stored in OUT_X (Y, Z)_MSB register and OUT_X(Y,Z) register with the smaller register address.
So, from the raw data to calculate g value, it need to combine OUT_X_MSB & OUT_X_LSB value together to a signed 16-bit variable (such as val).
Then customer can use following formula to calculate g value: g = 1/4 * val * [resolution]
resolution for +/-2g is 1/4096; +/-4g is 1/2048; +/-8g is 1/1024
Wish it helps.
B.R.
Ma Hui
Hello Kumar,
Looking at the function code, it seems that it reads the 2 bytes related to X axis, and then assembles a 16-bit (unsigned) variable using the values. It shifts MSByte 8 bits to the left, and adds LSByte.
If you look at MMA8451Q datasheet, you will see that raw values are separated into 2 8-bit values. First register keeps the 8 MSbits, while the other keeps the 6 LSbits, shifted by 2 positions. Thus, when we assemble 16 bits, we have the 14-bit value shifted by 2 bits to the left. And since your return value is unsigned, the value will be represented as a positive value.
In order to transform this raw value into the value it should be, first you have to recover signal information. You may cast your variable to a signed 16-bit variable, but I am not sure if it will work. If this does not work, you have to test the variable. If it is less than 32768, you just leave it as it is. If it is equal or bigger, you have to subtract 65536 from the unsigned value to a signed variable. After correcting signal, you have to divide the final value by 4 to eliminate the 2 useless LSBits. Do not shift it right, or you will lose signal.
I hope it helps. I would post a full function to convert values, but I am on a local holiday, and away from my lab, so I cannot test the function now, and I do not like posting anything I have not tested before. Feel free to talk to me should any problems occur.
Cheers
Antonio