2025-05-14 2:51 AM
I'm using the LSM303AGR magnetometer with an STM32L4 via I2C + HAL. The built-in self-test works fine for X and Y, but Z-axis always fails — the delta (self-test vs. normal) is out of range.
STM32L4, I2C, HAL drivers
Magnetometer config: 50 Hz, continuous mode
Self-test enabled via CFG_REG_C_M (0x62) = 0x12
50 samples averaged before/after self-test
Delays: 200ms (pre), 500ms (post self-test)
Problem
X/Y deltas: within 100–1000 LSB range → pass
Z delta: often too low or too high → fail
Question
Are 100–1000 LSB valid self-test limits for Z?
Is 500ms delay enough after enabling self-test?
Do I need to read output registers in burst mode for sync?
Any known Z-axis issues in continuous mode?
Any advice or working examples appreciated!
2025-05-14 4:09 AM
The self test procedure is outlined in AN4825. Follow that exactly and show the numerical results you get along with relevant code.
Ultra-compact high-performance eCompass module based on the LSM303AGR
2025-05-14 4:26 AM
Hello ST Community,
I shared complete example of the LSM303AGR magnetometer self-test, based on AN4825. This was tested on an STM32 platform using HAL over I2C. Below is the implementation and test results.
void LSM303AGR_Mag_selfTest(void)
{
uint8_t addr = LSM303_MAG_ADR, status;
int16_t x_nost = 0, y_nost = 0, z_nost = 0;
int16_t x_st = 0, y_st = 0, z_st = 0;
int i;
lsm303AGR_Write8b_res(addr, 0x60, 0x8C); // ODR = 100Hz, LP = 0, continuous mode, soft reset enabled
lsm303AGR_Write8b_res(addr, 0x61, 0x02); // Configures magnetometer for continuous mode
lsm303AGR_Write8b_res(addr, 0x62, 0x10); // Enable Block Data Update (BDU = 1)
HAL_Delay(200); // Wait for stabilization
// Collect baseline data (no self-test)
for (i = 0; i < 50; i++)
{
do {
status = lsm303AGR_Read8b_res(addr, 0x67);
HAL_Delay(5);
} while (!(status & 0x08)); // Wait for new ZYX data
x_nost += lsm303AGR_Read16b_res(addr, 0x68);
y_nost += lsm303AGR_Read16b_res(addr, 0x6A);
z_nost += lsm303AGR_Read16b_res(addr, 0x6C);
HAL_Delay(20);
}
x_nost /= 50;
y_nost /= 50;
z_nost /= 50;
// Enable self-test mode
lsm303AGR_Write8b_res(addr, 0x62, 0x12); // BDU = 1, Self-test = 1
HAL_Delay(500); // Wait for stabilization
uint8_t reg_val = lsm303AGR_Read8b_res(addr, 0x62);
if (reg_val & 0x02) {
printf("Self-test is ENABLED CTRL_REG4_A = 0x%02X\r\n", reg_val);
} else {
printf("Self-test is NOT enabled CTRL_REG4_A = 0x%02X\r\n", reg_val);
}
// Collect self-test data
for (i = 0; i < 50; i++)
{
do {
status = lsm303AGR_Read8b_res(addr, 0x67);
HAL_Delay(5);
} while (!(status & 0x08)); // Wait for new ZYX data
x_st += lsm303AGR_Read16b_res(addr, 0x68);
y_st += lsm303AGR_Read16b_res(addr, 0x6A);
z_st += lsm303AGR_Read16b_res(addr, 0x6C);
HAL_Delay(20);
}
x_st /= 50;
y_st /= 50;
z_st /= 50;
// Calculate deltas
int16_t deltaX = abs(x_st - x_nost);
int16_t deltaY = abs(y_st - y_nost);
int16_t deltaZ = abs(z_st - z_nost);
// Check against datasheet min/max (example range in LSB)
const int16_t ST_MIN = 100;
const int16_t ST_MAX = 1000;
// Check for pass/fail
bool x_pass = (deltaX >= ST_MIN && deltaX <= ST_MAX);
bool y_pass = (deltaY >= ST_MIN && deltaY <= ST_MAX);
bool z_pass = (deltaZ >= ST_MIN && deltaZ <= ST_MAX);
if (x_pass && y_pass && z_pass) {
printf(" LSM303AGR Magnetometer Self-Test PASSED\r\n");
} else {
printf(" LSM303AGR Magnetometer Self-Test FAILED\r\n");
}
// Disable self-test
lsm303AGR_Write8b_res(addr, 0x62, 0x10);
lsm303AGR_Write8b_res(addr, 0x60, 0x83);
}
Output:
Self-test ENABLED: CTRL_REG3_M = 0x12
Self-Test Results:
X: nost = -106, st = 35, Δ = 141 [PASS]
Y: nost = -186, st = -61, Δ = 122 [PASS]
Z: nost = 356, st = 412, Δ = 58 [FAIL]
LSM303AGR Magnetometer Self-Test FAILED
Thanks