6  RTOS

RTOS = Realtime Operating System

Multitasking ohne RTOS

Motivation

6.1 FreeRTOS

Multitasking mit RTOS

  • FreeRTOS als freies Projekt unter MIT Lizenz von Richard Barry gibt es seit 2003.

    https://www.freertos.org

  • Kleines Echtzeit-Betriebssystem, das viele verschiedene Entwicklungsumgebungen und CPU Architekturen unterstützt. Siehe dazu im FreeRTOS Quelltext das Verzeichnis:

    FreeRTOSv10.1.1/FreeRTOS/Source/portable

  • Von Amazon 2017 aufgekauft, deshalb nun “Amazon FreeRTOS” im Bereich der “Amazon Web Services” (AWS).

    https://aws.amazon.com/blogs/opensource/announcing-freertos-kernel-v10

Quelltext von FreeRTOS in Cube Bibliothek

  • Middlewares/Third_Party/FreeRTOS/

CMSIS RTOS “Wrapper” um FreeRTOS

  • Siehe im Verzeichnis Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/
  • Dateien cmsis_os.c und cmsis_os.h.

Variablennamen

Präfixe

  • u - unsigned
  • p - pointer
  • uc - uint8_t
  • v - void
  • pc - pointer to char
  • pv - pointer to char
  • s - int16_t
  • l - int32_t
  • x - BaseType_t (“bester” Integer Datentyp auf Architektur)

Tasks

[BARRY], Kap. 3

Zustandsdiagramm

  • Im SUSPENDED Zustand ist die Task nicht für den Scheduler verfügbar.

  • Aus dem BLOCKED Zustand kommt man nur durch

    1. Zeitliche Events, oder
    2. Synchronisations Events

Zustandsdiagramm einer Task ([BARRY], S. 67)

Task Timing

Zeitdiagramm zum Taskwechsel ([BARRY], S. 62)

Task Priorität

  • 0 ist die niedrigste Priorität
  • Bereich: 0 … configMAX_PRIORITIES-1

Tasks anlegen und löschen

  • xTaskCreate()
  • vTaskDelete()
  • vTaskStartScheduler()

Beispiel:

  /* Args: Ptr to C fkt, descriptive name, stack depth, task parameter void*, 
           priority, ptr to task handle 
           

     Returns PASS or FAIL. */

  xTaskCreate( prvQueueReceiveTask, "Rx", configMINIMAL_STACK_SIZE, NULL,
               mainQUEUE_RECEIVE_TASK_PRIORITY, NULL );

  ...

  /* sample task */
  prvQueueReceiveTask(void *pvParameters);

Tasks steuern

  • vTaskDelay()
  • vTaskDelayUntil()
  • vTaskSuspend()
  • vTaskResume()
  • vTaskResumeFromISR()

Timer

([BARRY], Kap. 5)

“One-shot” und “Periodic timers”

Zustände: Dormant, Running

API:

  • xTimerCreate()
  • xTimerDelete()
  • xTimerStart()
  • xTimerDelete()
  • xTimerStop()
  • xTimerReset()
  • xTimerChangePeriod()

Callback Funktion: void ATimerCallback( TimerHandle_t xTimer );

Beispiel für STM32F100 mit Timer, Queue und zwei Tasks:

https://www.freertos.org/FreeRTOS-for-Cortex-M3-STM32-STM32F100-Discovery.html

Synchronisationsmittel

  • queues ([BARRY] Kap. 4 - Queues)
    • Mailboxen (Sonderfall einer Queue der Länge 1, [BARRY], Kap. 4.7, S. 144)
  • binary semaphores ([BARRY], Kap. 6 - Interrupt Management)
  • counting semaphores
  • mutexes ([BARRY], Kap. 7 - Resource Management)
  • recursive mutexes
  • event groups ([BARRY], Kap. 8 - Event Groups)
  • direct to task notifications ([BARRY], Kap. 9 - Task Notifications)
  • gatekeeper task ([BARRY], Kap. 7 - Resource Management)

In [BARRY], Kap 4, S. 105 (Queue Management) sieht man einen Ablauf von mehreren Send/Receive Operationen.

Semaphore

([BARRY], Kap. 6 - Interrupt Management)

  • vSemaphoreCreateBinary (Queue der Länge 1)
  • vSemaphoreCreateCounting
  • vSemaphoreCreateMutex
  • vSemaphoreTake (synonym: Take, P, decrement, down)
  • vSemaphoreGive (synonym: Give, V, incr, up)
  • vSemaphoreGiveFromISR

