This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nrf52832 (Ruuvi tag) sdk17 random high current usage / failure to sleep when using SPI to communicate with accelerometer.

I've read many similar issues that were raised regarding high SPI power consumption, looked at various examples, but can't seem to find any issue that is close to what i'm observing.

I am developing a custom firmware for the RuuviTag that uses sdk17.0.2, based off the nrf sdk ble_app_template peripheral example. I have backported an spi wrapper and lis2dh12 accelerometer driver source from the original RuuviTag FW that uses sdk12. In terms of functionality, everything works correctly, but I am struggling with high power usage whenever the accelerometer is in use.

So far i believe i have narrowed down the problem to the SPI peripheral. The way my FW works is that every 1s, an app timer calls a handler that reads out axis acceleration values from the lis2dh12 via SPI (code snippet below). My guess is that the SPI peripheral randomly fails go to sleep properly after an SPI event happens. Please see the scope charts below:

The higher step is usually between 2-4mA.

What makes me believe that the issue lies with SPI is that the high/low power consumption interval duration is always a multiple of 1 second, same as the period of SPI transfers.

I have tried various configuration changes, enabling/disabling easyDMA, trying different wait/sleep functions in the wait loop, nothing has had any effect.

Additionally, no other code is really suspect as the original sdk12 based FW also reads accelerometer data via SPI at, what i believe, is 1s intervals, yet current usage (excluding transmitting peaks from bluetooth) is never over maybe 100-200uA. Furthermore, commenting out the entire SPI (and accelerometer) initialization gets the power usage permanently at the lower level, but leaving the initialization and removing the periodic data transfers results in the device getting stuck at the higher power level. Something else that draws suspicion to the SPI driver is that the SPI driver backend has been rewritten between the versions.

Below is the code used for the SPI transfer (originally taken from the sdk12 FW version, i updated the code to use nrfx)

static const nrfx_spim_t spi = NRFX_SPIM_INSTANCE(SPI_INSTANCE); 
volatile bool spi_xfer_done;
static bool initDone = false; 

extern void spi_init(void)
{
    nrfx_spim_config_t spi_config = NRFX_SPIM_DEFAULT_CONFIG;
    
    spi_config.sck_pin = SPIM0_SCK_PIN;
    spi_config.miso_pin = SPIM0_MISO_PIN;
    spi_config.mosi_pin = SPIM0_MOSI_PIN;
    spi_config.frequency = NRF_DRV_SPI_FREQ_1M;

    nrf_gpio_pin_dir_set(SPIM0_SS_ACC_PIN, NRF_GPIO_PIN_DIR_OUTPUT);
    nrf_gpio_cfg_output(SPIM0_SS_ACC_PIN);
    nrf_gpio_pin_set(SPIM0_SS_ACC_PIN);

    APP_ERROR_CHECK(nrfx_spim_init(&spi, &spi_config, spi_event_handler, NULL));
    spi_xfer_done = true;
    initDone = true;
}

extern bool spi_isInitialized(void)
{
    return initDone;
}

extern SPI_Ret spi_transfer_lis2dh12(uint8_t* const p_toWrite, uint8_t count, uint8_t* const p_toRead)
{
    SPI_Ret retVal = SPI_RET_OK;
    if ((NULL == p_toWrite) || (NULL == p_toRead))
    {
        retVal = SPI_RET_ERROR;
    }

    /* check if an other SPI transfer is running */
    if ((true == spi_xfer_done) && (SPI_RET_OK == retVal))
    {
        spi_xfer_done = false;

        nrf_gpio_pin_clear(SPIM0_SS_ACC_PIN);
        nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TRX(p_toWrite, count, p_toRead, count);
        nrfx_spim_xfer(&spi, &xfer_desc, 0);

        while (!spi_xfer_done)
        {
            nrf_pwr_mgmt_run();
        }
        nrf_gpio_pin_set(SPIM0_SS_ACC_PIN);
        retVal = SPI_RET_OK;
    }
    else
    {
        retVal = SPI_RET_BUSY;
    }
    return retVal;
}

