Suspend and resume FreeRTOS (ARM Cortex-M4F)

simonhaines wrote on Thursday, April 04, 2019:

I am looking for advice on the best way to suspend/resume FreeRTOS for a low power application.

The application has has two operating modes: active and suspended. When active, it uses FreeRTOS with tickless idle to manage many threads and peripherals. This works really well.

When suspended, the application is waiting for an interrupt from one of two sources: a button press or an ADC compare trigger (low battery voltage detect). All other peripherals and interrupt sources are shut down.

The application is running on a NXP Kinetis part (ARM Cortex-M4F). To enter low power, it requires moving through clock configurations to arrive at a low power clock domain with reduced core/bus/flash clocks. Because of this, it is easier to suspend FreeRTOS and disable the SysTick until one of the interrupts is triggered, then move back through the clock configurations to arrive at the active clock domain, then re-enable the SysTick and resume FreeRTOS.

Is this the right thing to do? I could instead re-clock the SysTick with the low power clock and adjust the tick rate just to keep FreeRTOS running, but it is not needed at all.

To suspend FreeRTOS, I am entering a critical section, calling vTaskSuspendAll, disabling the SysTick by clearing the bit in the SysTick CTRL register, clearing any pending SVC/SysTick by writing the appropriate bits in the SCB ICSR register, and then exiting the critical section.

To resume FreeRTOS, I am entering a critical section, resetting the SysTick value to 0, enabling the SysTick by writing the bit in the SysTick CTRL register, calling xTaskResumeAll and exiting the critical section.

I understand by doing this I’m losing software timers, but these aren’t needed in low power anyway. Is this a valid approach? It seems to work so far, but I’m worried about lurking pitfalls. Advice appreciated.

rtel wrote on Thursday, April 04, 2019:

I’m not following the difference between the tickless idle you are
already using and the scheme you describe below. Is it that you are
using the default tickless idle at the moment, which uses the systick,
but you want to implement a different scheme that doesn’t use the
systick? If so, you can still use the tickless idle mode but you will
have to implement your own ‘suppress ticks and sleep’ function (there
are several examples of how to do this in the kernel download). The
default ‘suppress ticks and sleep’ implementation is a weak symbol (or
at least, can be overridden using a macro depending) to enable you to do
this.

richard_damon wrote on Thursday, April 04, 2019:

I think what he is doing is having 3 levels of operation, Normal with the tick interrupt, Tickless idle for longer but still sort of short idle periods, and then a deeper sleep mode where the system tick is actually stopped (and perhaps some very low power timer keeping coarse track of time).

simonhaines wrote on Thursday, April 04, 2019:

Sorry I didn’t make myself clearer. Richard Damon is correct, there are 3 levels: run, idle, and sleep. FreeRTOS is used in run and idle, but entering sleep is a lengthy operation requiring clock changes to consume much less power than idle (it is called ‘Very Low Power Stop’). Sleep is expected to last for days with all peripherals and interrupt sources turned off (except ADC, a button, and the RTC) and the processor stopped.

I think I’ve found something similar developed by Erich Styger here: https://mcuoneclipse.com/2019/01/20/freertos-how-to-end-and-restart-the-scheduler/

In any case, here is my implementation for suspending/resuming FreeRTOS. If I’ve made any rookie mistakes, I’d appreciate a heads-up.

void osSuspend(void) {
	vPortEnterCritical();
	vTaskSuspendAll();
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;  // Disable SysTick
	SCB->ICSR |= SCB_ICSR_PENDSVCLR_Msk | SCB_ICSR_PENDSTCLR_Msk;  // Clear any pending SysTick/Svc
	vPortExitCritical();
}

void osResume(void) {
	vPortEnterCritical();
	SysTick->VAL = 0;  // Reload SysTick
	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;  // Enable SysTick
	xTaskResumeAll();
	vPortExitCritical();
}