cancel
Showing results for 
Search instead for 
Did you mean: 

LSM303AGR Self Test

MV.1
Associate

Hi,

I am quite new to MEMS IMUs. I have a custom board containing LSM303AGR IMU connected to SAMD21 MCU via I2C.

I am using the stm32duino LSM303AGR library.

I am having a hard time understanding the Self test procedure. Is it possible to get a simpler procedural explanation?

Also,

1.Does self test require the device to be immobile?

2.Can I take self test readings in mg (GetAxes() method instead of GetAxesRaw()), if so what is ST_X/Y/Z limits in mg?

(from table 3 in datasheet is ST_X/Y/Z same as LA_ST?)

(according to datasheet LA_ST min = 17LSB max = 360LSB, 1LSb = 3.9mg , so multiply by 3.9 for mg?)

Thank you.

1 ACCEPTED SOLUTION

Accepted Solutions
KLise.1
Associate II

Finally, I solved it! The problem was in bitwise operation. Should do

int16_t temp = 0;

temp = i2c_readbyte(addr, 0x28);

temp |= i2c_readbyte(addr, 0x29) << 8;

temp >>= 6;

OUTX_NOST += temp;

instead of

OUTX_NOST += ( i2c_readbyte(addr, 0x28) | (i2c_readbyte(addr, 0x29) << 8) ) >> 6;

Thanks!

View solution in original post

7 REPLIES 7
Eleon BORLINI
ST Employee

Hi @MV.1​ , as a general note, did you already check the self-test procedure described in the AN4825, p.63 (or equivalently in the device datasheet)?

0693W000001caGAQAY.png

As regard the two other questions:

1.Does self test require the device to be immobile? --> It is better to perform the self state in a steady-state condition, but is is not necessary, because the self-test disconnect the sensor from the outer mechanical stimulus, giving the MEMS structure a predefined (high) signal.

2.Can I take self test readings in mg (GetAxes() method instead of GetAxesRaw()), if so what is ST_X/Y/Z limits in mg? --> from the datasheet, the ST output for the accelerometer is in LSB, where 1LSb = 3.9 mg in normal mode (10-bit) at FS=±2 g.

Regards

KLise.1
Associate II

Hi @Eleon BORLINI​ ​ , we are struggling to solve the accelerometer self-test constant failure. We were referencing AN4825 self-test routine flow. We are facing this issue on three different boards. Besides, results depend on board orientation. Could you assist?

