2020-05-18 07:34 AM
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.
Solved! Go to Solution.
2020-05-26 09:09 AM
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!
2020-05-20 01:57 AM
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)?
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
2020-05-26 06:26 AM
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
2020-05-26 06:36 AM
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
2020-05-26 06:59 AM
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);
2020-05-26 07:02 AM
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
2020-05-26 07:11 AM
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.
2020-05-26 09:09 AM
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!