High power consumption of nRF52832 using TWI 100KHz ?

Hi, we are using nRF52832 for our project. SDK version is nRF5_SDK_17.1.0_ddde560. SoftDevice is S132.

The project requires TWI to read data from a sensor and send that data via BLE. The sensor will output 9 bytes every 2.5ms, or 3600B/s data rate. Currently, BLE consumes 0.1mA and it matches the estimated number. When data transfer via BLE is going on, our board consumes 3.8mA. We are trying to lower this number by determining if any part of the system consumes more power than usual.

So we first remove the code that sends data via BLE, which means only connect via BLE, and read data via TWI, but do not send it to the peer. We get a consumption of around 3.2mA
Then we remove the code that reads data from sensor via TWI. This time consumption reduces to 1.5mA. The sensor is still in its working state, just the code to read data in the sensor buffer is removed.

That means reading sensor data via TWI consumes 3.2-1.5=1.7mA. This is so high, especially for a quite low data rate. TWI clock is 100KHz. We check the sensor buffer every 60ms to see if any data is available. The buffer check is simply just 2 registers (head & tail) being read from the sensor. From this, we know how many bytes we can read from the sensor, and we read a burst of that many bytes from the sensor.

Is this number normal for TWI usage? Is there any way we can optimize the power consumption when using TWI? Please let us know soon. We really appreciate your speedy help.

Best regards,

Xander

Parents
  • A handy envelope shows 9 bytes or 81 bits at 100kHz is 810uSec active clock and data in 2500uSec, or about 32%. 32% active I2C data and clock with 4k7 pull-ups on SDA and SCK is (say) 400uA assuming something near 50% duty cycle on both. 1k0 pull ups this would be closer to 1mA, with 10k0 pull-ups closer to 200uA. Test this by switching to more power-efficient 400kHz to see if that part of the power reduces by x4 with the same value pull-ups.

    The key to the balance of power is likely to be "TWI"; TWI is inefficient since the CPU is awake more frequently. TWIM using DMA is much better, maybe approaching 9 times better, as although the same number of bytes are processed the 9 sleep/wakeup cycles and interrupt entry/exit cycles (power consumption overhead) are replaced by a single sleep/wakeup cycle and interrupt, particularly when errata workarounds are in effect. That means 9-byte TWIM, not 9 x 1-byte TWIM; 9 x 1-byte TWIM is even worse than 9 x 1-byte TWI and yes that is commonly used.

    Want optimum efficiency? Switch to SPIM (SPI with DMA), should that be an option, which does not require power-hungry pull-ups. I2C/TWI is old-school from the days when power consumption by other components was orders of magnitude higher; yes it's easy but no it's not a low-power solution. Data processing overhead for TWIM and SPIM both with DMA is similar.

  • Test this by switching to more power-efficient 400kHz to see if that part of the power reduces by x4 with the same value pull-ups.

    Yes it does. I changed to TWI at 400KHz and reading sensor data consumes 0.4mA instead of 1.7mA. I still don't understand why it can be this high?

  • Hi 

    A common issue with non blocking API's like these is to forget to make the right variables static or const. Then the variables will be put on the stack, and when you exit the function that declared them they will be overwritten with other data. 

    It seems you make most variables static or const, but possibly some are not (such as the p_write_buffer[] array). 

    Can you double check that this is handled correctly? 

    Putting functions in a different file can change where variables are put in memory, which could make a difference if there are some buffer overrun issues or similar, but exactly why it makes a difference in your case I can't spot from the attached code snippets.  

    Best regards
    Torbjørn

  • Thank you Ovrebekk for your help. It helps me solve the issue for most functions, except in this place I needs to calculate the number of bytes to read from TWI on the fly. So I cannot use static const for the transfers array. How should I deal with this case?

  • Hi 

    You don't need to use both static and const, just static is sufficient. 

    Just be aware that you won't be able to declare the variable and assign the data values on the same line, since the declaration of a static value only happens once and can not contain dynamic data. 

    Instead you can declare the static variable at the top of the function, without assigning a value to it, and assign values to it later on where the variable is to be used. 

    Best regards
    Torbjørn

  • Hi Ovrebekk,

    I tried your suggestion but the compiler doesn't allow it.

  • Hi 

    That makes sense, those macros will only work when initializing a struct, not when assigning values to it. Either you can create some const structs that you later copy into the transfers array, or you can simply assign the fields of the struct directly. 

    The nrf_twi_mngr_transfer_t struct only consists of the following elements, which you are free to assign directly without using the macros:

    typedef struct {
        uint8_t * p_data;     ///< Pointer to the buffer holding the data.
        uint8_t   length;     ///< Number of bytes to transfer.
        uint8_t   operation;  ///< Device address combined with transfer direction.
        uint8_t   flags;      ///< Transfer flags (see @ref NRF_TWI_MNGR_NO_STOP).
    } nrf_twi_mngr_transfer_t;

    Best regards
    Torbjørn

Reply
  • Hi 

    That makes sense, those macros will only work when initializing a struct, not when assigning values to it. Either you can create some const structs that you later copy into the transfers array, or you can simply assign the fields of the struct directly. 

    The nrf_twi_mngr_transfer_t struct only consists of the following elements, which you are free to assign directly without using the macros:

    typedef struct {
        uint8_t * p_data;     ///< Pointer to the buffer holding the data.
        uint8_t   length;     ///< Number of bytes to transfer.
        uint8_t   operation;  ///< Device address combined with transfer direction.
        uint8_t   flags;      ///< Transfer flags (see @ref NRF_TWI_MNGR_NO_STOP).
    } nrf_twi_mngr_transfer_t;

    Best regards
    Torbjørn

Children
No Data
Related