Arduino FreeRTOS Tutorials
Arduino FreeRTOS Tutorials
Arduino FreeRTOS Tutorials
Fre
eRTOS Task for Blink LED in Arduino UNO
The OS present inside the embedded devices is called an RTOS (Real-Time Operating
System). In embedded devices, real-time tasks are critical where timing plays a very
important role. Real-time tasks are Time Deterministic means the response time to any
event is always constant so that it can be guaranteed that any particular event will occur
at a fixed time. RTOS is designed to run applications with very precise timing and a high
degree of reliability. RTOS also helps in multi-tasking with a single core.
Now, let’s see what happens in the RTOS kernel if we want to create a task for LED
blinking with a one-second interval and put this task on the highest priority.
Apart from the LED task, there will be one more task which is created by the kernel, it is
known as an idle task. The idle task is created when no task is available for execution.
This task always runs on the lowest priority i.e. 0 priority. If we analyze the timing graph
given above, it can be seen that execution starts with an LED task and it runs for a
specified time then for remaining time, the idle task runs until a tick interrupt occurs.
Then kernel decides which task has to be executed according to the priority of the task
and total elapsed time of the LED task. When 1 second is completed, the kernel chooses
the led task again to execute because it has a higher priority than the idle task, we can
also say that the LED task preempts the idle task. If there are more than two tasks with
the same priority then they will run in round-robin fashion for a specified time.
Below the state diagram as it shows the switching of the non-running task into
running state.
Every newly created task goes in Ready state (part of not running state). If the created
task (Task1) has the highest priority than other tasks, then it will move to running state. If
this running task preempts by the other task, then it will go back to the ready state again.
Else if task1 is blocked by using blocking API, then CPU will not engage with this task
until the timeout defined by the user.
If Task1 is suspended in running state using Suspend APIs, then Task1 will go to
Suspended state and it is not available to the scheduler again. If you resume Task1 in
the suspended state then it will go back to the ready state as you can see in the block
diagram.
This is the basic idea of how Tasks run and change their states. In this tutorial, we
will implement two tasks in Arduino Uno using FreeRTOS API.
2. Scheduler: It is responsible for selecting a task from the ready state list to the running
state. Schedulers are often implemented so they keep all computer resources busy (as
in load balancing).
3. Preemption: It is the act of temporarily interrupting an already executing task with the
intention of removing it from the running state without its co-operation.
1. Preemptive Scheduling: In this type of scheduling, tasks run with equal time
slice without considering the priorities.
2. Priority-based Preemptive: High priority task will run first.
3. Co-operative Scheduling: Context switching will happen only with the co-
operation of running tasks. Task will run continuously until task yield is called.
6. Kernel Objects: For signaling the task to perform some work, the synchronization
process is used. To perform this process Kernel objects are used. Some Kernel objects
are Events, Semaphores, Queues, Mutex, Mailboxes, etc. We will see how to use these
objects in upcoming tutorials.
From the above discussion, we have got some basic ideas about the RTOS concept and
now we can implement the FreeRTOS project in Arduino. So, let's get started by
installing FreeRTOS libraries in Arduino IDE.
You can download the library from github and Add the .zip file in Sketch-> Include
Library -> Add .zip file.
Now, restart the Arduino IDE. This library provides some example code, also that can be
found in File -> Examples -> FreeRTOS as shown below.
Here we will write the code from scratch to understand the working, later you can check
the example codes and use them.
Circuit Diagram
Below is the circuit diagram for creating Blinking LED task using FreeRTOS on Arduino:
Arduino FreeRTOS Example- Creating FreeRTOS tasks in
Arduino IDE
Let’s see a basic structure to write a FreeRTOS project.
#include <Arduino_FreeRTOS.h>
2. Give the function prototype of all functions that you are writing for execution which is
written as
..
….
3. Now, in void setup() function, create tasks and start the task scheduler.
1. pvTaskCode: It is simply a pointer to the function that implements the task (in
effect, just the name of the function).
2. pcName: A descriptive name for the task. This is not used by FreeRTOS. It is
included purely for debugging purposes.
3. usStackDepth: Each task has its own unique stack that is allocated by the kernel
to the task when the task is created. The value specifies the number of words the
stack can hold, not the number of bytes. For example, if the stack is 32-bits wide
and usStackDepth is passed in as 100, then 400 bytes of stack space will be
allocated (100 * 4 bytes) in RAM. Use this wisely because Arduino Uno has only
2Kbytes of RAM.
4. pvParameters: Task input parameter (can be NULL).
5. uxPriority: Priority of the task ( 0 is the lowest priority).
6. pxCreatedTask: It can be used to pass out a handle to the task being created.
This handle can then be used to reference the task in API calls that, for example,
change the task priority or delete the task (can be NULL).
Example of task creation
xTaskCreate(task1,"task1",128,NULL,1,NULL);
xTaskCreate(task2,"task2",128,NULL,2,NULL);
5. Void loop() function will remain empty as we don’t want to run any task manually and
infinitely. Because task execution is now handled by Scheduler.
6. Now, we have to implement task functions and write the logic that you want to execute
inside these functions. The function name should be the same as the first argument
of xTaskCreate() API.
while(1) {
..
..//your logic
}
7. Most of the code needs delay function to stop the running task but in RTOS it is not
suggested to use Delay() function as it stops the CPU and hence RTOS also stops
working. So FreeRTOS has a kernel API to block the task for a specific time.
This API can be used for delay purposes. This API delay a task for a given number of
ticks. The actual time for which the task remains blocked depends on the tick rate. The
constant portTICK_PERIOD_MS can be used to calculate real-time from the tick rate.
This means if you want a delay of 200ms, just write this line
So for this tutorial, we will use these FreeRTOS APIs to implement three tasks.
APIs to be used:
1. xTaskCreate();
2. vTaskStartScheduler();
3. vTaskDelay();
Task to be created for this tutorial:
#include <Arduino_FreeRTOS.h>
2. In void setup() function, initialize serial communication at 9600 bits per second and
create all three tasks using xTaskCreate() API. Initially, make the priorities of all tasks
as ‘1’ and start the scheduler.
void setup() {
Serial.begin(9600);
xTaskCreate(TaskBlink1,"Task1",128,NULL,1,NULL);
xTaskCreate(TaskBlink2,"Task2 ",128,NULL,1,NULL);
xTaskCreate(Taskprint,"Task3",128,NULL,1,NULL);
vTaskStartScheduler();
3. Now, implement all three functions as shown below for task1 LED blink.
pinMode(8, OUTPUT);
while(1)
digitalWrite(8, HIGH);
digitalWrite(8, LOW);
int counter = 0;
while(1)
counter++;
Serial.println(counter);
vTaskDelay( 500 / portTICK_PERIOD_MS );
Finally, connect two LEDs at the digital pin 7 and 8 and upload the code on your Arduino
board and open the Serial monitor. You will see a counter is running once in 500ms with
task name as shown below.
Also, observe the LEDs, they are blinking at different time intervals. Try to play with the
priority argument in the xTaskCreate function. Change the number and observe the
behavior on serial monitor and LEDs.
Now, you can understand the first two example codes in which analog read and digital
read tasks are created. In this way, you can make more advance projects using just
Arduino Uno and FreeRTOS APIs.
Code
#include <Arduino_FreeRTOS.h>
void TaskBlink1( void *pvParameters );
void TaskBlink2( void *pvParameters );
void Taskprint( void *pvParameters );
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
xTaskCreate(
TaskBlink1
, "task1"
, 128
, NULL
, 1
, NULL );
xTaskCreate(
TaskBlink2
, "task2"
, 128
, NULL
, 1
, NULL );
xTaskCreate(
Taskprint
, "task3"
, 128
, NULL
, 1
, NULL );
vTaskStartScheduler();
}
void loop()
{
}
void TaskBlink1(void *pvParameters) {
pinMode(8, OUTPUT);
while(1)
{
Serial.println("Task1");
digitalWrite(8, HIGH);
vTaskDelay( 200 / portTICK_PERIOD_MS );
digitalWrite(8, LOW);
vTaskDelay( 200 / portTICK_PERIOD_MS );
}
}
void TaskBlink2(void *pvParameters)
{
pinMode(7, OUTPUT);
while(1)
{
Serial.println("Task2");
digitalWrite(7, HIGH);
vTaskDelay( 300 / portTICK_PERIOD_MS );
digitalWrite(7, LOW);
vTaskDelay( 300 / portTICK_PERIOD_MS );
}
}
void Taskprint(void *pvParameters) {
int counter = 0;
while(1)
{
counter++;
Serial.println(counter);
vTaskDelay(500 / portTICK_PERIOD_MS); }
}
Video
#include <Arduino_FreeRTOS.h>
void TaskBlink1( void *pvParameters );
void TaskBlink2( void *pvParameters );
void Taskprint( void *pvParameters );
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
xTaskCreate(
TaskBlink1
, "task1"
, 128
, NULL
, 1
, NULL );
xTaskCreate(
TaskBlink2
, "task2"
, 128
, NULL
, 1
, NULL );
xTaskCreate(
Taskprint
, "task3"
, 128
, NULL
, 1
, NULL );
vTaskStartScheduler();
}
void loop()
{
}
void TaskBlink1(void *pvParameters) {
pinMode(8, OUTPUT);
while(1)
{
Serial.println("Task1");
digitalWrite(8, HIGH);
vTaskDelay( 200 / portTICK_PERIOD_MS );
digitalWrite(8, LOW);
vTaskDelay( 200 / portTICK_PERIOD_MS );
}
}
void TaskBlink2(void *pvParameters)
{
pinMode(7, OUTPUT);
while(1)
{
Serial.println("Task2");
digitalWrite(7, HIGH);
vTaskDelay( 300 / portTICK_PERIOD_MS );
digitalWrite(7, LOW);
vTaskDelay( 300 / portTICK_PERIOD_MS );
}
}
void Taskprint(void *pvParameters) {
int counter = 0;
while(1)
{
counter++;
Serial.println(counter);
vTaskDelay(500 / portTICK_PERIOD_MS); }
}
PART 2
Arduino FreeRTOS Tutorial 2- Using
Queues in Arduino FreeRTOS
EMBEDDED
ByRishabh Jain Apr 06, 20200
Arduino FreeRTOS using Queues
In the previous tutorial, we introduced FreeRTOS in Arduino Uno and created a task for
the blinking LED. Now, in this tutorial, we will dive more into advance concepts of RTOS
APIs and learn about communication between different tasks. Here we also learn about
Queue to transfer data from one task to another and demonstrate the working of
queue APIs by interfacing 16x2 LCD and LDR with the Arduino Uno.
Before discussing about Queues, let’s see one more FreeRTOS API which is helpful in
deleting the tasks when it is finished with the assigned work. Sometimes the task needs
to be deleted to free the allotted memory. In continuation of the previous tutorial, we will
use vTaskDelete() API function in the same code to delete one of the tasks. A task can
use the vTaskDelete() API function to delete itself, or any other task.
To use this API, you have to configure the FreeRTOSConfig.h file. This file is used to
tailor FreeRTOS according to the application. It is used to change the scheduling
algorithms and many other parameters. The file can be found in the Arduino Directory
which is generally available in the Documents folder of your PC. In my case, it is
available in \Documents\Arduino\libraries\FreeRTOS\src as shown below.
Now, open this file using any text editor and search for the #define
INCLUDE_vTaskDelete and make sure its value is ‘1’ (1 means enable and 0 means
disable). It is 1 by default but checks for it.
We will be using this config file frequently in our next tutorials for setting the parameters.
pxTaskToDelete: It is the handle of the task that is to be deleted. It is the same as the
6th argument of xTaskCreate() API. In the previous tutorial, this argument is set as NULL
but you can pass the address of the contents of the task by using any name. Let say if
you want to set task handle for Task2 which is declared as
TaskHandle_t any_name;
The content of this task can be now accessed using the handle given by you.
Also, a task can delete itself by passing NULL in place of a valid task handle.
vTaskDelete(NULL);
pinMode(7, OUTPUT);
while(1)
digitalWrite(7, HIGH);
vTaskDelay( 300 / portTICK_PERIOD_MS );
digitalWrite(7, LOW);
Now, upload the code and observe the LEDs and Serial monitor. You will see that the
second LED is not blinking now and task2 is deleted after encountering the delete API.
So this API can be used to stop the execution of the particular task.
The maximum number of elements queue can hold is called its “length”. Both the length
and the size of each element are set when the queue is created.
An example of how the queue is used for data transfer is illustrated well in FreeRTOS
documentation that can be found here. You can easily understand the given example.
After understanding the Queues, let’s try to understand the process of creating a queue
and try to implement it in our FreeRTOS code.
We want to print the value of the LDR sensor on 16*2 LCD. So there are two tasks
now
1. Creating a Queue
2. Sending data to Queue
3. Receiving data from Queue
1. Creating a Queue
For creating queue, use xQueueCreate() function API. It takes two arguments.
uxQueueLength: The maximum number of items that the queue being created can hold
at any one time.
uxItemSize: The size in bytes of each data item that can be stored in the queue.
If this function returns NULL then the queue is not created due to insufficient memory
and if it returns a non-NULL value, the queue is created successfully. Store this return
value to a variable to use it as a handle to access the queue as shown below.
QueueHandle_t queue1;
queue1 = xQueueCreate(4,sizeof(int));
This will create a 4 element queue in heap memory of int size (2 bytes of each block)
and store the return value to the queue1 handle variable.
To send the values to the queue, FreeRTOS has 2 variants of API for this purpose.
xQueue: The handle of the queue to which the data is being sent (written). This variable
is the same as used to store the return value of xQueueCreate API.
xTicksToWait: The maximum amount of time the task should remain in the Blocked
state to wait for space to become available in the queue.
Setting xTicksToWait to portMAX_DELAY will cause the task to wait indefinitely (without
timing out), provided INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h else
you can use the macro pdMS_TO_TICKS() to convert a time specified in milliseconds
into a time specified in ticks.
First and third arguments are the same as sending API. Only the second argument is
different.
const pvBuffer: A pointer to the memory into which the received data will be copied.
Hope you understood the three APIs. Now, we will implement these APIs in the Arduino
IDE and try to solve the problem statement that we have described above.
Circuit Diagram
1. First, open Arduino IDE and include the Arduino_FreeRTOS.h header file. Now, if any
kernel object like queue is used then include the header file of it. As we are using 16*2
LCD so include the library for it also.
#include <Arduino_FreeRTOS.h>
#include <queue.h>
#include <LiquidCrystal.h>
2. Initialize a queue handle to store the contents of the queue. Also, initialize LCD pin
numbers.
QueueHandle_t queue_1;
3. In void setup(), initialize LCD and serial monitor with 9600 baud rate. Create a queue
and two tasks using the respective APIs. Here we will create a queue of size 4 with
integer type. Create a task with equal priorities and later on try to play with this number.
Finally, start the scheduler as shown below.
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
if (queue_1 == NULL) {
vTaskStartScheduler();
int current_intensity;
while(1) {
Serial.println("Task1");
current_intensity = analogRead(A0);
Serial.println(current_intensity);
int intensity = 0;
while(1) {
Serial.println("Task2");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Intensity:");
lcd.setCursor(11, 0);
lcd.print(intensity);
That’s it. We have finished the coding part of Queue implementation. Complete code
with a working Video can be found at the end.
Now, connect the LCD and LDR with Arduino UNO according to the circuit diagram
upload the code. Open the serial monitor and observe the tasks. You will see tasks are
switching and LDR values are changing according to the light intensity.
NOTE: Most of the libraries made for different sensors are not supported by the
FreeRTOS kernel due to delay function implementation inside the libraries. Delay makes
the CPU stop completely, therefore, the FreeRTOS kernel also stops working and code
will not execute further and it starts misbehaving. So, we have to make the libraries
delay-free to work with the FreeRTOS.
Code
#include <Arduino_FreeRTOS.h>
#include <queue.h>
#include <LiquidCrystal.h>
QueueHandle_t queue_1;
LiquidCrystal lcd(7, 8, 9, 10, 11, 12); // RST E D4 D5 D6 D7
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
queue_1 = xQueueCreate(5, sizeof(int));
if (queue_1 == NULL) {
Serial.println("Queue can not be created");
}
xTaskCreate(TaskDisplay, "Display_task", 128, NULL, 1, NULL);
xTaskCreate(TaskLDR, "LDR_task", 128, NULL, 1, NULL);
vTaskStartScheduler();
}
void loop() {
}
void TaskDisplay(void * pvParameters) {
int intensity = 0;
while(1) {
Serial.println("TaskDisplay");
if (xQueueReceive(queue_1, &intensity, portMAX_DELAY) == pdPASS) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Intensity:");
lcd.setCursor(11, 0);
lcd.print(intensity);
}
}
}
void TaskLDR(void * pvParameters) {
int current_intensity;
while(1) {
Serial.println("TaskLDR");
current_intensity = analogRead(A0);
Serial.println(current_intensity);
xQueueSend(queue_1, ¤t_intensity, portMAX_DELAY);
vTaskDelay( 1000 / portTICK_PERIOD_MS );
}
}
Video
PART 3
Arduino FreeRTOS Tutorial 3- Using
Semaphore and Mutex in FreeRTOS
with Arduino
EMBEDDED
ByRishabh Jain Apr 09, 20200
Semaphore and Mutex in FreeRTOS with Arduino
In previous tutorials, we have covered the basics of FreeRTOS with Arduino and
the Queue kernel object in FreeRTOS Arduino. Now, in this third FreeRTOS tutorial, we
will learn more about FreeRTOS and its advance APIs, which can make you understand
the multi-tasking platform more deeply.
Semaphore and Mutex (Mutual Exclusion) are the kernel objects that are used for
synchronization, resource management and protecting resources from corruption. In the
first half of this tutorial, we will see the idea behind Semaphore, how and where to use
it. In the second half, we will continue with Mutex.
What is Semaphore?
In previous tutorials, we have discussed about task priorities and also get to know that a
higher priority task pre-empts a lower priority task so while execution of high priority task
there may be a possibility that data corruption can happen in lower priority task because
it is not executed yet and data is coming continuously to this task from a sensor which
causes data loss and malfunctioning of the whole application.
So, there is a need to protect resources from data loss and here Semaphore plays an
important role.
1. Binary Semaphore
2. Counting Semaphore
1. Binary Semaphore: It has two integer values 0 and 1. It is somewhat similar to the
Queue of length 1. For example, we have two tasks, task1 and task2. Task1 sends data
to task2 so task2 continuously checks the queue item if there is 1, then it can read the
data else it has to wait until it becomes 1. After taking the data, task2 decrement the
queue and make it 0 That means task1 again can send the data to task2.
From the above example, it can be said that binary semaphore is used for
synchronization between tasks or between tasks and interrupt.
2. Counting Semaphore: It has values greater than 0 and can be thought of queue of
length more than 1. This semaphore is used for counting events. In this usage scenario,
an event handler will ‘give’ a semaphore each time an event occurs (incrementing the
semaphore count value), and a handler task will ‘take’ a semaphore each time it
processes an event (decrementing the semaphore count value).
The count value is, therefore, the difference between the number of events that have
occurred and the number that has been processed.
Now, there can be two types of APIs for the same kernel object. If we have to give
semaphore from an ISR, then normal semaphore API cannot be used. You should use
interrupt protected APIs.
Creating a Semaphore:
To use any kernel object, we have to first create it. For creating a binary semaphore,
use vSemaphoreCreateBinary().
This API does not take any parameter and returns a variable of type
SemaphoreHandle_t. A global variable name sema_v is created to store the
semaphore.
SemaphoreHandle_t sema_v;
sema_v = xSemaphoreCreateBinary();
Giving a semaphore:
For giving a semaphore, there are two versions- one for interrupt and another one for the
normal task.
xTicksToWait: This is the maximum amount of time that the task will wait in Blocked
state for the semaphore to become available. In our project, we will
set xTicksToWait to portMAX_DELAY to make the task_1 to wait indefinitely in Blocked
state until the sema_v is available.
Now, let's use these APIs and write a code to perform some tasks.
Here one push-button and two LEDs are interfaced. The push-button will act as an
interrupt button which is attached to pin 2 of Arduino Uno. When this button is pressed
an interrupt will be generated and an LED which is connected to pin 8 will be turned ON
and when you press it again it will be OFF.
To make the system look multitasking, connect other LEDs with pin 7 which will be in
always blinking state.
#include <Arduino_FreeRTOS.h>
#include <semphr.h>
SemaphoreHandle_t interruptSemaphore;
3. In void setup(), create two tasks(TaskLED and TaskBlink)using the xTaskCreate() API
and then create a semaphore using xSemaphoreCreateBinary().Create a task with equal
priorities and later on try to play with this number. Also, Configure pin 2 as an input and
enable the internal pull-up resistor and attach the interrupt pin. Finally, start the
scheduler as shown below.
void setup() {
pinMode(2, INPUT_PULLUP);
interruptSemaphore = xSemaphoreCreateBinary();
if (interruptSemaphore != NULL) {
4. Now, implement the ISR function. Make a function and name it the same as the
second argument of attachInterrupt() function. To make the interrupt work properly, you
need to remove the debounce problem of the pushbutton using millis or micros function
and by adjusting the debouncing time. From this function,
call interruptHandler() function as shown below.
void debounceInterrupt() {
interruptHandler();
last_micros = micros();
}
In interruptHandler() function, call xSemaphoreGiveFromISR() API.
void interruptHandler() {
xSemaphoreGiveFromISR(interruptSemaphore, NULL);
(void) pvParameters;
pinMode(8, OUTPUT);
while(1) {
digitalWrite(8, !digitalRead(8));
(void) pvParameters;
pinMode(7, OUTPUT);
while(1) {
digitalWrite(7, HIGH);
vTaskDelay(200 / portTICK_PERIOD_MS);
digitalWrite(7, LOW);
vTaskDelay(200 / portTICK_PERIOD_MS);
7. The void loop function will remain empty. Don’t forget it.
void loop() {}
That’s it, complete code can be found at the end of this tutorial. Now, upload this code
and connect the LEDs and push-button with the Arduino UNO according to the circuit
diagram.
Circuit Diagram
After uploading the code, you will see an LED is blinking after 200ms and when the
button is pressed, immediately the second LED will glow as shown in the video given at
the end.
In this way, semaphores can be used in FreeRTOS with Arduino where it needs to
pass the data from one task to another without any loss.
What is Mutex?
As explained above semaphore is a signaling mechanism, similarly, Mutex is a locking
mechanism unlike the semaphore that has separate functions for increment and
decrement but in Mutex, the function takes and gives in itself. It is a technique to avoid
the corruption of shared resources.
To protect the shared resource, one assigns a token card (mutex) to the resource.
Whoever has this card can access the other resource. Others should wait until the card
returned. In this way, only one resource can access the task and others wait for their
chance.
Here we have three tasks, One for printing data on LCD, second for sending LDR data
to LCD task and last task for sending Temperature data on LCD. So here two tasks are
sharing the same resource i.e. LCD. If the LDR task and temperature task send data
simultaneously then one of the data may be corrupted or lost.
So to protect the data loss, we need to lock the LCD resource for task1 until it finished
the display task. Then the LCD task will unlock and then task2 can perform its work.
You can observe the working of Mutex and semaphores in the below diagram.
How to use Mutex in FreeRTOS?
Mutexs are also used in the same way as semaphores. First, create it, then give and
take using respective APIs.
Creating a Mutex:
This API does not take any argument and returns a variable of
type SemaphoreHandle_t. If the mutex cannot be
created, xSemaphoreCreateMutex() return NULL.
SemaphoreHandle_t mutex_v;
mutex_v = xSemaphoreCreateMutex();
Taking a Mutex:
xTicksToWait: This is the maximum amount of time that the task will wait in Blocked
state for the Mutex to become available. In our project, we will
set xTicksToWait to portMAX_DELAY to make the task_1 to wait indefinitely in Blocked
state until the mutex_v is available.
Giving a Mutex:
After accessing the shared resource, the task should return the Mutex so that other
tasks can access it. xSemaphoreGive() API is used to give the Mutex back.
The xSemaphoreGive() function takes only one argument which is the Mutex to be given
in our case mutex_v.
Using the above APIs, Let’s implement Mutex in the FreeRTOS code using Arduino
IDE.
#include <Arduino_FreeRTOS.h>
#include <semphr.h>
SemaphoreHandle_t mutex_v;
3. In void setup(), initialize serial monitor with 9600 baud rate and create two
tasks(Task1 and Task2) using the xTaskCreate() API. Then create a Mutex
using xSemaphoreCreateMutex(). Create a task with equal priorities and later on try to
play with this number.
void setup() {
Serial.begin(9600);
mutex_v = xSemaphoreCreateMutex();
if (mutex_v == NULL) {
4. Now, make task functions for Task1 and Task2. In a while loop of task function, before
printing a message on the serial monitor we have to take a Mutex
using xSemaphoreTake() then print the message and then return the Mutex
using xSemaphoreGive(). Then give some delay.
while(1) {
xSemaphoreTake(mutex_v, portMAX_DELAY);
xSemaphoreGive(mutex_v);
vTaskDelay(pdMS_TO_TICKS(1000));
Now, upload this code on Arduino UNO and open the serial monitor.
You will see messages are printing from task1 and task2.
Complete codes and video for Semaphore and Mutes are given below.
Code
1 CODE for Semaphore:
2
#include <Arduino_FreeRTOS.h>
3
#include <semphr.h>
4
long debouncing_time = 150;
5
volatile unsigned long last_micros;
6
7
SemaphoreHandle_t interruptSemaphore;
8
9
void setup() {
10
pinMode(2, INPUT_PULLUP);
11
xTaskCreate(TaskLed, "Led", 128, NULL, 0, NULL );
12 xTaskCreate(TaskBlink, "LedBlink", 128, NULL, 0, NULL );
interruptSemaphore = xSemaphoreCreateBinary();
13
if (interruptSemaphore != NULL) {
14
attachInterrupt(digitalPinToInterrupt(2), debounceInterrupt, LOW);
15
}
16
}
17
18
void loop() {}
19
20
void interruptHandler() {
21
xSemaphoreGiveFromISR(interruptSemaphore, NULL);
22 }
23
25 {
26 (void) pvParameters;
pinMode(8, OUTPUT);
27
for (;;) {
28
if (xSemaphoreTake(interruptSemaphore, portMAX_DELAY) == pdPASS) {
29
digitalWrite(8, !digitalRead(8));
30
}
31
}
32
}
34 {
35 (void) pvParameters;
36 pinMode(7, OUTPUT);
for (;;) {
37
digitalWrite(7, HIGH);
38
vTaskDelay(200 / portTICK_PERIOD_MS);
39
digitalWrite(7, LOW);
40
vTaskDelay(200 / portTICK_PERIOD_MS);
41
}
42
}
43
void debounceInterrupt() {
45 interruptHandler();
46 last_micros = micros();
47 }
}
48
49
56 mutex_v = xSemaphoreCreateMutex();
59 }
60 xTaskCreate(Task1, "Task1", 128, NULL, 1, NULL);
68
vTaskDelay(pdMS_TO_TICKS(1000));
69 }
70 }
71
73 while(1) {
xSemaphoreTake(mutex_v, portMAX_DELAY);
74
Serial.println("Hi from Task2");
75
xSemaphoreGive(mutex_v);
76
vTaskDelay(pdMS_TO_TICKS(500));
77
}
78
}
79
80
void loop() {
81 }
82
83
84
85
86
87
88
Video
CODE for Semaphore:
#include <Arduino_FreeRTOS.h>
#include <semphr.h>
long debouncing_time = 150;
volatile unsigned long last_micros;
SemaphoreHandle_t interruptSemaphore;
void setup() {
pinMode(2, INPUT_PULLUP);
xTaskCreate(TaskLed, "Led", 128, NULL, 0, NULL );
xTaskCreate(TaskBlink, "LedBlink", 128, NULL, 0, NULL );
interruptSemaphore = xSemaphoreCreateBinary();
if (interruptSemaphore != NULL) {
attachInterrupt(digitalPinToInterrupt(2), debounceInterrupt, LOW);
}
}
void loop() {}
void interruptHandler() {
xSemaphoreGiveFromISR(interruptSemaphore, NULL);
}
void TaskLed(void *pvParameters)
{
(void) pvParameters;
pinMode(8, OUTPUT);
for (;;) {
if (xSemaphoreTake(interruptSemaphore, portMAX_DELAY) == pdPASS) {
digitalWrite(8, !digitalRead(8));
}
}
}
void TaskBlink(void *pvParameters)
{
(void) pvParameters;
pinMode(7, OUTPUT);
for (;;) {
digitalWrite(7, HIGH);
vTaskDelay(200 / portTICK_PERIOD_MS);
digitalWrite(7, LOW);
vTaskDelay(200 / portTICK_PERIOD_MS);
}
}
void debounceInterrupt() {
if((long)(micros() - last_micros) >= debouncing_time * 1000) {
interruptHandler();
last_micros = micros();
}
}
CODE for Mutex:
#include <Arduino_FreeRTOS.h>
#include <semphr.h>
SemaphoreHandle_t mutex_v;
void setup() {
Serial.begin(9600);
mutex_v = xSemaphoreCreateMutex();
if (mutex_v == NULL) {
Serial.println("Mutex can not be created");
}
xTaskCreate(Task1, "Task1", 128, NULL, 1, NULL);
xTaskCreate(Task2, "Task2", 128, NULL, 1, NULL);
}
void Task1(void *pvParameters) {
while(1) {
xSemaphoreTake(mutex_v, portMAX_DELAY);
Serial.println("Hi from Task1");
xSemaphoreGive(mutex_v);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void Task2(void *pvParameters) {
while(1) {
xSemaphoreTake(mutex_v, portMAX_DELAY);
Serial.println("Hi from Task2");
xSemaphoreGive(mutex_v);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void loop() {
}
Tutorial
Understanding Real Time Operating
System (RTOS) and How to use it for
your next Embedded Design
EMBEDDED
BySunakshi Aug 29, 20190
Understanding Real
Time Operating System (RTOS)
Embedded systems have a wide range of application in all the electronic devices around
us, an evident example is the mini laptop that we carry around with us all the time, yes I
am referring to our mobile phones.
What is RTOS?
Real time operating system popularly known as RTOS provides controller with the ability
to respond to input and complete tasks within a specific period of time based on priority.
On the first look, an RTOS might sound like just any other embedded program or
firmware, but it is built on the architecture of an Operating system. Hence, like any
operating system RTOS can allow multiple programs to execute at the same time
supporting multiplexing. As we know the core of a processor or controller can only
execute a single instruction at a time, but the RTOS has something called
the scheduler which decides which instruction to execute first and thus executes the
instructions of multiple programs one after the other. Technically an RTOS only creates
an illusion of multi-taking by executing paralleled instructions one at a time.
This makes RTOS suitable for various applications in real world. In RTOS for any input
whenever a logic has been evaluated which gives the corresponding output. This logic is
measured on the basis of not only the logical creativeness but also on the time duration
in which the specific task has been performed. If a system fails in performing task in that
specific duration of time it is known as system failure.
Why RTOS??
Availability of drivers: There are many drivers available within RTOS, which
allows us to use them directly for various applications.
Scheduled files: RTOS takes care of scheduling so instead of focusing on
scheduling any system we can simply focus on developing application. For example,
task scheduling files are used to define certain actions whenever a set of conditions
are met. RTOS uses certain advanced algorithms for scheduling typically running,
ready and blocked states which while running RTOS keeps more focus on developing
application rather than scheduling.
Flexibility of adding features: Within RTOS even if you are willing to add new
features , you can simply add it without disturbing the existing features
Time sharing is the basis of execution Processes are executed on the basis of
1
of processes in operating system the order of their priority
Types of RTOS
We can categorize real time operating system majorly into three parts namely
Let’s start understanding this type of operating system using an example, the live
example of it is flight control system. Within flight control system whatever tasks is given
by the pilot in the form of an input it should be performed on time. In a hard real time
Operating system, system failures can be tolerated. The features of hard RTOS are:
Easiest example of using soft RTOS is online database, as within soft RTOS the
parameter we are more worried about is speed. Hence, the features of soft RTOS are:
Robot arm which is used to pick objects can be considered as among one of the
example of firm RTOS. Here, within this firm RTOS even if the process is delayed it’s
tolerated.
No firewall issues
Low bandwidth for enhanced performance
Improved security and privacy
Low cost, due to reduction in hardware and software components used for
development
Some major issues related to RTOS
Now, despite of having many advantages for RTOS in real world application, it has
various disadvantages also. Some of the issues related to it are discussed here.
Interrupts are normally used in programs to halt the executing program to divert
the flow to some other important part of the code. Here, within RTOS since quick
response time is required; it is recommended that interrupts should be disabled for
a minimum possible time.
Since, the kernel should also respond for various events it is required to
have lesser size of kernel so that it should fit properly within ROM
Sophisticated features of RTOS should be removed as there is no concept of as
such virtual memory within it.
Tornado – VxWorks
1) VxWorks
VxWorks is a networked real time operating system. To begin with VxWorks we should
have one development kit (target) along with one workstation. Here, development kit is
nothing but the target host or component which communicates with the target server on
the workstation. The target here connects tornado tools such as the shell and
the debugger. Therefore, using VxWorks we will configure and built the systems while
Tornado provides us a graphical user interface and command line tools for
configuration and build.
Very important point which comes into picture here is that while installing tornado within
your system the installation directory should use the pathnames as:
installDir/target. For example if you wish to store your tornado in C:\tornado on a
windows host the full pathname should be identified in that case as
installDir/target/h/vxworks.h.
Here, we will not discuss in detail regarding the features of Vx works (we will leave that
for next tutorial) but we will discuss how the development can be done using C++ within
Vxworks using WindRiver GNU. WindRiver GNU helps us in providing a graphical
analysis regarding the interrupt involved during execution as well as the memory usage
report.
For example, the above stated view of WindRiver explains the associated processor
number along with the priority of tasks (tLowPri & tHighPri). Idle state i.e green color line
stated the time period for which processor is not in its working state, which is observed
to be after every few seconds. t1 , t7, t8 & t9 are nothing but the various processors
used. Here, we are selecting only t7 processor.
Hence, this Windriver is capable of invoking both VxWorks and application module
subroutines. You can launch the Windriver application either form the tornado launch
tool bar (-> i button) later click on menu and then click on shell. Lastly, from the
command prompt type “>windsh target server”.
Compile C++ application source program and get for example hello.cpp file . Later run it
to munch on the .o and compile the generated ctdt.c file. Further, link the application with
ctdt.o to generate downloadable module , hello.out within VxWorks. The output after
executing this VxWorks will be a make file which will be using on some target .
Free RTOS
Generally, whenever we begin with RTOS we generally prefer Vx Works RTOS. But ,
here let’s have a discussion in brief regarding the Free RTOS , which can also be used
to by beginners to go through concept of real time operating system . Free RTOS is
developed by Richard Barry and FreeRTOS team ,also it is owned by Real time
engineers ltd but it is free to use and can be simply downloaded by clicking on the link
below
The biggest advantage of free RTOS which makes it superior in terms of the other
RTOS is its platform independent behavior in terms of hardware i.e the c code which
we will be using to execute an operating system can run on various platforms having
different architecture. Therefore irrespective of whether you are using 8051
microcontroller or some latest ARM microcontroller the code which you wrote along with
the process of execution will be similar for both.
There are many other benefits of using free RTOS over Vx works and other RTOS
operating tools. Some of them can be stated as:
One of the examples of using free RTOS can be explained by using the concept of
combining Free RTOS with Nabto. Nabto is a free web device used to transfer the
information from the device to the browser.