Hello, I'm working on a device with the ICM-42688 MEMS sensor and recently I've been implementing the self test feature. At first glance it worked very well, the acceleration values go to 2 m/s². But on repeat tests, the acceleration values don't go to 2 m/s², they go to a range between 20m/s² and 48 m/s². That in turn makes the self test fail.
I tested it on 3 different sensors and they work normally on pretty much all of my tests, only having this problem on the self test. I do think it might be some kind of issue with my code and configuration.
It is from my work, so some I can't share all of the code. What I can share is the following:
`
uint8_t ret = 0, count_out = 0;
while ((ret = ICM_begin() != 0) && (count_out++ < 1))
DWT_Delay_us(1000);
setODRandFSR(ODR_2KHZ, FSR_0);
startAccelMeasure(LN_MODE);
ICMAccelTriplet_t accel_data = {.x = 0, .y = 0, .z = 0};
memset(base_data.acc[EIXO_X], 0xff, sizeof(base_data.acc[EIXO_X]));
memset(base_data.acc[EIXO_Y], 0xff, sizeof(base_data.acc[EIXO_Y]));
memset(base_data.acc[EIXO_Z], 0xff, sizeof(base_data.acc[EIXO_Z]));
base_data.array_counter = 0;
uint32_t start = DWT_GetMicros();
uint32_t last_micros = start;
uint32_t current_micros = start;
while (base_data.array_counter < 50)
{
if (current_micros - last_micros >= 1000) // para executar uma leitura à cada 1ms mais ou menos
{
last_micros = current_micros;
if (ICM_get_accel_data(&accel_data) != HAL_OK)
return EIO;
base_data.acc[EIXO_X][base_data.array_counter] = accel_data.x;
base_data.acc[EIXO_Y][base_data.array_counter] = accel_data.y;
base_data.acc[EIXO_Z][base_data.array_counter] = accel_data.z;
base_data.array_counter++;
}
current_micros = DWT_GetMicros();
}
float low_level_avg[3] = {0.0, 0.0, 0.0};
for (uint8_t i = 0; i < 50; i++)
{
low_level_avg[EIXO_X] += base_data.acc[EIXO_X][i] / 50;
low_level_avg[EIXO_Y] += base_data.acc[EIXO_Y][i] / 50;
low_level_avg[EIXO_Z] += base_data.acc[EIXO_Z][i] / 50;
}
if (writeReg(ICM42688_SELF_TEST_CONFIG,
ICM42688_SELF_TEST_ACCEL_ST_POWER | ICM42688_SELF_TEST_EN_AZ_ST |
ICM42688_SELF_TEST_EN_AY_ST | ICM42688_SELF_TEST_EN_AX_ST) != HAL_OK)
break;
while (base_data.array_counter < 100)
{
if (current_micros - last_micros >= 1000) // para executar uma leitura à cada 1ms mais ou menos
{
last_micros = current_micros;
if (ICM_get_accel_data(&accel_data) != HAL_OK)
return EIO;
base_data.acc[EIXO_X][base_data.array_counter] = accel_data.x;
base_data.acc[EIXO_Y][base_data.array_counter] = accel_data.y;
base_data.acc[EIXO_Z][base_data.array_counter] = accel_data.z;
base_data.array_counter++;
}
current_micros = DWT_GetMicros();
}
float high_level_avg[3] = {0.0, 0.0, 0.0};
for (uint8_t i = 50; i < 100; i++)
{
high_level_avg[EIXO_X] += base_data.acc[EIXO_X][i] / 50;
high_level_avg[EIXO_Y] += base_data.acc[EIXO_Y][i] / 50;
high_level_avg[EIXO_Z] += base_data.acc[EIXO_Z][i] / 50;
}
float accelDiff[3] = {
fabs(high_level_avg[EIXO_X] - low_level_avg[EIXO_X]) * 1000.0f,
fabs(high_level_avg[EIXO_Y] - low_level_avg[EIXO_Y]) * 1000.0f,
fabs(high_level_avg[EIXO_Z] - low_level_avg[EIXO_Z]) * 1000.0f,
};
writeReg(ICM42688_REG_BANK_SEL, 2); // select register bank 2
uint8_t self_test_data[3];
readReg(ICM42688_XA_ST_DATA, &self_test_data[EIXO_X], 1); // accel self-test output generated during manufacturing tests
readReg(ICM42688_YA_ST_DATA, &self_test_data[EIXO_Y], 1); // accel self-test output generated during manufacturing tests
readReg(ICM42688_ZA_ST_DATA, &self_test_data[EIXO_Z], 1); // accel self-test output generated during manufacturing tests
float ratio[3];
ratio[EIXO_X] = accelDiff[EIXO_X] / (1310.0f * powf(1.01f, self_test_data[EIXO_X] - 1) + 0.5f);
ratio[EIXO_Y] = accelDiff[EIXO_Y] / (1310.0f * powf(1.01f, self_test_data[EIXO_Y] - 1) + 0.5f);
ratio[EIXO_Z] = accelDiff[EIXO_Z] / (1310.0f * powf(1.01f, self_test_data[EIXO_Z] - 1) + 0.5f);
ret = HAL_OK;
if (ratio[EIXO_X] < 0.5f || ratio[EIXO_X] > 1.5f)
ret = EIO;
if (ratio[EIXO_Y] < 0.5f || ratio[EIXO_Y] > 1.5f)
ret = EIO;
if (ratio[EIXO_Z] < 0.5f || ratio[EIXO_Z] > 1.5f)
ret = EIO;
`
So to elucidate on some of the functions that might not be that obvious:
"readReg" and "writeReg" both serve to read and write to the registers, as the names imply.
"ICM_get_accel_data" reads the current acceleration values for X, Y and Z.
"ICM_Begin" simply checks if the current sensor truly is an ICM42688 and resets the sensor.
Thank you for any help you might be able to give.
Hello,
Have you check out our self-test example? You can download DK-42688-P eMD file from https://invensense.tdk.com/developers/software-downloads/#smartmotion
If you have Dk-42688-P board you can run this using Atmel Studio. If not, check out the source code for the self-test example, and look into doc folder to see software user guide and API documentation.
Hello, thank you very much, by configuring the measurement settings as it is done in the example it is now working as expected. I repeated 1000 repetitions of the self test and they all passed on a sensor that is working, while before it would fail 2 times even with only 30 repetitions.
The configurations that changed were the ODR from 2kHz to 1kHz, the FSR from 16g to 2g and added the configuration of the acceleration filter order to 3rd order.
That's good to hear, let us know if you have more questions.