cancel
Showing results for 
Search instead for 
Did you mean: 

Trouble with STM32F4 and LSM303DLHC accelerometer data

Bogdan
Senior
Posted on October 27, 2014 at 21:50

Hello all, i started a while ago some IMU project... but i have some doubts about reading the correct data from my accelerometer, wich is LSM303DLHC

Acording to my code, i setup the following registers

CTRL_REG1_A   0x77  set  400hz data rate

CTRL_REG4_A   0x00  2g, little endian

When i set the MSB of the first register to be read ( 0x28, with msb set results 0xA8) then acording to the LSM datasheet

it continues to send me data from all 6 register in the following order:

uint8_t dataBuffer[6];

1st  dataBuffer[0] X_LSB ( address 0x28)

2nd  dataBuffer[1] X_MSB ( address 0x29)

3rd  dataBuffer[2] Y_LSB ( address 0x2A)

4th  dataBuffer[3] Y_MSB ( address 0x2B)

5th  dataBuffer[4] Z_LSB ( address 0x2C)

6th  dataBuffer[5] Z_MSB ( address 0x2D)

then i integrate the MSB`s and the LSBs in 16bit integer like this:

 int16_t Buf[3];

 

 

 Buf[0]=((dataBuffer[1]<<8)+dataBuffer[0])/16;

 Buf[1]=((dataBuffer[3]<<8)+dataBuffer[2])/16;

 Buf[2]=((dataBuffer[4]<<8)+dataBuffer[4])/16;

The /16 division is for 4 bit right shift since LSM303DLHC outputs on accelerometer only 12bit values from the 16bit register

and after this i divide by the 2g factor, wich acording to the datsheet is 1 for 2g, ending in 1LSB/mg

float x,y,z;

x=Buf[0]/1

y=Buf[1]/1

z=Buf[2]/1

I want to know, at this poit did i interpret the data corectly?

And these final values are somehow doubtfull to be corectly, the values where printed directly troug serial port

X:4.00    Y:36.00   Z:0.00   

X:4.00    Y:40.00   Z:0.00   

X:4.00    Y:36.00   Z:2056.00   

X:4.00    Y:20.00   Z:1028.00   

X:8.00    Y:36.00   Z:3084.00   

X:4.00    Y:40.00   Z:3084.00   

X:4.00    Y:32.00   Z:2056.00   

X:8.00    Y:28.00   Z:0.00   

X:0.00    Y:28.00   Z:0.00   

X:12.00   Y:20.00   Z:1028.00   

X:16.00   Y:24.00   Z:2056.00   

X:4092.00 Y:20.00   Z:2056.00   

X:16.00   Y:32.00   Z:3084.00   

 

The end result is in miliG`s? or LSB`s ?

5 REPLIES 5
raptorhal2
Lead
Posted on October 28, 2014 at 01:00

The user's manual doesn't provide bit definitions for data out registers, so one has to guess if divide by 16 is correct.

Your problem looks greater than a wrong 4 bit shift. First send MSB and LSB registers to the serial port and see if those values looks correct. If not, I suspect problems in the setup or I2C interface, which are not shown.

Does the I2C interface have the required pullup resisters ?

Cheers, Hal

Bogdan
Senior
Posted on October 28, 2014 at 06:00

Hello, i dont know how much is this problem related to the i2c comunication wich from my point of view seems to work ok, see bellow the LSM initialization registers where :

0x32 is the write adress of the LSM CTRL_REG1_A (20h) 0x37 - data for setting up CTRL_reg1 with 25hz output data rate, xyz enable axes, normal mode CTRL_REG4_A (23h) 0x00 - sets +/- 2G range, little endian (BLE bit is set 0 ) ''data LSB @ lower address'' 0690X0000060MmoQAE.gif Bellow is the i2c reading of the 6 axis registers x32 is LSM write address A8 (binary

1

0101000) is 0x28 with the msb set ( 0x28 -

0

0101000) After this i send the x33 LSM read command and the chip starts to ouput every acc register values starting with x_lsb ( 0x00) x_msb(0xFD) y_lsb ( 0x00 ) y_msb(0x02) z_lsb(0x80) and the last is z_msb (0x3e ) 0690X0000060MmpQAE.gif Regarding the >>4 or /16 i saw that this is used in the ST LSM303dlhc library wich was my reference for the setup code here is my code and i2c functions

void LSM303_init(){
i2c_send_cmd(awAddr, CTRL_REG1_A, 0x37); // 25hz ODR
i2c_send_cmd(awAddr, CTRL_REG4_A, 0x00);//+2g, little endian
}
void lsm_readAccRaw(int16_t* tempBuf){
uint8_t dataBuffer[6];
i2c_readBuffer(awAddr,dataBuffer,OUT_X_L_A,6);
tempBuf[0]=((dataBuffer[1]<<8)+dataBuffer[0])/16;
tempBuf[1]=((dataBuffer[3]<<8)+dataBuffer[2])/16;
tempBuf[2]=((dataBuffer[4]<<8)+dataBuffer[4])/16;
}
void lsm_readAcc(float* tempBuf){
int16_t aData[3];
lsm_readAccRaw(aData);
tempBuf[0]=aData[0]/1.0; // x axis
tempBuf[1]=aData[1]/1.0; // y axis
tempBuf[2]=aData[2]/1.0; // z axis 
}

And my main program

int main(void){
system_init(); // cals for i2c initialization and for other periphs setups
LSM303_init();
float ccc[3];
char txtx[20]
char txty[20]
char txtz[20]
while(1){
lsm_readAcc(ccc);
sprintf(txtx,''%f'',ccc[0]);
sprintf(txty,''%f'',ccc[1]);
sprintf(txtz,''%f'',ccc[2]);
lcd_char(1,1,txtx);
lcd_char(1,1,txty);
lcd_char(1,1,txtz);
delay_ms(200); // 200 ms aprox delay
}
}

Regarding usage of USART module, the results are the same even displayed on a plain 20x4 LCD the datsheet is here http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/DM000275pdf
raptorhal2
Lead
Posted on October 28, 2014 at 18:35

The logic analyzer trace shows the  I2C interface is working correctly.

The reason for displaying the register values is to verify that the accelerometer outputs look reasonable. Doing the math manually:

X = FD00. Shift right 4 bits = minus 0x2F  = -47.   Accel is -47/1024 = -46 mg.

Y = 0200. Shift right 4 bits = 0x20 = 32. Accel is 32/1024 = 31 mg.

Z = 3E80. Shift right 4 bits is 0x3E8 = 1000. Accel is 1000/1024 = 977 mg.

Considering that the unit typically has a 60 mg offset in any axis, the results are acceptable.

Check the post conversion code. A routine is called to place raw assembled register values in the tempbuf table, then this table is overwritten by the aData table.

Cheers, Hal

Bogdan
Senior
Posted on October 28, 2014 at 19:14

Hello, thank you for the reply. However i noticed i have a type error in my code, namely here

The last MSB of the Z axis where the same as for the LSB , i corected with dataBuffer5

tempBuf[0]=((dataBuffer[1]<<8)+dataBuffer[0])/16;

tempBuf[1]=((dataBuffer[3]<<8)+dataBuffer[2])/16;

tempBuf[2]=((dataBuffer[

4

]<<8)+dataBuffer[4])/16;

And stil i had wrong values .

I did a little trick, by declaring all the lsb`s and msb`s as fixed value like bellow

uint8_t dataBuffer[6];

dataBuffer[0]=0x00;

dataBuffer[1]=0xfd;

dataBuffer[2]=0x00;

dataBuffer[3]=0x02;

dataBuffer[4]=0x80;

dataBuffer[5]=0x3e;

//i2c_readBuffer(awAddr,dataBuffer,OUT_X_L_A,6);

tempBuf[0]=((dataBuffer[1]<<8)+dataBuffer[0])/16;

tempBuf[1]=((dataBuffer[3]<<8)+dataBuffer[2])/16;

tempBuf[2]=((dataBuffer[5]<<8)+dataBuffer[4])/16;

After this, the readings are

X: 4048

Y: 32

Z: 1000

Then i tryied declare dataBuffer as SIGNED INT, and the values are like yours,namely

X: -48

Y: 32

Z: 984

What i dont understand here is why in the ST standard libraries  and in ST`s  app note  AN3182the MSB`s and the LSB`s alone are declared as

UNSIGNED

int  ?

raptorhal2
Lead
Posted on October 28, 2014 at 21:26

Probably because deep in the bowels of the device, the value is built as a signed twos complement 16 bit number, then split for storing in the output registers. The most significant bit of the LSB is not a sign bit. When the bytes are reassembled into a signed word, the twos complement representation is preserved.

Now you can tackle the magnetic output with confidence.

Cheers, Hal