Hello TDK support
Our company is using the iim42652 sensor with a custom driver to implement a "shock detection".
When the MCU goes to a low power hibernation mode, it should be woken up again when there is a sudden high acceleration.
The WOM and SMD feature of the iim42652 seems to fit our purpose.
However, I struggle to get the SMD interrupt event, althogh I see that WOM events happen fine and they wake up the MCU when hibernating.
The threshold range of [0g - 1g] is a bit to small and the MCU wakes up too fast from WOM events.
So I was hoping to get the SMD working as some kind of debouncing. Here is how I set up SMD:
void iim42652_apex_wom_smd_enable(iim42652_t *dev, iim42652_int_line_t int_line,
uint32_t x_th_mg, uint32_t y_th_mg, uint32_t z_th_mg,
iim42652_smd_mode_t smd)
{
if (x_th_mg > 999) {
x_th_mg = 999; /* prevent uint8_t overflow */
}
else if (x_th_mg < 4) {
x_th_mg = 4; /* prevent uint8_t underflow */
}
if (y_th_mg > 999) {
y_th_mg = 999;
}
else if (y_th_mg < 4) {
y_th_mg = 4;
}
if (z_th_mg > 999) {
z_th_mg = 999;
}
else if (z_th_mg < 4) {
z_th_mg = 4;
}
/* Threshold value for the Wake on Motion Interrupt for X-axis accelerometer
WoM thresholds are expressed in fixed “mg” independent of the selected
Range [0g : 1g]; Resolution 1g/256=~3.9mg */
/* Motion if: */
/* 0: >= 3.9 mg */
/* 1: >= 7.8 mg */
/* ... */
/* 255: >= 999 mg */
_acquire(dev);
/* 1. Set WOM_X_TH (Register 0x4Ah in Bank 4) */
_iim42652_set_bank_register(dev, REG_BANK_SEL_BANK_SEL_4);
_write(dev, ACCEL_WOM_X_THR, (x_th_mg * 10 / 39) - 1);
/* 2. Set WOM_Y_TH (Register 0x4Bh in Bank 4) */
_write(dev, ACCEL_WOM_Y_THR, (y_th_mg * 10 / 39) - 1);
/* 3. Set WOM_Z_TH (Register 0x4Ch in Bank 4) */
_write(dev, ACCEL_WOM_Z_THR, (z_th_mg * 10 / 39) - 1);
/* 4. Wait 1 millisecond */
ztimer_sleep(ZTIMER_MSEC, 1);
/* 5. Enable all 3 axes as WOM sources for INT1 by setting bits 2:0 in register INT_SOURCE1 (Register
0x66h in Bank 0) to 1. Or if INT2 is selected for WOM, enable all 3 axes as WOM sources by setting
bits 2:0 in register INT_SOURCE4 (Register 0x69h in Bank 0) to 1. */
_iim42652_set_bank_register(dev, REG_BANK_SEL_BANK_SEL_0);
if (int_line == IIM42652_INT_1) {
iim42652_int_source1_reg_t int_source1_reg;
int_source1_reg.reg = _read(dev, INT_SOURCE1);
int_source1_reg.bit.WOM_X_INT1_EN = 1;
int_source1_reg.bit.WOM_Y_INT1_EN = 1;
int_source1_reg.bit.WOM_Z_INT1_EN = 1;
int_source1_reg.bit.SMD_INT1_EN = (smd == IIM42652_SMD_LONG || smd == IIM42652_SMD_SHORT) ? 1 : 0;
_write(dev, INT_SOURCE1, int_source1_reg.reg);
}
else if (int_line == IIM42652_INT_2) {
iim42652_int_source4_reg_t int_source4_reg;
int_source4_reg.reg = _read(dev, INT_SOURCE4);
int_source4_reg.bit.WOM_X_INT2_EN = 1;
int_source4_reg.bit.WOM_Y_INT2_EN = 1;
int_source4_reg.bit.WOM_Z_INT2_EN = 1;
int_source4_reg.bit.SMD_INT2_EN = (smd == IIM42652_SMD_LONG || smd == IIM42652_SMD_SHORT) ? 1 : 0;
_write(dev, INT_SOURCE4, int_source4_reg.reg);
}
/* 6. Wait 50 milliseconds */
ztimer_sleep(ZTIMER_MSEC, 50);
/* 7. Turn on WOM feature by setting WOM_INT_MODE to 0, WOM_MODE to 1, SMD_MODE to 1
(Register 0x57h in Bank 0) */
iim42652_smd_config_reg_t smd_config_reg;
_iim42652_set_bank_register(dev, REG_BANK_SEL_BANK_SEL_0);
smd_config_reg.reg = _read(dev, SMD_CONFIG);
smd_config_reg.bit.WOM_INT_MODE = 0; /* OR of all enabled accelerometer thresholds */
smd_config_reg.bit.WOM_MODE = 1; /* Compare current sample to previous sample */
smd_config_reg.bit.SMD_MODE = smd;
_write(dev, SMD_CONFIG, smd_config_reg.reg);
_release(dev);
}
Here is how I set up the DMP rate:
void iim42652_set_dmp_rate(iim42652_t *dev, iim42652_dmp_rate_t rate,
bool power_save, bool ped, bool tilt, bool free_fall) {
_acquire(dev);
/* 1. Disable Pedometer and Tilt Detection if they are enabled */
_iim42652_set_bank_register(dev, REG_BANK_SEL_BANK_SEL_0);
iim42652_apex_config0_reg_t apex_config0_reg;
apex_config0_reg.reg = _read(dev, APEX_CONFIG0);
apex_config0_reg.bit.TILT_ENABLE = 0;
apex_config0_reg.bit.PED_ENABLE = 0;
_write(dev, APEX_CONFIG0, apex_config0_reg.reg);
/* 2. Change DMP ODR */
apex_config0_reg.bit.DMP_ODR = rate;
/* 3. Set DMP_INIT_EN for one cycle (Register 0x4Bh in Bank 0) */
iim42652_signal_path_reset_reg_t signal_path_reset_reg;
signal_path_reset_reg.reg = _read(dev, SIGNAL_PATH_RESET);
signal_path_reset_reg.bit.DMP_INIT_EN = 1;
_write(dev, SIGNAL_PATH_RESET, signal_path_reset_reg.reg);
ztimer_sleep(ZTIMER_MSEC, 1); /* one cycle assuming 1KHz sensor rate */
/* 4. Unset DMP_INIT_EN (Register 0x4Bh in Bank 0) */
signal_path_reset_reg.bit.DMP_INIT_EN = 0;
_write(dev, SIGNAL_PATH_RESET, signal_path_reset_reg.reg);
/* 5. Enable APEX features of interest */
apex_config0_reg.bit.TILT_ENABLE = tilt ? 1 : 0;
apex_config0_reg.bit.PED_ENABLE = ped ? 1 : 0;
apex_config0_reg.bit.FF_ENABLE = free_fall ? 1 : 0;
apex_config0_reg.bit.DMP_POWER_SAVE = power_save ? 1 : 0;
_write(dev, APEX_CONFIG0, apex_config0_reg.reg);
_release(dev);
}
In main() the IIM42652_STREAM_TO_FIFO is set up and produces acceleration and gyroscome samples with a watermark of 80.
Accelerometer and Gyroscope are running in low noise mode at a rate of 1KHz.
The ACC scle is 16g. The GYRO scale is 2000dps
SMD and WOM are enabled like this:
Thresholds are set to 980mg but that does not matter much. All I can tell is that when I lower the threshold WOM events happen faster.
if (IIM42652_INT_LINE == IIM42652_INT_2 && iim42652_params.int2_pin != GPIO_UNDEF) {
iim42652_apex_wom_smd_enable(&dev, IIM42652_INT_NONE,
IIM42652_SMD_X_TH_MG, IIM42652_SMD_Y_TH_MG, IIM42652_SMD_Z_TH_MG,
IIM42652_SMD_SHORT);
iim42652_set_dmp_rate(&dev, IIM42652_DMP_RATE_50HZ, true, false, false, false);
}
else if (IIM42652_INT_LINE == IIM42652_INT_1 && iim42652_params.int1_pin != GPIO_UNDEF) {
iim42652_apex_wom_smd_enable(&dev, IIM42652_INT_NONE,
IIM42652_SMD_X_TH_MG, IIM42652_SMD_Y_TH_MG, IIM42652_SMD_Z_TH_MG,
IIM42652_SMD_SHORT);
iim42652_set_dmp_rate(&dev, IIM42652_DMP_RATE_50HZ, true, false, false, false);
}
When I let the board fall to the ground I get WOM events/interrupts, but no SMD event, no matter how I move the board.
Before hibernation the FIFO interrupt is disabled and the WOM and SMD interrupts are enabled.
iim42652_fifo_disable(&dev);
iim42652_set_rate(&dev, IIM42652_RATE_200HZ);
iim42652_set_acc_mode(&dev, IIM42652_ACC_LOW_POWER);
iim42652_set_gyro_mode(&dev, IIM42652_GYRO_POWER_OFF);
iim42652_int_disable(&dev, IIM42652_INT_LINE, IIM42652_FIFO_THS_INT);
/* SMD interrupt does not trigger for some reason.
So using WOM interrupts here as well*/
iim42652_int_enable(&dev, IIM42652_INT_LINE, IIM42652_SMD_INT |
IIM42652_WOM_X_INT |
IIM42652_WOM_Y_INT |
IIM42652_WOM_Z_INT);
printf("CPU is entering power mode %d.\n", mode);
puts("Now waiting for a wakeup event...");
fflush(stdout);
pm_set(mode);
printf("CPU has returned from power mode %d.\n", mode);
iim42652_int_disable(&dev, IIM42652_INT_LINE, IIM42652_SMD_INT |
IIM42652_WOM_X_INT |
IIM42652_WOM_Y_INT |
IIM42652_WOM_Z_INT);
iim42652_int_enable(&dev, IIM42652_INT_LINE, IIM42652_FIFO_THS_INT);
iim42652_set_rate(&dev, dev.p->rate);
iim42652_set_acc_mode(&dev, IIM42652_ACC_LOW_NOISE);
iim42652_set_gyro_mode(&dev, IIM42652_GYRO_LOW_NOISE);
iim42652_fifo_enable(&dev, IIM42652_STREAM_TO_FIFO);
As you can see by the comments, I think I followed section 8 in the datasheet correctly.
What must be taken care of for the SMD event to happen?
Are there any common pitfalls or errata documents?
Thank you for your help.
Best regards,
Fabian
Hello,
I understand that you are using custom driver, and if you followed the instructions on the datasheet it should work. You can also look at our SMD example for our Atmel driver. You can download the driver and check out the example code for SMD from here: https://invensense.tdk.com/developers/software-downloads/#smartindustrial