This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

NCS v2.4.0: low power by example

Dear Support team,

I'm involved in the activity to migrate to NCS a project already developed & successfuly tested with using nRF52 SDK v17.1.0, over the custom board initially designed.

That custom board is based on nRF52832_xxAA, battery powered, equipped with an inertial sensor from STM (MEMS) over SPI0, and a 8Mbit flash memory over SPI1, plus some LEDs and one button. The original .dts file is contained in the folder "<ncs_path>\v2.4.0\zephyr\boards\arm\nrf52dk_nrf52832" successfully overlayed for what concerns the custom I/O mapping. Indeed, everything is working fine without MCUBOOT (I'll add it as a final step when everything will be fixed), until I decided to check the power absorbment from the battery that reported around 150 microAmps in "idle" mode (advertising active and the whole system waiting for events).

As I expect an absorbment from 10 to 20 microAmp, I decided to start from the beginning: the "empty" main as reported below:

int main(void)
{
   while(1)
      NRF_POWER->SYSTEMOFF = 1;

   return 0;
}

This "empty" main reports 30.0 microAmps (NCS) against the 0.30 microAmps reported by the same "empty" main in the APP built with SDK v17.1.0, anyone could tell me whats's possibly wrong ?


PS: "CONFIG_SERIAL=n" has been applied already as many posts seeemed to be resolutive, also in the overlay "&uart0" is stated as "disabled".

PS #2: already included and built some examples from zephyr/samples/bluetooth (only advertising just to remain basic) and I never saw an absorbment less than 100 microAmps, therefore I can't exclude the extension nRF Connect for VSCode, as it's common to all test made so far

Parents
  • Hi,

     

    Could you try setting the /CSN pins for these sensors to the inactive? 

    If the SPI Flash is enabled, could you try setting it to a suspended state before entering systemoff?

    dev = DEVICE_DT_GET(DT_NODELABEL(my_device));
    pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND);

     

    Kind regards,

    Håkon

  • Dear Hakon,

    thank you for your prompt reply.

    You got the point, indeed, even if no modules were compiled other than "main.c", there was CONFIG_SPI=y in prj.conf still enabled therefore your doubt about SPI initialized before main() was correct. Now the absorbment is back to 0.3 microAmps as reported from my multimeter (see picture attached).

    Let me take advantage of your availability to ask you a working example of pm_device_action_run() so that I can understand the correct approach to save power when no resource are needed.

    thank you so much for now

    regards, Paolo

  • Hi,

    I wouldn't change Zephyr code but is quite clear that without those changes the pm_device_action_run(SUSPEND) doesn't handle /CSN as spi_transceive() instead does.

    Indeed, when I cut the power to the sensor, it remains powered from /CSN, until I set /CSN to low.

    Please have a look at the Zephyr sources, in particolar "spi_nrfx_spi.c" where the field "skip_gpio_cfg" is initialized to TRUE: this initialization prevent any PIN re-configuration from pm_device_action_run(SUSPEND).

    Do you have an idea of why that hard-coded initialization ?

    Furthermore, why the field .ss_csn is not loaded with the correct pin value ?

    Everything seems to be strange, I'm worried about the possibility to have made a mistake somewhere in the configuration files.


    thank you, Paolo

  • pzuck said:
    Please have a look at the Zephyr sources, in particolar "spi_nrfx_spi.c" where the field "skip_gpio_cfg" is initialized to TRUE: this initialization prevent any PIN re-configuration from pm_device_action_run(SUSPEND).

    It is setup this way on purpose, and allows the module/driver that uses it to configure it on-the-fly.

    I would strongly recommend that you use a logic analyzer or a oscilloscope to verify the pin states.

    pzuck said:
    Furthermore, why the field .ss_csn is not loaded with the correct pin value ?

    It highly likely is, just at a later point in time.

     

    Kind regards,

    Håkon

  • Hi Hakon,

    What's happen is clear to me as I followed the code step by step with the debugger and checked the pin states with an oscilloscope.

    I can confirm also that the sensor communications over SPI are OK, just I wanted to turn it OFF by using pm_device_action_run(...SUSPEND).

    Can't understand why you don't want to get the evidences reported and answer properly.

    thank you, Paolo

  • Hi Paulo,

     

    Regarding the .ss_pin member: there is a difference on how the module(s) work at boot-time vs. how they work when actually used.

    On boot-up, it will skip the gpio configuration, while when addressed and used in main.c, the configuration will be passed to the spi_nrfx_spi.c and be configured on each run.

    As the SPI hardware peripheral does not have a dedicated /CSN pin, this is handled by the gpio driver.

     

    Here's the minimal main.c (based on your code):

    /*
     * Copyright (c) 2012-2014 Wind River Systems, Inc.
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/drivers/spi.h>
    #include <zephyr/pm/device.h>
    
    LOG_MODULE_REGISTER(app);
    
    #define SPI_BUF_SIZE 16
    #define MY_SPI_NODE spi0
    
    static const struct device *spi_dev = DEVICE_DT_GET(DT_NODELABEL(MY_SPI_NODE));
    static const struct spi_cs_control spi_cs_ctrl = {
    	.gpio = GPIO_DT_SPEC_GET(DT_NODELABEL(MY_SPI_NODE), cs_gpios),
    	.delay = 2
    };
    
    static const struct spi_config spi_cfg = {
    	.frequency = 8 * 1000 * 1000,
    	.operation = SPI_WORD_SET(8),
    	.cs = &spi_cs_ctrl
    };
    
    static uint8_t tx_buf[SPI_BUF_SIZE];
    static uint8_t rx_buf[SPI_BUF_SIZE];
    
    static struct spi_buf tx_spi_bufs[] = {
    	{
    		.len = 4,
    		.buf = tx_buf
    	}
    };
    
    static struct spi_buf rx_spi_bufs[] = {
    	{
    		.len = 4,
    		.buf = rx_buf
    	}
    };
    
    static const struct spi_buf_set	spi_tx_buf_set = {
    	.buffers = tx_spi_bufs,
    	.count = 1
    };
    
    static const struct spi_buf_set	spi_rx_buf_set = {
    	.buffers = rx_spi_bufs,
    	.count = 1
    };
    
    uint8_t test_spi_read(uint8_t addr, uint8_t len)
    {
    	__ASSERT_NO_MSG(len < SPI_BUF_SIZE);
    
    	tx_buf[0] = addr | BIT(7);
    
    	tx_spi_bufs[0].len = rx_spi_bufs[0].len = len;
    
    	int err = spi_transceive(spi_dev, &spi_cfg, &spi_tx_buf_set, &spi_rx_buf_set);
    	if (err < 0)
    	{
    		LOG_WRN("transceive() failed! err=%d", err);
    		return 1;
    	} else {
    		LOG_INF("OK");
    	}
    	return 0;
    }
    
    int main(void)
    {
    	printk("Hello World! %s\n", CONFIG_BOARD);
    	while (1) {
    		test_spi_read(0, 2);
    		/* Keep it active for testing purposes */
    		k_msleep(1000);
    		pm_device_action_run(spi_dev, PM_DEVICE_ACTION_SUSPEND);
    		k_msleep(1000);
    		pm_device_action_run(spi_dev, PM_DEVICE_ACTION_RESUME);
    		k_msleep(1);
    	}
    }

    Here's a scope of the SPI communication on &spi0 (P0.26/P0.27/P0.28, CSN=P0.13):

    And here's the overlay that I used:

    &spi0 {
            //compatible = "nordic,nrf-spi";
            status = "okay";
            // cs-gpios = <&gpio0 18 GPIO_ACTIVE_LOW>;
            cs-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
            spi0_cs: spi0_cs@0 {
                    reg = <0>;
            };
    };

    As I do not have any sensor connected on the SPI bus, there's no response on MISO.

     

    And here's the power consumption:

    pzuck said:
    Indeed, when I cut the power to the sensor, it remains powered from /CSN, until I set /CSN to low.

    This is as intended. If you are powering off your external sensor, you need to ensure that all GPIOs/signals going to your external sensor are kept at GND level.

     

    Kind regards,

    Håkon

  • Hi Hakon,

    In your scope screenshot I don't see /CSN going low for 1 sec by the call:

           pm_device_action_run(spi_dev, PM_DEVICE_ACTION_SUSPEND);

    therefore the sensor (virtual in your scenario) will never turn OFF.

    If everything worked we should see two times CS hi-low-hi transaction. The first very short as SPI read from sensor, the second with a duration of 1000 ms

    Let me know

    thank you, Paolo

Reply
  • Hi Hakon,

    In your scope screenshot I don't see /CSN going low for 1 sec by the call:

           pm_device_action_run(spi_dev, PM_DEVICE_ACTION_SUSPEND);

    therefore the sensor (virtual in your scenario) will never turn OFF.

    If everything worked we should see two times CS hi-low-hi transaction. The first very short as SPI read from sensor, the second with a duration of 1000 ms

    Let me know

    thank you, Paolo

Children
Related