Go Down

Topic: Problems with a i2c accelerometer... (Read 1 time) previous topic - next topic

Pentium

Hey guys,
I' ve a problem with an accelerometer, really it' s a magneto with a built-in accelerometer for tilt-compensated output but the two devices work in two different i2c address so I think it' s fine... I' m working with this device LSM303DLH, and I wrote this code to get the accelerometer output...
Code: [Select]
#include <Wire.h>
#define AXEL 0x18
//Funzione di setup
// Accelerometer address: 0011000b  0x18
// Magnetometer address:  0011110b  0x1E
// Registi di setup acceletometro:
// CTRL_REG1_A address:   0x20  parameters:  0x27
// CTRL_REG4_A address:   0x23  parameters:  0x40
//
// Registri di setup magnetometro:
// CRA_REG_M  address:   0x00  parameters:  0x14
// MR_REG_M   address:   0x02  parameters:  0x00
void lsmsetup(byte address, byte reg, byte setting);
void lsmsetup(byte address, byte reg, byte setting){
  Wire.beginTransmission(address);
  Wire.send(reg);
  Wire.send(setting);
  Wire.endTransmission();
}

//Funzione di lettura dei dati da un registro
int reading(int address, byte reg);
int reading(int address, byte reg){
  byte value;
  Wire.beginTransmission(address);
  Wire.send(reg);
  Wire.endTransmission();
  Wire.requestFrom(address, 1);
  while(Wire.available()){
    value=Wire.receive();
  }return value;
}

byte xlow, ylow, zlow, xhigh, yhigh, zhigh;
int X, Y, Z;
void setup(){
  Serial.begin(9600);
  Wire.begin();
  lsmsetup(AXEL, 0x20, 0x27);
  lsmsetup(AXEL, 0x23, 0x40);
  lsmsetup(0x1E, 0x00, 0x14);
  lsmsetup(0x1E, 0x02, 0x00);
}

void loop(){
  xhigh= reading(AXEL, 0x28);
  xlow= reading(AXEL, 0x29);
  X = (xhigh << 8) + xlow;
 
  yhigh= reading(AXEL, 0x2A);
  ylow= reading(AXEL, 0x2B);
  Y = ((yhigh << 8) | ylow);
   
  zhigh= reading(AXEL, 0x2C);
  zlow= reading(AXEL, 0x2D);
  Z = ((zhigh << 8) | zlow);
 
  Serial.print(X);
  Serial.print('\t');
  Serial.print(Y);
  Serial.print('\t');
  Serial.print(Z);
  Serial.print('\n');
  delay(100);
}




/* //Funzione di lettura multipla dei dati da un registro
int multireading(byte address, byte register);
int multireading(byte address, byte register){
  int value;
  int temp[1];
  Wire.beginTransmission(address);
  Wire.send(register);
  Wire.send(register + 1);
  Wire.endTransmission();
  Wire.requestFrom(address, 2);
  while(Wire.available()){
    temp[0]=Wire.receive();
    temp[1]=Wire.receive();
  }
  temp[0] << 8;
  temp[0] += temp[1];
  value= temp[0];
  return value;
} */


I really don' t understand why the values I get are something which doesn' t match with real forces: these are some examples...
Quote
1136   368   49040
1136   384   49008
2176   1984   48256
5936   65280   51984

Respectly x, y and z axis; I don' t understand why it doesn' t give me negative numbers: I tried to join the two byte of z axis and it gives me "-something" but the "print" function says something like "51512". It' s strange, isn' t?
Here a quick start guide I followed to setup correctly the accelerometer http://www.sparkfun.com/datasheets/Sensors/Magneto/Tilt%20Compensated%20Compass.pdf
And here the datasheet:
http://www.sparkfun.com/products/10703

It' s my first time approaching with Arduino, but I' ve programmed some stuffs with C, I checked multiple times the code and I can' t find the error, someone can help me? :( Thank you,
Pentium

Grumpy_Mike

Looking at the data sheet you seem to have your high and low registers mixed up.
For example the X has the low at address 0x28 and high at 0x29, this does not match your code.

Pentium

