|
|
View previous topic :: View next topic |
Author |
Message |
kein
Joined: 23 Jul 2007 Posts: 103
|
Adxl330 Results conflicting |
Posted: Sat Sep 08, 2007 9:46 am |
|
|
from the following code I'm getting wrong results/readings after calibration. The problem is when I put the accelerometer on the table, the results for x, y & z axis should read zero since zerog values have already been deducted .
Is my calibration procedure correct or what am i doing wrong?
Appreciate your help
Code: |
#define MA_x 1.23466258 /// = 805/652 from [805, 652, 523] >>>x
#define MB_x 1.24665392 // = 652/523 from [805, 652, 523] >>>x
#define MA_y 1.20296296 // = 812/675 from [812, 675, 523] >>>y
#define MB_y 1.29063098 // = 675/523 from [812, 675, 523] >>>y
#define MA_Z 1.19850746 // = 803/670 from [803, 670, 530] >>>z
#define MB_Z 1.26415094 // = 670/530 from [803, 670, 530] >>>z
float VinX, VinY, VinZ;
unsigned int16 AccelBuff[4];
unsigned int16 x;
unsigned int16 y;//[256];
unsigned int16 z;//[256];
void ADXL330ACCAnalysisAdcData(void)
{
int8 j;
float Calibrate_Output;
unsigned int16 *buf_xptr;
unsigned int16 *buf_yptr;
/*buf_xptr = &buff_x;
buf_yptr = &buff_y;
buf_zptr = &buff_z;*/
//for(n = 0; n < 128; n++) {
/* for the case of "X" */
// VinX = READ ADC Value for X_Axis
for(j = 0; j < 4; j++)
{
AccelBuff[j] = ADXL330ACCELReadAdc( j );
}
x = AccelBuff[0]; // read the raw adc value
y = AccelBuff[1]; // read the raw adc value
z = AccelBuff[2];
// read the raw adc value
VinX = ((float)x * 0.002431640625); //x*(vref/1024)..vref = 2.49v
VinY = ((float)y * 0.002431640625);
VinZ = ((float)z * 0.002431640625);
if(VinX > 1.69)
{
Calibrate_Output = ((VinX - 1.69 ) * MA_x ) + 1.65;
}else if( VinX == 1.69 )
{
Calibrate_Output = 0.0; //1.69 ;
}else if( VinX < 1.69 )
{
Calibrate_Output = 1.69 - ((VinX - 1.69 ) * MB_x );
}
lcd_goto_xy(1 , 3);
printf(lcd_putc, "%01.3f " ,Calibrate_Output);
//*buf_xptr++ = Calibrated_Output;
Calibrate_Output = 0;
/* for the case of "Y" */
//VinY = READ ADC Value for Y_Axis
If( VinY > 1.64 )
{
Calibrate_Output = (( VinY - 1.64 ) * MA_y ) + 1.65;
}else if( VinY == 1.64)
{
Calibrate_Output = 0.0; //1.64;
}else if( VinY < 1.64 )
{
Calibrate_Output = 1.64 - ((VinY - 1.64 ) * MB_y );
}
lcd_goto_xy(10, 3);
printf(lcd_putc, "%01.3f " ,Calibrate_Output);
//*buf_yptr++ = Calibrated_Output;
Calibrate_Output = 0;
If( VinZ > 1.63 )
{
Calibrate_Output = (( VinZ - 1.63 ) * MA_Z ) + 1.65;
}else if( VinZ == 1.63)
{
Calibrate_Output = 0.0; //1.63;
}else if( VinZ < 1.63 )
{
Calibrate_Output = 1.63 - ((VinZ - 1.63 ) * MB_Z );
}
lcd_goto_xy(1, 4);
printf(lcd_putc, "%01.3f " ,Calibrate_Output);
//*buf_yptr++ = Calibrated_Output;
//}
INT16U ADXL330ACCELReadAdc(unsigned char chan)
{
//INT16U results;
set_adc_channel(chan); // select the required channel
delay_us(10);
return (unsigned int16)read_adc();
}
void main()
{
xy = x = y = z = 0;
lcd_init();
setup_adc_ports(AN0_AN1_AN2_AN4_AN5_VSS_VREF);
setup_adc(ADC_CLOCK_DIV_64); // setup_adc(ADC_OFF);
set_adc_channel(0); // select the required channe
delay_ms(1000);
lcd_goto_xy(1,1); // Type some characters and backspace over them.
lcd_put_c(" Z Y X");
delay_ms(1000);
// TODO: USER CODE!!
while(1)
{
ADXL330ACCELCalibrateXYZ();
delay_ms(2);
//ADLX330ACCELReadXYZ();
ADXL330ACCAnalysisAdcData();
delay_ms(200);
}
}
} |
|
|
|
Ttelmah Guest
|
|
Posted: Sat Sep 08, 2007 10:05 am |
|
|
What do they read?.
When you put an accelerometer 'down' on a table, flat to the surface, it should read basically 1G vertically (not zero)....
Best Wishes |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
Acceleromter Readings |
Posted: Sat Sep 08, 2007 6:55 pm |
|
|
The readings are [x y z] = [1.698 1.660 1.983]. If it should 1g which is equivalent to 1.983v(at 3.3v/2 at vref+ set 2.49v and vref- set to ground) . Is that right?
But why does conversion to Gs produce more g values then expected for example
sensitivity at 3.3v is 0.333v/g
there4
Code: |
Gs_z=(Calibrate_Output/sensitivity) = 1.983/0.333 = 5.955g
Gs_y = 1.660/0.333 = 4.985g
Gs_x = 1.698/0.333 = 5.099g
|
I thought I was going to be getting 1g not 5.955g. Any idea as to why?
Thanks Confused |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sat Sep 08, 2007 10:12 pm |
|
|
My advice is to look at posts in the Sparkfun forum archives.
http://forum.sparkfun.com/index.php
Sparkfun sells boards with the ADXL330 or similar parts.
I did a search on their forum and got 19 hits.
This thread has 16 posts and would help you a lot:
Reading the Triple Axis Accelerometer Breakout - ADXL330
This one mentions that the output impedance of this type of
accelerometer is 32K ohms, but the PIC data sheet specifies a maximum
of either 10K (for a 16F877) or only 2.5K for an 18F452 as the source
impedance for devices driving the A/D inputs. Because of this, you
must buffer the ADXL330 outputs with Op-Amps.
ADXL320 Flakiness
There is a lot of information on that forum about using the ADXL330.
I suggest that you read all those threads in detail.
One more thing. It's about your style of posting. You like to do a new
thread on the same project about every day or so. I mean, instead
of keeping them all in one large thread. Each new thread then stands
on it's own, as an isolated little tidbit. It's not easy for newcomers to
the problem, to review all the work and knowledge that you and others
have gained up to this point in time. It's all lost back there in many,
many threads spread all over the archives. This way of asking for help
causes people to lose interest. It doesn't seem like they're helping on
a project that's slowly making some progress. It's all just disconnected
little posts. You can do whatever you want, but it's just a suggestion. |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
Sorry for causing that mess |
Posted: Sat Sep 08, 2007 11:55 pm |
|
|
Thanks PC programmer and Sorry for causing that mess. U are right I should keep thing together from now on. Appreciate.
Thanks |
|
|
Ttelmah Guest
|
Re: Acceleromter Readings |
Posted: Sun Sep 09, 2007 2:41 am |
|
|
kein wrote: | The readings are [x y z] = [1.698 1.660 1.983]. If it should 1g which is equivalent to 1.983v(at 3.3v/2 at vref+ set 2.49v and vref- set to ground) . Is that right?
But why does conversion to Gs produce more g values then expected for example
sensitivity at 3.3v is 0.333v/g
there4
Code: |
Gs_z=(Calibrate_Output/sensitivity) = 1.983/0.333 = 5.955g
Gs_y = 1.660/0.333 = 4.985g
Gs_x = 1.698/0.333 = 5.099g
|
I thought I was going to be getting 1g not 5.955g. Any idea as to why?
Thanks Confused |
PCProgrammer has answered pretty well, but I'll do a separate comment about what you are seeing.
First thing, is that devices are _not_ accurate. The 0.333v/g figure is 'nominal', as is the 0.5Vref figure for the output. The former is designed to be pretty accurate (only a few fractions of a percent change), but the latter figure, is very variable (1.2 to 1.8v, when operating off 3v...).
This is why another poster suggested that _you_ need to calibrate your axes. Basically, you can sit the unit in each of six positions (z+, z-, x+, x-, y+, y-), and get six voltage readings. You can then say that the ADC reading at z+, minus the ADC reading at z-, is for a 2g change, and get the scale factor that needs to be used. If you read the other two values at this point, you get the zero values for x, and y, and you can then do the same in the other positions, to get the correct calibrations for the chip. Have a 'calibrate' routine, that asks you to orient the device in each position, and store the results in EEPROM, and the unit will then have a good starting point.
The unit is not reading badly. The nominal 'half reference' voltage,for the 3.3v source, should be 1.65v. You are seeing 1.698, and 1.660, which are very close to the right figure. We don't have the equivalent zero point value for z, but using the nominal value, the calculation is:
(1.983-1.65)/0.333 = 1.
Spot on!. You are handling the calculation wrong, which is why your result is screwy.
In 'G' terms, without correcting the zero, the readings are:
0.14, 0.03, 1
If you corrected the zeros, I'd expect the values to be pretty much perfect.
Best Wishes |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
thanks |
Posted: Sun Sep 09, 2007 6:11 am |
|
|
i've checked the link pcm programmer but it didn't help that much with regards to accelerometer calibration. My problem is I don't know which is the best algorithm
to use. There are two methods given in the link:http://www.ccsinfo.com/forum/viewtopic.php?t=31907 by Ttelmah & icesynth. I don't know which method best suit me since I'm
interestd in shock & vibrations analysis.
icesynth provided an elaborate procedure with a code but I don't why he is adding a 1.5v to (( Vin - 1.55V ) * MA ). Thirdly if Voltage == 1.55V shouldn't Calibrated_Output be 0.0v?
I Code: | f( Voltage > 1.55V )
{
Calibrated Output = (( Vin - 1.55V ) * MA ) + 1.5V
}else if( Voltage == 1.55V ){
Calibrated Output = 1.55V
}else if( Voltage < 1.55V ){
Calibrated Output = 1.55V - ((Vin - 1.55V ) * MB )
} |
lastly Ttelmah thank you very much for the clarification. will it be right if I find the adjustment factor by diving 1.65 by zerog value (for x 1.65/652 coz [Xmax Xog Xmin] = [803 652 523])?
Then adjust values as follow:
Quote: | I've to correct the other values to be(for x-axis):
--------------------------------------------------
- For an Accelerometer reading of 805, the real voltage measured is (805 x 0.002530675) = 2.04V
- For an accelerometer reading of 652, the real voltage measured is (652 x 0.002530675) = 1.65V
- For an accelerometer reading of 523, the real voltage measured is (523 x 0.002530675) = 1.32V |
provided the following method for calculating g values. I can understand it a little bit. But if I were to use will the following function be correct (void AccelReadxyz())?
g = ((adc_val)-512)/204.8
The 'general formula', becomes:
g = ((adc_val)-count_at_0g)/counts_per_g
Code: |
#define x_count_at_0g 512
#define y_count_at_0g 512
#define z_count_at_0g 512
#define counts_per_g 136.8 /*((0.333*A)/Vref)*1024 = ((0.333*1)/2.495)*1024 = 136.8 >>A=gain=1*/
float x_g, y_g, z_g;
unsigned int16 x,y,z;
void AccelReadxyz()
{
int8 i;
/*<<< take two samples for each channel >>>>>>*/
for( i=0; i < 2; i++){
x += ADXL330ReadAdc(0);
y += ADXL330ReadAdc(1);
z += ADXL330ReadAdc(2);
}
x = x >> 1; /* find average x = x/2*/
y = y >> 1; /* find average y = y/2*/
z = z >> 1; /* find average y = y/2*/
if( x > x_count_at_0g ) x_g = (x - x_count_at_0g )/counts_per_g;
else if( x == x_count_at_0g) x_g = 0.0;
else if( x < x_count_at_0g ) x_g = (x - x_count_at_0g )/counts_per_g;
if( y > y_count_at_0g ) y_g = (y - y_count_at_0g )/counts_per_g;
else if( y == y_count_at_0g) y_g = 0.0;
else if( y < y_count_at_0g ) y_g = (y - y_count_at_0g )/counts_per_g;
if( z > z_count_at_0g ) z_g = (z - z_count_at_0g )/counts_per_g;
else if( z == z_count_at_0g) z_g = 0.0;
else if( z < z_count_at_0g ) z_g = (z - z_count_at_0g )/counts_per_g;
ADXL330ACCELDisplayF(x_g, x_g, x_g, 1, 3); //
}
void ADXL330ACCELDisplayF(float zeroX, float zeroY, float zeroZ, int8 xpos, int8 ypos)
{
lcd_goto_xy(xpos,ypos);
printf(lcd_putc, "%01.3f " , zeroX);
lcd_goto_xy((xpos+8),ypos);
printf(lcd_putc, "%01.3f " , zeroY );
lcd_goto_xy((xpos+3),(ypos+1));
printf(lcd_putc, "%01.3f " , zeroZ);
}
unsigned int16 ADXL330ReadAdc(int8 chan)
{
set_adc_channel(chan); // select the required channel
delay_us(10);
return (unsigned int16)read_adc();
} |
|
|
|
Ttelmah Guest
|
|
Posted: Sun Sep 09, 2007 7:48 am |
|
|
Without looking too far, 1.55v, would only be the nominal 'zero', if you were using a 3.1v supply. According to your other posts, you are using 3.3v, hence your zero point is not the one posted for the code you are trying to use...
Yes, the general version of the code I posted before, would be the right solution for you. What you can do, in the 'calibration', is take the reading from the axes with no load (so the x, and y axes, when z is vertical, the x and z axes when y is vertical, etc.), and average the two numbers you get for each axes, as your 'counts for zero' for this axis. Then take the highest and lowest readings rom each channel (which will be the +/- 0.98g if you want reasonable accuracy...), and divide the difference between these readings, by 1.96, to get the 'counts per g' for that axis. So after calibration, you have a counts per g for each axis, and a zero point for each axis. then the generalised formula, should give 'cracking' results.
Best Wishes |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
I'm very stressed up! |
Posted: Sun Sep 09, 2007 9:18 am |
|
|
Hi
I'm getting worst results and they are [x y z] = [4.454 4.682 6.561] using g = (adcCount - count_at_0g )/counts_per_g;
counts_per_g = (AdcCount_max - AdcCount_min)/1.96 at vref = 3.3v Vdd = 3.3v
[Xmax X0g Xmin] = [608 485 396]
[Ymax Y0g Ymin] = [612 512 396]
[Zmax Z0g Zmin] = [608 512 396]
Here is the code:
Code: | #define x_count_at_0g 502 // (608+396)/2
#define y_count_at_0g 504 //512 //(612+396)/2
#define z_count_at_0g 502 //512 //(608+396)/2
#define x_counts_per_g 108.2 /*(608 - 396)/1.96)*/
#define y_counts_per_g 110.2 /*(612 - 396)/1.96)*/
#define z_counts_per_g 108.2 /*(608 - 396)/1.96)*/
float VinX, VinY, VinZ;
float x_g, y_g, z_g;
unsigned int16 xx,yy,zz;
void ADLX330ACCELReadsX_Y_Z()
{
int8 i;
/*<<< take two samples for each channel >>>>>>*/
for( i=0; i < 2; i++){
xx += ADXL330ACCELReadAdc(0);
yy += ADXL330ACCELReadAdc(1);
zz += ADXL330ACCELReadAdc(2);
}
xx = xx >> 1; /* find average x = x/2*/
yy = yy >> 1; /* find average y = y/2*/
zz = zz >> 1; /* find average y = y/2*/
if( xx > x_count_at_0g ) x_g = (xx - x_count_at_0g )/x_counts_per_g;
else if( xx == x_count_at_0g) x_g = 0.0;
else if( xx < x_count_at_0g ) x_g = (xx - x_count_at_0g )/x_counts_per_g;
if( yy > y_count_at_0g ) y_g = (yy - y_count_at_0g )/y_counts_per_g;
else if( yy == y_count_at_0g) y_g = 0.0;
else if( yy < y_count_at_0g ) y_g = (yy - y_count_at_0g )/y_counts_per_g;
if( zz > z_count_at_0g ) z_g = (zz - z_count_at_0g )/z_counts_per_g;
else if( zz == z_count_at_0g) z_g = 0.0;
else if( zz < z_count_at_0g ) z_g = (zz - z_count_at_0g)/z_counts_per_g;
ADXL330ACCELDisplayF(x_g, y_g, z_g, 1, 3); //
} |
what is wrong ???????????? |
|
|
Ttelmah Guest
|
|
Posted: Sun Sep 09, 2007 9:59 am |
|
|
You need to zero xx,yy, and zz, at the start of the routine.
Your tests and arithmetic, can be made smaller, by just using:
Code: |
if( xx == x_count_at_0g )
x_g = 0.0;
else
x_g = (xx - x_count_at_0g )/x_counts_per_g;
|
And the same for the other channels.
Best Wishes |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
Hi Ttelmah? |
Posted: Sun Sep 09, 2007 10:11 am |
|
|
Hi Ttelmah? Mate thanks alot for your help. It's now 2:00am and I'm going to sleep. I'll test it tommorow. and I'll be back.
God bless you mate |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
Hi Ttelmah? |
Posted: Mon Sep 10, 2007 7:54 am |
|
|
Hi Ttelmah?
I've done exactly what you told me to do and found the results as follow:
questions:
1) why am I getting values so big like 605 for z-axis and 594.621 for both x & y axis at -1g orientations?
2) why are we deriving count per g using equation:
counts_per_g = (AdcCount_max - AdcCount_min)/1.96 instead of equation: counts_per_g= ((0.333*A)/Vref)*1024 where sensitivity (0.333) is taken into account?
3) adxl330 Z output while leaning 90 degrees UP for X, Y & Z axis are supposed to be 1.0g instead of 2.262 for X, 2.273 for Z, & 2.295 for Y axis. Why is this the case? What am I doing wrong?
Appreciate your your help
Quote: | Z-axis by reading adxl330 Z output while leaning 90 degrees UP,
90 degrees down and straight up.
at counts_per_g = (AdcCount_max - AdcCount_min)/1.96
+1g, position
[X Y Z] = [2.273 1.179 1.006]gs
-1g, position
[X Y Z] = [605.576 1.016 1.219]gs
X-axis by readingadxl330 X output while leaning 90 degrees UP,X output while leaning 90 degrees UP,90 degrees down and straight up.
at counts_per_g = (AdcCount_max - AdcCount_min)/1.96
+1g position
[X Y Z] = [2.262 1.092 1.063]gs
-1g, position
[X Y Z] = [594.621 1.092 1.396]gs
reading adxl330 Y output while leaning 90 degrees left,
90 degrees right and straight up.
+1g
X Y Z] = [ 1.1202 2.295 1.053]gs
-1g position
X Y Z] = [ 1.092 1.396 594.621]gs |
and the code is:
Code: | #define x_count_at_0g 502 // (608+396)/2
#define y_count_at_0g 504 //512 //(612+396)/2
#define z_count_at_0g 502 //512 //(608+396)/2
#define x_counts_per_g 108.2 /*(608 - 396)/1.96)*/
#define y_counts_per_g 110.2 /*(612 - 396)/1.96)*/
#define z_counts_per_g 108.2 /*(608 - 396)/1.96)*/
float VinX, VinY, VinZ;
float x_g, y_g, z_g;
unsigned int16 xx,yy,zz;
void ADLX330ACCELReadsX_Y_Z()
{
int8 i;
xx=yy=zz=0;
/*<<< take two samples for each channel >>>>>>*/
for( i=0; i < 2; i++){
xx += ADXL330ACCELReadAdc(0);
yy += ADXL330ACCELReadAdc(1);
zz += ADXL330ACCELReadAdc(2);
}
xx = xx >> 1; /* find average x = x/2*/
yy = yy >> 1; /* find average y = y/2*/
zz = zz >> 1; /* find average y = y/2*/
if( xx == x_count_at_0g)
x_g = 0.0;
else
x_g = (xx - x_count_at_0g )/x_counts_per_g;
if( yy == y_count_at_0g)
y_g = 0.0;
else
y_g = (yy - y_count_at_0g )/y_counts_per_g;
if( zz == z_count_at_0g) z_g = 0.0;
else
z_g = (zz - z_count_at_0g)/z_counts_per_g;
ADXL330ACCELDisplayF(x_g, y_g, z_g, 1, 3); //
} |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Sep 10, 2007 11:19 am |
|
|
What happens if you disconnect the ADXL330 from your PIC, and put
a voltmeter on the three output pins ? What voltages do you read ?
This would be a basic "sanity check".
According to the data sheet, the chip has the word "TOP" printed on
the top of the device. Lay it on the table with the top surface flat.
Then according to the data sheet, you should get approximately these readings:
Quote: |
X = 1.5v (represents 0 G)
Y = 1.5v (represents 0 G)
Z = 1.8v (represents 1 G)
|
Make sure nothing is connected to the ST pin. |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
|
Posted: Tue Sep 11, 2007 7:53 am |
|
|
i'M getting:
X = 1.58v (represents 0 G)
Y = 1.65v (represents 0 G)
Z = 1.98v (represents 1 G) at vref+ of 3.3V Vref- =GND.
I think the problem is the floats and other variables especially int16 that overflows quickly. Just a quick question PCM programmer, Why do you add or subtract 1.5 (Vs/2 = 1.5) here ?
Code: | f( Voltage > 1.55V )
{
Calibrated Output = (( Vin - 1.55V ) * MA ) + 1.5V
}else if( Voltage == 1.55V ){
( Calibrated Output = 1.55V
}else if( Voltage < 1.55V ){
Calibrated Output = 1.55V - ((Vin - 1.55V ) * MB )
} |
Why can't we just use
Calibrated_Output_x = (Vin - 1.55V ) * MA ),
Calibrated_Output_y= (Vin - 1.55V ) * MA )
& Calibrated_Output_z = (Vin - 1.55V ) * MA ) rather than checking for >, < or ==?
I'm trying to use fixed point math by first multiplying the inverse of (count_per_g =(Xmax-Xmin)/2)^-1 *1000. and the acceleration I will be getting will be in mg(miligs)
example.
This function returns gs in mg.
Code: |
typedef signed int16 INT16S;
INT16S ADXL330ACCELReturnZ(INT16S data)
{
INT16S cnts;
INT16S temp;
cnts = (data << 5) - 16096; // ((Xmax+Xmin)/2)*32 = 16096
temp = (INT16S)(((INT32S)cnts * (INT32S)19321) >> 16L); //= (((Xmax-Xmin)/2)^-1 *1000)* 2048) =19321 or shift left 11 bits
return (temp);
} |
This seems to work alright but I'm not sure whether I do it this or there is a better way out there. Please help.
Thanks |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Sep 11, 2007 11:15 am |
|
|
Quote: |
Just a quick question PCM programmer, Why do you add or subtract
1.5 (Vs/2 = 1.5) here ? |
That's not my code. I haven't been posting code in this thread. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|