CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Converting accelerometer adc values to Gs
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
kein



Joined: 23 Jul 2007
Posts: 103

View user's profile Send private message

Converting accelerometer adc values to Gs
PostPosted: Thu Aug 30, 2007 7:49 am     Reply with quote

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.
Very Happy
Ttelmah
Guest







PostPosted: Thu Aug 30, 2007 8:45 am     Reply with quote

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

View user's profile Send private message

Waow thank you very much!
PostPosted: Fri Aug 31, 2007 10:13 pm     Reply with quote

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
Sad
Ttelmah
Guest







PostPosted: Sat Sep 01, 2007 2:32 am     Reply with quote

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

View user's profile Send private message

PostPosted: Sat Sep 01, 2007 11:01 am     Reply with quote

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

View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger

accelerometer calibration procedure
PostPosted: Sat Sep 01, 2007 12:09 pm     Reply with quote

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

View user's profile Send private message

Calibrating adxl330
PostPosted: Mon Sep 03, 2007 12:00 am     Reply with quote

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

View user's profile Send private message

Results are weird!
PostPosted: Mon Sep 03, 2007 3:19 am     Reply with quote

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?Embarassed
kein



Joined: 23 Jul 2007
Posts: 103

View user's profile Send private message

what am i duing wrong
PostPosted: Mon Sep 03, 2007 3:52 pm     Reply with quote

what am i doing wrong here? I'm very stressed ! Mad
icesynth



Joined: 03 Sep 2007
Posts: 32
Location: Edmonton, Alberta

View user's profile Send private message Visit poster's website

PostPosted: Mon Sep 03, 2007 6:11 pm     Reply with quote

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

View user's profile Send private message

Thanks
PostPosted: Tue Sep 04, 2007 3:54 am     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Tue Sep 04, 2007 7:26 am     Reply with quote

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

View user's profile Send private message

Re: Calibrating adxl330
PostPosted: Tue Sep 04, 2007 6:37 pm     Reply with quote

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

View user's profile Send private message

Hi icesynth?
PostPosted: Wed Sep 05, 2007 5:32 am     Reply with quote

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
Code:
Voltage
? 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

View user's profile Send private message Visit poster's website

PostPosted: Wed Sep 05, 2007 8:25 am     Reply with quote

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.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
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