NRF5340 power management

Hi Devzone,

I am using an nrf5340 with NCS v2.0.0 and am trying to implement power management.
I found this link and have been taking hints from it
https://github.com/nrfconnect/sdk-zephyr/tree/main/samples/boards/nrf/system_off

This is the scenario I would like to implement.

  1. Enter deep sleep mode (used primarily for shipping device and storage)
  2. Wakeup on GPIOTE interrupt when ready for use
  3. Advertise/pair/bond
  4. Do a few tasks based on timer
    1. Sample data from various devices attached to peripherals
  5. Setup to enter PM_STATE_SUSPEND_TO_RAM sleep mode
    1. Un-init peripherals
      1. Set peripherals to PM_DEVICE_ACTION_SUSPEND
      2. Set peripheral pins to output low
    2. Enter PM_STATE_SUSPEND_TO_RAM
  6. Wakeup on timer
    1. Init peripherals
      1. Set peripherals to PM_DEVICE_ACTION_RESUME
      2. Initialize peripherals
    2. Sample data from various devices attached to peripherals
  7. Got back to 5

Peripherals used:
I2C1
SPIM2
SPIM4
SAADC
DPPI

reduced prj.conf setup

CONFIG_I2C=y
CONFIG_SENSOR=y
CONFIG_TMP116=y
CONFIG_NRFX_SPIM2=y
CONFIG_NRFX_SPIM4=y
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_NRFX_SAADC=y
CONFIG_NRFX_TIMER1=y
CONFIG_NRFX_DPPI=y

 To prevent the nrf5340 from entering deep sleep mode until I tell it I call

pm_policy_state_lock_get(PM_STATE_SOFT_OFF);
And to enter sleep SUSPEND_TO_RAM I call
    struct pm_state_info sleep_state = {PM_STATE_SUSPEND_TO_RAM, 0, 0};
    pm_state_force(0u, &sleep_state);

I have mostly everything mostly working, I can read and write to my devices on the various peripherals. The issue I am running into is directly setting the power setting of the various peripherals.

Calling this code to set the I2C1 device to suspend, results in a system crash.

struct device *i2c_dev = DEVICE_DT_GET(DT_NODELABEL(i2c1));
pm_device_action_run(i2c_dev, PM_DEVICE_ACTION_SUSPEND);

After stepping through that code I see that the crash occurs after calling

atomic_test_bit(&pm->flags,
			       PM_DEVICE_FLAG_STATE_LOCKED);

I saw this same issue when setting the power state for SPI2.

I have a few questions:

  1. What devices are supported by pm_device_action_run? It runs OK in the sample for UART0 listed above but not for I2C1 or SPI2.
  2. Does the peripherals have to be un-initialized and the pins configured in output low mode for the greatest power saving?
  3. What is the correct order of operation for the peripherals:
    1. For wakeup? Set to active state, then initialize or vice versa?
    2. For sleep? Set to suspend then un-initialize or vice versa?
  4. Should I even bother trying to run a pm_device_action_run on the I2C1, SPIM2, etc... or will the power management set that for me automatically?
  5. I have a tmp117 attached to I2C1 and am using the zephyr driver to communicate with it. How do you unconfigure the I2C1? Again is it even necessary to configure it, use it then unconfigure it after use to achieve power savings?
  6. Do I need to setup power states in my overlay file? I didn't see one in the sample I sited above.
  7. What is the best way to achieve the scenario I described above with the best power savings?
  8. How do you set wake on interrupt for a single GPIOTE? The example has this for a GPIO sw0 on the nrf5340dk
    1. 	/* Configure to generate PORT event (wakeup) on button 1 press. */
      	nrf_gpio_cfg_input(NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(sw0), gpios),
      			   NRF_GPIO_PIN_PULLUP);
      	nrf_gpio_cfg_sense_set(NRF_DT_GPIOS_TO_PSEL(DT_ALIAS(sw0), gpios),
      			       NRF_GPIO_PIN_SENSE_LOW);
      
    2. This is what I currently have for GPIOTE using pin31. Will this work the same way?
    3. #define GPIOTE31             NRF_GPIO_PIN_MAP(0,31)
      	            
      const nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
      nrfx_gpiote_in_init(GPIOTE31, &in_config, set_wakeup_detected);