Any tips would be greatly appreciated, so thank you very much in advance for the help Slight smile

  • Still hopeful that someone else has also encountered this before!

  • Is nrf_pwr_mgmt_run() used elsewhere, say in the main loop? Maybe try commenting this one out and simply waiting the 8 x n x 1uSecs for the SPI transfer to complete without trying to sleep and see how that effects the power. There are known errata with power. That assumes the LIS2DH12 is not read at very frequent intervals. The other option is to simply not use the nrf/nrfx code, instead using bare-metal drivers. I can post some if you don't find any.

    As an aside, the LIS2DH12 works fine at 8MHz, though high drive CLK, /CS ans MOSI are recommended.

    Is the default mode 0, by the way? Worth checking but that would not probably make any difference.

      pSPIM->CONFIG = 0;                        // CPOL 0 -- clock polarity active high, CPHA 1 -- sample on trailing clock edge, send Msb first
      pSPIM->FREQUENCY = 0x80000000UL;          // LIS2DH12 works up to 10MHz on SPI
      pSPIM->ORC =0;                            // Unused Tx bytes, set all low

    Lastly try running the SPIM as is but comment out the /CS low; that isolates the increased current to the nRF52 and away from something strange in the LIS2DH12.

    Errata issues, supposed to be in softdevice 7.2.0:

    static void mSleep(void)
    {
        // Errata 220: CPU: RAM is not ready when written - Disable IRQ while using WFE
        // Symptoms - Memory is not written in the first cycle after wake-up
        // Consequences - The address of the next instruction is not written to the stack. In stack frame, the link register is corrupted
        // Workaround
        // ==========
        // Enable SEVONPEND to disable interrupts so the internal events that generate the interrupt cause wakeuup in __WFE context and not in interrupt context
        // Before: ENABLE_WAKEUP_SOURCE -> __WFE -> WAKEUP_SOURCE_ISR -> CONTINUE_FROM_ISR  next line of __WFE
        // After:  ENABLE_WAKEUP_SOURCE -> SEVONPEND -> DISABLE_INTERRUPTS -> __WFE -> WAKEUP inside __WFE -> ENABLE_interrupts -> WAKEUP_SOURCE_ISR
        // Applications must not modify the SEVONPEND flag in the SCR register when running in priority levels higher than 6 (priority level numerical
        // values lower than 6) as this can lead to undefined behavior with SoftDevice enabled
        //
        // Errata 75: MWU: Increased current consumption
        // This has to be handled by turning off MWU but it is used in SoftDevice
        // see https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52832_EngB%2FERR%2FnRF52832%2FEngineeringB%2Flatest%2Fanomaly_832_75.html
        //
        // Errata 220: Enable SEVONPEND
        SCB->SCR |= SCB_SCR_SEVONPEND_Msk;
        __disable_irq();
        // Errata 75: MWU Disable
        uint32_t MWU_AccessWatchMask = NRF_MWU->REGIONEN & MWU_ACCESS_WATCH_MASK;
        // Handle MNU if any areas are enabled
        if (MWU_AccessWatchMask)
        {
            NRF_MWU->REGIONENCLR = MWU_AccessWatchMask; // Disable write access watch in region[0] and PREGION[0]
            __WFE();
            __NOP(); __NOP(); __NOP(); __NOP();
            // Errata 75: MWU Enable
            NRF_MWU->REGIONENSET = MWU_AccessWatchMask;
        }
        else
        {
            __WFE();
            __NOP(); __NOP(); __NOP(); __NOP();
        }
        __enable_irq();
    }

  • Hi, did you ever figure this out?

    I'm doing almost the same thing but with the AIS2DW12 accelerometer.  

    devzone.nordicsemi.com/.../high-sleep-current-with-spi-reading-accelerometer

Related