Siehe Abb. in [BARRY], S. 194 (Synchronisieren einer Task mit einem Interrupt über eine binäre Semaphore).

Queue Management

([BARRY], Kap. 4 - Queues)

  • Queues sind FIFO buffer.

  • Daten werden in die Queue kopiert.

  • Können von mehreren Tasks aus angesprochen werden.

  • Blockierzeit beim Lesen und beim Schreiben einstellbar.

    • Mehrere Schreiber können blockieren, wenn die Queue voll ist. Wenn Platz frei ist, wird nur ein Schreiber aktiviert.

    • Falls mehrere Leser blockieren, dann wird nur ein Leser bei Datenempfang aktiviert.

  • Queues können gruppiert werden (“queue sets”).

API

  • xQueueCreate
  • xQueueSend
  • xQueueReceive

Mutex

([BARRY], Kap. 7 - Resource Management) * mutex = MUTual EXclusion

  • Spezielle Form einer binären Semaphore um auf eine Resource zuzugreifen, die von mehreren Tasks geteilt wird.

  • Beispiel: vPrintString()

  • xSemaphoreCreateMutex()

  • Abb. [BARRY], S. 245

  • Prioritätsinversion ([BARRY], S. 250)

  • Rekursive Mutexe

  • Gatekeeper Tasks

    • Gegenseitiger Ausschluss ohne Prioritätsinversion oder Deadlock
    • Dem Gatekeeper “gehört” die Resource
    • “Klienten” des Gatekeeper senden Nachricht über Queue
    • vPrintString() jetzt mit Gatekeeper

Signale

Heap Management

[BARRY], Kap. 2

  • pvPortMalloc()
  • vPortFree()

Verschiedene Strategien:

  • heap1: deterministisch (kein free())
  • heap2: best fit (nicht deterministisch)
  • heap3: stdlib malloc() und free()
  • heap4: first fit algo

API

FreeRTOS API

6.2 CMSIS RTOS

Dieser Abschnitt dient als Übersicht zum CMSIS RTOS API. Dieses API wird für RTOS-Beispiele der Cube-Bibliothek verwendet. Die darunterliegende Implementierung ist mit FreeRTOS gemacht. Fast der komplette Abschnitt wurde aus der Keil CMSIS RTOS Doku entnommen, siehe [CMSISRTOS]. Wer das Thema auch noch in einem Buch nachlesen möchte, kann in [MARTIN] Kap. 9 oder [YIUDG] Kap. 19 schauen.

CMSIS RTOS API (1/3)

CMSIS RTOS API (2/3)

CMSIS RTOS API (3/3)

6.2.1 API

API Struktur

     Funktion               Aus ISR aufrufbar   Kategorie
     ---------------------|-------------------|-----------------

     osThreadCreate         nein                Task Management           
     osThreadGetId          nein
     osThreadSetPriority    nein
     osThreadTerminate      nein
     osThreadYield          nein

     osSignalClear          nein                 ITC: Signale
     osSignalSet            ja
     osSignalWait           nein

     osSemaphoreCreate      nein                 ITC: Semaphore
     osSemaphoreDelete      nein
     osSemaphoreWait        nein
     osSemaphoreRelease     ja 

     osMutexCreate          nein                 ITC: Mutex
     osMutexDelete          nein
     osMutexWait            nein
     osMutexRelease         nein

     osMailAlloc            ja                   ITC: Mail
     osMailCreate           nein
     osMailFree             ja
     osMailGet              ja
     osMailPut              ja
    
     osMessageCreate        nein                 ITC: Message Queue
     osMessageGet           ja
     osMessagePut           ja

     osPoolCreate           nein                 Memory Pool
     osPoolAlloc            ja
     osPoolFree             ja

     osTimerCreate          nein                 Timer
     osTimerDelete          nein
     osTimerStart           nein
     osTimerStop            nein

     osDelay                nein                  Delay
     osKernelSysTick        nein 

Inter-Thread Communication (ITC) and Resource Sharing

  • Signal Events Synchronize threads using signals.

  • Message Queue Exchange messages between threads in a FIFO-like operation.

  • Memory Pool Manage thread-safe fixed-size blocks of dynamic memory.

  • Mail Queue Exchange data between threads using a queue of memory blocks.

  • Mutexes Synchronize resource access using Mutual Exclusion (Mutex).

  • Semaphores Access shared resources simultaneously from different threads.

