Sleep current during k_sleep()

In a nrf52840 project using Zephyr 3.0 following Zephyr drivers are used:

  • the modbus serial driver(RTU mode) which uses uart1 in interrupt mode
  • the lora-mac driver which uses spi1
  • serial logging on uart0

The application works fine but during inactivity periods the standby current is sometimes much too high: 520uA instead of ~10uA

I know that the uarts don't suspend automatically so immediately before k_sleep() I execute:

pm_device_action_run(uart0, PM_DEVICE_ACTION_SUSPEND);
pm_device_action_run(uart1, PM_DEVICE_ACTION_SUSPEND);

Without success I also tried:

  • executing nrf_uart_errorsrc_get_and_clear() for both uarts before k_sleep().
  • disable logging completely but as well without success.
  • "disable" all unneeded periphery in the device tree.
  • checked thread activity via the Zephyr Thread Analyzer but I could not find excessive activity.
  • executing pm_device_action_run()  for spi1, gpio0, gpio1 , ...
  • modbus_disable() during sleep
  •  ...

Do you have any hints how I could enclose or solve the problem?

Thx  Matthias

Parents Reply Children
  • Unfortunately I can't prevent transitions on RX without additional logic.

    But I had some progress ...

    By defining 'CONFIG_LOG_MODE_IMMEDIATE=y' and additionally waiting about 50ms (by a additional k_sleep() possibly  to write out the interrupt driven buffer) and thereafter put the uarts into suspend mode as follows:

    while (log_process(false));   // unneeded when CONFIG_LOG_MODE_IMMEDIATE=y

    pm_device_action_run(uart0, PM_DEVICE_ACTION_SUSPEND);
    pm_device_action_run(uart1, PM_DEVICE_ACTION_SUSPEND);
    nrf_uart_errorsrc_get_and_clear((NRF_UART_Type *)0x40002000);
    nrf_uart_errorsrc_get_and_clear((NRF_UART_Type *)0x40002800);
    k_sleep(several minutes)

    ... the standby current will go down to 12uA .

    But unfortunately when enabling the BLE stack by use of 'CONFIG_LOG_MODE_IMMEDIATE' results in following compiler error:
    error: static assertion failed: "Immediate logging on selected backend(s) not supported with the software Link Layer"

    So I had to be disabled logging completely by 'CONFIG_LOG=n' .

    And regarding your suggestion it seems the problem is not caused by the transitions on uart1 (which is used for RS485 Modbus) but by the console logging task.

    I can live with this solution but the 12uA are still a bit high according the nrf52840 product datasheet.

    Shouldn't it about 3uA in SYSTEM-ON mode when using the LF clock (RC or XTAL)?

    What could be the reason for the additional 9uA?

  • Take a look at this guide for optimizing power on nRF52: https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/optimizing-power-on-nrf52-designs

    If you are using a custom board, are there any other chips on the board which may be contributing to this current draw?

    How are you measuring the current? If you are using a PPK could you upload the power profiler trace?

  • Thx for suggestion but I already checked the power optimization guide you referred and took all into account. (Unfortunately there is no nrf52 specific guide for zephyr.)

    I ordered a PPK2 but will have to wait about a month - I will come back if I get it.

  • Is there a easy way to check the power state of ALL periphery of the SoC (nrf52840) under Zephyr?

    (possibly by using pm_device_state_get() but then I would have to define all 10th of device handles manually. Does it exist a easier solution?)

  • Finally I came to following solution to solve my standby current problem:

    1.)

    Running ths function with argument true/false before/after k_sleep() :

    void set_pm_mode_suspend(bool suspend)
    {
      const struct device *uart0 = device_get_binding(DT_LABEL(DT_NODELABEL(uart0)));
      const struct device *uart1 = device_get_binding(DT_LABEL(DT_NODELABEL(uart1)));
      const struct device *spi1 = device_get_binding(DT_LABEL(DT_NODELABEL(spi1)));

      enum pm_device_action action =
           suspend ? PM_DEVICE_ACTION_SUSPEND : PM_DEVICE_ACTION_RESUME;


    #ifdef CONFIG_LOG
      if (suspend)
        while (log_process(false)) ; // flush log buffer, otherwise it is lost
    #endif

      pm_device_action_run(uart1, action); // -1mA !!
      pm_device_action_run(uart0, action); // -3mA !!
      nrf_uart_errorsrc_get_and_clear((NRF_UART_Type *) DT_REG_ADDR(DT_NODELABEL(uart0)));
      nrf_uart_errorsrc_get_and_clear((NRF_UART_Type *) DT_REG_ADDR(DT_NODELABEL(uart1)));
      uart_irq_rx_disable(uart0);    // no input (eg. shell) used on console port
      if (suspend) uart_irq_rx_disable(uart1);
      pm_device_action_run(spi1, action);
    }

    Remark: with solution above, I could used normal console logging enabled using the default log buffering but mention that log messages from other tasks during set_pm_mode_suspend(true) will be lost.

    2.)

    Enabling a edge triggered GPIO button interrupt will caused additional 15uA current. Because of this level triggered interrupt is used by:

      gpio_pin_interrupt_configure_dt(&button, GPIO_INT_LEVEL_ACTIVE);

    Then disabling the interrupt in the interrupt handler by:

      gpio_pin_interrupt_configure_dt(&button, GPIO_INT_DISABLE);

    And reenable it later eg. by using a rtc timer or some application logic.

    3.)

    Before doing any current measurement, a power cycle is needed after flashing to disable the SWD interface!

    Forther the connection to SWD and the console RxD/TxD has to be disconnected if the power supply is not sourced from the programmers VCC supply (eg. when using the PPK2 power supply).

Related