Corso di Elettronica dei Sistemi Programmabili

Similar documents
Corso di Elettronica dei Sistemi Programmabili

Context Switching & Task Scheduling

FreeRTOS. A Brief Overview. Christopher Kenna. October 1, Avionics. FreeRTOS 1 / 34

FreeRTOS. A Brief Overview. Christopher Kenna. October 1, Avionics. FreeRTOS 1 / 34

COMP 7860 Embedded Real- Time Systems: Threads

AVR Subroutine Basics

Free-RTOS Implementation

What we will learn. Threaded Systems: Threads. Threads. Threads. Bag of functions & procedures ST OS. AUCs. Threads. AUCs. Processes MT OS MP OS

An Analysis and Description of the Inner Workings of the FreeRTOS Kernel

real-time kernel documentation

Lecture 4: Mechanism of process execution. Mythili Vutukuru IIT Bombay

Embedded Systems and Software

Module 8: Atmega32 Stack & Subroutine. Stack Pointer Subroutine Call function

Grundlagen Microcontroller Interrupts. Günther Gridling Bettina Weiss

Microprocessors & Interfacing

Interrupts (I) Lecturer: Sri Notes by Annie Guo. Week8 1

12. Interrupts and Programmable Multilevel Interrupt Controller

EE458 - Embedded Systems Exceptions and Interrupts

EE 308: Microcontrollers

CODE TIME TECHNOLOGIES. Abassi RTOS. Porting Document. ATmega128 IAR

COMP2121: Microprocessors and Interfacing

AN HONORS UNIVERSITY IN MARYLAND UMBC. AvrX. Yousef Ebrahimi Professor Ryan Robucci

ATmega Interrupts. Reading. The AVR Microcontroller and Embedded Systems using Assembly and C) by Muhammad Ali Mazidi, Sarmad Naimi, and Sepehr Naimi

Microprocessors & Interfacing

Interrupts (II) Lecturer: Sri Parameswaran Notes by: Annie Guo

UNIVERSITY OF MANITOBA Final Exam

CODE TIME TECHNOLOGIES. Abassi RTOS. Porting Document. ATmega IAR

Embedding OS in AVR microcontrollers. Prof. Prabhat Ranjan DA-IICT, Gandhinagar

Embedded Systems. 5. Operating Systems. Lothar Thiele. Computer Engineering and Networks Laboratory

Wireless Sensor Networks (WSN)

Interrupts and timers

Concurrent programming: Introduction I

Newbie s Guide to AVR Interrupts

Processes and Non-Preemptive Scheduling. Otto J. Anshus

Precept 2: Non-preemptive Scheduler. COS 318: Fall 2018

Interrupts & Interrupt Service Routines (ISRs)

Micrium µc/os II RTOS Introduction EE J. E. Lumpp

Implementation of the ART Real Time Micro Kernel

CODE TIME TECHNOLOGIES. Abassi RTOS. Porting Document. ATmega128 GCC

Why use an Operating System? Operating System Definition

Today s Topics. u Thread implementation. l Non-preemptive versus preemptive threads. l Kernel vs. user threads

ADAPTING MODE SWITCHES INTO THE HIERARCHICAL SCHEDULING DANIEL SANCHEZ VILLALBA

Computer architecture. A simplified model

AGH University of Science and Technology Cracow Department of Electronics

Engineer To Engineer Note

Interrupts and Time. Real-Time Systems, Lecture 5. Martina Maggio 28 January Lund University, Department of Automatic Control

Today s Menu. >Use the Internal Register(s) >Use the Program Memory Space >Use the Stack >Use global memory

Chapters 2, 3: bits and pieces. Chapters 2 & 3. Chapters 2, 3: bits and pieces. Chapters 2, 3: bits and pieces. Using C. A last word about hardware

Interrupts and Time. Interrupts. Content. Real-Time Systems, Lecture 5. External Communication. Interrupts. Interrupts

Introduction to Real-Time Systems and Multitasking. Microcomputer Architecture and Interfacing Colorado School of Mines Professor William Hoff

