I want to join FreeRTOS and the deepest sleep mode in STM32, which doesn’t maintain on-chip RAM contest. I have an eval board STM3240G with external SRAM. The FreeRTOS version is 7.5.2. My app operates as follows:
1) initialization all of needed peripherals
2) creating 4 tasks blinking leds
3) starting scheduler ( vTaskStartScheduler() )
then app simply blinks leds and waits for external interrupt (from button)
button IRQ handler operates as follows:
it calls vTaskEndScheduler(); then it stores content of on-chip RAM in external RAM and calls system reset (integrally it simulates the deepest sleep mode)
after reset there is checked flag, which indicates MCU was hibernated
If it is true content of on-chip RAM is restored from external RAM and then it is called API function xPortStartScheduler.
And the issue is: sometimes app works after hibernation but sometimes - not. I think it depends on moment of pressing button. Has anybody any clues? Another issue is: when I replace xPortStartScheduler by xTaskResumeAll app always stop working. Thanks in advance for any suggestion how to solve the problem. Obviously all tips how to deal with “sleeping” FreeRTOS and “waking” it up with external RAM are welcome.
I’ve never tried it, but presumably you will also have to re-initialise the peripherals after the reset too.
That won’t do anything.
then it stores content of on-chip RAM in external RAM and calls system reset (integrally it simulates the deepest sleep mode)
It sounds like you have interrupted a running task (using the button interrupt), then saved the RAM context, but not saved the CPU context. The running task had a stack pointer, registers, program counter, etc. that are part of its context, and it can only be restarted if that is all saved.
On a Cortex-M the only place where the context of every task is completely saved is in the PendSV handler, but there is no place in there that you could easily add your own code. The PendSV handler does however call vTaskSwitchContext(), which in turn calls some trace macros. It might be an idea to perform the save to RAM from the traceTASK_SWITCHED_OUT() macro. To use the macro define it at the bottom of FreeRTOSConfig.h.
Your button interrupt could then set a flag to say “about to hibernate”, then force a context switch by calling portEND_SWITCHING_ISR( 1 ). That will cause the PendSV handler to run after the button handler, and the PendSV handler will cause the traceTASK_SWITCHED_OUT() macro to be called. In the macro, check the “about to hibernate” flag, and if it is set, perform the copy to external RAM.
Then when you wake from Hibernate you don’t want to call vTaskStartScheduler() again because that will create the Idle and possibly the timer task again. Instead, you could jump t the end of the scheduler start process by simply executing an “SVC 0” assembly instruction. That will cause the SVC handler to run, which is configured to start the highest priority task executing.
Also consider what will happen to your stack pointer (the one used by main()). It will get reset to the start of the stack when the system resets if it really is resetting - so you will not have to worry about resetting it. If however your real exit from sleep does not actually reset the CPU, and it leaves the stack pointer unchanged (I don’t think it does from memory), then you will have to reset it manually because the last time it was used will have been inside an interrupt.
I have no idea if this will work, or what the gotchas might be along the way, but that is how I would go about it as a first attempt because it would try to keep the context of everything consistent.