Current consumption increase when enabling LIS2DH trigger

nRF52832 custom board + NCS 3.0.1

We are using the Zephyr driver for LIS2DH sensor in "any motion" detection mode, which triggers an interrupt when acceleration exceeds a set threshold. The system works as expected but the current consumption is excessively high, >450uA. This is a port of an existing project written the old way (SoftDevice) to NCS so we have a reference that the same board can work in the same way by consuming only about 7uA.

Here is the relevant config & code:

prj.conf:

CONFIG_LOG=y
CONFIG_LOG_FUNC_NAME_PREFIX_WRN=y
CONFIG_LOG_FUNC_NAME_PREFIX_ERR=y

CONFIG_USE_SEGGER_RTT=y

CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL=y

CONFIG_GPIO=y
CONFIG_ADC=y

CONFIG_I2C=y
CONFIG_SENSOR=y
CONFIG_LIS2DH=y
CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD=y
CONFIG_LIS2DH_ACCEL_HP_FILTERS=y
CONFIG_LIS2DH_ACCEL_RANGE_2G=y
CONFIG_LIS2DH_ODR_2=y
CONFIG_LIS2DH_OPER_MODE_LOW_POWER=y

CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_NVS=y
CONFIG_MPU_ALLOW_FLASH_WRITE=y
CONFIG_NVS_LOG_LEVEL_WRN=y

Sensor initialization - we are using deferred initialization:

bool MOTION_InitSensor()
{
    // Initialize accelerometer
    const struct device *const sensor = DEVICE_DT_GET_ANY(st_lis2dh12);
    if (sensor == NULL)
    {
        LOG_ERR("No sensor device found");
        return false;
    }
    int rc = device_init(sensor);
    if (rc < 0)
    {
        LOG_ERR("device_init() failed: %d", rc);
        return false;
    }
    if (!device_is_ready(sensor))
    {
        LOG_ERR("Device %s is not ready", sensor->name);
        return false;
    }

    // Configure slope trigger threshold
    struct sensor_value slopeThresholdAttr = {
        .val1 = 0,
        .val2 = (int32_t)(0.2 * SENSOR_G),
    };
    rc = sensor_attr_set(sensor, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_SLOPE_TH, &slopeThresholdAttr);
    if (rc < 0)
    {
        LOG_ERR("Failed to set slope threshold: %d", rc);
        return false;
    }

    // Configure slope trigger duration
    struct sensor_value slopeDurationAttr = {
        .val1 = 2, // 1 LSB = duration of 1/ODR
    };
    rc = sensor_attr_set(sensor, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_SLOPE_DUR, &slopeDurationAttr);
    if (rc < 0)
    {
        LOG_ERR("Failed to set slope duration: %d", rc);
        return false;
    }

    // Configure HP filter
    struct sensor_value configAttr = {
        .val1 = 0x02, // HP_IA2 = 1
    };
    rc = sensor_attr_set(sensor, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_CONFIGURATION, &configAttr);
    if (rc < 0)
    {
        LOG_ERR("Failed to set HP filter: %d", rc);
        return false;
    }

    // Set trigger
    struct sensor_trigger trig = {
        .type = SENSOR_TRIG_DELTA,
        .chan = SENSOR_CHAN_ACCEL_XYZ,
    };
    rc = sensor_trigger_set(sensor, &trig, trigger_handler);
    if (rc != 0)
    {
        LOG_ERR("Failed to set trigger: %d", rc);
        return false;
    }
    return true;
}

I analyzed every step of sensor configuration to see where the current consumption increase occurs. It turns out that this happens here:

MOTION_InitSensor() -> sensor_trigger_set()

Jumping to ...ncs//v3.0.1/zephyr/drivers/sensor/st/lis2dh/lis2dh_trigger.c:

lis2dh_trigger_set() -> lis2dh_trigger_anym_set() -> lis2dh_trigger_anym_tap_set()

In the last function, the last thing it does is:

k_work_submit(&lis2dh->work);

Commenting out just this line makes the average consumption decrease back to normal, which is < 10uA. but then, of course, we have no trigger. Uncommenting it causes a big static consumption increase - see the figure below:

I checked that the trigger handler is called as expected, i.e. only when acceleration is present, no spurious interrupts occur. There is no I2C traffic when the sensor is at rest, as expected.

One solution would be to drop the Zephyr sensor driver and tweak the accelerometer registers & configure the interrupt GPIO input manually, but this is against the elegance that Zephyr brings. So my question: what causes this current consumption and how to avoid it?

Parents
  • I forget where I originally found this advice, but I remember someone suggesting to use Sense for GPIO that aren't very high frequency (as opposed to GPIOTE by default).

    I forget what kind of power savings I saw, but it wasn't negligible.

    Here's a link that actually mentions using sense for LIS2DH. And here's another talking about general power savings and sense.

    For example, in DTS, just

    &gpio0 {
        sense-edge-mask = <0xffff>;
    };

Reply
  • I forget where I originally found this advice, but I remember someone suggesting to use Sense for GPIO that aren't very high frequency (as opposed to GPIOTE by default).

    I forget what kind of power savings I saw, but it wasn't negligible.

    Here's a link that actually mentions using sense for LIS2DH. And here's another talking about general power savings and sense.

    For example, in DTS, just

    &gpio0 {
        sense-edge-mask = <0xffff>;
    };

Children
Related