FreeRTOS Task Scheduling and Low-Power Mode Integration in LoRa Communication

I’m working on implementing low-power functionality in an STM32 project using FreeRTOS and a LoRa module (SX1262). My project includes two tasks: a default task that simply delays for 1000 ms in an infinite loop, and a LoRa task responsible for setting LoRa parameters, initializing the radio module, and handling transmission logic. The LoRa task initializes system settings, sets up the radio, and enters a loop where the task is suspended on a queue and starts a transmission operation upon each transmission queue event. The application software timer triggers a transmission event every 3 seconds using « osMessagePut(ApplicationEventsQueueHandle,doNow ,10); ». I’ve activated tickless mode and implemented pre-sleep and post-sleep processing functions to manage entering and exiting low-power STOP mode. The pre-sleep processing function deinitializes SPI and sets the RTC to wake up the system every 10 seconds, while the post-sleep function reinitializes the system clock and SPI. Despite this setup, during debugging, the system never enters the LoRa task and oscillates between the default task and the pre/post-sleep processing functions. I’m seeking advice on why the LoRa task is not being executed and how to ensure it runs as expected in the context of a low-power implementation.
Here is the implementation of the PreSleepProcessing and PostSleepProcessing functions:

void PreSleepProcessing(uint32_t ulExpectedIdleTime)
{
	/* Called by the kernel before it places the MCU into a sleep mode because
	  configPRE_SLEEP_PROCESSING() is #defined to PreSleepProcessing().

	  NOTE:  Additional actions can be taken here to get the power consumption
	  even lower.  For example, peripherals can be turned off here, and then back
	  on again in the post sleep processing function.  For maximum power saving
	  ensure all unused pins are in their lowest power state. */
	HAL_DBGMCU_EnableDBGStopMode();
	LP_GPIO_Init();
	HAL_SPI_DeInit(&hspi1);
	HAL_SPI_MspDeInit(&hspi1);
	HAL_SuspendTick();
 	HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0x500B, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
	//STOP2 MODE
	HAL_PWREx_EnterSTOP2Mode(PWR_SLEEPENTRY_WFI);
}

void PostSleepProcessing(uint32_t ulExpectedIdleTime)
{
	/* Called by the kernel when the MCU exits a sleep mode because
	  configPOST_SLEEP_PROCESSING is #defined to PostSleepProcessing(). */

	/* WAKE UP WITH RTC */
 	HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
	MX_GPIO_Init();
	HAL_ResumeTick();
	SystemClock_Config();
	HAL_SPI_Init(&hspi1);
	HAL_SPI_MspInit(&hspi1);
}

Is the timer (that sends a message to the queue to unblock the LoRa task) firing? If yes, is the send operation successful? Does the same behavior happen when you let the application run without debugging?

No the timer is not firing, and even without debugging, the transmission operation is not successful. I cannot see any signals on SDR Sharp. However, when I comment out the low power-related code, the timer fires, and I can see signals every 3 seconds as expected:

This is the issue then. Is it a hardware timer? If yes, can you figure out why is it getting disabled because of entering sleep mode - may be you need to reconfigure it after waking up?

No it’s a periodic software timer.

Do you mean the FreeRTOS software timer? If yes, can you check if the SysTick is firing. Also, check the value of xNextExpireTime.

Yes the Systick is firing and the value of xNextExpireTime is 0;

That does not seem correct. Is it same before going into sleep and after waking up?

Just a sanity check your software timer is set up to auto-reload, right?

When I start debugging, the system first hits the breakpoint at the default task, then hits the breakpoint at the function where xNextExpireTime is set from prvGetNextExpireTime(&xListWasEmpty), which returns 0. Next, prvProcessTimerOrBlockTask evaluates a condition and calls portYIELD_WITHIN_API(). The system then hits a breakpoint in LoRaApplicationTask in the initRadio function, which contains an osDelay(20). Following this, it directly hits the breakpoint at the preSleepProcessing function. After 10 seconds, the RTC wakes the system, hitting a breakpoint in postSleepProcessing, then in defaultTask before re-entering low power mode. Over time, the system only hits the breakpoint in defaultTask. So to sum up, it only hits the breakpoint in xNextExpireTime once.

I think the problem resides in the osDelay in the initRadio function, as it causes the system to enter low power mode without completing the code execution in the LoRaApplicationtask, preventing the timer from starting. Could this explain the absence of signal visualization ? Is there a solution for this, considering I am using the built-in functionality of the tickless mode?

yes the sw timer is set up to auto-reload

Hi @SaDe,

During STOP mode, the SysTick timer stops. Because FreeRTOS is using SysTick for all of its timekeeping, all FreeRTOS timekeeping stops too. Thus, the call to osDelay() will “never” return because no time elapses as far as FreeRTOS is concerned.

Your PreSleepProcessing() function must use STOP mode conditionally. The simplest condition is when no tasks are waiting for a timeout. You can detect this condition by calling eTaskConfirmSleepModeStatus(). If no tasks are awaiting a timeout (eNoTasksWaitingTimeout), you can use STOP mode. If there are tasks waiting for a timeout (eStandardSleep), then do not use STOP mode.

As an alternative, if you want to be able to use STOP mode even when some tasks are waiting for a timeout, you’ll need to provide the FreeRTOS tick from a timer that keeps running in stop mode. If you have an available LPTIM, you can use lptimTick.c.

1 Like