|
|
The EFM32 provides four energy saving modes called EM1 to EM4 with EM4 providing the greatest energy saving. However, the demo presented on this page uses the standard FreeRTOS Cortex M3 port so the kernel itself does not take advantage of these energy saving modes. Instead, some basic energy saving functionality has been added to the demo code (rather than the kernel code), but this means that only EM1 can be demonstrated. Using any other mode would cause the tick interrupt to stop. Future demos will make better use of the energy saving features available.
The development kit is fitted with an EFM32G890F128 microcontroller, which has 128KBytes of Flash and 16KBytes of RAM. It also includes the standard Cortex M3 Memory Protection Unit (MPU), making the EFM32 a suitable target for FreeRTOS-MPU based applications.
The demo uses:
The FreeRTOS Cortex M3 port includes a full interrupt nesting model. Interrupt priorities must be set in accordance with the instructions on the Customisation page for correct operation.
See also the FAQ My application does not run, what could be wrong?
The FreeRTOS zip file download contains the files for all the ports and demo application projects. It therefore contains many more files than used by this demo. See the Source Code Organization section for a description of the downloaded files and information on creating a new project.

The following tasks and tests are created in addition to the standard demo tasks:
The check task is included to provide visual feedback of the system status. It periodically queries all the standard demo tasks before toggling user LED 0. If user LED 0 is toggling every five seconds then the check task has not discovered any errors. If the toggle rate increases to every 200ms then an error has been discovered in at least one task.
The check task also places the microcontroller into energy saving mode EM1 for two out of every five seconds. The effect of this is described later on this page.
The LCD task writes a continually repeating series of patterns to the LCD display. The pattern will pause for two out of every five seconds due to the check task placing the microcontroller into energy saving mode.
This is a very simple task that just turns on user LEDs 8 to 15 in turn, before turning them off again. The LED pattern will pause for two out of every five seconds due to the check task placing the microcontroller into energy saving mode.
The demo application uses EM1 in two places:
The idle hook is called by the idle task, which will only run if there are no higher priority tasks that are able to do so (all higher priority tasks are in either the Blocked or Suspended state).
Having the idle task place the microcontroller into a low power state is a very easy and almost automatic way of saving power. Note however that this demo includes tasks that both share the idle task priority and never enter the blocked state. Removing this type of task will give more processing time to the idle task, and therefore save even more power (as the microcontroller will be placed into the power saving mode sooner and for longer).
The check task is the highest priority task in the system and will therefore always be the task that is selected to execute whenever it is not in the Blocked or Suspended state. Purely for demonstration reasons the check task in this demo will sit in a loop for two seconds during each iteration of its function: Each time the loop executes the check task places the microcontroller into energy saving mode EM1 - each time the tick interrupt occurs the microcontroller will leave EM1 and the loop will execute again. This has the effect of lowing the power consumption for two out of every five seconds, but also has the side effect of causing all the other tasks to pause execution for the full two seconds. The energy saving achieved can be viewed using the Advanced Energy Monitor graph on the development kits board controller display.
This sets the frequency of the RTOS tick.
See the kernel configuration documentation for full information on these configuration constants. It is vital that this documentation is read and understood!
If an interrupt service routine makes use of a FreeRTOS API function then the priority of the interrupt must be set. Leaving it at its default value will result in the interrupt executing with the highest possible priority.
The lowest priority on a Cortex M3 core is in fact 255 - however different Cortex M3 vendors implement a different number of priority bits and supply library functions that expect priorities to be specified in different ways.
Each port #defines 'portBASE_TYPE' to equal the most efficient data type for that processor. This port defines portBASE_TYPE to be of type long.
Note that vPortEndScheduler() has not been implemented.
Unlike most ports, interrupt service routines that cause a context switch have no special requirements and can be written as per the compiler documentation. The macro portEND_SWITCHING_ISR() can be used to request a context switch from within an ISR. An example interrupt service routine template is provided below. This should be used as a reference example.
Note that portEND_SWITCHING_ISR() will leave interrupts enabled. Also note that the priority of any interrupt that makes use of the FreeRTOS API (as does the template below) must be set to be equal to or lower than confgiMAX_SYSCALL_INTERRUPT_PRIORITY. For an interrupt priority to be lower than configMAX_SYSCALL_INTERRUPT_PRIORITY it must be numerically higher than configMAX_SYSCALL_INTERRUPT_PRIORITY because on the Cortex M3 numerically low priority values represent logically high interrupt priorities.
/* The interrupt service routine can be written as a standard C function. */
void vAnExampleISR( void )
{
long lTaskWokenByPost = pdFALSE;
unsigned long ulDataValue;
/* Remember to clear the interrupt source. */
....
/* Assume the interrupting peripheral supplies a value that needs to be
passed to a task. */
ulDataValue = ulGetValueFromPeripheral();
/* In this example, a queue is used to send data to a task. This could
equally be a counting or other type of semaphore. Note that lTaskWokenByPost
has already been initialised to pdFALSE. The queue must have already been
created before this interrupt service routine executes for the first time!. */
xQueueSendFromISR( xQueue, &ulDataValue, &lTaskWokenByPost );
/* If sending to the queue caused a task to unblock, and the task that was
unblocked has a priority equal to or above the currently executing task, then
lTaskWokenByPost will have been set to pdTRUE. Passing pdTRUE as the
parameter to portEND_SWITCHING_ISR() will cause a context switch to occur as
soon as the interrupt completes. */
portEND_SWITCHING_ISR( lTaskWokenByPost );
}