Morse Micro IoT SDK  2.9.7
Host Power Save

The Morse Micro IoT SDK provides out-of-the-box support for host side power saving for mm-ekh08-u575 and mm-ekh08-wb55 reference platforms. This is achieved using the tickless idle mechanism of FreeRTOS.

Overview

In normal operation, FreeRTOS relies on a periodic tick for the passage of time. In the MM-IoT-SDK, this tick is configured with a 1 ms period. However, when all FreeRTOS tasks (except the idle task) are blocked FreeRTOS can opt to enter tickless sleep for a specified period of time. In tickless sleep the periodic tick (normally provided by SysTick) is suspended and the MCU can enter a low power sleep mode until an interrupt occurs. It is necessary to use an MCU timer to ensure that the MCU does not sleep for longer than the period requested by FreeRTOS.

The sleep implementation provided by the MM-IoT-SDK is split into two parts, the hook function vPortSuppressTicksAndSleep() in hooks.c, which is platform independent, and platform-specific HAL functions.

There are three sleep states supported by the HAL: disabled, shallow sleep and deep sleep. On STM32 platforms the shallow sleep corresponds to the MCU Sleep mode, while deep sleep corresponds to Stop 2 mode. Because deep sleep requires more time to wake up, there are restrictions placed on when this mode may be entered.

Implementation

A custom implementation of the function vPortSuppressTicksAndSleep() is provided as part of the MM-IoT-SDK in hooks.c. This is invoked by FreeRTOS to enter tickless sleep. This function begins by invoking the HAL-specific function mmhal_sleep_prepare(), to prepare the system for entry to sleep. This function makes the decision as to which mmhal_sleep_state to enter and disables the systick, if applicable to the sleep state.

vPortSuppressTicksAndSleep() then confirms with FreeRTOS that it still is able to enter sleep by invoking eTaskConfirmSleepModeStatus(). If this returns eAbortSleep then the sleep process is aborted and the HAL function mmhal_sleep_abort() is invoked. Otherwise, mmhal_sleep() is invoked to enter sleep. This function is responsible for scheduling a timer so that it does not sleep for longer than the specified expected idle time. However, it may sleep for a shorter period of time if woken by an interrupt.

Following sleep, the FreeRTOS function vTaskStepTick() is invoked to advance the FreeRTOS time based on the elapsed sleep time, and then the HAL function mmhal_sleep_cleanup() is invoked to re-enable interrupts.

The diagram below summarises the process of entering and exiting tickless idle for both the mm-ekh08-wb55 (STM32WB55 host) and mm-ekh08-u575 (STM32U575 host) platforms.

Deep sleep decision logic

The decision whether to enter deep or shallow sleep is made based on several factors:

  • The sleep duration must be long enough that entering deep sleep is worthwhile, since it takes time to enter and exit deep sleep.
  • There must be no ongoing perhipheral activity (e.g., DMA transfers, UART transfers, etc.).
  • There must be no higher-layer veto of deep sleep.

A HAL API is provided to allow higher layers to prevent entry into deep sleep. This is via the functions mmhal_set_deep_sleep_veto() and mmhal_clear_deep_sleep_veto(). There may be up to 32 veto sources, each of which is allocated a unique identifier. These IDs are allocated to various subsystems (see mmhal_veto_id). An application may use veto IDs MMHAL_VETO_ID_APP_MIN to MMHAL_VETO_ID_APP_MAX (inclusive). It may use there, for example, to protect important operations where timing is critical.

Deep sleep exceptions

Currently the deep sleep state is not supported by the following subsystems:

Enabling either of these subsystems will cause a veto of deep sleep. As such neither the cli nor emmet example applications enter deep sleep.

Debugging during deep sleep

In normal operation, when the MCU is in deep sleep it is not possible to attach a debugger (e.g., ST-Link). However, it is possible to enable a special mode in the MCU that keeps the clocks required by the debugger active, at the expense of higher power consumption.

The MM-IoT-SDK provides a build define to enable/disable this mode called ENABLE_DEBUG_IN_STOP_MODE. If this is set to 0 then the debugger will not attach in stop mode (i.e., deep sleep). If set to a non-zero value then it will be possible to attach a debugger even if the MCU is in deep sleep. The default value for this is 1 (i.e., enabled). Note that with this flag enabled the MCU will not be able to achieve its lowest possible power consumption.

If ENABLE_DEBUG_IN_STOP_MODE is not enabled then it is not possible to start OpenOCD while the MCU is in deep sleep unless the MCU is held in reset. This can be achieved by adding -c "set CONNECT_ASSERT_SRST 1" to the OpenOCD command line before -f openocd.cfg. Be warned that this flag will cause the device to be reset when OpenOCD is started, potentially causing it to lose any volatile state.

Note, for EKH05 platforms, debugging in stop mode can optionally be controlled via OpenOCD. Debugging during deep sleep is enabled by default, and can be disabled by adding -c "set DISABLE_DEEP_SLEEP_DEBUG 1" to the OpenOCD command line before -f openocd.cfg. As above, you may need to hold the MCU in reset on connection if debugging in deep sleep is disabled. You can enable both DISABLE_DEEP_SLEEP_DEBUG and CONNECT_ASSERT_SRST together through a single helper variable -c "set MM_PROGRAM 1", which must be passed to the OpenOCD command line before -f openocd.cfg. For best power results, it is recommended to disable debugging in deep sleep on both the MCU, by passing ENABLE_DEBUG_IN_STOP_MODE=0 when compiling, and in OpenOCD, by passing -c "set DISABLE_DEEP_SLEEP_DEBUG 1" as described above.

See also Unable to connect to the MCU with OpenOCD in the Getting Started guide.