6.2.2 Threads

Thread Status

Threads als enum-Typen

   typedef enum
   {
          THREAD_1 = 0,
          THREAD_2
   } Thread_TypeDef;

osThreadDef

  • Interessante Frage: Was ist osThreadDef eigentlich genaugenommen?

  • Argumente: name, thread, priority, instances, stack

  • Beispiel

   osThreadDef(THREAD_1, LED_Thread1, osPriorityNormal, 0, configMINIMAL_STACK_SIZE);

osThreadCreate

Aus [CMSISRTOS]:

Remove the thread function from the active thread list. If the thread is currently RUNNING the execution will stop.

  • Startet die Ausführung eines Threads

  • Gegenteil: osThreadTerminate()

  • Argumente: Ptr to thread def, ptr to args

   osThreadId LEDThread1Handle;

   LEDThread1Handle = osThreadCreate(osThread(THREAD_1), NULL);

osKernelStart();

Kernel starten.

Thread functions

   static void LED_Thread1(void const *argument);

   ...

   static void LED_Thread1(void const *argument)
   {
      ...
   }

osThreadSuspend

  • Suspend thread1
     osThreadSuspend(thread_id);
  • Suspend own thread
     osThreadSuspend(NULL);    // osThreadYield(void)

osThreadResume

   osThreadResume(thread_id); 

6.2.3 Signal

osSignalSet

Aus [CMSISRTOS]:

Set the signal flags of an active thread. This function may be used also within interrupt service routines.

Returns previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters.

Ein Signal ist ein diesem Fall ein 32-Bit Integer (int32_t).

   osSignalSet( LED1_ThreadId, BIT_0);

osSignalClear

Clear the signal flags of an active thread.

      signals = osSignalClear (thread_id, 0x01);

osSignalWait

Aus [CMSISRTOS]:

Suspend the execution of the current RUNNING thread until all specified signal flags with the parameter signals are set. When the parameter signals is 0 the current RUNNING thread is suspended until any signal is set. When these signal flags are already set, the function returns instantly. Otherwise the thread is put into the state WAITING. Signal flags that are reported as event are automatically cleared.