static uint8_t LSM303AGR_selfTest(int vendor)
{
	uint8_t	addr, status;
	uint8_t uReturn = 0;
	int i;
 
	if (vendor == 0x41)
		addr = ACCELEROMETER_ADDR_OLD;
	else
		addr = ACCELEROMETER_ADDR;
 
	// Init sensor
	//Set BDU to 1, FS to 2G, select normal mode, ODR 100Hz
	i2c_writebyte(addr, 0x21, 0x00);
	i2c_writebyte(addr, 0x22, 0x00);
	i2c_writebyte(addr, 0x23, 0x81);
	i2c_writebyte(addr, 0x20, 0x57);
	wait_ms(200);
 
	int32_t OUTX_NOST = 0;
	int32_t OUTY_NOST = 0;
	int32_t OUTZ_NOST = 0;
 
	do {
		status = i2c_readbyte(addr, 0x27);
		wait_ms(50);
	} while (!(status & 0x08));
 
	//Read dump values
	i2c_readbyte(addr, 0x28);
	i2c_readbyte(addr, 0x29); 
	i2c_readbyte(addr, 0x2A);
	i2c_readbyte(addr, 0x2B); 
	i2c_readbyte(addr, 0x2C);
	i2c_readbyte(addr, 0x2D); 
 
	//Read 50 samples
	for (i = 0; i < 50; i++) 
	{
		do {
			status = i2c_readbyte(addr, 0x27);
			wait_ms(50);
		} while (!(status & 0x08));
 
		OUTX_NOST += ( i2c_readbyte(addr, 0x28) | (i2c_readbyte(addr, 0x29) << 8) ) >> 6;
		OUTY_NOST += ( i2c_readbyte(addr, 0x2A) | (i2c_readbyte(addr, 0x2B) << 8) ) >> 6;
		OUTZ_NOST += ( i2c_readbyte(addr, 0x2C) | (i2c_readbyte(addr, 0x2D) << 8) ) >> 6;
	}
 
	/* Calculate the average values */
	OUTX_NOST /= 50;
	OUTY_NOST /= 50;
	OUTZ_NOST /= 50;
 
	// enter self-test mode
	i2c_writebyte(addr, 0x23, 0x85);
	wait_ms(200);
 
	int32_t OUTX_ST = 0;
	int32_t OUTY_ST = 0;
	int32_t OUTZ_ST = 0;
 
	do {
		status = i2c_readbyte(addr, 0x27);
	} while (!(status & 0x08));
 
	//Read dump values
	i2c_readbyte(addr, 0x28);
	i2c_readbyte(addr, 0x29); 
	i2c_readbyte(addr, 0x2A);
	i2c_readbyte(addr, 0x2B); 
	i2c_readbyte(addr, 0x2C);
	i2c_readbyte(addr, 0x2D); 
 
	//Read 50 samples
	for (i = 0; i < 50; i++) 
	{
		do {
			status = i2c_readbyte(addr, 0x27);
		} while (!(status & 0x08));
 
		OUTX_ST += ( i2c_readbyte(addr, 0x28) | (i2c_readbyte(addr, 0x29) << 8) ) >> 6;
		OUTY_ST += ( i2c_readbyte(addr, 0x2A) | (i2c_readbyte(addr, 0x2B) << 8) ) >> 6;
		OUTZ_ST += ( i2c_readbyte(addr, 0x2C) | (i2c_readbyte(addr, 0x2D) << 8) ) >> 6;
	}
 
	/* Calculate the average values */
	OUTX_ST /= 50;
	OUTY_ST /= 50;
	OUTZ_ST /= 50;
 
	// disable sensor
	i2c_writebyte(addr, 0x20, 0x00);
 
	// disable self-test
	i2c_writebyte(addr, 0x23, 0x01);
 
	console_puts("\tOUTX_ST = ");
	console_puti(abs(OUTX_ST));
	console_puts("\r\n");
	console_puts("\tOUTY_ST = ");
	console_puti(abs(OUTY_ST));
	console_puts("\r\n");
	console_puts("\tOUTZ_ST = ");
	console_puti(abs(OUTZ_ST));
	console_puts("\r\n");
 
	console_puts("\tOUTX_NOST = ");
	console_puti(abs(OUTX_NOST));
	console_puts("\r\n");
	console_puts("\tOUTY_NOST = ");
	console_puti(abs(OUTY_NOST));
	console_puts("\r\n");
	console_puts("\tOUTZ_NOST = ");
	console_puti(abs(OUTZ_NOST));
	console_puts("\r\n");
 
	console_puts("\tOUTX_ST-OUTX_NOST = ");
	console_puti(abs(OUTX_ST-OUTX_NOST));
	console_puts("\r\n");
	console_puts("\tOUTY_ST-OUTY_NOST = ");
	console_puti(abs(OUTY_ST-OUTY_NOST));
	console_puts("\r\n");
	console_puts("\tOUTZ_ST-OUTZ_NOST = ");
	console_puti(abs(OUTZ_ST-OUTZ_NOST));
	console_puts("\r\n");
	
	console_puts("\tChecking X axis");
	if(	17 < abs(OUTX_ST-OUTX_NOST) &&
		360 > abs(OUTX_ST-OUTX_NOST))
	{
		console_puts("\tPASS");
		uReturn |= ACC_X_TEST_MASK;
	}
	else
	{
		console_puts("\tFAIL");
	}
	console_puts("\r\n");
 
	console_puts("\tChecking Y axis");
	if(	17 < abs(OUTY_ST-OUTY_NOST) &&
		360 > abs(OUTY_ST-OUTY_NOST))
	{
		console_puts("\tPASS");
		uReturn |= ACC_Y_TEST_MASK;
	}
	else
	{
		console_puts("\tFAIL");
	}
	console_puts("\r\n");
	
	console_puts("\tChecking Z axis");
	if(	17 < abs(OUTZ_ST-OUTZ_NOST) &&
		360 > abs(OUTZ_ST-OUTZ_NOST))
	{
		console_puts("\tPASS");
		uReturn |= ACC_Z_TEST_MASK;
	}
	else
	{
		console_puts("\tFAIL");
	}
	console_puts("\r\n");
 
	return uReturn;
}

Console output:

   OUTX_ST = 96

   OUTY_ST = 910

   OUTZ_ST = 149

   OUTX_NOST = 1018

   OUTY_NOST = 1019

   OUTZ_NOST = 246

   OUTX_ST-OUTX_NOST = 922

   OUTY_ST-OUTY_NOST = 109

   OUTZ_ST-OUTZ_NOST = 97

   Checking X axis   FAIL

   Checking Y axis   PASS

   Checking Z axis   PASS

Eleon BORLINI
ST Employee

Hi @KLise.1​ , are you facing the issue always on the X axis or this is random? Did you discarded the 1st sample data as described in the flow? Regards

are you facing the issue always on the X axis or this is random?

If the board is in one position, then only X-axis fails. If orientation changes, then it could be two axes fail or only Y-axis.

Did you discarded the 1st sample data as described in the flow?

Yes, as you can see in the code above.

//Read dump values

i2c_readbyte(addr, 0x28);

i2c_readbyte(addr, 0x29);

i2c_readbyte(addr, 0x2A);

i2c_readbyte(addr, 0x2B);

i2c_readbyte(addr, 0x2C);

i2c_readbyte(addr, 0x2D);

Ok thank you. Can you check your code with the lsm303agr_self_test.c C example for LSM303AGR, available on Github, if not already done? Regards

Can you check your code with the lsm303agr_self_test.c C example for LSM303AGR

Unfortunately, already has been done, but had no luck :( Shall check once more.

KLise.1
Associate II

Finally, I solved it! The problem was in bitwise operation. Should do

int16_t temp = 0;

temp = i2c_readbyte(addr, 0x28);

temp |= i2c_readbyte(addr, 0x29) << 8;

temp >>= 6;

OUTX_NOST += temp;

instead of

OUTX_NOST += ( i2c_readbyte(addr, 0x28) | (i2c_readbyte(addr, 0x29) << 8) ) >> 6;

Thanks!