Migration from a bare metal application to an RTOS application

From SEGGER Wiki
Jump to: navigation, search

The classic way of designing embedded systems does not use the services of an RTOS, which is also called bare metal application. Typically, no real time kernel is used, so interrupt service routines (ISRs) are used for the real-time parts of the application and for critical operations (at interrupt level). This type of system is typically used in small, simple systems or if real-time behavior is not critical.

This article describes how to migrate from such a bare metal application to an RTOS application.

Bare metal

Bare metal applications often use a superloop concept. A hardware timer interrupt routine increments a counter variable. main() executes an endless loop which checks for certain timeouts. In this example actions are executed at 10, 100 and 1000 system ticks.

This concept has several disadvantages. It consumes 100% CPU time because the counter variable is polled continuously. The real-time behavior of Task10() is not guaranteed when the Task1000() or Task100() routine consume more than 10 system ticks. It is not possible to give Task10() a higher execution priority. Interrupts are the only possibility to use a higher execution priority.

The following example shows a simple superloop application:

static volatile unsigned long Time;

void SystemTick_ISR(void) {
  Time++;
}

static void Task1000(void) {
  DoSomething();
}

static void Task100(void) {
  DoSomething();
}

static void Task10(void) {
  DoSomething();
}

int main(void) {
  OS_InitHW();
  //
  // Start superloop
  //
  while (1) {
    if ((Time % 1000) == 0) {
      Task1000();
    }

    if ((Time % 100) == 0) {
      Task100();
    }

    if ((Time % 10) == 0) {
      Task10();
    }
  }
  return 0;
}

Interrupt service routines (ISRs) are used for the real-time parts of the application (at interrupt level).

superloop.png

RTOS

The same application can be easily migrated to an RTOS application. As a first step each routine, which is called at different time intervals, is implemented as a separate task. With OS_TIME_GetTicks() and OS_TASK_DelayUntil() the DoSomething() routine is executed at the desired time interval.

Now it is possible to control the real time behavior of Task1000(), Task100() and Task10(). In this example Task10() has the highest task priority of 102 and with it the highest real time behavior. Even if Task1000() runs for more than 10 system ticks the RTOS will interrupt Task1000() and execute Task10() when its timeout has expired.

#include "RTOS.h"

static OS_STACKPTR int Stack1000;
static OS_STACKPTR int Stack100;
static OS_STACKPTR int Stack10;
static OS_TASK         TCB1000;
static OS_TASK         TCB100;
static OS_TASK         TCB10;

static void Task1000(void) {
  OS_TIME t0;
  
  t0 = OS_TIME_GetTicks();
  while (1) {
    DoSomething();
    t0 += 1000;
    OS_TASK_DelayUntil(t0);
  }
}

static void Task100(void) {
  OS_TIME t0;
  
  t0 = OS_TIME_GetTicks();
  while (1) {
    DoSomething();
    t0 += 100;
    OS_TASK_DelayUntil(t0);
  }
}

static void Task10(void) {
  OS_TIME t0;
  
  t0 = OS_TIME_GetTicks();
  while (1) {
    DoSomething();
    t0 += 10;
    OS_TASK_DelayUntil(t0);
  }
}

int main(void) {
  OS_Init();    // Initialize embOS
  OS_InitHW();  // Initialize required hardware
  OS_TASK_CREATE(&TCB1000, "Task 1000", 100, Task1000, Stack1000);
  OS_TASK_CREATE(&TCB100,  "Task 100",  101, Task100,  Stack100);
  OS_TASK_CREATE(&TCB10,   "Task 10",   102, Task10,   Stack10);
  OS_Start();   // Start embOS
  return 0;
}

This application already saves CPU load. The RTOS executes the routine OS_Idle() instead of polling a counter variable when Task1000(), Task100() and Task10() are not currently executed. OS_Idle() starts a CPU low power mode which runs until the next timer interrupt. The timer interrupt checks whether a timeout has expired and starts the scheduler which executes the according task routine.

void OS_Idle(void) {
  while (1) {
    StartLowPowerMode();
  }
}

In a multitasking system, there are different ways to distribute CPU time among different tasks with different execution priorities.

Multitasking.png

Polling vs. Event based programming

The above example executes all tasks at a specific time interval even when a task currently doesn't need to execute any application code. If possible, this polling based RTOS programming should be avoided because it wastes CPU time.

Instead a task should be executed only when there was an according event. This event can be triggered by an interrupt or another task. The RTOS will execute the task only when such an event is triggered. With it the task does not consume any CPU time when it waits for such an event.

Please have a look in the embOS manual for more details.

The application could be rewritten as:

#include "RTOS.h"

static OS_STACKPTR int Stack1000;
static OS_STACKPTR int Stack100;
static OS_STACKPTR int Stack10;
static OS_TASK         TCB1000;
static OS_TASK         TCB100;
static OS_TASK         TCB10;

void _ButtonPressed_ISR(void) {
  OS_INT_Enter();
  OS_TASKEVENT_Set(&TCB1000, 1);
  OS_INT_Leave();
}

void _UART_RX_ISR(void) {
  OS_INT_Enter();
  OS_TASKEVENT_Set(&TCB100, 1);
  OS_INT_Leave();
}

void _HW_Timer_ISR(void) {
  OS_INT_Enter();
  OS_TASKEVENT_Set(&TCB10, 1);
  OS_INT_Leave();
}

static void Task1000(void) {
  while (1) {
    OS_TASKEVENT_GetBlocked(1);
    HandleButtonPressed();
  }
}

static void UartRxTask(void) {
  while (1) {
    OS_TASKEVENT_GetBlocked(1);
    HandleReceivedUARTCharacter();
  }
}

static void Task10(void) {
  while (1) {
    OS_TASKEVENT_GetBlocked(1);
    HandleHWTimer();
  }
}

int main(void) {
  OS_Init();    // Initialize embOS
  OS_InitHW();  // Initialize required hardware
  OS_TASK_CREATE(&TCB1000, "Task 1000", 100, Task1000,   Stack1000);
  OS_TASK_CREATE(&TCB100,  "UartRxTask", 101, UartRxTask, Stack100);
  OS_TASK_CREATE(&TCB10,   "Task 10",   102, Task10,     Stack10);
  OS_Start();   // Start embOS
  return 0;
}

UartRxTask() is executed only when a new UART character is received and does not waste any precious computation time and energy.

CommunicationISRTask.png