The argument millisec specifies how long the system waits for the specified signal flags. While the system waits the thread calling this function is put into the state WAITING. The timeout value can have the following values:

  • when millisec is 0, the function returns instantly.
  • when millisec is set to osWaitForever the function will wait for an infinite time until a specified signal is set.
  • all other values specify a time in millisecond for a timeout.
   for (;;)  
   {
       event = osSignalWait( BIT_1 | BIT_2, osWaitForever);
       if (event.value.signals == (BIT_1 | BIT_2)) 
       {
              BSP_LED_Toggle(LED2); 
              ...

Es gibt auch event.status:

    if (evt.status == osEventSignal)  {
        // handle event status
        ...

6.2.4 Semaphore

Semaphore

Macro osSemaphoreDef. SEM steht für die neu zu definierende Semaphore.

osSemaphoreDef(SEM); 

osSemaphoreCreate

Argumente: Ptr auf SemaphoreDef, Anzahl Resourcen

Aufgabe: Was ist osSemaphoreId? (Tags verwenden)

osSemaphoreId osSemaphore;

...

osSemaphore = osSemaphoreCreate(osSemaphore(SEM) , 1);

...

// Semaphore z.B. als Argument an osThreadCreate uebergeben
SemThread1Handle = osThreadCreate(osThread(SEM_Thread1), (void *) osSemaphore);

osSemaphoreWait

osSemaphoreWait(sema, millis);

Aus [CMSISRTOS]:

Wait until a Semaphore token becomes available. When no Semaphore token is available, the function waits for the time specified with the parameter millisec.

The argument millisec specifies how long the system waits for a Semaphore token to become available. While the system waits the thread that is calling this function is put into the state WAITING. The millisec timeout can have the following values:

  • when millisec is 0, the function returns instantly.

  • when millisec is set to osWaitForever the function will wait for an infinite time until the Semaphore token becomes available.

  • all other values specify a time in millisecond for a timeout.

The return value indicates the number of available tokens (the semaphore count value). If 0 is returned, then no semaphore was available. The value -1 is returned in case of incorrect parameters.

osSemaphoreId semaphore;
...

if (osSemaphoreWait(semaphore , 100) == osOK)
        ...

osSemaphoreRelease

Release a Semaphore token. This increments the count of available semaphore tokens.

   osSemaphoreRelease(semaphore);

6.2.5 Mutex

Mutex

Aus [CMSISRTOS]:

Synchronize resource access using Mutual Exclusion (Mutex).

Mutual exclusion (widely known as Mutex) is used in various operating systems for resource management. Many resources in a microcontroller device can be used repeatedly, but only by one thread at a time (for example communication channels, memory, and files). Mutexes are used to protect access to a shared resource. A mutex is created and then passed between the threads (they can acquire and release the mutex).

A mutex is a special version of a semaphore. Like the semaphore, it is a container for tokens. But instead of being able to have multiple tokens, a mutex can only carry one (representing the resource). Thus, a mutex token is binary and bounded. The advantage of a mutex is that it introduces thread ownership. When a thread acquires a mutex and becomes its owner, subsequent mutex acquires from that thread will succeed immediately without any latency. Thus, mutex acquires/releases can be nested.

#define mutexTWO_TICK_DELAY  ((uint32_t) 2)

static osMutexId os_mutex_id;

osMutexDef(os_mutex);

osMutexCreate

Create and initialize a Mutex object.

os_mutex_id = osMutexCreate(osMutex(os_mutex));

osMutexWait

Aus [CMSISRTOS]:

 Wait until a Mutex becomes available. If no other thread has obtained the
 Mutex, the function instantly returns and blocks the mutex object.

 The argument millisec specifies how long the system waits for a mutex.
 While the system waits the thread that is calling this function is put
 into the state WAITING. The millisec timeout can have the following
 values:

 - when millisec is 0, the function returns instantly.
 - when millisec is set to osWaitForever the function will wait for an
   infinite time until the mutex becomes available.
 - all other values specify a time in millisecond for a timeout.
if (osMutexWait(os_mutex_id, mutexTWO_TICK_DELAY) != osOK)
{
   ...

osMutexRelease

Aus [CMSISRTOS]:

Release a Mutex that was obtained with osMutexWait. Other threads that currently wait for the same mutex will be now put into the state READY.

if (osMutexRelease(os_mutex_id) != osOK)
{
    BSP_LED_Toggle(LED3);
}

osMutexDelete

Aus [CMSISRTOS]:

Delete a Mutex object. The function releases internal memory obtained for Mutex handling. After this call the mutex_id is no longer valid and cannot be used. The Mutex may be created again using the function osMutexCreate.

if (mutex_id != NULL)  {
   status = osMutexDelete(mutex_id);
   if (status != osOK)  {
      ... 

6.2.6 Mail Queue

Mail Queue

Aus [CMSISRTOS]:

A mail queue resembles a Message Queue, but the data that is being transferred consists of memory blocks that need to be allocated (before putting data in) and freed (after taking data out). The mail queue uses a Memory Pool to create formatted memory blocks and passes pointers to these blocks in a message queue. This allows the data to stay in an allocated memory block while only a pointer is moved between the separate threads. This is an advantage over messages that can transfer only a 32-bit value or a pointer. Using the mail queue functions, you can control, send, receive, or wait for mail.

osMailQId mailId;
#define MAIL_SIZE        (uint32_t) 1

...

typedef struct  // Mail object structure 
{ 
     uint32_t var1; // var1 is a uint32_t 
     uint32_t var2; // var2 is a uint32_t 
     uint8_t  var3; // var3 is a uint8_t 
} Amail_TypeDef;   

...

// Define Mail Queue (Macro)
osMailQDef(mail, MAIL_SIZE, Amail_TypeDef);

...

// Create Mail Queue
mailId = osMailCreate(osMailQ(mail), NULL);

osMailAlloc

Aus [CMSISRTOS]:

Allocate a memory block from the mail queue that is filled with the mail information.

The argument queue_id specifies a mail queue identifier that is obtain with osMailCreate.

The argument millisec specifies how long the system waits for a mail slot to become available. While the system waits the thread calling this function is put into the state WAITING. The millisec timeout can have the following values:

  • when millisec is 0, the function returns instantly.
  • when millisec is set to osWaitForever the function will wait for an infinite time until a mail slot can be allocated.
  • all other values specify a time in millisecond for a timeout.
pTMail = osMailAlloc(mailId, osWaitForever);
pTMail->var1 = ProducerValue1;
...

osMailPut

Aus [CMSISRTOS]:

Put the memory block specified with mail into the mail queue specified by queue.

Status and Error Codes

  • osOK: the message is put into the queue.
  • osErrorValue: mail was previously not allocated as memory slot.
  • osErrorParameter: a parameter is invalid or outside of a permitted range.
if (osMailPut(mailId, pTMail) != osOK) /* Send Mail */   

osMailGet

Aus [CMSISRTOS]:

Suspend the execution of the current RUNNING thread until a mail arrives. When a mail is already in the queue, the function returns instantly with the mail information.

The argument millisec specifies how long the system waits for a mail to arrive. While the system waits the thread that is calling this function is put into the state WAITING. The millisec timeout can have the following values:

  • when millisec is 0, the function returns instantly.
  • when millisec is set to osWaitForever the function will wait for an infinite time until a mail arrives.
  • all other values specify a time in millisecond for a timeout.
event = osMailGet(mailId, osWaitForever); /* wait for mail */               
if (event.status == osEventMail)                                             
{                                                                           
     pRMail = event.value.p;           
     pRMail->var1 ...
     ...

osMailFree

Aus [CMSISRTOS]:

Free the memory block specified by mail and return it to the mail queue.

osMailFree(mailId, pRMail); /* free memory allocated for mail */

6.2.7 Message Queue

Message Queue

Code Beispiel siehe www.keil.com/pack/doc/CMSIS/RTOS/html/group__CMSIS__RTOS__Message.html

Aus [CMSISRTOS]:

Message passing is another basic communication model between threads. In the message passing model, one thread sends data explicitly, while another thread receives it. The operation is more like some kind of I/O rather than a direct access to information to be shared. In CMSIS-RTOS, this mechanism is called s message queue. The data is passed from one thread to another in a FIFO-like operation. Using message queue functions, you can control, send, receive, or wait for messages. The data to be passed can be of integer or pointer type:

osMessageCreate

#define QUEUE_SIZE 4
osMessageQId msg_id;

typedef struct {                       
   float    voltage;                   
   float    current;                 
   int      counter;              
} T_MEAS;

osPoolDef(mpool, QUEUE_SIZE, T_MEAS);     // Define memory pool
osPoolId  mpool;
osMessageQDef(msg, QUEUE_SIZE, &T_MEAS);
msg_id = osMessageCreate(osMessageQ(msg), NULL);
...

osMessagePut

Aus [CMSISRTOS]:

Put the message info in a message queue specified by queue_id.

When the message queue is full, the system retries for a specified time with millisec. While the system retries the thread that is calling this function is put into the state WAITING. The millisec timeout can have the following values:

  • when millisec is 0, the function returns instantly.
  • when millisec is set to osWaitForever the function will wait for an infinite time until a message queue slot becomes available.
  • all other values specify a time in millisecond for a timeout.
T_MEAS    *mptr;
mptr = osPoolAlloc(mpool);    //  also osPoolcreate() ...
...
osMessagePut(msg_id, (uint32_t)mptr, osWaitForever);

osMessageGet

T_MEAS  *rptr;
osEvent  evt;

evt = osMessageGet(msg_id, osWaitForever);  // wait for message
if (evt.status == osEventMessage) {
    rptr = evt.value.p;
    ...

6.2.8 Memory Pool

Code Beispiel siehe https://www.keil.com/pack/doc/CMSIS/RTOS/html/group__CMSIS__RTOS__PoolMgmt.html

osPoolCreate

typedef struct {
       uint32_t length;
       uint32_t width;
       uint32_t height;
       uint32_t weight;
} properties_t;


osPoolDef (object_pool, 10, properties_t);  // Declare memory pool
osPoolId  (object_pool_id);                 // Memory pool ID

object_pool_id = osPoolCreate(osPool(object_pool)); 

osPoolAlloc

properties_t *object_data;
object_data = (properties_t *) osPoolAlloc(object_pool_id);

object_data->length = 100;
object_data->width  = 10;
object_data->height = 23;
object_data->weight = 1000;

osPoolFree

status = osPoolFree (MemPool_Id, object_data);
if (status==osOK)  {
    // handle status code
    ...
}

6.2.9 Time, Timer

osKernelSysTick()

uint32_t osKernelSysTick(void)       

osTimerCreate

Aus [CMSISRTOS]:

Create a one-shot or periodic timer and associate it with a callback function argument. The timer is in stopped until it is started with osTimerStart.

void Timer1_Callback  (void const *arg);

osTimerDef (Timer1, Timer1_Callback); 

exec1 = 1;
id1 = osTimerCreate (osTimer(Timer1), osTimerOnce, &exec1);

osTimerStart

timerDelay = 1000;
status = osTimerStart(id, timerDelay); 

osTimerStop

status = osTimerStop(id1); 
if (status != osOK)  {
    ...  

osTimerDelete

Stop and delete timer.

status = osTimerDelete(id);  
if (status != osOK)  {
   ...

6.2.10 Delays

osDelay(millis)

Führt zu einem Suspend der Task bis Delay abgelaufen ist.

Aus [CMSISRTOS]:

Wait for a specified time period in millisec.

The millisec value specifies the number of timer ticks and is therefore an upper bound. The exact time delay depends on the actual time elapsed since the last timer tick.

For a value of 1, the system waits until the next timer tick occurs. That means that the actual time delay may be up to one timer tick less.

6.3 Beispielprogramme

Die Beispielprogramme für Nucleo L476 sind auf gitlab:

https://gitlab.com/hhoegl-tha/es/l476/cube-demos

Alle Beispiele sind mit CMSIS-RTOS v1 API.

Damit Sie die Beispiele besser studieren können, navigieren Sie wieder mit dem Tags Mechanismus in Vim.

  • FreeRTOS_EXTI
  • FreeRTOS_LowPower
  • FreeRTOS_Mail
  • FreeRTOS_Mutexes
  • FreeRTOS_Queues
  • FreeRTOS_Semaphore
  • FreeRTOS_Semaphore3
  • FreeRTOS_SemaphoreFromISR
  • FreeRTOS_Signal
  • FreeRTOS_SignalFromISR
  • FreeRTOS_ThreadCreation
  • FreeRTOS_ThreadCreation2
  • FreeRTOS_Timers

Veranschaulichung durch “Bildchen”

FreeRTOS_ThreadCreation

FreeRTOS_Semaphore und FreeRTOS_Signal

FreeRTOS_EXTI und FreeRTOS_Queue

FreeRTOS_Mail und FreeRTOS_Timers

FreeRTOS_Mutexes

FreeRTOS_SignalFromISR

6.4 Literatur zum RTOS Kapitel

FreeRTOS

[BARRY] Mastering the FreeRTOS Real Time Kernel, 2016. Link

  1. The FreeRTOS distribution
  2. Heap Memory Management
  3. Task Management
  4. Queue Management
  5. Software Timer Management
  6. Interrupt Management
  7. Resource Management
  8. Event Groups
  9. Task Notifications
  10. Low Power Support
  11. Developer support

[BARRY2] Real Time Application Design Tutorial. Using FreeRTOS in small embedded systems. https://www.freertos.org/tutorial Lokale Kopie: Link

[CMSISRTOS] CMSIS RTOS ist ein API, die Implementierung erfolgt durch Keil RTX oder FreeRTOS. https://www.keil.com/pack/doc/CMSIS/RTOS/html/index.html

[BROWN] Kap. 16 (Real-Time Operating Systems), S. 217-235.

[UM1722] Developing Applications on STM32Cube with RTOS, 26 Seiten. Erklärt auch kurz die FreeRTOS Beispiele (mit CMSIS-RTOS API) in der CubeF4 Bibliothek. Link

[FREERTOS] FreeRTOS Konzepte https://www.freertos.org/implementation/main.html

[AOSARTOS] The Architecture of Open-Source Applications (Volume II), FreeRTOS https://www.aosabook.org/en/freertos.html

[OSBOOKS] Allgemein zu empfehlen sind auch alle Standardwerke zu Betriebssystemen: Tanenbaum, Silberschatz, Stallings …

  • Tanenbaum, Modern Operating Systems, Kap. “Processes and Threads”.

  • Silberschatz, Operating System Concepts, Kap. 6 “Synchronization”.

  • Stallings, Operating Systems. Internals and Design Principles, Kap. 5 “Concurrency: Mutual Exclusion and Synchronization”.

Verweise auf die allgemeinen Literaturangaben (Kapitel 10):

[YIUDG], Kap. 19 (Using Embedded Operating Systems), S. 605-645. Behandelt CMSIS-RTOS, nicht FreeRTOS.

[MARTIN], Kap. 6 (Developing with CMSIS RTOS), S. 165-216 (erste Auflage von 2013).

[WHITE], Kap. 5 (Managing the Flow of Activity)

[NOVIELLO], Kap. 23 (Running FreeRTOS), S. 635-703.