How to convert analogRead value from accelerometer

Hi, I am trying to write a program that reads the values from a three axis accelerometer (adxl330 from sparkfun) and then prints the "g" value through the serial monitor. I know that the adxl330 is ratiometric but I'm not sure what the equation is to take the value I get from analogRead() and say how many g's it is.

I built a small DC-DC converter to reduce the arduino's 5V down to 3V to power the adxl330. At rest my voltmeter reads x=1.49V, y=1.54, z=1.82 volts. The analogRead() values are around 315, 325, 390 for x,y,z. This makes sense because the 10-bit ADC on the arduino follows the equation (voltage/5)*(2^10).

I think the equation to convert to g's is (outputvoltage - 0g_voltage)/0.3

Does anyone know if this is the correct way to do it?

I have the same type accelerometer, and I find I need to calibrate each axis for -1, 0, and 1 G. You can do this by orienting the accelerometer with each axis pointing up, down, or sideways, and averaging a large number of samples. Ideally, 0 should be -3 G, 511 should be zero G and 1023 should be +3 G, but I find they're each a little different.

Have fun!

D.

I should have mentioned that aref needs to be connected to 3v to use the entire 0-1023 range. Otherwise 1023 will mean 5v and -3 to +3G will in the first 3/5 of that range.

D.

Thx Drd. I was wondering why my 0g position was reading ~315 instead of 512, but I never set AREF to 3V so I guess that explains it.

How many samples do you take and average? I had not thought of that. Have you made anything interesting with your accelerometer? These little things are cool.

Edit- How do you set AREF to 3V? I just connected the AREF pin to my 3V output but the pin still reads 5V on my meter. Do I have to configure AREF in my program?

What I tried was 512 samples at 20 ms apart, and use a long variable to accumulate it into, so as not to overflow a 16-bit int. I'm wanting to make a IMU to take flying, mainly for attitude reference. These little sensors are very neat! I'm also using some ADRSX401 gyro/rate sensors, which are 5v devices, so the aref thing gets complicated.

If you are doing IMU stuff, there's good info over Spark Fun forum.

D.

So are you powering your adxl330 with 5V to use the full ADC scale?

I've been searching for info about setting the AREF to a value besides 5V but I can't find anything.

No! The 330 is a 3v device, so don't feed it 5v - you might fry it. If you aren't reading any 5v analog devices I think you can connect the 3v to aref. Be careful - I don't know your situation, and I might be wrong.

I'll look for better information, and get back to you.

D.

Only connecting AREF to 3V doesn't work without doing something to the code. My AREF pin is 5V on its own, but when I connect the 3V it does nothing and actually 'overrides' my 3V bus so that anything else connected to it now sees 5V.

According to Massimo:

The aref works like this...

When you do analogue to digital conversion the arduino board uses the 5V supply as a reference. this means that any voltage between 0 and 5v is represented as numbers between 0 and 1024. If the signal you are measuring never reaches 5V it is possible to change the reference to an internal 2.5 v source and this divides the space between 0 and 2.5v in 1024 steps effectively doubling the resolution. If this still doesn't work for you then you can ask the processor to use an external voltage applied to aref. if you find a super stable 1V refernce source this will give you 1milli volt resolution for the ADC. this is used only in special applications because at that resolution the noise becomes a big issue etc etc

switching reference voltage requires adding a few lines of C code to your arduino code.

Summarising : changing your aref is advised only if you have good grasp of analogue electronics otherwise it's a mess..

Now if only I could figure out what these lines of code are.

Okay I was able to get it working thanks to some help from Daniel here.

In order to use an external voltage reference for the ADC you must change two lines in the wiring.c file to the following:

// set a2d reference to external voltage from AREF pin
cbi (ADMUX, REFS1);
cbi (ADMUX, REFS0);

For reference see pages 205 & 206 in the Atmega datasheet.

Mellis informed me that I don't need to modify wiring.c and can make the needed modifications to the ADMUX register in my setup function, I just had to add the following definitions to my program:

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

Now that I have the ADC values does anyone know how to calculate the g value?
The equation should be: gvalue = [(ADCvalue * 3) / 1024 - 1.5] / 0.3
but I don't know how to program these fractional calculations.