Any help in configuring and setup to achieve best power saving would be appreciated. 

Thanks

  • Seems like a lot of questions bundled into one thread. I will go through them on Monday and will try to answer you point by point.

  • What devices are supported by pm_device_action_run? It runs OK in the sample for UART0 listed above but not for I2C1 or SPI2.

    Here you can see that the uart/i2c/spi implements the pm actions so it should be ok to call pm_actions on any of these peripherals as long as you are using nrfx drivers for these.

    Does the peripherals have to be un-initialized and the pins configured in output low mode for the greatest power saving?

    The pin configurations and their release/assignment to the peripherals should be taken care by the drivers when they implement the pm states. The application should not do anything else apart from knowing that it does not start any transactions on the peripherals before calling the power management functions. I fyou look in zephyr\drivers\i2c\i2c_nrfx_twim.c you can see life in function i2c_nrfx_twim_recover_bus that the driver tries to set the pin states to the config that suits the state

    What is the correct order of operation for the peripherals:
    1. For wakeup? Set to active state, then initialize or vice versa?
    2. For sleep? Set to suspend then un-initialize or vice versa?

    1. I would first initialize the peripheral and state the active state.
    2. I would first uninitialize the peripheral and set to suspend. 

    Should I even bother trying to run a pm_device_action_run on the I2C1, SPIM2, etc... or will the power management set that for me automatically?

    The drivers in the nrfx lib implements the callback for the pm action, so the application can call them when needed.

    I have a tmp117 attached to I2C1 and am using the zephyr driver to communicate with it. How do you unconfigure the I2C1? Again is it even necessary to configure it, use it then unconfigure it after use to achieve power savings?

    Please create a separate thread for this.

    Do I need to setup power states in my overlay file? I didn't see one in the sample I sited above.

    No, i do not see or know any pm states that you can set in device tree. There are some pinctrl states you can set for the pins but not for the peripherals.

    Please create separate tickets for the rest of the points, please do not mix too many questions into one as they tend to flood the ticket with unrelated info which is difficult to search for others.

  • Thanks for the reply sorry to include so many different questions. I will open a different ticket for the other points as you suggested.

    Based on your reply, I see that in order to get the power management for SPIM2 enabled I have to this define

    PM_DEVICE_DT_DEFINE(SPIM(idx), spim_nrfx_pm_action);
     enabled but, that requires 

    #define SPI_NRFX_SPIM_DEVICE(idx)
    After looking at  https://github.com/nrfconnect/sdk-zephyr/blob/main/drivers/spi/spi_nrfx_spim.c I see
    CONFIG_SPI_2_NRF_SPIM has to be enabled. It is not settable directly according to the documentation.
    In order to get that defined, I added these two new settings to my prj.conf file
    CONFIG_SPI=y
    CONFIG_SPI_NRFX=y
    This results in a build error below for the expansion of that macro with regards to PINCTRL and legacy pins. What setting am I missing in my config file to get the PM_DEVICE_DT_DEFINE for my peripherals? Code builds fin and runs OK with those two settings but the power management doesn't get enabled.
    -c C:/ncs/v2.0.0/zephyr/drivers/spi/spi_nrfx_spim.c
    In file included from C:\ncs\v2.0.0\zephyr\include\zephyr\toolchain.h:50,
    from C:\ncs\v2.0.0\zephyr\include\zephyr\init.h:10,
    from C:\ncs\v2.0.0\zephyr\include\zephyr\device.h:29,
    from C:\ncs\v2.0.0\zephyr\include\zephyr\drivers\spi.h:24,
    from C:\ncs\v2.0.0\zephyr\drivers\spi\spi_nrfx_spim.c:7:
    C:\ncs\v2.0.0\zephyr\include\zephyr\toolchain\gcc.h:77:36: error: static assertion failed: "/soc/peripheral@50000000/spi@b000 has legacy *-pin properties defined although PINCTRL is enabled"
    77 | #define BUILD_ASSERT(EXPR, MSG...) _Static_assert(EXPR, "" MSG)
    | ^~~~~~~~~~~~~~
    c:\ncs\v2.0.0\zephyr\soc\arm\nordic_nrf\common\soc_nrf_common.h:229:2: note: in expansion of macro 'BUILD_ASSERT'
    229 | BUILD_ASSERT(!IS_ENABLED(CONFIG_PINCTRL) || \
    | ^~~~~~~~~~~~
    C:\ncs\v2.0.0\zephyr\drivers\spi\spi_nrfx_spim.c:518:2: note: in expansion of macro 'NRF_DT_CHECK_PIN_ASSIGNMENTS'
    518 | NRF_DT_CHECK_PIN_ASSIGNMENTS(SPIM(idx), 1, \
    | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
    C:\ncs\v2.0.0\zephyr\drivers\spi\spi_nrfx_spim.c:586:1: note: in expansion of macro 'SPI_NRFX_SPIM_DEVICE'
    586 | SPI_NRFX_SPIM_DEVICE(2);
    | ^~~~~~~~~~~~~~~~~~~~
    C:\ncs\v2.0.0\zephyr\include\zephyr\toolchain\gcc.h:77:36: error: static assertion failed: "/soc/peripheral@50000000/spi@a000 has legacy *-pin properties defined although PINCTRL is enabled"
    77 | #define BUILD_ASSERT(EXPR, MSG...) _Static_assert(EXPR, "" MSG)
    | ^~~~~~~~~~~~~~
    c:\ncs\v2.0.0\zephyr\soc\arm\nordic_nrf\common\soc_nrf_common.h:229:2: note: in expansion of macro 'BUILD_ASSERT'
    229 | BUILD_ASSERT(!IS_ENABLED(CONFIG_PINCTRL) || \
    | ^~~~~~~~~~~~
    C:\ncs\v2.0.0\zephyr\drivers\spi\spi_nrfx_spim.c:518:2: note: in expansion of macro 'NRF_DT_CHECK_PIN_ASSIGNMENTS'
    518 | NRF_DT_CHECK_PIN_ASSIGNMENTS(SPIM(idx), 1, \
    | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
    C:\ncs\v2.0.0\zephyr\drivers\spi\spi_nrfx_spim.c:594:1: note: in expansion of macro 'SPI_NRFX_SPIM_DEVICE'
    594 | SPI_NRFX_SPIM_DEVICE(4);
    | ^~~~~~~~~~~~~~~~~~~~
    [140/431] Building C object zephyr/drivers/eeprom/CMakeFiles/drivers__eeprom.dir/eeprom_handlers.c.obj
    [141/431] Building C object zephyr/drivers/spi/CMakeFiles/drivers__spi.dir/spi_handlers.c.obj
    [142/431] Linking C static library zephyr\drivers\i2c\libdrivers__i2c.a
    [143/431] Linking C static library zephyr\drivers\sensor\libdrivers__sensor.a
    [144/431] Linking C static library zephyr\drivers\sensor\tmp116\libdrivers__sensor__tmp116.a
    ninja: build stopped: subcommand failed.
    FATAL ERROR: command exited with status 1: 'c:\ncs\toolchains\v2.0.0\opt\bin\cmake.EXE' --build 'c:\Projects\VHI\Code\oncodisc\build'
  • I have not tried this combo before, So I need to evaluate this myself, can you please upload your project here so that I can try to debug this myself here? It seems like the spi config and default pinctrl config are not allowing us to enable the SPIM2 instance, but not sure what extra needs to be done here.

  • I would rather not post my entire project publicly. Can I get a private link to upload it so only you can see and debug it?

Related