|
|
View previous topic :: View next topic |
Author |
Message |
kein
Joined: 23 Jul 2007 Posts: 103
|
Converting accelerometer adc values to Gs |
Posted: Thu Aug 30, 2007 7:49 am |
|
|
Does anyone know how to convert the digital values read from adxl330 tri-axels accelerometer into their corresponding g(s).
I've managed to get good adc values from the sensor but don't know how to convert this to g. Please help!
Any simple set up, illustration is highly appreciated.
|
|
|
Ttelmah Guest
|
|
Posted: Thu Aug 30, 2007 8:45 am |
|
|
It'll depend on your hardware.
The chip itself, gives (nominally) 300mV/g. You presumably have an op-amp, giving gain, and have the Vref of the PIC, set to the 3v, that the chip uses as it's full scale. So the first thing will be what is the gain of the op-amp?. So if (for instance), you are using a 3v reference on the ADC, and have the op-amp, giving *2 gain, then you will see 0.3*2v/g. A reading of 512, will correspond to 0g, and the formula to convert readings to g, would be:
g = ((adc_val)-512)/204.8
The 'general formula', becomes:
g = ((adc_val)-count_at_0g)/counts_per_g
So in the example, the 'count_per_g' value, is the voltage per g (0.3*2 = 0.6, over the reference (3), times the counts at 3v (1024- impossible to actually get, but the count that would theorectically happen at this voltage).
So, for the example, (0.6/3)*1024 = 204.8
You will need to work out the numbers for your hardware.
As a general comment though, it is often worth adjusting the hardware, to make the maths easy, and for speed, possibly working in scaled integers. So (for example), if you decided to have the output in thousandths of a g, and altered the gain of the op-amp to 2.44*, then the calculation would become:
thousandthsg = ((adc_val)-512)*4
This can be done totally in signed int16 arithmetic, can be displayed with a decimal point, using the '%w' output format, and would be hundreds of times faster for the chip to calculate, than the 'floating point' version.
The 'key' here is that changing the gain, makes the 'counts_per_g' figure, into 250, which *4, gives 1000, for 1g.
Best Wishes |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
Waow thank you very much! |
Posted: Fri Aug 31, 2007 10:13 pm |
|
|
Thank you very much. What a clean clear explanation. Just a quick question, suppose I use the gain of 2 or 2.44 and my Vs=Vref=3.0v, don't you think this will give an output Vo(at the adxl330)=Gain*Vox(assumming worse case scenario Vox=Vref)=2*3.0=6.0v. This off course is not good for the PIC processor which require Vin to ADC pins of between 0-5V!
finally, what would be the best OPAmp that you may recommend to me. Coz I heard some post where some people advise not to use lm324.
Thanks again
|
|
|
Ttelmah Guest
|
|
Posted: Sat Sep 01, 2007 2:32 am |
|
|
I'd suspect people are thinking here of the Voh figure for this amplifier.
Now, no op-amp is 'perfect' for all applications. The LM321, is quite a useable single supply op-amp, with relatively good behaviour for it's price, in many areas. However it's 'downside', is that it can't pull it's output, even remotely 'close' to it's +ve supply rail (Voh). I don't knw what supplies you are running, but if (for instance), your circuit is running off 5v, with a separate 3v supply for the accelerometer, and reference, then if the 321 is run off 5v, it'd probably be OK.
For precision, you are looking at something rather more expensive. The LT1218 for example, will typically swing withing a few hundredths of a volt of it's supply rail, also having an input range covering 'rail to rail', and is really a much more accurate amplifier, but at a much higher cost (up to about 20* that of the 321...). Part of design (for production use), is in many cases, working out how to modify your hardware, to allow cheaper parts to be acceptable...
Best Wishes |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1908
|
|
Posted: Sat Sep 01, 2007 11:01 am |
|
|
The Texas Instruments TLV237x (x = 1, 2 or 4) is a relatively inexpensive true rail-to-rail input and output opamp. It's the standard in all my designs (so far). |
|
|
kender
Joined: 09 Aug 2004 Posts: 768 Location: Silicon Valley
|
accelerometer calibration procedure |
Posted: Sat Sep 01, 2007 12:09 pm |
|
|
Here’s an easy procedure to calibrate the accelerometer:
- Pick an axis. Point the axis vertically up. Accelerometer sees +1g on that axis. Note the A/D reading.
- Point the axis vertically down. Accelerometer sees -1g. Note the A/D reading.
- Point the axis horizontally. Accelerometer sees 0g. Note the A/D reading.
- Fit a line to these three points and use this line equation to convert from A/D counts to gs. _________________ Read the label, before opening a can of worms. |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
Calibrating adxl330 |
Posted: Mon Sep 03, 2007 12:00 am |
|
|
Could you please give an example. The followings are values obtained from various orientation of the accelerometer.
ADXL330 FLAT ON TABLE: Z=1.83V, Y= 1.53V, X=1.53V
ADXL330 UPSIDE DOWN: Z=1.23V, Y=1.49V, X=1.50V
ADXL330 Y-AXIS VERTICAL: Z=1.55V, Y= 1.55V, X=1.20V
ADXL330 Y-AXIS OPPOSITE DIRECTION: Z=1.48V, Y=1.45V, X=1.81V\
ADXL330 X-AXIS VERTICAL: Z=1.51V, Y= 1.20V, X=1.53V
ADXL330 X-AXIS OPPOSITE DIRECTION: Z=1.47V, Y=1.82V, X=1.50V
from these results how would you draw a line to workout the equation?
Thanks |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
Results are weird! |
Posted: Mon Sep 03, 2007 3:19 am |
|
|
Hi Ttelmah?
I've used your equations with no success. The results I'm getting are way too big.
g = ((adc_val)-512)/204.8
The 'general formula', becomes:
g = ((adc_val)-count_at_0g)/counts_per_g
I've defined a const #define counts_per_g 101.1. Since my OPAMP has unity again,
Code: | counts_per_g = (0.3/3.04)*1024
|
0.3=300mV/G
My vref=3.04 which is the same voltage used for adxl330 Vs(supply voltage).
Below is my code.
# Code: | define CONST_GX 621, #define CONST_GY 521, #define CONST_GZ 504 | are zeroG values obtained when the accelerometer is placed on a flat surface of a table
Results:
xg = (zeroG_x - CONST_GX )/counts_per_g ;
xg=4248.2368 or 0.0 but when adxl330 tilted with x-axis vertical this value remains at 4248.2368
yg = (zeroG_y - CONST_GY )/counts_per_g ;
yg=0.0002368 but change to 4248.2368 when adxl330 is tilted vertically with y-axis vertical
zg = (zeroG_z - CONST_GZ )/counts_per_g ;
zg=0.00000 but change to 4248.2368 when adxl330 is tilted
any help is appreciated thank you.
Code: |
#include <16F877A.h>
#device *=16
#device adc=10
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //High speed Osc (> 4mhz)
#FUSES NOPUT //No Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#use delay(clock=20000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
#include <KBD.C>
#include <ctype.h>
#include <float.h>
#include <math.h>
#include <string.h>
#include "LCD204.c"
#define CONST_GX 621
#define CONST_GY 521
#define CONST_GZ 504
#define counts_per_g 101.1
int32 zeroG_x;
int32 zeroG_y;
int32 zeroG_z;
int32 xg,yg,zg;
int32 newG_x,newG_y, newG_z;
int32 accel[3];
int32 x;
int32 y;//[256];
int32 z;//[256];
void AccelCalibrateXYZ();
void AccelReadXYZ();
float AccelConvertVolt(float adcval);
void main()
{
//int8 ch,ii;
newG_x = newG_y = newG_z = 0;
//accel[0] = accel[1] = accel[2] = 0;
setup_adc_ports(AN0_AN1_AN4_VREF_VREF);
setup_adc(ADC_CLOCK_DIV_32);
lcd_init();
AccelCalibrateXYZ();
lcd_goto_xy(1,1);
printf(lcd_putc, "%Lu", newG_x);
lcd_goto_xy(8,1);
printf(lcd_putc, "%Lu", newG_y );
lcd_goto_xy(15,1);
printf(lcd_putc, "%Lu", newG_z);
delay_Ms(1000);
delay_Ms(1000);
delay_Ms(1000);
delay_Ms(1000);
delay_Ms(1000);
// TODO: USER CODE!!
while(1)
{
AccelReadXYZ();
lcd_goto_xy(1,2);
printf(lcd_putc, "%2.4w", (xg));
lcd_goto_xy(1,3);
printf(lcd_putc, "%2.4w", (yg));
lcd_goto_xy(1,4);
printf(lcd_putc, "%2.4w", (zg));
delay_ms(500);
}
//return 0;
}
void AccelCalibrateXYZ()
{
int8 i;
x = y = z = 0;
for(i = 0; i < 8; i++)
{
set_adc_channel(0); // select the required channel
delay_us(10); // allow to settle
x += read_adc(); // read the raw adc value
}
newG_x = (x >> 3) //newG_x=z/8
;
for(i = 0; i < 8; i++)
{
set_adc_channel(1); // select the required channel
delay_us(10); // allow to settle
y += read_adc(); // read the raw adc value
}
newG_y = y >> 3; //newG_y=z/8
for(i = 0; i < 8; i++)
{
set_adc_channel(4); // select the required channel
delay_us(10); // allow to settle
z += read_adc(); // read the raw adc value
}
newG_z = z >> 3; //newG_z=z/8
}
void AccelReadXYZ()
{
int8 i;
x = y = z = 0;
xg = yg = zg = 0;
for(i = 0; i < 2; i++)
{
set_adc_channel(0); // select the required channel
delay_us(10); // allow to settle
x += read_adc(); // read the raw adc value
set_adc_channel(1); // select the required channel
delay_us(10); // allow to settle
y += read_adc(); // read the raw adc value
set_adc_channel(4); // select the required channel
delay_us(10);// allow to settle
z += read_adc(); // read the raw adc value
}
zeroG_x = (x/2);
xg = (zeroG_x - CONST_GX )/counts_per_g ;
zeroG_y = (y/2);
yg = (zeroG_y - CONST_GY )/counts_per_g ;
zeroG_z = (z/2);
zg = (zeroG_z - CONST_GZ )/counts_per_g ;
}
float AccelConvertVolt(float adcval)
{
float value;
value = (adcval*3.0)/1024;
return value;
} |
what am i doing wrong here? |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
what am i duing wrong |
Posted: Mon Sep 03, 2007 3:52 pm |
|
|
what am i doing wrong here? I'm very stressed ! |
|
|
icesynth
Joined: 03 Sep 2007 Posts: 32 Location: Edmonton, Alberta
|
|
Posted: Mon Sep 03, 2007 6:11 pm |
|
|
We're all stressed, But I think I understand.
Quote: | - Pick an axis. Point the axis vertically up. Accelerometer sees +1g on that axis. Note the A/D reading.
- Point the axis vertically down. Accelerometer sees -1g. Note the A/D reading.
- Point the axis horizontally. Accelerometer sees 0g. Note the A/D reading.
- Fit a line to these three points and use this line equation to convert from A/D counts to gs. |
As for your code, remember that you have to physically point the accelerometer "Up", Down, and "Horizontal", pausing in between each of the positions to actually re-orient the device. I don't see you doing this in your AccelCalibrateXYZ() function.
Thus, it becomes->
Wait for user to position "Axis 1" Up
Take "Axis 1 Up" reading
Wait for user to position "Axis 1" Down
Take "Axis 1 Down" reading
Wait for user to position "Axis 1" Horizontal
Take "Axis 1 Horizontal" reading
Wait for user to position "Axis 2" Up
Take "Axis 2 Up" reading
Wait for user to position "Axis 2" Down
Take "Axis 2 Down" reading
Wait for user to position "Axis 2" Horizontal
Take "Axis 2 Horizontal" reading
Wait for user to position "Axis 3" Up
Take "Axis 3 Up" reading
Wait for user to position "Axis 3" Down
Take "Axis 3 Down" reading
Wait for user to position "Axis 3" Horizontal
Take "Axis 3 Horizontal" reading
Of, course you could do it by sampling the "XYZ" values as you re-orient it fewer (6) times (Cube... 6 sides...)
At this point, you are left with 3 values for each axis, So let's do the calculation for Axis 1-> Your "Z".
1.83V, 1.55V, 1.23V
Calibration Line:
**Remember**
Equation for a line is Y = MX+B
1.55 is our horizontal "Zero Crossing" Position = B
M is the slope, or calibration constant.
X is the input voltage in this case.
We have 2 regions, above and below the zero point, so 2 lines.
Let's calculate the "Above Zero (MA)" Calibration Constant:
MA = 1.83 / 1.55 = 1.1806
Let's calculate the "Below Zero (MB)" Calibration Constant:
MB = 1.55 / 1.23 = 1.2601
And to use it:
Code: |
If( 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 )
}
|
I highly you browse http://en.wikipedia.org/wiki/Linear_interpolation for a liitle more background on using interpolation. Also, for more accurate results, use sine / cosine interpolation after you get the concept in place. _________________ Programming for the the real world.
--Chris Burchett
Sylver Technologies Inc. |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
Thanks |
Posted: Tue Sep 04, 2007 3:54 am |
|
|
Sorry if I'm bugging you to much but I'm still confused.
So I should repeat this equation for both x and y values to obtain their corresponding constants/values i.e.
Equation for a line is Z = M*Vin+B...................(1)
Equation for a line is Y = M*Vin+B ...................(2)
Equation for a line is X = M*Vin+B ...................(3)
Is that right?
Thank you for your reply. |
|
|
icesynth
Joined: 03 Sep 2007 Posts: 32 Location: Edmonton, Alberta
|
|
Posted: Tue Sep 04, 2007 7:26 am |
|
|
Yes, that is correct. You will need to determine the calibration values for the X, Y, and Z axis. _________________ Programming for the the real world.
--Chris Burchett
Sylver Technologies Inc. |
|
|
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
Re: Calibrating adxl330 |
Posted: Tue Sep 04, 2007 6:37 pm |
|
|
kein wrote: | Could you please give an example. The followings are values obtained from various orientation of the accelerometer.
ADXL330 FLAT ON TABLE: Z=1.83V, Y= 1.53V, X=1.53V
ADXL330 UPSIDE DOWN: Z=1.23V, Y=1.49V, X=1.50V
ADXL330 Y-AXIS VERTICAL: Z=1.55V, Y= 1.55V, X=1.20V
ADXL330 Y-AXIS OPPOSITE DIRECTION: Z=1.48V, Y=1.45V, X=1.81V\
ADXL330 X-AXIS VERTICAL: Z=1.51V, Y= 1.20V, X=1.53V
ADXL330 X-AXIS OPPOSITE DIRECTION: Z=1.47V, Y=1.82V, X=1.50V
from these results how would you draw a line to workout the equation?
Thanks |
You seem to have your X and Y axis swapped. Between "Y AXIS VERTICAL" and its opposite it is your X voltage that responds. Also your X AXIS motion causes the Y voltage to respond. _________________ The search for better is endless. Instead simply find very good and get the job done. |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
Hi icesynth? |
Posted: Wed Sep 05, 2007 5:32 am |
|
|
Just a quick questions. from the following code(your code), I understand that Vin is the same as the analogue input to the adc (output from the adxl330 accelerometer), what about the variable ? What is it?
Code: |
If( 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 here is how I'm going to implement in code. Is the following modified routine correct?
Thanks
Code: | /*----------------------------------------------------------------------------------
-- calculation for Axis 1-> Your "X". ------------
-- [x, y, z] = [1.98V, 1.63V, 1.3V] = [808, 665, 528] ------------
-- ------------
-- Y = Mx +B ------------
-- 1.63 (665) is our horizontal "Zero Crossing" Position = B ------------
-- Let's calculate the "Above Zero (MA)" Calibration Constant: ------------
-- MA_x = 1.98 / 1.63 = 1.214724
--
-- Let's calculate the "Below Zero (MB)" Calibration Constant:
-- MB_x = 1.63 / 1.30 = 1.253862
--
--
--
-- calculation for Axis 1-> Your "Y". ------------
-- [x, y, z] = [1.996V, 1.64V, 1.3V] = [817, 669, 527] ------------
--
-- Y = Mx +B ------------
-- 1.63 (665) is our horizontal "Zero Crossing" Position = B ------------
-- Let's calculate the "Above Zero (MA_y)" Calibration Constant: ------------
-- MA_y = 1.996 / 1.64 = 1.2170732
--
-- Let's calculate the "Below Zero (MB_y)" Calibration Constant:
-- MB_Y = 1.64 / 1.30 = 1.2615385
-- ------------
-- calculation for Axis 1-> Your "Z". ------------
-- [x, y, z] = [0.0V, 0.0V, 0.0V] = [000, 000, 000] ------------
----------------------------------------------------------------------------------
----------------------------------------------------------------------------------*/
#define MA_x 1.214724
#define MB_x 1.253862
#define MA_y 1.217073
#define MB_y 1.261539
unsigned int16 buff_x[128];
unsigned int16 buff_y[128];
unsigned float Calibrated_Output;
void ACCAnalysisAdcData(int16 Vin)
{
unsigned int16 *buf_xptr;
unsigned int16 *buf_yptr;
buf_xptr = buff_x;
buf_yptr = buff_y;
for(n = 0; n < 128; n++) {
/* for the case of "Y" */
If( Voltage > 1.63 )
{
Calibrate_Output = (( Vin - 1.63 ) * MA_x ) + 1.65
}else if( Voltage == 1.63 )
{
Calibrated_Output = 1.63
}else if( Voltage < 1.63 )
{
Calibrated_Output = 1.63 - ((Vin - 1.63 ) * MB_x )
}
*buf_xptr++ = Calibrated_Output;
Calibrated_Output = 0;
/* for the case of "Y" */
If( Voltage > 1.64 )
{
Calibrated_Output = (( Vin - 1.64 ) * MA_y ) + 1.65
}else if( Voltage == 1.64)
{
Calibrated_Output = 1.64
}else if( Voltage < 1.63 )
{
Calibrated_Output = 1.64 - ((Vin - 1.64 ) * MB_y )
}
*buf_yptr++ = Calibrated_Output;
Calibrated_Output = 0;
}
} |
|
|
|
icesynth
Joined: 03 Sep 2007 Posts: 32 Location: Edmonton, Alberta
|
|
Posted: Wed Sep 05, 2007 8:25 am |
|
|
Sorry, Voltage variable is the Vin.
Fixed up the code a bit to what I think you are doing with it....
Code: | #define MA_x 1.214724
#define MB_x 1.253862
#define MA_y 1.217073
#define MB_y 1.261539
unsigned int16 buff_x[128];
unsigned int16 buff_y[128];
unsigned float Calibrated_Output;
void ACCAnalysisAdcData(void)
{
int16 VinX, VinY;
unsigned int16 *buf_xptr;
unsigned int16 *buf_yptr;
buf_xptr = &buff_x;
buf_yptr = &buff_y;
for(n = 0; n < 128; n++) {
/* for the case of "X" */
VinX = READ ADC Value for X_Axis
If( VinX > 1.63 )
{
Calibrate_Output = (( VinY - 1.63 ) * MA_x ) + 1.65
}else if( VinX == 1.63 )
{
Calibrated_Output = 1.63
}else if( VinX < 1.63 )
{
Calibrated_Output = 1.63 - ((VinX - 1.63 ) * MB_x )
}
*buf_xptr++ = Calibrated_Output;
Calibrated_Output = 0;
/* for the case of "Y" */
VinY = READ ADC Value for Y_Axis
If( VinY > 1.64 )
{
Calibrated_Output = (( VinY - 1.64 ) * MA_y ) + 1.65
}else if( VinY == 1.64)
{
Calibrated_Output = 1.64
}else if( VinY < 1.63 )
{
Calibrated_Output = 1.64 - ((Vin - 1.64 ) * MB_y )
}
*buf_yptr++ = Calibrated_Output;
Calibrated_Output = 0;
}
} |
_________________ Programming for the the real world.
--Chris Burchett
Sylver Technologies Inc. |
|
|
|
|
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
|