Well, you' re right: my high is actually the low; I' ve done the correction but I' ve always a problem:
Quote
61475   16386   32971
22   12288   53441
32782   4097   4288
4107   16385   45247
24582   57344   61630
53252   32768   41150

This doesn' t sound promising at all, what' s the matter? I really don' t understand :(

Grumpy_Mike

I don't see how you get an int with a value of 61475 because as http://www.arduino.cc/en/Reference/Int says
Quote
Integers are your primary datatype for number storage, and store a 2 byte value. This yields a range of -32,768 to 32,767 (minimum value of -2^15 and a maximum value of (2^15) - 1).


So you are not making the int correctly.

I am not sure why this is but I would try:-
Y = ((int(yhigh) << 8) | int(ylow));

Pentium

Nope, it doesn' t work :( :(
The code is the simplest ever wrote, it' s divided just in few phases:
-Read data from register MSB X
-Store in xhigh

-Read data from register LSB X
-Store in xlow

-X= (Bitshift xhigh of 8 and merge with OR to xlow)
I really can' t understand why it gives values over the maximum int value D:

Nick Gammon

Is what you had above your current sketch? What operating system and version of Arduino are you using?

I tried to reproduce with a simple test:

Code: [Select]
void setup ()
{
  volatile byte xhigh, xlow;

  xhigh = 0xBC;
  xlow = 0x10;
  int x = (xhigh << 8) | xlow;
  Serial.begin (115200);

  Serial.print (x);
}

void loop () {}


But that printed:

Code: [Select]
-17392

As you would expect.
http://www.gammon.com.au/electronics

wayneft

Quote
I really can' t understand why it gives values over the maximum int value D:

It's not your fault, you can blame the technical writers! That IC claims to give 16 bit data but what they fail to mention is that the accelerometer data is only 12 bits so you have to shift it by 4 to the right.
I2C GPS Shield

Checkout my Open Source GPS Tracker on Kickstarter

wayneft

Try this instead, it should work.

Code: [Select]

  yhigh= reading(AXEL, 0x2A);
  ylow= reading(AXEL, 0x2B);
  Y = ((yhigh << 12) | ylow << 4) >> 4;

I2C GPS Shield

Checkout my Open Source GPS Tracker on Kickstarter

Pentium


Try this instead, it should work.

Code: [Select]

  yhigh= reading(AXEL, 0x2A);
  ylow= reading(AXEL, 0x2B);
  Y = ((yhigh << 12) | ylow << 4) >> 4;



Hey, thank you a lot!!!By the way, I think you meant:
Code: [Select]

  yhigh= reading(AXEL, 0x2A);
  ylow= reading(AXEL, 0x2B);
  Y = ((yhigh << 8) | ylow ) >> 4;


I tried this code:
Code: [Select]
  xhigh= reading(AXEL, 0x29);
  xlow= reading(AXEL, 0x28);
  X = ((xhigh << 8) + xlow) >> 4;

Now, I get smaller values but I' ve still a problem: at first I know that if the first bit is equal to 1 then the number should be negative( for example B11111111 is negative!), now, this doesn' t work with my variable; don' t know why...

wayneft

Quote
By the way, I think you meant:

Whoops, your right.
Quote
now, this doesn' t work with my variable; don' t know why...

It's because you have the high byte and low byte backwards. Using the default configuration would put the LSB at the lower address but you have the Control Register4A (0x23) set to 0x40 which swaps the LSB to the upper address and MSB to the lower address.
I2C GPS Shield

Checkout my Open Source GPS Tracker on Kickstarter

Pentium

#10
Nov 01, 2011, 07:46 pm Last Edit: Nov 01, 2011, 09:15 pm by Pentium Reason: 1
ok, now it works!!!Thank you really, I would never thought of that setting, how stupid I' ve been! :D
That' s my final code, to solve the negative numbers problem...
Code: [Select]
  xhigh= reading(AXEL, 0x29);
  xlow= reading(AXEL, 0x28);
  X = ((xhigh << 8) + xlow) >> 4;
  if(X>2048){
    X -= 4096;
  }

Instead of working with the xhigh binary argument I' ve just written that if the joint value of X is over 2048( the xhigh has the first bit on!), then substract the amount of 2^12, because my final value is 12 bit composed, but it' s like a word...
:)

Go Up