Processes Prof. James L. Frankel Harvard University. Version of 6:16 PM 10-Feb-2017 Copyright 2017, 2015 James L. Frankel. All rights reserved.

OPERATING SYSTEM OVERVIEW

Today s Topics. u Thread implementation. l Non-preemptive versus preemptive threads. l Kernel vs. user threads

Common Computer-System and OS Structures

SMD149 - Operating Systems

CprE 288 Translating C Control Statements and Function Calls, Loops, Interrupt Processing. Instructors: Dr. Phillip Jones Dr.

8051 Microcontroller Interrupts

Cooperative Multitasking

Automatic Creation of Define.xml for ADaM

AVR Microcontrollers Architecture

hardware interrupts software interrupts


COMP2121: Microprocessors and Interfacing

Process Context & Interrupts. New process can mess up information in old process. (i.e. what if they both use the same register?)

ARM Cortex-M and RTOSs Are Meant for Each Other

Interrupts (Exceptions) Gary J. Minden September 11, 2014

INTERRUPTS in microprocessor systems

MWS3-3 - MOC NETWORKING WITH WINDOWS SERVER 2016

Real-Time Programming

x86 architecture et similia

AGH University of Science and Technology Cracow Department of Electronics

RT3 - FreeRTOS Real Time Programming

Computer Systems Overview

Interrupts (Exceptions) (From LM3S1968) Gary J. Minden August 29, 2016

Objectives. Making TOS preemptive Avoiding race conditions

The Kernel Abstraction. Chapter 2 OSPP Part I

Preemptive Scheduling

8-bit Microcontroller with 8K Bytes Programmable Flash AT90C8534. Preliminary

Mechatronics and Microcomputers. Stipendium Hungaricum 2018/2019 Autumn Semester Szilárd Aradi, PhD

Q.1 Explain Computer s Basic Elements

