Skip to main content
Visitor II
May 18, 2020
Solved

LSM303AGR Self Test

  • May 18, 2020
  • 4 replies
  • 2319 views

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.

    This topic has been closed for replies.
    Best answer by KLise.1

    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!

    4 replies

    ST Employee
    May 20, 2020

    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

    Visitor II
    May 26, 2020

    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

    ST Employee
    May 26, 2020

    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

    Visitor II
    May 26, 2020

    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);

    ST Employee
    May 26, 2020

    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

    KLise.1Answer
    Visitor II
    May 26, 2020

    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!