SPI Peripheral Power Consumption much higher than nrf5 SDK

Hi,
We are currently migrating our nrf5 SDK code to NCS 2.2.0 on a custom nrf52833 board (using a .overlay file on the DK).

Our power consumption for the same peripheral is ~6.1 uA average, as opposed to our expected ~3.5 uA average on nrf5SDK - but we are able to achieve around ~4 uA average by not calling the SPI module at all.

Some points to note before the code:

  • According to the data sheet of the peripheral - we should be working in NRF_SPI_MODE_3 on nrf5 SDK, which seems to translate to | CPOL | CPHA on zephyr. However - if we try to use this mode, SPI does not work at all, and we see a very high baseline on PPK.
  • This peripheral and another peripheral on our board share 2 pins: SCK, and another pin which is MISO for this peripheral, and MISO_MOSI for the other peripheral. This does not cause problems on nrf5 SDK.

Our SPI usage is as follows:

const struct gpio_dt_spec spec = {
.dt_flags = GPIO_ACTIVE_LOW,
.pin = 5,
.port = DEVICE_DT_GET(DT_NODELABEL(gpio0))
}; 

struct spi_cs_control spi_cs = {
    .gpio = spec,
    /* delay in microseconds to wait before starting the transmission and before releasing the CS line */
    .delay = 10,
};

#define SPI_CS (&spi_cs)

struct spi_config spi_cfg = {
    .frequency = 0x4000000,
    .operation = SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8), // | SPI_MODE_CPOL | SPI_MODE_CPHA,
    .cs = SPI_CS,
};

const struct device *spi1_dev =  DEVICE_DT_GET(DT_NODELABEL(spi1));

void acc_spi_write(uint8_t cmd, uint8_t val)
{
    uint8_t com[2] = { cmd, val };
    struct spi_buf tx_buf[1] = {
        {
            .buf = com,
            .len = 2
        }
    };

    struct spi_buf_set tx_bufs = {
        .buffers = tx_buf,
        .count = 1
    };

    int ans = spi_transceive(spi1_dev,&spi_cfg,&tx_bufs,NULL);
    if(ans)
    {
        printk("SPI write failed - %d\n",ans);
    }
}

And in the overlay file:

&pinctrl {

    // acc
    status = "okay";
    spi1_default: spi1_default {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 4)>,
                <NRF_PSEL(SPIM_MOSI, 0, 11)>,
                <NRF_PSEL(SPIM_MISO, 1, 9)>;
        };
    };

    spi1_sleep: spi1_sleep {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 4)>,
                <NRF_PSEL(SPIM_MOSI, 0, 11)>,
                <NRF_PSEL(SPIM_MISO, 1, 9)>;
                
            low-power-enable;
        };
    };
    spi0_default: spi0_default {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 4)>,
                <NRF_PSEL(SPIM_MOSI, 0, 11)>;
        };
    };

    spi0_sleep: spi0_sleep {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 4)>,
                <NRF_PSEL(SPIM_MOSI, 0, 11)>;
            low-power-enable;
        };
    };
};


&spi1 {
    compatible = "nordic,nrf-spi";
    status = "okay";
    clock-frequency = <0x4000000>;
    overrun-character = < 255 >;
    cs-gpios = < &gpio0 5 GPIO_ACTIVE_LOW >;
};

&spi0 {
    compatible = "nordic,nrf-spi";
    status = "okay";
    clock-frequency = <0x4000000>;
    overrun-character = < 255 >;
    cs-gpios = < &gpio0 15 GPIO_ACTIVE_LOW >;
};

  • Hello,

    I am not entirely sure I understand how you solved this in the old nRF5 SDK and how you are planning to do this on the new nRF Connect SDK, and what the problem may be. Dynamic switching / sharing of pins is not straight forward, you need to ensure that two peripherals trying to use the same pin(s) are not enabled at the same time at any given time. In your case you might also want to measure the pins in question using a logic analyzer to ensure that the pin level and mode is the same between old and new SDK.

    Today it can be possible to use the device power management to achieve pin sharing. If you use CONFIG_PM_DEVICE=y, then you have the option to call for instance pm_device_action_run(peripheral_dev, PM_DEVICE_ACTION_RESUME); and pm_device_action_run(peripheral_dev, PM_DEVICE_ACTION_SUSPEND); to control which configuration is used. It should be possible to switch between usage that way, as long as only one peripheral_dev is active ("RESUME") at any given time.

    Best regards,
    Kenneth

  • Hi , 

    from my understanding there is no problem to share the SPI pins except Chip/Slave Select
    if you manage to not use both of them at the same time.  

    After I use CONFIG_PM_DEVICE=y, both of my peripherals are not working and the RTT is not working as well, there is an example with similar case in zephyr?

    Thanks!

  • S_C said:
    from my understanding there is no problem to share the SPI pins except Chip/Slave Select
    if you manage to not use both of them at the same time.  

    True, but from your code it looks like you are using both spi0 and spi1, and they use the pins? This will be problematic, since you are using two hardware peripherals with the same pins.

    If you only have different spi chip select pin, then why not just do something like:

    csn_1 = 0;
    spi_transceive(spi0,...)
    csn_1= 1;

    csn_2 = 0;
    spi_transceive(spi0,...)
    csn_2= 1;

    Kenneth

  • Hi Kenneth,
    We were able to use your suggestion on spi1 without having to turn off the CS pins manually - which enabled us to use the correct mode (CPOL | CPHA) on the second SPI peripheral which we were unable to do before. Thanks!

    Our only question is that we cannot remove the unused spi0 (by disabling in overlay) or remove it's pinctrl without harming the "spi0" peripheral functionality, even though it is now pointing to spi1. Do you happen to know why this is?

    Thanks!
    Shahar

  • S_C said:
    Our only question is that we cannot remove the unused spi0 (by disabling in overlay) or remove it's pinctrl without harming the "spi0" peripheral functionality, even though it is now pointing to spi1. Do you happen to know why this is?

    Are you controlling both csn pins (e.g. set them high at startup)?

    Kenneth

Related