High power usage when access to ADXL345 i2c

Hi, I'm using a Nordic nrf52832 in a custom board. Zephyr is working OK, but i have a strange issue. I have a ADXL345 accelerometer connected with i2c, I can read the sensor OK but has a high power consume, like 600uA when i start reading it and i can't put it to sleep.


Definition of device:

&pinctrl {
    i2c1_default: i2c1_default {
        group1 {
            psels = <NRF_PSEL(TWIM_SDA, 0, 16)>,
                    <NRF_PSEL(TWIM_SCL, 0, 15)>;
        };
    };
    i2c1_sleep: i2c1_sleep {
        group1 {
            psels = <NRF_PSEL(TWIM_SDA, 0, 16)>,
                    <NRF_PSEL(TWIM_SCL, 0, 15)>;
            low-power-enable;
        };
     };
}

&i2c1 {
    status = "okay";
    
    pinctrl-0 = <&i2c1_default>;
    pinctrl-1 = <&i2c1_sleep>;
    pinctrl-names = "default", "sleep";

    clock-frequency = <I2C_BITRATE_STANDARD>;

    accel0: adxl345@53 {
	compatible = "adi,adxl345";
        reg = < 0x53 >;
	label = "ADX1345";
        interrupts = < 27 26 >;
    };
};

If I never access to the i2c then never get that high power consume, for example if in my device init function i put this:

i2c_dt_spec _accel = {
        .bus = DEVICE_DT_GET(DT_BUS(DT_NODELABEL(accel0))),
        .addr = DT_REG_ADDR(DT_NODELABEL(accel0))
}

if (!device_is_ready(_accel.bus)) {
        __ASSERT(false, "Accel device is not ready");
}

Then the power usage of my device maintain a base of ~200-300 uA, but if i only read the ADXL345 ID registry:

enum class ACCEL_REGISTERS {
    ADXL345_DEVICE_ID_REG = 0x00,
    ADXL345_RATE_REG = 0x2c,
    ADXL345_POWER_CTL_REG = 0x2d,
    ADXL345_DATA_FORMAT_REG = 0x31,
    ADXL345_X_AXIS_DATA_0_REG = 0x32,
    ADXL345_FIFO_CTL_REG = 0x38,
    ADXL345_FIFO_STATUS_REG = 0x39,
    ADXL345_INT_ENABLE_REG = 0x2e,
};

auto dev_id = reg_read_byte(ACCEL_REGISTERS::ADXL345_DEVICE_ID_REG);

if (dev_id != ACCEL_DEVICE_ID)
{
        LOG_ERR("Read PART ID failed: 0x%x", dev_id);
}

Then power usage rise up to ~600uA. I tried to use PM on i2c and not work:

pm_device_action_run(_accel.bus, PM_DEVICE_ACTION_SUSPEND);

or using the SLEEP registry of the ADX:

enum ACCEL_REGISTERS_VALUES {
        ADXL345_FIFO_STREAM_MODE = (1 << 7),
        ADXL345_RANGE_16G = 0x3,
        //ADXL345_RATE_25HZ = 0x8,
        ADXL345_RATE_0_78HZ = 0x3,

        ADXL345_ENABLE_SLEEP_BIT = (1 << 2),
        ADXL345_ENABLE_MEASURE_BIT = (1 << 3),

        AXL345_POWER_CTL_SLEEP = 0b00110111,
}

reg_write_byte(ACCEL_REGISTERS::ADXL345_POWER_CTL_REG, AXL345_POWER_CTL_SLEEP);

My functions to read and write i2c:

enum class ACCEL_CMDS {
    ADXL345_READ_CMD = 0x80,
    ADXL345_WRITE_CMD = 0x00
};

void Accel::access_i2c(ACCEL_CMDS cmd, ACCEL_REGISTERS reg, std::span<std::uint8_t> data) const
{
    int ret;

    if (cmd == ACCEL_CMDS::ADXL345_READ_CMD) {
        ret = i2c_burst_read_dt(&_accel, static_cast<std::uint8_t>(reg), data.data(), data.size());
    } else {
        ret = i2c_burst_write_dt(&_accel, static_cast<std::uint8_t>(reg), data.data(), data.size());
    }

    if (ret != 0) {
        LOG_WRN("Error trying to access to i2c bus for accel: %d", ret);
    }
}

std::vector<std::uint8_t> Accel::reg_read(ACCEL_REGISTERS address, std::size_t to_read) const
{
    std::vector<std::uint8_t> ret(to_read, 0);

    access_i2c(ACCEL_CMDS::ADXL345_READ_CMD, address, std::span{ret});

    return ret;
}

void Accel::reg_write(ACCEL_REGISTERS address, std::span<std::uint8_t> data)
{
    return access_i2c(ACCEL_CMDS::ADXL345_WRITE_CMD, address, data);
}

std::uint8_t Accel::reg_read_byte(ACCEL_REGISTERS address) {
    return reg_read(address, 1)[0];
}

void Accel::reg_write_byte(ACCEL_REGISTERS address, std::uint8_t data)
{
    std::array<std::uint8_t, 1> data_send = {data};

    return reg_write(address, std::span{data_send});
}

Maybe I missing something, but i think that I2C activate on first sensor read and never sleep again, can you help me to find out the problem?

Power Usage of full device without ADXL345 reading:

When use ADXL345 reading:

  • Finnaly found the reason. Is a hardware bug, found on nRF52832 Errata 89. To solve i changed my sleep and wakeup function to this:

    void Accel::sleep() {
        reg_write_byte(ACCEL_REGISTERS::ADXL345_POWER_CTL_REG, static_cast<uint8_t>(ADXL345_DISABLE_MEASURE_BIT));
    
        pm_device_action_run(_accel.bus, PM_DEVICE_ACTION_SUSPEND);
    
        // Workaround for Nordic NRF52832 Errata 89 hardware bug
        // https://infocenter.nordicsemi.com/topic/errata_nRF52832_Rev2/ERR/nRF52832/Rev2/latest/anomaly_832_89.html
        // This disables the i2c twi1 peripheral power domain
        // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-reinterpret-cast)
        reinterpret_cast<volatile std::uint32_t *>(0x40004FFC)[0] = 0;
        reinterpret_cast<volatile std::uint32_t *>(0x40004FFC)[0];
        reinterpret_cast<volatile std::uint32_t *>(0x40004FFC)[0] = 1;
        // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-reinterpret-cast)
    }
    
    void Accel::wakeup() {
        pm_device_action_run(_accel.bus, PM_DEVICE_ACTION_RESUME);
    
        // Must reinitialize the accel device
        init_accel();
    }

Related