Lecture Topics. Announcements. Today: Uniprocessor Scheduling (Stallings, chapter ) Next: Advanced Scheduling (Stallings, chapter

AVR ISA & AVR Programming (I) Lecturer: Sri Parameswaran Notes by: Annie Guo

Scheduling - Overview

Homework / Exam. Return and Review Exam #1 Reading. Machine Projects. Labs. S&S Extracts , PIC Data Sheet. Start on mp3 (Due Class 19)

Computer System Overview. Chapter 1

Multitasking on Cortex-M(0) class MCU A deepdive into the Chromium-EC scheduler

Lecture 3. Introduction to Real-Time kernels. Real-Time Systems

VCC PB2 (SCK/ADC1/T0/PCINT2) PB1 (MISO/AIN1/OC0B/INT0/PCINT1) PB0 (MOSI/AIN0/OC0A/PCINT0)

Processes and Operating Systems

FreeRTOS - Common Task Design Patterns in Multi-tasking Applications

Chapter 7 Subroutines. Richard P. Paul, SPARC Architecture, Assembly Language Programming, and C

Review: Program Execution. Memory program code program data program stack containing procedure activation records

Introduction to Real-Time Operating Systems

Basic Concepts. Based on original slides by Silberschatz, Galvin, Gagne, and Anastasi. PerLab

CS 550 Operating Systems Spring System Call

AVR ISA & AVR Programming (I) Lecturer: Sri Parameswaran Notes by: Annie Guo

Fast Interrupts. Krste Asanovic, UC Berkeley / SiFive Inc. (Chair) Kevin Chen, Andes (Vice-Chair)

CS 318 Principles of Operating Systems

Threads and Concurrency

Threads and Concurrency

Transcription:

Corso di Elettronica dei Sistemi Programmabili Sistemi Operativi Real Time freertos implementation Aprile 2014 Stefano Salvatori 1/40

Sommario RTOS tick Execution context Context switch example 2/40

RTOS Tick Oltre allo stato di esecuzione, un task può trovarsi in uno di questi stati: sleep block 3/40

RTOS Tick When sleeping, a task will specify a time after which it requires 'waking'. When blocking, a task can specify a maximum time it wishes to wait. 4/40

RTOS Tick Time measure The FreeRTOS real time kernel measures time using a tick count variable. A timer interrupt (the RTOS tick interrupt) increments the tick count with strict temporal accuracy - allowing the real time kernel to measure time to a resolution of the chosen timer interrupt frequency. 5/40

RTOS Tick At (1) the RTOS idle task is executing. At (2) the RTOS tick occurs, and control transfers to the tick ISR (3). The RTOS tick ISR makes vcontroltask ready to run, and as vcontroltask has a higher priority than the RTOS idle task, switches the context to that of vcontroltask. As the execution context is now that of vcontroltask, exiting the ISR (4) returns control to vcontroltask, which starts executing (5). 6/40

preempitive A context switch occurring in this way is said to be Preemptive, as the interrupted task is preempted without suspending itself voluntarily. The uc port of FreeRTOS uses a compare match event on a timer to generate the RTOS tick. In the following, the RTOS tick ISR is described in detail. In computing, preemption is the act of temporarily interrupting a task being carried out by a computer system, without requiring its cooperation, and with the intention of resuming the task at a later time. Such a change is known as a context switch. It is normally carried out by a privileged task or part of the system known as a preemptive scheduler, which has the power to preempt, or interrupt, and later resume, other tasks in the system. 7/40

FreeRTOS Tick Code /*--------------------------------------------------*/ /* Interrupt service routine for the RTOS tick. */ void SIG_OUTPUT_COMPARE1A( void ) { /* Call the tick function. */ vportyieldfromtick(); /* Return from the interrupt. If a context switch has occurred this will return to a different task. */ asm volatile ( "reti" ); } /*--------------------------------------------------*/ 8/40

FreeRTOS Tick Code void vportyieldfromtick( void ) { /* This is a naked function so the context is saved. */ portsave_context(); /* Increment the tick count and check to see if the new tick value has caused a delay period to expire. This function call can cause a task to become ready to run. */ vtaskincrementtick(); /* See if a context switch is required. Switch to the context of a task made ready to run by vtaskincrementtick() if it has a priority higher than the interrupted task. */ vtaskswitchcontext(); /* Restore the context. If a context switch has occurred this will restore the context of the task being resumed. */ portrestore_context(); /* Return from this naked function. */ asm volatile ( "ret" ); } /*--------------------------------------------------*/ 9/40

Execution Context es.: uc AVR 10/40

Saving the Context #define portsave_context() asm volatile ( \ "push r0 \n\t" \ "in r0, SREG \n\t" \ "cli \n\t" \ "push r0 \n\t" \ "push r1 \n\t" \ "clr r1 \n\t" \ "push r2 \n\t" \ "push r3 \n\t" \ "push r4 \n\t" \ "push r5 \n\t" \... "push r30 \n\t" \ "push r31 \n\t" \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "in r0, SP_L \n\t" \ "st x+, r0 \n\t" \ "in r0, SP_H \n\t" \ "st x+, r0 \n\t" \ ); macro 11/40

Saving the Context #define portsave_context() asm volatile ( \ "push r0 \n\t" \ "in r0, SREG \n\t" \ "cli \n\t" \ "push r0 \n\t" \ "push r1 \n\t" \ "clr r1 \n\t" \ "push r2 \n\t" \ "push r3 \n\t" \ "push r4 \n\t" \ "push r5 \n\t" \... "push r30 \n\t" \ "push r31 \n\t" \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "in r0, SP_L \n\t" \ "st x+, r0 \n\t" \ "in r0, SP_H \n\t" \ "st x+, r0 \n\t" \ ); r0 usato per salvare lo status register va salvato 12/40

Saving the Context #define portsave_context() asm volatile ( \ "push r0 \n\t" \ "in r0, SREG \n\t" \ "cli \n\t" \ "push r0 \n\t" \ "push r1 \n\t" \ "clr r1 \n\t" \ "push r2 \n\t" \ "push r3 \n\t" \ "push r4 \n\t" \ "push r5 \n\t" \... "push r30 \n\t" \ "push r31 \n\t" \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "in r0, SP_L \n\t" \ "st x+, r0 \n\t" \ "in r0, SP_H \n\t" \ "st x+, r0 \n\t" \ ); r0 SR 13/40

Saving the Context #define portsave_context() asm volatile ( \ "push r0 \n\t" \ "in r0, SREG \n\t" \ "cli \n\t" \ "push r0 \n\t" \ "push r1 \n\t" \ "clr r1 \n\t" \ "push r2 \n\t" \ "push r3 \n\t" \ "push r4 \n\t" \ "push r5 \n\t" \... "push r30 \n\t" \ "push r31 \n\t" \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "in r0, SP_L \n\t" \ "st x+, r0 \n\t" \ "in r0, SP_H \n\t" \ "st x+, r0 \n\t" \ ); interrupt disabilitati portsave_context potrebbe essere chiamata non dall'interno della ISR (che già disab. IRQ) p.es. un task che si autosospende 14/40

Saving the Context #define portsave_context() asm volatile ( \ "push r0 \n\t" \ "in r0, SREG \n\t" \ "cli \n\t" \ "push r0 \n\t" \ "push r1 \n\t" \ "clr r1 \n\t" \ "push r2 \n\t" \ "push r3 \n\t" \ "push r4 \n\t" \ "push r5 \n\t" \... "push r30 \n\t" \ "push r31 \n\t" \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "in r0, SP_L \n\t" \ "st x+, r0 \n\t" \ "in r0, SP_H \n\t" \ "st x+, r0 \n\t" \ ); salvo SR perché la prima istruzione non può essere CLI? 15/40

Saving the Context #define portsave_context() asm volatile ( \ "push r0 \n\t" \ "in r0, SREG \n\t" \ "cli \n\t" \ "push r0 \n\t" \ "push r1 \n\t" \ "clr r1 \n\t" \ "push r2 \n\t" \ "push r3 \n\t" \ "push r4 \n\t" \ "push r5 \n\t" \... "push r30 \n\t" \ "push r31 \n\t" \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "in r0, SP_L \n\t" \ "st x+, r0 \n\t" \ "in r0, SP_H \n\t" \ "st x+, r0 \n\t" \ ); salvo r1 16/40

Saving the Context #define portsave_context() asm volatile ( \ "push r0 \n\t" \ "in r0, SREG \n\t" \ "cli \n\t" \ "push r0 \n\t" \ "push r1 \n\t" \ "clr r1 \n\t" \ "push r2 \n\t" \ "push r3 \n\t" \ "push r4 \n\t" \ "push r5 \n\t" \... "push r30 \n\t" \ "push r31 \n\t" \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "in r0, SP_L \n\t" \ "st x+, r0 \n\t" \ "in r0, SP_H \n\t" \ "st x+, r0 \n\t" \ ); r1 è azzerato (ISR di FreeRTOS vede se r1=0) 17/40

Saving the Context #define portsave_context() asm volatile ( \ "push r0 \n\t" \ "in r0, SREG \n\t" \ "cli \n\t" \ "push r0 \n\t" \ "push r1 \n\t" \ "clr r1 \n\t" \ "push r2 \n\t" \ "push r3 \n\t" \ "push r4 \n\t" \ "push r5 \n\t" \... "push r30 \n\t" \ "push r31 \n\t" \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "in r0, SP_L \n\t" \ "st x+, r0 \n\t" \ "in r0, SP_H \n\t" \ "st x+, r0 \n\t" \ ); salvo tutti i registri della CPU 18/40

Saving the Context #define portsave_context() asm volatile ( \ "push r0 \n\t" \ "in r0, SREG \n\t" \ "cli \n\t" \ "push r0 \n\t" \ "push r1 \n\t" \ "clr r1 \n\t" \ ); "push r2 \n\t" \ "push r3 \n\t" \ "push r4 \n\t" \ "push r5 \n\t" \... è un puntatore "push r30 \n\t" \ "push r31 \n\t" \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "in r0, SP_L \n\t" \ "st x+, r0 \n\t" \ "in r0, SP_H \n\t" \ "st x+, r0 \n\t" \ salva l'indirizzo nel registro X (r27-r26) della CPU La variabile pxcurrenttcb di FreeRTOS contiene l'indirizzo della locazione di memoria da cui recuperare lo stack pointer dei task (vd. più avanti) 19/40

Saving the Context #define portsave_context() asm volatile ( \ "push r0 \n\t" \ "in r0, SREG \n\t" \ "cli \n\t" \ "push r0 \n\t" \ "push r1 \n\t" \ "clr r1 \n\t" \ "push r2 \n\t" \ "push r3 \n\t" \ "push r4 \n\t" \ "push r5 \n\t" \... "push r30 \n\t" \ "push r31 \n\t" \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "in r0, SP_L \n\t" \ "st x+, r0 \n\t" \ "in r0, SP_H \n\t" \ "st x+, r0 \n\t" \ ); lo stack pointer viene salvato 20/40

Saving the Context #define portrestore_context() \ asm volatile ( \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "ld r28, x+ \n\t" \ "out SP_L, r28 \n\t" \ "ld r29, x+ \n\t" \ "out SP_H, r29 \n\t" \ "pop r31 \n\t" \ "pop r30 \n\t" \... "pop r1 \n\t" \ "pop r0 \n\t" \ "out SREG, r0 \n\t" \ "pop r0 \n\t" \ anche questa è una macro ); 21/40

Saving the Context #define portrestore_context() \ asm volatile ( \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "ld r28, x+ \n\t" \ "out SP_L, r28 \n\t" \ "ld r29, x+ \n\t" \ "out SP_H, r29 \n\t" \ "pop r31 \n\t" \ "pop r30 \n\t" \... "pop r1 \n\t" \ "pop r0 \n\t" \ "out SREG, r0 \n\t" \ "pop r0 \n\t" \ carica pccurrenttcb nel registro X della CPU pxcurrenttcb contiene l'indirizzo della locazione di memoria da cui recuperare lo stack pointer dei task ); 22/40

Saving the Context #define portrestore_context() \ asm volatile ( \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "ld r28, x+ \n\t" \ "out SP_L, r28 \n\t" \ "ld r29, x+ \n\t" \ "out SP_H, r29 \n\t" \ "pop r31 \n\t" \ "pop r30 \n\t" \... "pop r1 \n\t" \ "pop r0 \n\t" \ "out SREG, r0 \n\t" \ "pop r0 \n\t" \ ripristina lo stack pointer della CPU ); 23/40

Saving the Context #define portrestore_context() \ asm volatile ( \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "ld r28, x+ \n\t" \ "out SP_L, r28 \n\t" \ "ld r29, x+ \n\t" \ "out SP_H, r29 \n\t" \ "pop r31 \n\t" \ "pop r30 \n\t" \... "pop r1 \n\t" \ "pop r0 \n\t" \ "out SREG, r0 \n\t" \ "pop r0 \n\t" \ recupera tutti i registri della CPU ); 24/40

Saving the Context #define portrestore_context() \ asm volatile ( \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "ld r28, x+ \n\t" \ "out SP_L, r28 \n\t" \ "ld r29, x+ \n\t" \ "out SP_H, r29 \n\t" \ "pop r31 \n\t" \ "pop r30 \n\t" \... "pop r1 \n\t" \ "pop r0 \n\t" \ "out SREG, r0 \n\t" \ "pop r0 \n\t" \ recupera lo SR ); 25/40

Saving the Context #define portrestore_context() \ asm volatile ( \ "lds r26, pxcurrenttcb \n\t" \ "lds r27, pxcurrenttcb + 1 \n\t" \ "ld r28, x+ \n\t" \ "out SP_L, r28 \n\t" \ "ld r29, x+ \n\t" \ "out SP_H, r29 \n\t" \ "pop r31 \n\t" \ "pop r30 \n\t" \... "pop r1 \n\t" \ "pop r0 \n\t" \ "out SREG, r0 \n\t" \ "pop r0 \n\t" \ recupera infine r0 ); 26/40

Context Switch Detailed Example 27/40

Detailed example (1) Prior to the RTOS tick interrupt This example starts with TaskA executing. TaskB has previously been suspended so its context has already been stored on the TaskB stack. 28/40

Detailed example (2) The RTOS tick interrupt occurs The RTOS tick occurs just as TaskA is about to execute an LDI instruction. When the interrupt occurs the uc automatically places the current program counter (PC) onto the stack before jumping to the start of the RTOS tick ISR. auto 29/40

Detailed example (3) The RTOS tick interrupt executes /* Interrupt service routine for the RTOS tick. */ void SIG_OUTPUT_COMPARE1A( void ) { vportyieldfromtick(); asm volatile ( "reti" ); } void vportyieldfromtick( void ) { portsave_context(); vtaskincrementtick(); vtaskswitchcontext(); portrestore_context(); asm volatile ( "ret" ); } 30/40

Detailed example (4) void vportyieldfromtick( void ) { } portsave_context(); vtaskincrementtick(); vtaskswitchcontext(); portrestore_context(); asm volatile ( "ret" ); 2 1 31/40

Detailed example (5) void vportyieldfromtick( void ) { portsave_context(); vtaskincrementtick(); vtaskswitchcontext(); portrestore_context(); asm volatile ( "ret" ); } Incrementing the Tick Count vtaskincrementtick() executes after the TaskA context has been saved. For the purposes of this example assume that incrementing the tick count has caused TaskB to become ready to run. 32/40

Detailed example (6) void vportyieldfromtick( void ) { } portsave_context(); vtaskincrementtick(); vtaskswitchcontext(); portrestore_context(); asm volatile ( "ret" ); Incrementing the Tick Count TaskB has a higher priority than TaskA so vtaskswitchcontext() selects TaskB as the task to be given processing time when the ISR completes. 33/40

Detailed example (7) The TaskB stack pointer is retrieved void vportyieldfromtick( void ) { 1 } portsave_context(); vtaskincrementtick(); vtaskswitchcontext(); portrestore_context(); asm volatile ( "ret" ); 34/40

Detailed example (8) Restore the TaskB context portrestore_context() completes by restoring the TaskB context from its stack into the appropriate processor registers Only the program counter remains on the stack. void vportyieldfromtick( void ) { portsave_context(); vtaskincrementtick(); vtaskswitchcontext(); portrestore_context(); asm volatile ( "ret" ); } 35/40

Detailed example (9) The RTOS tick exits vportyieldfromtick() returns to SIG_OUTPUT_COMPARE1A()... void vportyieldfromtick( void ) { portsave_context(); vtaskincrementtick(); vtaskswitchcontext(); portrestore_context(); asm volatile ( "ret" ); } 36/40

Detailed example (10) The RTOS tick exits vportyieldfromtick() returns to SIG_OUTPUT_COMPARE1A() where the final instruction is a return from interrupt (RETI). A RETI instruction assumes the next value on the stack is a return address placed onto the stack when the interrupt occurred. void SIG_OUTPUT_COMPARE1A ( void ) { vportyieldfromtick(); asm volatile ( "reti" ); } 37/40

Detailed example (11) The RTOS tick exits When the RTOS tick interrupt started the AVR automatically placed the TaskA return address onto the stack - the address of the next instruction to execute in TaskA. The ISR altered the stack pointer so it now points to the TaskB stack. Therefore the return address POP'ed from the stack by the RETI instruction is actually the address of the instruction TaskB was going to execute immediately before it was suspended. The RTOS tick interrupt interrupted TaskA, but is returning to TaskB the context switch is complete! 38/40

Detailed example (12) The RTOS tick interrupt interrupted TaskA, but is returning to TaskB the context switch is complete! 2 1 39/40

Riferimenti Richard Barry, FreeRTOS, at www.freertos.org, chapter 16, pp. 13, 14, 17-27 40/40