Blinky

In this demo, we want to toggle an LED with a single task. This is a very simple RTOS application which allows us to demonstrate some common thoughts we should consider for larger applications, too.

Application Overview

  1. After power on, the CPU starts with privileged access mode at the reset vector, the generated project will initialize the C runtime environment, enter the first function, called main(), and initialize the configured peripherals (see “Entry Function main()” in the process layout).

  2. In main(), we call MX_Blinky_Init() which initializes the RTOS, creates the first (and only) task, and passes the execution control to the Flexible Safety RTOS (see “RTOS Scheduler” and “Idle Task” in the process layout).

    • When creating the first task, the Flexible Safety RTOS requires that we assign a writable memory region to this task. This memory region contains at least the task stack. We call such a memory space “process” (see: “Blinky Task Data” in the process layout).

    ../_images/blinky-process.drawio.png

    The Blinky Process Layout

    Note

    All tasks we create before the RTOS is started, will start execution in privileged access mode. We will lower the task to unprivileged access mode in the task itself.

  3. Peripherals activates interrupt service handlers, which are started in privileged access mode by hardware design (see: “Systick Exception Handler” in the process layout).

  4. Using the HAL, and in consequence the peripheral registers, needs some preparation for tasks in unprivileged access mode. There are multiple ways in granting permission to the peripherals:

    a. creating and assigning a shared memory for the peripheral register memory region to the process

    b. adding an I/O handler task, running with privileged access mode

    c. temporarily raise single function call to run in privileged access mode

    Note: We choose method “c.” for the generated demo application.

Memory Map

With this highlevel view on the application, we can start organizing the memory. In general, we think on each single task and decide which data must be readable, writable, and/or executable.

For the single “Blinky Task” we want the following memory access permissions:

memory region

readable

writable

executable

Appliction Code

yes

no

yes

main data

no

no

no

Task Stack

yes

yes

no

main heap

no

no

no

main stack

no

no

no

GPIO Registers

yes

yes

no

../_images/blinky-memory.drawio.png

The Blinky Memory Map

Note

The Entry function main() is used during startup and is never activated again after passing control to the RTOS.

Demo Files

The demo implementation consists of the following files:

+- Blinky                          : demo application directory
|  +- App                          : ST standard application directory
|  |  +- app_blinky_callbacks.c    : RTOS callback interface
|  |  +- app_blinky.h              : Blinky interface header
|  |  +- app_blinky.c              : Blinky demo application

Find the detailed description of the application implementation within these files. The main functionality is located in app_blinky.c in the single task function:

static void BlinkyTask(void *p_arg)
{
    (void)p_arg;                       /* unused; prevent compiler warning         */

    SP_SET_USR_MODE();                 /* lower task to unprivileged access mode   */

    while(1) {                         /* the mandatory endless task loop          */

        SP_DO_PRIVIL(                  /* call next function in privileged mode    */
            BSP_LED_Toggle(LED_GREEN); /* to get access to peripheral registers    */
        )

        OSTimeDly(500u);               /* wait for 500 ticks                       */
    }
}