I can't figure out how to handle fractions since I only have int variables :-?

You can use float variables. You may need to include math.h for some functions. Like so:

float gvalue, adc;

adc = (float)(ADCvalue);
gvalue = ((adc*3.0)/1024.-1.5))/3.0;

Floating point takes a while on the ATMega, so be careful about timing.

D.

Perhaps a stupid answer, but let's try it :

You said :
gvalue = [(ADCvalue * 3) / 1024 - 1.5] / 0.3 = [(ADCvalue * 3) / 1024 - (15/10)] / (3/10) = [(ADCvalue * 10) / 1024 - 5]

Assuming that << 10 divide by 1024

this 'll give you :

[((ADCvalue * 10)<<10) - 5] = [((ADCvalue * 5)<<9) - 5] , but you will loose decimal precision

But if your target precision is around 1/10 of g (look at the datasheet or depend on what you want to do), you can compute :

10gvalue = [((ADCvalue * 510)<<9) - 5*10] = [((ADCvalue * 25)<<8) - 50]

This way you can avoid float

Not sure of what I am doing :-[, but CosineKitty (the Math expert) can probably confirm.

Nicolas

Nicolas: Doesn't N<<10 multipy N by 1024 while N>>10 divides, and those only return integers right?

Drd: That sounds easy to use float, I didn't know I could do that. I don't need very fast timing so hopefully it will do.

I'll try both ways and see what happens.

Ooops,

Sure you are right, so replace << by >> in my post (see the CosineKitty math tutorial, bit shift section Arduino Playground - BitMath )

Thanks, others tipos ?

Nico

I have nearly finished my code. I tried following nrollands idea for my calculations and in theory it should work, at least it does when I test it on my calculator. However I'm getting strange results that I don't understand. An example is it sometimes displays something like "589 = 0.-22g" and the digit in front of the decimal point is always 0 when it should change to 1,2,3...

Here is my code:

//Definitions needed to for cbi (clear bit) and sbi (set bit)
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

#define zerog_x 531 //X ADC value in the zero G position & is theoretically 512
#define calconst 1087//constant used when converting ADC values to g's

//variables                                                                
int xadc=0; //X ADC value
int yadc=0; //Y ADC value
int zadc=0; //Z ADC value
long xGval=0;   //X g value
int num1=0;    //digit in front of decimal place
int num2=0;    //digits after decimal place
   
void setup() {
  cbi(ADMUX, REFS1);   //set a2d reference voltage to external AREF pin ...
  cbi(ADMUX, REFS0);   //... so that ADC uses 3V instead of 5V
  Serial.begin(9600);  // use the serial port to send values back to the PC
}

void loop() {
      xadc=analogRead(0); //read x-axis values from analog in 0
      yadc=analogRead(1); //read y-axis values from analog in 1
      zadc=analogRead(2); //read z-axis values from analog in 2
      delay(1);
      xadc+=analogRead(0);
      xadc = xadc / 2; //take an average for accuracy & less error
      yadc+=analogRead(1);
      yadc = yadc / 2;
      zadc+=analogRead(2);
      zadc = zadc / 2;
      
      if(xadc < zerog_x){ //calculations for negative g values
        xGval = calconst*(xadc-zerog_x);
        xGval = abs(xGval);
        xGval = xGval>>10;
        num1=xGval/100;
        num2=xGval%100;
        Serial.print(xadc);
        Serial.print("=  -"); 
        Serial.print(num1); 
        Serial.print("."); 
        Serial.print(num2); 
        Serial.println("g");
      }
      else{ //calculations for positive g values
      xGval = calconst*(xadc-zerog_x);
      xGval = xGval>>10;
      num1=xGval/100;
      num2=xGval%100;
      Serial.print(xadc);
      Serial.print("=   "); 
      Serial.print(num1); 
      Serial.print("."); 
      Serial.print(num2); 
      Serial.println("g"); // should look like: "xadc= num1.num2 g"
      }
     
      delay(1000); //slow down so its easier to read the serial monitor
      Serial.println();
     
}

Any suggestions? I know my coding style is not very good, but I'm learning :slight_smile: