Introduction to Multi-Tasking
Tasks are functions to get a specific job done. In a complex program many different things have to get done: - User Input has to be received and interpreted (keyboard, buttons, sliders, ...) - Information has to be provided to users e.g. via a display so that Users are informed about the status of some hardware to be controlled, or some problems can be brought to the attention of the user. - Some hardware needs to be controlled (i.e. the program of the washing machine)
All these tasks have to be executed at the same time. Since many microcontrollers only have one (or maximal 2) cores and each core can only execute one program at a time a solution to this dilemma has to be found. One possible solution is to write a "spaghetti program" which tries to do everything at once. This is not impossible but will become impossible to maintain if the complexity of the system increases (if you succeed in writing such a program, for sure after one week you will have forgotten how it works). A more elegant solution is called "Multi-tasking". For this the time is divided into short periods called time slices and every core is executing any specific tasks only during the period of one time slice. When the time slice is over the state task is probably not yet finished, but the controller now decides according to some algorithm which task is executed in the next time slice. And this is repeated whenever a new time slice starts. This is a much better solution since the tasks are largely independent of each other and are programmed to only do one specific thing. This makes them easy to maintain.
Once possible scheduling algorithm (i.e. an algorithm which decides which task is running next) is to execute the tasks in round-robin manner: If you have one core and 3 tasks to execute every task gets exactly every third time slice to work on its job. Since time slices are rather short (e.g. O(10ms)) tasks are switched around with a high frequency (O(100Hz)) and to a human being it seems the tasks are being executed at the same time (you will not realize that a keystroke is not registered by the controller immediately but after 30ms.)
This administration of multiple tasks in a microcontroller is the purpose of the FreeRTOS "operating system". At the heart of the system is the "scheduler" process which provides the algorithm to decide which task is running in the next time slice. In addition it handles the procedure to put a running task into the not-running state at the end of a time slice (e.g. it needs to save all CPU registers so that they can be re-loaded once the task resumes its activity so that the task can continue at the point where it was interrupted.) and vice versa to put a sleeping task into the running state. Schedulers are programmed by professionals and highly optimized for the targeted platform (i.e. the exploit all possible features of the specific microcontroller they are running on.) This ensures they are efficient and the switching of a tasks takes the minimum amount of time (you do not want the operating system to eat up your precious CPU time but you want to get your jour job done.)
In addition FreeRTOS provides mechanisms which let tasks communicate with each other. This is not completely trivial since you never know when a task is interrupted. Imagine one task wants to give some data structure to another task. It is interrupted half way through writing into a memory location which both tasks have agreed upon for communicating data. If the reading task is then activated and reads this memory location it finds a half written (i.e. corrupted) data structure and will probably crash on this. The synchronization of the tasks and the exchange of data between them is the second important challenge of a multi tasking system and FreeRTOS provides multiple tools to handle this.
FreeRTOS is written in the C programming language. It is distributed in form of code source files and when you build your project you are compile the necessary FreeRTOS source files into your application every time you build it. This mechanism ensures that you only compile features you really use into your application and the size of your applications remains as small as possible (this is completely different form full fledged operating systems (OS) where you use compiled libraries of the OS to link with them.) Being a system which should efficiently run on small microcontroller systems with only taking the minimum amount of resources, the FreeRTOS system is highly configurable. A lot of features can be enable or disable so that the code-size taken up by the system can be minimized. There are many options to choose from (e.g. the scheduling algorithm). The configuration is done my simply editing an include file with a lot of #define statements. Like this parameters can be set or options can be switched on and off.
Technicalities on how to use FreeRTOS are described very well in the Tutorial Guide "Mastering the FreeRTOS Real Time Kernel" [2] and they are not repeated here. This chapter only explains some of the most important features of the system to given an idea of its potential for complex embedded systems.
References
[1] The FreeRTOS homepage : https://www.freertos.org/index.html
[2] Mastering the FreeRTOS Real Time Kernel and FreerRTOS reference manual