0% found this document useful (0 votes)
615 views102 pages

Single Multi Core Comparision Report Final

This major project report discusses parameters estimation and performance analysis of a single-core processor and multi-core processor using FreeRTOS. It was submitted by 3 students in partial fulfillment of the requirements for the degree of Bachelor of Technology in Electronics and Communication Engineering. The report covers topics related to real-time operating systems, FreeRTOS, the ESP32 microcontroller board, setting up the ESP-IDF framework, project implementation, and results analysis.

Uploaded by

vaishnavi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
615 views102 pages

Single Multi Core Comparision Report Final

This major project report discusses parameters estimation and performance analysis of a single-core processor and multi-core processor using FreeRTOS. It was submitted by 3 students in partial fulfillment of the requirements for the degree of Bachelor of Technology in Electronics and Communication Engineering. The report covers topics related to real-time operating systems, FreeRTOS, the ESP32 microcontroller board, setting up the ESP-IDF framework, project implementation, and results analysis.

Uploaded by

vaishnavi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 102

Major Project Report

On

Parameters Estimation and Performance Analysis of a


Single-core processor and Multi-core processor using
FreeRTOS

Submitted in partial fulfilment of the


Requirements for the award of the degree of Bachelor of Technology
In
Electronics and Communication Engineering
By
Mr. Mutluri Issac 16011A0409
Mr. Mohammed Abrar Pasha 16011A0423
Ms. Kothapalli Ragamayee 16011A0433

Department of Electronics and Communication Engineering


Jawaharlal Nehru Technological University
Hyderabad, College of Engineering Hyderabad
Kukatpally, Hyderabad – 500085
Jawaharlal Nehru Technological University Hyderabad,
College of Engineering Hyderabad.
Department of Electronics and Communication Engineering

CERTIFICATE
This is to certify that the Major Project entitled “Parameters Estimation and
Performance Analysis of a Single-core processor and Multi-core
processor using FreeRTOS” is submitted by

Mr. Mutluri Issac 16011A0409

Mr. Mohammed Abrar Pasha 16011A0423

Ms. Kothapalli Ragamayee 16011A0433

in partial fulfilment for the award of degree in Bachelor of Technology in


Electronics and Communication Engineering during academic year 2019-2020.

Project Guide

Dr. M. Asha Rani


Professor
Jawaharlal Nehru Technological University Hyderabad,
College of Engineering Hyderabad.
Department of Electronics and Communication Engineering

CERTIFICATE
This is to certify that the Major Project entitled “Parameters Estimation and
Performance Analysis of a Single-core processor and Multi-core
processor using FreeRTOS” is submitted by

Mr. Mutluri Issac 16011A0409

Mr. Mohammed Abrar Pasha 16011A0423

Ms. Kothapalli Ragamayee 16011A0433

in partial fulfilment for the award of degree in Bachelor of Technology in


Electronics and Communication Engineering during academic year 2019-2020.

Head of the Department

Dr. K. Anitha Sheela


Professor

Head of Department
ACKNOWLEDGEMENT

There are many people who helped us directly and indirectly to


complete our project successfully. We would like to take this opportunity to thank
one and all. First we would like to express our deep gratitude towards our guide
Dr.M. Asha Rani, Professor, Department of Electronics and Communication
Engineering, JNTUHCEH for her support in the completion of our dissertation. We
would like to express our sincere thanks to Mrs.Dhruva R. Rinku, Associate
professor, Department of ECE, CVRCE for her support and mentoring. We would
like to express our sincere thanks to Dr.K. Anitha Sheela, HOD, Department of
ECE, JNTUHCEH for providing the facilities to complete the dissertation. We
would also like to express our sincere gratitude to Mr. Satyanarayana, Phd
Research Scholar, Department of ECE,JNTUHCEH. We would like to thank all
our faculty and friends for their help and constructive criticism during the project
period. Finally we are very much indebted to our parents for their moral support
and encouragement to achieve goals.

Mr. Mutluri Issac 16011A0409

Mr. Mohammed Abrar Pasha 16011A0423

Ms. Kothapalli Ragamayee 16011A0433

(i)
DECLARATION

We hereby declare that the major project entitled “Parameters


Estimation and Performance Analysis of a Single-core processor
and Multi-core processor using FreeRTOS” is the work done during
the period from December 2019 to June 2020 and is submitted in the
partial fulfilment of the requirements for the award of degree of Bachelor
of Technology in Electronics and Communication Engineering from
JNTUH College of Engineering Hyderabad. The results embodied in this
project have not been submitted to any other university or Institution for
the award of any degree or diploma.

Mr. Mutluri Issac 16011A0409

Mr. Mohammed Abrar Pasha 16011A0423

Ms. Kothapalli Ragamayee 16011A0433

(ii)
TABLE OF CONTENTS

No: Name Page No


Abstract vi
1 Introduction 1

2 Motivation and Problem 3


Statement
3 Real – Time Operating 4
System

4 RTOS and Multi-core 20

5 FreeRTOS – A Detailed 24
Study
6 ESP32- A Feature-rich 42
MCU Board ported with
FreeRTOS

7 Setting up ESP-IDF 49

8 Project Development and 65


Implementation
9 Results and Discussion 85

10 Conclusion and Future 90


scope

References 92

(iii)
LIST OF FIGURES
Fig 3.1 RTOS Architecture
Fig 3.2 RTOS within the embedded system abstraction layers
Fig 3.3 RTOS task states with pre-emptive scheduling
Fig 3.4Pre-emptive Scheduling
Fig 3.5Non-pre-emptive scheduling
Fig 4.1 Asymmetric Multi-core System
Fig 4.2 Symmetric Multi-core System
Fig 5.1 Multi-tasking (v/s) Concurrency
Fig 5.2 Context Switching
Fig 5.3 scheduler task
Fig 5.4 FreeRTOS kernel
Fig 5.5 Writing to and reading from a queue. In this example the queue was created
to hold 5 items, and the queue never becomes full.
Fig 5.6 Use case of a matrix
Fig 5.7 A binary semaphore is equivalent to a queue which can contain one
element
Fig 6.1 ESP-32 Board
Fig 6.2 ESP-32 Structure
Fig 6.3 Esp-32 Pin-Diagram
Fig 7.1 Development of applications for ESP32
Fig 7.2 Project configuration - Home window
Fig 8.1 Folder organisation of the project
Fig 8.2 Folder organisation of project after building the project
Fig 8.3 Example of Task List
Fig 8.4 Example of Run Time Stats
Fig 8.5 Adding tools to the path
Fig 8.6 Configuration Window
Fig 8.7 Serial Flasher Config Window
Fig 8.8 Component Config Window
Fig 8.9 Process of building the project
Fig 8.10 Flashing binaries to the device
Fig 9.1 Result obtained for single-core processor
Fig 9.2 Result obtained for single-core processor
Fig 9.3 Result obtained for dual-core processor
Fig 9.4 Result obtained for dual-core processor

(iv)
LIST OF TABLES

Table 3.1 Comparison between GPOS and RTOS


Table 3.2 Example for pre-emptive scheduling
Table 3.3 Example for Arrival time determination
Table 3.4 Example for Response time determination
Table 3.5 Example for Waiting time determination
Table 3.6 Gnatt Chart
Table 3.7 Example for Waiting time determination
Table 3.8 Gnatt Chart
Table 6.1 ESP-32 Pin Specifications
Table 9.1 Results

(v)
ABSTRACT

With the changing needs, modern-day computers are expected to do multitasking,


which is to run more than one process at a time. Consequently, a Real-Time
Operating System (RTOS) came into the picture and hence, executing embedded
applications demanding strict deadlines and handling multiple tasks has become
widely achievable. An RTOS is similar to a general operating system with added
features of time stamping and pre-emption which ensures its highly predictability
and quick response.
There are many types of RTOS available in the market. The popular ones being,
PSOS, VRTX, RTLinux and FreeRTOS. For microcontroller-based IoT
applications, the need for a built-in functionality to connect to local networks or
cloud has made FreeRTOS a reliable option. FreeRTOS is an open-source
operating system for microcontrollers that make small, low-power edge devices
easy to program, deploy, secure, connect, and manage. FreeRTOS provides both
the core operating system as well as software libraries and hence, it can be greatly
used to run applications feasibly on microcontrollers. The kernel is provided with a
multitasking schedule, multiple memory allocation options, and inter-task
coordination primitives.
This project centres on a detailed study of the FreeRTOS. It aims at running the an
application on single-core processor and multi-core processor and thereby,
analysing the results. The crucial parameters such as execution time, CPU
utilization are assessed on the processor and the performance is analysed for two
processors.

(vi)
1. INTRODUCTION

When we hear the word operating system, the primary thought that strikes our
mind is that of the operating systems used in laptops & computers. Generally, we
use operating systems like windows XP, Linux, Ubuntu, Windows 7,8.8.1, and 10.
In the smart phones, the operating systems are like KitKat, Jellybean,
Marshmallow, and Nougat. In a digital electronic device, there is some sort of
operating system which is developed by the microcontroller program. There are
different types of operating systems to develop for the microcontroller, but our area
of interest over here is a real-time operating system (RTOS).
Why an RTOS? The obvious choice of an RTOS comes from the wide benefits it
offers. The real difference between an RTOS and a general-purpose OS is that with
an RTOS the designers have taken care to ensure that the response times are
known. This is not as simple as it may sound. Modern general-purpose operating
system kernels are very large, with several million lines of code. It can be difficult
to trace through them to find all the possible sources of delay in response. An
RTOS tends to be much smaller than a general-purpose OS making guarantying
the response time more practical. It is intended to serve real-time applications that
process data as it comes in, typically without buffer delays. At the beginning of
this documentation, we shall briefly scrutinize the advantages and classifications of
an RTOS.
We propose to run the applications on a microcontroller device using an RTOS, to
schedule tasks across its cores. A wide variety of RTOS are available in the market
today. The choice for our project is FreeRTOS. We shall also have an introspection
into its features and what makes it reliable for the project.
In this project, we intend to run one or more applications on the chosen
microcontroller, ESP32- a series of low-cost, low-power system on a chip
microcontroller with integrated Wi-Fi and dual-mode Bluetooth. ESP32 is a go-to
chip for making several devices as they’re small, powerful, have a ton of onboard
features, and they’re relatively easy to program, offering lesser challenges
compared to its vast advantages. We shall also dive into its features and try and
understand the pros and cons of the device. The ESP32 comes with a light
operating system – FreeRTOS. The methods to program this chip don’t replace the
FreeRTOS firmware, but rather deploy applications for it to run.
A further peek into this project shows us its aim at running an application on the
single-core processor and the dual-core processor and thereby, analysing and
comparing the results. The real time statistics are assessed and the performance
shall be analysed for both the scenarios.

Page | 1
1.1 Project Scope:
It is possible to re-design the processing algorithm to improve processing speed.
Real time based system performance is improved comparing to non-real-time
based system. Further real time system performance can be improved by
implementing different RTOS algorithm. Also, same design can be implement
using different RTOS operating system.
Moreover, RTOS offers wide areas of benefits. Embedded systems are highly
customized, developed and programmed as per user requirements. Embedded
systems will play an important role in Internet of Things (IoT) due to their unique
characteristics and features such as real time computing, low power consumption,
low maintenance and high availability are becoming the key enabler of IoT. Major
players in embedded system hardware and software developments are aiming to
bring these transformations into their products to take advantage of growing IoT
market. The areas that are going to transform are Real Time Operating Systems
(RTOS) and microprocessors and microcontrollers, followed by memory footprints
and networking, open source communities and developers. Hence, the study of
RTOS provides us with scientifically stimulating opportunities.
2. MOTIVATION AND PROBLEM STATEMENT

2.1 Motivation:
Necessity is the mother of invention and embedded systems are inventions that
were fuelled by the idea of making pre-programs to perform a dedicated narrow
range of functions as part of large systems. Embedded systems are being based
more on instruction-oriented design but not on design-oriented instructions.
Embedded systems and real-time operating systems (RTOS) are fast achieving
ubiquity, blurring the lines between science fiction and hard reality. In general, an
RTOS has features like multitasking, process threads that can be prioritized, and a
sufficient number of interrupt levels. An embedded system is any device controlled
by instructions stored on a chip. These devices are usually controlled by a
microprocessor that executes the instructions stored. With these systems being
widely used in devising many tools, we concern ourselves with running
applications on it that uses the benefits of RTOS.
Among the various RTOS available in the market, one is FreeRTOS. With an
available kernel for it, software developers gain great advantages. Application code
can be developed to be portable and flexible. FreeRTOS is a class of RTOS that is
designed to be small enough to run on a microcontroller – although its use is not
limited to microcontroller applications. Utilizing its benefits and analyzing the
performance is a benchmark initiative.
2.2 Problem Statement:
The objective of the project lies at running an application on a single-core and on
the dual-core of the microcontroller, ESP-32 and thereupon, studying the results. A
comparison of the results shall be drawn to understand which is more
advantageous. The application to be developed is a calculator which performs
addition, subtraction, multiplication, division, square, cosine, sine, natural
logarithm functions and conversion of temperature from one unit to other. The real-
time statistics, response time and CPU utilization shall be determined for the
application. The project also focusses on building the necessary foundations such
as, the study of the real-time operating system and its usage in the embedded field.
For the project, the choice of the real-time operating system is the FreeRTOS, and
a detailed explanation of why and how it plays a crucial role in the project is given
in the further chapters. We shall also inspect the advantages and limitations of
FreeRTOS. Subsequently, it includes the installation of ESP-IDF in the Linux
environment wherein we shall run the required codes.
3. REAL-TIME OPERATING SYSTEM

3.1 Introduction:
Beginning with the basic functionality of an Operating System, it hides the difficult
computations performed by hardware, which the software does on the back end.
We are presented a computer screen that we can work on and all other details that
is the communication between software and hardware is hidden form us. So, an
operating system is a type of software which communicates between application
software and hardware. Moving forward, we shall now discuss what is a real-time
operating system and what makes it more reliable.
An operating system has to provide 3 essential things:

1. Task Scheduling:
The scheduler determines which task to run and when a task will run.
2. Task Dispatching:
The dispatcher handles the necessary operations to get a task ready to go.
3. Inter-task Communication:
This is the mechanism that handles how data and information is
exchanged between tasks and processes on the same machine or from
other machines.

These 3 essential things are what makes up the smallest portion of an OS called the
Kernel. The kernel is considered to be the core of an operating system. Its main
task is to manage the hardware and the processes running on it.

A real time operating system is just a special purpose operating system. The 'real
time' part of the name does not mean that the system responds quickly, it just
means that there are rigid time requirements that must be met. If these time
requirements are not met, the results can become inaccurate or unreliable.
Now, let us compare a general purpose operating system and real time operating
system:

RTOS GPOS

RTOS has unfair scheduling i.e. GPOS has fair scheduling i.e. it can be
scheduling is based on priority. adjusted dynamically for optimized
throughput.

Kernel is pre-emptive either Kernel is non-pre-emptive or has long


completely or up to maximum degree. non-pre-emptive code sections.

Priority inversion is a major issue. Priority inversion usually remains


unnoticed.

It has a predictable behaviour. There is no predictability.

It works under worst case assumptions. It optimizes for the average case.

It does not have a large memory. It has a large memory.

Table 3.1 Comparison between GPOS and RTOS

From the above table it is evident that the need to use an RTOS is when there is a
need to monitor and control physical processes in a timely manner. The constraints
one has to deal with when using RTOS are tight scheduling, predictability, and
robustness.

Fig 3.1 RTOS Architecture


There are three kinds of RTOS:

1. Hard Real Time OS:


In the hard real-time OS, system delays are known or are at the least, bounded.
It is said to be operating correctly if the system can return results within any
time constraints.
2. Soft Real Time OS:
In the soft real-time OS, critical tasks get priority over other tasks and will
retain priority until the task is completed. This is another way of saying that
real time tasks cannot be kept waiting indefinitely. Soft real time makes it
easier to mix the system with other systems.
3. Firm Real Time OS:
In the firm real-time, an operating system has certain time constraints, they are
not strict ad it may cause undesired effects.

3.2 Working with Real-Time Operating System:


Embedded systems are microcontroller-based systems that are designed to perform
specific functions such as reading sensor data, responding to external events,
communicating with other systems, controlling processes, etc. The tricky part is to
make the distinction of what exactly qualifies such a system as real-time. Aren’t all
embedded systems operating in real-time? In order for an embedded system to be
classified as real-time, it must guarantee a strictly defined response time to the
events it is tasked with observing and controlling. It should be noted that all
systems have a response time (latency). Real-time embedded systems do not react
immediately to every event but can guarantee a worse case response time.

Real-time operating systems (RTOS) provide a framework that enables guaranteed


response times and deterministic behaviour. This is achieved using a scheduling
mechanism. This mechanism is at the heart of every RTOS. We can design a real-
time embedded system without the use of RTOS, however, using one can make the
design process shorter and the whole system easier to manage.

As part of the embedded system abstraction layers, an RTOS is placed above the
low-level device drives and below the user application. The RTOS does not
provide low-level drivers for microcontroller peripherals. Some RTOS may
contain middleware software such as networking, file systems, etc.
Fig 3.2 RTOS within the embedded system abstraction layers

3.3 RTOS Main Components:

3.3.1 Tasks:
A piece of code performing a specific function is usually called a task. A task can
be also referred to as a thread, process, activity, etc. in different operating systems.
An operating system is expected to execute many different tasks at once – reading
inputs, outputting data, reacting to events, etc. A single microprocessor, however,
can execute code from only one task at a time. Achieving the required multitasking
is possible using a mechanism known as time multiplexing. Each task that the
operating system should execute is given a time window in which it can utilize the
CPU, and then the execution of another task proceeds according to a predefined
scheduling algorithm. If the timing requirements of all the tasks are fulfilled, we
can say we have concurrent like executions of multiple tasks without the use of
separate microprocessors for each task.

Multitasking is the way of sharing the processor’s time, so that a number of tasks
can execute pseudo-parallelly by taking turns to use the CPU. At any given point in
time, only one process can be present in the running state.

An RTOS task usually has the following main states:

 Running State: The task’s code is currently being executed by the CPU.
 Ready State: The task is ready to be put into the running state. In the ready
state, the task does not consume any CPU cycles.
 Blocked State: The task is in this state when it waits for the occurrence of
some event. In this state, the task does not consume any CPU cycles.
 Suspended State: Tasks in the suspended state are not active anymore.

Some RTOS implementations may contain additional task states or have a different
naming convention than the ones listed above, but they are all following the same
logic.

Fig 3.3. RTOS task states with pre-emptive scheduling


The Kernel takes care of the task. It involves the following.

 Creating a task
 Deleting a task
 Changing the priority of the task
 Changing state of the task

3.3.2. Scheduler:
The scheduler is an integral part of every RTOS. It controls which task should be
executed at any given point in time. The scheduler may use various types of
algorithms for performing the scheduling of the tasks. Almost all of these
algorithms can be classified into two main types:
 Pre-emptive Scheduling: This algorithm allows the interruption of a
currently running task, so another one with higher priority can be run.
 Non-pre-emptive Scheduling (Co-operative Scheduling): Once a task is
started it cannot be interrupted, it will run until it decides that it should
release the CPU to another task.

3.3.3 Synchronization and Messaging:

Messaging provides a means of communication with other system and between the
tasks. The messaging services include the following.

 Semaphores
 Event flags
 Mailboxes
 Pipes
 Message queues
Semaphores are used to synchronize access to shared resources, such as common
data areas. Event flags are used to synchronize the inter-task activities. Mailboxes,
pipes, and message queues are used to send messages among tasks.

3.3.4. RTOS Service:

The most important part of the operating system is the Kernel. Tasks are relived of
monitoring the hardware. It’s the responsibility of the kernel to manage and
allocate the resources. As tasks cannot acquire CPU attention all the time, the
kernel must also provide some more services. These include the following.

 Memory management
 I/O system management
 System protection
 Networking
 Command interruption
 Time services
 Device management services
3.4 Features of an RTOS:

Selecting a real-time operating system (RTOS) involves these five key features as
must-haves.

3.4.1. Reliability
Any RTOS must be reliable. This means that it operates for a reasonably long time
without human interference. Reliability also means the configuration to enable the
system to choose the right or most profitable action for current operations. For
example, a system for the telecom or banking industries needs to consider the cost
of terminating or engaging certain actions, compared to a phone or calculator
whose cost is limited to an individual, game, app function, etc.

3.4.2. Predictability
A system must execute actions within a known time frame and produce known
results. Such results are determined by the procedures or operations taking place.
The determination is by targets set during production or procedural planning.

3.4.3. Performance
Real-time operating systems are designed to make work easier. Every system must
solve a problem or reduce the workload. As such, the developer should provide a
system that is easily assimilated with existing software and hardware as well as
aligned to the goals of the organization.

3.4.4. Manageability
This means a system whose veracity or bulkiness is manageable. The software and
hardware required to operate the RTOS must be of reasonable size. Technicians
should also be easy to find and orient. The idea is to reduce the cost of
implementation.

3.4.5. Scalability
The needs of any production or event environment change with time. This means
that a system may require an upgrade or downgrade. Such provisions must be
made during design and installation of any RTOS.

3.5 RTOS Scheduling Algorithms:

Having introduced the basic concepts of real-time operating systems (RTOS), we


now take a deeper look into one of the most important things when designing an
embedded system using an RTOS – the scheduling of the tasks and the algorithms
that are used.

3.5.1 Scheduling Process:

Scheduling is the process of deciding which task should be executed at any point in
time based on a predefined algorithm. The logic for the scheduling is implemented
in a functional unit called the scheduler. The scheduling process is not present only
in RTOS, it can be found in one form or another even in simple “bare-bone”
applications.

Different RTOS distributions may support a variety of scheduling algorithms. It is


important that we choose the algorithm before the development of the user
application starts. Like many things in the engineering field, there is not a universal
algorithm that is suitable for every use case. There are always trade-offs. In this
case, they are mainly related to speed (response times), implementation complexity,
etc. The chosen algorithm should always enable the timing requirements of the
tasks to be met.

3.5.2 Types of Scheduling Algorithms:

There are many scheduling algorithms that can be used for scheduling task
execution on a CPU. They can be classified into two main types: pre-emptive
scheduling algorithms and non-pre-emptive scheduling algorithms.

3.5.2.1 Pre-emptive Scheduling:

Pre-emptive scheduling allows the interruption of a currently running task, so


another one with more “urgent” status can be run. The interrupted task is
involuntarily moved by the scheduler from running state to ready state. This
dynamic switching between tasks that this algorithm employs is, in fact, a form of
multitasking. It requires assigning a priority level for each task. A running task can
be interrupted if a task with a higher priority enters the queue.
Fig 3.4 Pre-emptive Scheduling

As an example, let’s have three tasks called Task 1, Task 2 and Task 3. Task 1 has
the lowest priority and Task 3 has the highest priority. Their arrival times and
execute times are listed in the table below.

Task Name Arrival Time Execute


(microseconds) Time(microseconds)

Task1 10 50

Task 2 40 50

Task 3 40 40

Table 3.1 Example for pre-emptive scheduling

In the table 3.1, we can see that Task 1 is the first to start executing, as it is the first
one to arrive (at t = 10 μs). Task 2 arrives at t = 40μs and since it has a higher
priority, the scheduler interrupts the execution of Task 1 and puts Task 2 into
running state. Task 3 which has the highest priority arrives at t = 60 μs. At this
moment Task 2 is interrupted and Task 3 is put into running state. As it is the
highest priority task it runs until it completes at t = 100 μs. Then Task 2 resumes
its operation as the current highest priority task. Task 1 is the last to complete is
operation.
3.5.2.2 Non-pre-emptive Scheduling (Co-Operative Scheduling):

In non-pre-emptive scheduling, the scheduler has more restricted control over the
tasks. It can only start a task and then it has to wait for the task to finish or for the
task to voluntarily return the control. A running task cannot be stopped by the
scheduler.

Fig 3.5 Non-pre-emptive scheduling


If we take the three tasks specified in the table from the previous chapter and
schedule them using a non-pre-emptive algorithm we get the behaviour shown in
the figure. Once started, each task completes its operation and then the next one
starts.

The non-pre-emptive scheduling can simplify the synchronization of the tasks, but
that is at the cost of increased response times to events. This reduces its practical
use in complex real-time systems.

3.6 Popular Scheduling Algorithms:

We will now introduce some of the most popular scheduling algorithms that are
used in CPU scheduling. Not all of them are suitable for use in real-time embedded
systems. Currently, the most used algorithms in practical RTOS are round-robin
scheduling, and pre-emptive priority scheduling. In an attempt to present the right
explanation, the description and implementation of a few examples in simple C-
language are given.

3.6.1 First Come, First Served (FCFS):

FCFS is a non-pre-emptive scheduling algorithm that has no priority levels


assigned to the tasks. The task that arrives first into the scheduling queue (i.e.
enters ready state), gets put into the running state first and starts utilizing the CPU.
It is a relatively simple scheduling algorithm where all the tasks will get executed
eventually. The response time is high as this is a non-pre-emptive type of
algorithm.

3.6.2 Shortest Job First (SJF):

In the shortest job first scheduling algorithm, the scheduler must obtain
information about the execution time of each task and it then schedules the one
with the shortest execution time to run next.

SJF is a non-pre-emptive algorithm, but it also has a pre-emptive version. In the


pre-emptive version of the algorithm (shortest remaining time) the parameter on
which the scheduling is based is the remaining execution time of a task. If a task is
running it can be interrupted if another task with shorter remaining execution time
enters the queue.

A disadvantage of this algorithm is that it requires the total execution time of a task
to be known before it is run.

3.6.3 Priority Scheduling:

Priority scheduling is one of the most popular scheduling algorithms. Each task is
assigned a priority level. The basic principle is that the task with the highest
priority will be given the opportunity to use the CPU.

In the pre-emptive version of the algorithm, a running task can be stopped if a


higher priority task enters the scheduling queue. In the non-pre-emptive version of
the algorithm once a task is started it can’t be interrupted by a higher priority task.

Of course, not all tasks can have unique priority levels and there will always be
tasks that have the same priority. Different approaches can be used for handling the
scheduling of those tasks (e.g. FCFS scheduling or round-robin scheduling).

3.6.4 Round Robin Scheduling:

Round-robin is a pre-emptive type of scheduling algorithm. There are no priorities


assigned to the tasks. Each task is put into a running state for a fixed predefined
time. This time is commonly referred to as time-slice (aka quantum). A task cannot
run longer than the time-slice. In case a task has not completed by the end of its
dedicated time-slice, it is interrupted, so the next task from the scheduling queue
can be run in the following time slice. A pre-emptied task has an opportunity to
complete its operation once it’s again its turn to use a time-slice.

An advantage of this type of scheduling is its simplicity and relatively easy


implementation.

RTOS is thereby, a preferred choice for many embedded device manufacturers and
embedded programmers. There are many RTOS flavours available. FreeRTOS is
widely followed by practitioners. Further from here, a detailed study on FreeRTOS
and its features is done to analyse what makes it the right choice for the project.

3.7 Parameters involved in Scheduling:

While dealing with some CPU scheduling algorithms, we encounter with


some terms like Burst time, Arrival time, Exit time, Waiting time, Response
time, Turnaround time, and throughput. They are explained in detail in the
coming pages.

3.7.1 Burst Time:

Every process in a computer system requires some amount of time for its
execution. This time is both the CPU time and the I/O time. The CPU time is the
time taken by CPU to execute the process. While the I/O time is the time taken by
the process to perform some I/O operation. In general, we ignore the I/O time and
we consider only the CPU time for a process. So, burst time is the total time
taken by the process for its execution on the CPU.

3.7.2 Arrival Time:


Arrival time is the time when a process enters into the ready state and is ready for
its execution.
Consider the following example.
Process Arrival time (in ms) Burst time (in ms)
P1 0 8
P2 1 7
P3 2 10
Table 3.2 Example for Arrival time determination
Here in the above example, the arrival time of all the 3 processes are 0 ms, 1 ms,
and 2 ms respectively.

3.7.3 Exit Time:

Exit time is the time when a process completes its execution and exit from the
system.

3.7.4 Response Time:

Response time is the time spent when the process is in the ready state and gets the
CPU for the first time. For example, here we are using the First Come First Serve
CPU scheduling algorithm for the below 3 processes:

Process Arrival time (in ms) Burst time (in ms)


P1 0 8
P2 1 7
P3 2 10
Table 3.3 Example for Response time determination
Here, the response time of all the 3 processes are:

 P1: 0 ms
 P2: 7 ms because the process P2 have to wait for 8 ms during the execution
of P1 and then after it will get the CPU for the first time. Also, the arrival
time of P2 is 1 ms. So, the response time will be 8-1 = 7 ms.
 P3: 13 ms because the process P3 have to wait for the execution of P1 and
P2 i.e. after 8+7 = 15 ms, the CPU will be allocated to the process P3 for
the first time. Also, the arrival of P3 is 2 ms. So, the response time for P3
will be 15-2 = 13 ms.
Response time = Time at which the process gets the CPU for the first time -
Arrival time
3.7.5 Waiting Time:

Waiting time is the total time spent by the process in the ready state waiting for
CPU. For example, consider the arrival time of all the below 3 processes to be 0
ms, 0 ms, and 2 ms and we are using the First Come First Serve scheduling
algorithm.

Process Arrival time (in ms) Burst time (in ms)


P1 0 8
P2 0 7
P3 2 10
Table 3.4 Example for Waiting time determination
P1 P2 P3
0ms 8ms 8ms 15ms 15ms 25ms
Table 3.5 Gnatt Chart
Then the waiting time for all the 3 processes will be:

 P1: 0 ms
 P2: 8 ms because P2 have to wait for the complete execution of P1 and
arrival time of P2 is 0 ms.
 P3: 13 ms because P3 will be executed after P1 and P2 i.e. after 8+7 = 15
ms and the arrival time of P3 is 2 ms. So, the waiting time of P3 will be: 15-
2 = 13 ms.
Waiting time = Turnaround time - Burst time

In the above example, the processes have to wait only once. But in many other
scheduling algorithms, the CPU may be allocated to the process for some time
and then the process will be moved to the waiting state and again after some time,
the process will get the CPU and so on.

There is a difference between waiting time and response time. Response time is
the time spent between the ready state and getting the CPU for the first time. But
the waiting time is the total time taken by the process in the ready state. Let's take
an example of a round-robin scheduling algorithm. The time quantum is 2 ms.
Process Arrival time (in ms) Burst time (in ms)
P1 0 4
P2 0 6
Table 3.6 Example for Waiting time determination

P1 P2 P1 P2 P2
0ms 2ms 2ms 4ms 4ms 6ms 6ms 8ms 8ms 10ms
Table 3.7 Gnatt Chart

In the above example, the response time of the process P2 is 2 ms because after 2
ms, the CPU is allocated to P2 and the waiting time of the process P2 is 4 ms i.e
turnaround time - burst time (10 - 6 = 4 ms).

3.7.6 Turnaround Time:

Turnaround time is the total amount of time spent by the process from coming in
the ready state for the first time to its completion.

Turnaround time = Burst time + Waiting time

or

Turnaround time = Exit time - Arrival time

For example, if we take the First Come First Serve scheduling algorithm, and the
order of arrival of processes is P1, P2, P3 and each process is taking 2, 5, 10
seconds. Then the turnaround time of P1 is 2 seconds because when it comes at
0th second, then the CPU is allocated to it and so the waiting time of P1 is 0 sec
and the turnaround time will be the Burst time only i.e. 2 seconds. The
turnaround time of P2 is 7 seconds because the process P2 have to wait for 2
seconds for the execution of P1 and hence the waiting time of P2 will be 2
seconds. After 2 seconds, the CPU will be given to P2 and P2 will execute its
task. So, the turnaround time will be 2+5 = 7 seconds. Similarly, the turnaround
time for P3 will be 17 seconds because the waiting time of P3 is 2+5 = 7 seconds
and the burst time of P3 is 10 seconds. So, turnaround time of P3 is 7+10 = 17
seconds.
Different CPU scheduling algorithms produce different turnaround time for the
same set of processes. This is because the waiting time of processes differ when
we change the CPU scheduling algorithm.

3.7.7 Throughput:

Throughput is a way to find the efficiency of a CPU. It can be defined as the


number of processes executed by the CPU in a given amount of time. For
example, let's say, the process P1 takes 3 seconds for execution, P2 takes 5
seconds, and P3 takes 10 seconds. So, throughput, in this case will be (3+5+10)/3
= 18/3 = 6 seconds.
4. RTOS AND MULTICORE
4.1 MULTI-CORE PROCESSORS:
Recently introduced multi-core based MCUs (Microcontroller Units) have raised
the complexity level of embedded system designs. The use of an RTOS, however,
provides a framework for multi-core that can dramatically reduce time to market.
A real-time kernel is software that manages the time of a CPU and ensures that
critical tasks are processed in a timely manner. The term “real-time operating
system” (RTOS) is often confused with real-time kernels; a kernel is actually a
subset of an RTOS.
Most 32-bit embedded designs would benefit from the use of a real-time kernel,
given that a kernel offers a framework to build upon as well as the ability to split
application code into smaller, more manageable chunks. The framework includes
many useful services for the application programmer such as multitasking;
interrupt management, inter-task communication and signalling, resource
management, time management, memory partition management, and more.
Microcontrollers have traditionally been equipped with single cores. In fact, they
have been called as single-core processors, not until recently when multi core
processors emerged two decades back and have proven to be beneficial in many
ways.

Performance is the first thing one thinks about when considering the use of a multi-
core device, but, just as important especially for battery-operated embedded
systems, multiple cores allow you to run your product slower and thus reduce the
power consumption with basically the same performance as a single-core system
running at a much higher frequency.

With multi-core embedded systems becoming so common, let us outline some of


the basics, reviewing the possible hardware architectures. Broadly, there are two
options: Asymmetric Multi-Processing and Symmetric Multi-Processing.

4.1.1 Asymmetric Multi-core – AMP:

An AMP system has multiple CPUs, each of which may be a different architecture
(but can be the same – i.e. may be either heterogeneous or homogeneous multi-
core). Each has its own address space (though some or all of the memory may be
shared with other cores), and each may or may not run an OS (and the OSes need
not be the same). Some kind of communication facility between the CPUs is
provided for each.
Fig 4.1 Asymmetric Multi-core System

Advantages:

 High real-time performance.

Disadvantages:

 Necessary to assign each program to a CPU.

 Programs must take the CPU into consideration.

 CPU load balance is fixed.

4.1.2 Symmetric Multi-core – SMP:

Like AMP, an SMP system has multiple CPUs, but in this case each has exactly
the same architecture – i.e. it must be a homogeneous multi-core design. CPUs
share memory space (or at least some of it). Normally a single OS is used that runs
on all the CPUs, dividing work between them – another significant difference from
AMP. Some kind of communication facility between the CPUs is provided – this is
normally through shared memory, but accessed through the API of the OS.

Advantages:

 Dynamic load balance.

 Software can be developed without concern for the code.


Disadvantages:

 Low real-time performance.

Fig 4.2 Symmetric Multi-core System

4.2 Multi-core and RTOSs:

Having multiple cores doesn’t render obsolete the need for an RTOS kernel. A
kernel is still highly desirable to make optimum use of both cores. In fact, both
cores run their own instance of the same or possibly different kernels to manage
the multiple tasks.

Debugging a multiple-core system is certainly more complicated than a single-core


system, but if you keep a clean separation of functionality between cores then each
can be debugged in isolation and possibly by different development teams

A kernel allows a designer to determine CPU use on a per-task basis and thus
better optimize the design by load balancing the tasks between cores. This load
balancing would typically be done at design time as opposed to run time. In other
words, having an RTOS kernel makes it easier to move functionality between
cores.

Ideally, both cores would be identical. This would make development slightly
easier since you would not have to deal with the slightly different cores. Also, it’s
quite unlikely that these devices will be limited to only two cores. AMP is very
scalable and in the coming years, we should see MCUs with many cores showing
up on the market.

Multi-core-based MCUs increase the complexity of embedded system designs but


offers the benefit of increased performance as well as lower power consumption.
Each core should use an RTOS to make better use of the CPU and its associated
resources and functions. Inter-core communication should be kept to a minimum to
avoid interdependencies.

4.3 DUAL-CORE PROCESSORS:


Due to extensive use of digital data there is a need of dual core processors to meet
the required speeds. Multi core processors are more capable of handling huge
databases and offer high performances than the single core processors. A dual-
core CPU has two independent processors and their caches and cache controllers
on a single silicon chip. In dual core processors each core has the capability of
handling incoming streams which increases the performance efficiency. In this
paper we will see how the clock speeds of dual core processors vary from the
single core processors and their design and computational capabilities.

SMT (simultaneous multi threading) technology is used in the dual core processors
so that the software can recognize the multi threading. A dual processor system has
two separate physical computer processors located on the same circuit board and it
is considered as a subset of a larger set of symmetrical processor systems.

A dual core processor design can provide each physical processor its own die
cache or may provide a single die cache which is shared by two processors. The
memory performance of the dual core processors can be viewed by the random
latency and by the sequential access.

Advantages:

The dual core processors can execute the multitasking operations and the user can
do the other work in the background simultaneously. Dual core CPUs have high
performance in terms of cache operations than single core processors..These CPUs
can deliver a result with high processing speeds and minimal risk of errors

The appearance of dual-core processors and solutions with additional new features
once again indicate that the clock frequency race is no longer acute. I believe that
in the near future we will not select processors according to their clock rates but
will take into account the whole lot of various characteristics, including not only
the working frequency, but also the number of processor cores, cache memory size,
additional technologies support and maybe more.

As the time passes and the OS and software developers adapt more to the new
working conditions, the multi-core processor architectures can lead the industry to
the new performance levels.
5. FREERTOS – A Detailed Study

FreeRTOS stands for Free Real-Time Operating System. It is an open-source


operating system targeted on embedded applications that run on a microcontroller
and need real-time event processing. It is basically a real-time operating
system kernel for embedded devices that has been ported to 35 microcontroller
platforms.

5.1 Features of FreeRTOS:


FreeRTOS is designed to be small and simple. To make the code readable, easy to
port, and maintainable, it is written mostly in C, but there are a few assembly
functions included where needed (mostly in architecture-specific scheduler
routines).
FreeRTOS provides methods for multiple threads or tasks, mutexes, semaphores
and software timers. A tick-less mode is provided for low power applications.
Thread priorities are supported. FreeRTOS applications can be completely
statistically allocated. Alternatively, RTOS objects can be dynamically allocated
with five schemes of memory allocation provided:

 allocate only;
 allocate and free with a very simple, fast, algorithm;
 a more complex but fast allocate and free algorithm with memory coalescence;
 an alternative to the more complex scheme that includes memory coalescence
that allows a heap to be broken across multiple memory areas.
 and C library allocate and free with some mutual exclusion protection.

Here are some reasons why FreeRTOS is a good choice for our application:

 Provides a single and independent solution for many different architectures


and development tools.
 Is known to be reliable. Confidence is assured by the activities undertaken
by the SafeRTOS sister project.
 Is feature rich and still undergoing continuous active development.
 Has a minimal ROM, RAM and processing overhead. Typically,an RTOS
kernel binary image will be in the region of 6K to 12K bytes.
 Is very simple – the core of the RTOS kernel is contained in only 3 C files.
The majority of the many files included in the .zip file download relate only
to the numerous demonstration applications.
 Is truly free for use in commercial applications (see license conditions for
details).
 Has commercial licensing, professional support and porting services
available in the form of OPENRTOS from our partner WITTENSTEIN high
integrity systems.
 Has a migration path to SafeRTOS, which includes certifications for the
medical, automotive and industrial sectors.
 Is well established with a large and ever-growing user base.
 Contains a pre-configured example for each port. No need to figure out how
to setup a project – just download and compile!
 Has an excellent, monitored, and active free support forum.
 Has the assurance that commercial support is available should it be required.
 Provides ample documentation.
 Is very scalable, simple and easy to use.

Before understanding the terminology used, we shall consider an example to


magnify where and till when the usage of FreeRTOS becomes necessary.

Consider a Simple Digital Watch. Its only functionality is that it tells time. Such a
product can be developed using a simple 8-bit microcontroller and an operating
system is of very little use here as it only runs a single thread continuously. A
thread is basically a step by step process.

Now let’s add a little bit more complexity, Let’s add a stopwatch to the timer, and
an alarm clock and a count-down timer. Now the system has to run multiple tasks
at the same time. These tasks include

 it has to keep track of the time,


 if a stopwatch is running it has to run count the number of seconds that have
expired since the start button has been clicked and
 if it’s time to ring the alarm to wake you up it needs to start beeping.
For such an application there are effective software design patterns that can be
used so that interrupts and loops can be used to implement this without an
operating system on a microcontroller.
Yes, using an operating system can certainly help ease the development process as
it will take care of task priorities and the timing needs. But these devices are
usually resource-constrained in terms of memory and processing power as an
attempt to keep the cost low to keep up with the competition.The software
complexity without an operating system is still considered manageable and hence
they are usually built without an OS.

Now let’s add more functionality to our digital watch, let’s put in a pedometer, an
altimeter and a temperature sensor. Now the tasks that it needs to compute
parallelly includes

 Keeping track of time


 Stopwatch
 Alarm clock
 Pedometer signal processing
 Temperature sensing
 Altitude sensing
 Display information on an LCD/LED screen
So, on one side it has to do the job of a normal digital watch, on the other hand it
needs to do some smart features by taking in raw accelerometer and gyroscope
values from an IMU sensor and do some signal processing to calculate the steps,
continuously read the temperature values from the temperature sensor, read altitude
measurements and display it all on the screen.
Even this application can be designed without an operating system, by just
architecting some good design patterns, but usually, this kind of system can be
benefited by adding a simple embedded operating system as the software
complexity without an operating system is usually considered to be not very easy
to develop and maintain. Also, such a system is usually built using 32-bit
microcontrollers with enough resources to accommodate an operating system and
this makes engineers of such products to go for an operating system-based
software. This is where simple real-time operating systems like FreeRTOS comes
in.
Let’s go a bit further and look at a more complex device to see where the benefits
of FreeRTOS ends.

Let’s now add-in a few more features, let’s add in a heart rate monitor, a sleep
monitor, a Bluetooth communication system to communicate with your phone, an
mp3 player to stream music to your Bluetooth earphones and a notification system
to make your smartwatch vibrate every time you receive a call on your paired
smartphone. Essentially all the features of smartwatches these days.

Now the system is not simple enough to be made using simple embedded operating
systems like FreeRTOS. It is still possible to do it but then the added complexity is
not easy for the development and maintenance of the system. Now we will
probably need a much more sophisticated Operating system like Embedded
Linux to accomplish our tasks.

This example gives us a brief idea of the places where simple embedded operating
systems like FreeRTOS fit in.

5.2 Concepts to understand FreeRTOS:

There are 7 concepts and associated terminology to start developing using


FreeRTOS, they include the following.

 Tasks
 Multitasking
 Context switching
 Schedulers
 Kernels
 Inter-task communications
 Interrupts

We have previously looked into a few concepts like Tasks, Schedulers in the
previous chapter, Real-Time Operating System.

5.2.1 Tasks:

In the examples above of using a digital watch, each separate activity/feature is a


Process.
Process is a single line of actions, that are executed one after the other.
In FreeRTOS these processes are called Tasks. Each such task has its own stack in
combination with another block of memory to store information about itself known
as a Task Control Block which is used by FreeRTOS kernel to manage the task.
In FreeRTOS, these tasks are implemented as C functions with a never-ending
loop.
A task defined by a simple C function, taking one void* argument and returning no
thing.
void ATaskFunction( void *pvParameters );

Any created task should never end before it is destroyed. It is common for task’s
code to be wrapped in an infinite loop, or to invoke vTaskDestroy(NULL) before it
reaches its final brace. As any code in infinite loop can fail and exit, it is safer even
for a repetitive task, to invoke vTaskDelete() before its final brace.

The task created using vTaskCreate() takes the following arguments.


 pvTaskCode: A pointer to the function where the task is implemented.
 pcName: It is the name given to the task. This is useless to FreeRTOS but is
intended for debugging purpose only.
 usStackDepth: It is the length of the stack for this task in words. The actual
size of the stack depends on the microcontroller.
 pvParameters: A pointer to arguments given to the task. A good practice
consists in creating a dedicated structure, instantiate and fill it then give its
pointer to the task.
 uxPriority: Priority given to the task.
 pxCreatedTask: A pointer to an identifier that allows to handle the task. If
the task does not have to be handled in the future, this can be leavedNULL.

A task is destroyed using xTaskDestroy() routine. It takes an argument


pxCreatedTask which is given when the task was created.
void vTaskDelete( xTaskHandlepxTask );

When a task is deleted, it is responsibility of idle task to free all allocated memory
to this task by kernel. Notice that all memory dynamically allocated must be
manually freed.

5.2.2 Multitasking and state changes:

Multitasking is the way of sharing the processor’s time, so that a number of tasks
can execute pseudo-parallelly by taking turns to use the CPU. At any given point in
time, only one process can be present in the running state. Other processes are
usually in one of the following 3 states. Ready state, Blocked state or Suspended
state. They have been discussed in detail in the previous chapter, Real-Time
Operating System.

The use of a multitasking operating system can simplify the design of what would
otherwise be a complex software application:

 The multitasking and inter-task communications features of the operating


system allow the complex application to be partitioned into a set of smaller
and more manageable tasks.
 The partitioning can result in easier software testing, work breakdown
within teams, and code reuse.
 Complex timing and sequencing details can be removed from the
application code and become the responsibility of the operating system.

Multitasking Vs Concurrency

A conventional processor can only execute a single task at a time – but by rapidly
switching between tasks a multitasking operating system can make it appear as if
each task is executing concurrently. This is depicted by the diagram below which
shows the execution pattern of three tasks with respect to time. The task names are
colour coded and written down the left hand. Time moves from left to right, with
the coloured lines showing which task is executing at any particular time. The
upper diagram demonstrates the perceived concurrent execution pattern, and the
lower the actual multitasking execution pattern.

Fig 5.1 Multitasking vs Concurrency


5.2.3 Context Switching:

As a task executes it utilizes the processor / microcontroller registers and accesses


RAM and ROM just as any other program. These resources together (the processor
registers, stack, etc.) comprise the task execution context.
Fig 5.2 Context Switching

A task is a sequential piece of code – it does not know when it is going to get
suspended (swapped out or switched out) or resumed (swapped in or switched in)
by the kernel and does not even know when this has happened. Consider the
example of a task being suspended immediately before executing an instruction
that sums the values contained within two processor registers. While the task is
suspended other tasks will execute and may modify the processor register values.
Upon resumption the task will not know that the processor registers have been
altered – if it used the modified values the summation would result in an incorrect
value.

To prevent this type of error it is essential that upon resumption a task has a
context identical to that immediately prior to its suspension. The operating system
kernel is responsible for ensuring this is the case – and does so by saving the
context of a task as it is suspended. When the task is resumed its saved context is
restored by the operating system kernel prior to its execution. The process of
saving the context of a task being suspended and restoring the context of a task
being resumed is called context switching.
5.2.4 Schedulers:

Tasks are made to change from running state to one of the inactive states by
the scheduler by the process of pre-empting.
A currently running task is stopped without any notice, in favour of a higher
priority task, and its contents and status information are stored in memory so that
when it runs again, it can start from where it left off.
It is a task from the FreeRTOS operating system, whose task is to manage the state
of the other tasks.
It is the most important part of any real-time operating system. Its duty is to make
sure no lower priority tasks can be in running state while a higher priority task is in
the ready state so that the timing requirements can be met.

But as we just saw, the scheduler is also a task, which means it can only take a
limited amount of processor’s time, else it will do more harm than good as the
actual application needs to run on top of the operating system.

So, the scheduler usually runs once every fixed duration of time, say for example
once every 10 milliseconds. The scheduler occupies the CPU, say for 0.5ms and
during this time, it will see the manage the tasks so that only the highest priority
task in the ready state gets the next slot in time.

Fig 5.3 Scheduler Task


Here the green task is the scheduler. It starts at 0ms and runs till 0.5ms and chooses
Task1 to run till 10ms. Then at 10ms the scheduler comes back again and this time
picks another task to run from the ready queue which is Task 2 and now Task 2
runs till 20ms.

If both the tasks of the same priority are in the ready state, then the scheduler will
choose the one that hasn’t had a chance to run for the longest period of time and
give it the next 9.5ms period to use the processor. Once that’s done the scheduler
comes in again and gives the CPU to the other same priority task. Thus, both these
tasks can take turns running of using the processor.

As an application designer, we need to architect our application with proper


priority levels, so that the most important tasks that are ready to run are always
getting executed when needed, but at the same time, we do not let the lower
priority tasks starve too much for CPU time.

In FreeRTOS the higher the number, the higher the priority.

5.2.5 Kernel:

The kernel is considered to be the core of an operating system. Its main task is to
manage the hardware and the processes running on it.
The scheduler we saw above is the most important part of a kernel. Other
important parts include inter-task communications and synchronizations.

Fig 5.4 Kernel

As seen in the figure above, the operating system can have device drivers, file
systems, networking stacks like TCP/IP, file systems, etc., The central part of any
operating system is its kernel and it consists of the scheduler and inter-task-
communication systems.

5.2.6 Inter-task Communications:

There are several options available for tasks to communicate with each other
through the kernel of FreeRTOS like queues, mutex, semaphores and notifications.
Queues:

Queues are the primary form of inter-task communications. They can be used to
send messages between tasks, and between interrupts and tasks. In most cases they
are used as thread safe FIFO (First In First Out) buffers with new data being sent to
the back of the queue, although data can also be sent to the front.
Fig 5.5 Writing to and reading from a queue. In this example the queue was created
to hold 5 items, and the queue never becomes full.

The FreeRTOS queue usage model manages to combine simplicity with flexibility
– attributes that are normally mutually exclusive. Messages are sent through
queues by copy, meaning the data (which can be a pointer to larger buffers) is itself
copied into the queue rather than the queue always storing just a reference to the
data. This is the best approach because:

 Small messages that are already contained in C variables (integers, small


structures, etc.) can be sent into a queue directly. There is no need to
allocate a buffer for the message and then copy the variable into the
allocated buffer. Likewise, messages can be read from queues directly into
C variables.

Further, sending to a queue in this way allows the sending task to


immediately overwrite the variable or buffer that was sent to the queue, even
when the sent message remains in the queue. Because the data contained in
the variable was copied into the queue the variable itself is available for re-
use. There is no requirement for the task that sends the message and the task
that receives the message to agree which task owns the message, and which
task is responsible for freeing the message when it is no longer required.

 Using queues that pass data by copy does not prevent queues from being
used to pass data by reference. When the size of a message reaches a point
where it is not practical to copy the entire message into the queue byte for
byte, define the queue to hold pointers and copy just a pointer to the
message into the queue instead. This is exactly how
the FreeRTOS+UDP implementation passes large network buffers around
the FreeRTOS IP stack.
 The kernel takes complete responsibility for allocating the memory used as
the queue storage area.
 Variable sized messages can be sent by defining queues to hold structures
that contain a member that points to the queued message, and another
member that holds the size of the queued message.
 A single queue can be used to receive different message types, and messages
from multiple locations, by defining the queue to hold a structure that has a
member that holds the message type, and another member that holds the
message data (or a pointer to the message data). How the data is interpreted
depends on the message type. This is exactly how the task that manages
the FreeRTOS+UDP IP stack is able to use a single queue to receive
notifications of ARP timer events, packets being received from the Ethernet
hardware, packets being received from the application, network down
events, etc.
 The implementation is naturally suited for use in a memory protected
environment. A task that is restricted to a protected memory area can pass
data to a task that is restricted to a different protected memory area because
invoking the RTOS by calling the queue send function will raise the
microcontroller privilege level. The queue storage area is only accessed by
the RTOS (with full privileges).
 A separate API is provided for use inside of an interrupt. Separating the API
used from an RTOS task from that used from an interrupt service routine
means the implementation of the RTOS API functions do not carry the
overhead of checking their call context each time they execute. Using a
separate interrupt API also means, in most cases, creating RTOS aware
interrupt service routines is simpler for end users than when compared to
alternative RTOS products.
 In every way, the API is simpler.

Blocking the Queues:

Queue API functions permit a block time to be specified.

When a task attempts to read from an empty queue the task will be placed into the
Blocked state (so it is not consuming any CPU time and other tasks can run) until
either data becomes available on the queue, or the block time expires.
When a task attempts to write to a full queue the task will be placed into the
Blocked state (so it is not consuming any CPU time and other tasks can run) until
either space becomes available in the queue, or the block time expires.

If more than one task block on the same queue then the task with the highest
priority will be the task that is unblocked first.

See the Queue Management section of the user documentation for a list of queue
related API functions. Searching the files in
the FreeRTOS/Demo/Common/Minimal directory will reveal multiple examples of
their usage.

Note that interrupts must NOT use API functions that do not end in “FromISR”.

Queue Creation function is as follows.

xQueueHandlexQueueCreate( unsignedportBASE_TYPEuxQueueLength,
unsigned portBASE_TYPEuxItemSize
);
 uxQueueLenght gives the number of elements this queue will be able to
handle at any given time.

 uxItemSize is thesizeinbyteofanyelement storedinthequeue.

 xQueueCreatereturnsNULLifthequeuewasnotcreatedduetolackof memory
available; if not, the returned value should be kept to handle the newly
created queue.
Mutex:

Mutex is used for controlling access to the shared resource. It is used to avoid
extended priority inversion using priority inheritance technique.

Priority inheritance can be implemented in two ways: changing the priority of the
task trying to access the mutex

1. to the priority equal to the priority of the task acquiring the mutex or
2. to the higher priority than the priority of the task acquiring the
mutex so that the task trying to access the mutex will immediately get the
mutex

when another task releases the mutex.

The first way as adopted in FreeRTOS.


Mutexes are designed to prevent mutual exclusion or deadlocking. A mutex is used
similarly to a binary semaphore, except the task which take the semaphore must
give it back. This can be though with a token associated with the resource to access
to. A task holds the token, works with the resource then gives back the token; in
the meanwhile, no other token can be given to the mutex.

The usual use case of a mutex is as shown in the Fig 4.6.

Semaphore:

Semaphores are just an extended version of mutexes. The shared resource guarded
by a mutex has only one key. On the other hand, the resource guarded by
semaphores can have multiple keys, so that a number of processes can access that
particular resource simultaneously.

Fig 5.6 Use case of a matrix

As an example, we can consider an elevator, which can support only four people at
a time. Here the elevator is the shared resource. If you need access to it, you need
to press the button outside the elevator signalling the elevator system that you need
to use it. Now if there is space in the elevator once it opens, say only one person is
already inside, then you can go in. But if there are already 4 people inside the
elevator, then you can either wait and take the next one.
Semaphores work much the same way. The elevator control system is analogous to
the kernel and the number of spaces available at any given time is analogous to the
number of free keys available to our shared resource at a given instant. If a key is
needed, it is asked for, from the kernel and if one is available, it will be given.

Since mutex and semaphores are so similar to each other, mutexes are also called
binary semaphores. Collectively mutexes, semaphores, queues, etc., are called
communication objects as they are used to communicate events, statuses and data
from one task to another task.

Creation of Semiphore:

void vSemaphoreCreateBinary( xSemaphoreHandlexSemaphore );

Taking a Semiphore:
portBASE_TYPExSemaphoreTake( xSemaphoreHandlexSemaphore,
portTickTypexTicksToWait );

Giving a Semiphore:
portBASE_TYPExSemaphoreGive( xSemaphoreHandlexSemaphore );

The semaphore is not available, so the task is blocked, wating for the semaphore.

xSemaphoreTake()

An interrupt occurs and gives a semaphore

xGiveSemaphoreFromISR() xSemaphoreTake()
Which unblocks the task

xGiveSemaphoreFromISR() xSemaphoreTake()

And allows it to take succesfully the semaphore.

xSemaphoreTake()

Another interrupt occurs and gives another semaphore. In the meanwhile, the task is processing
the first interrupt.

xGiveSemaphoreFromISR() xSemaphoreTake()

When the task has finished to preccess its first work, it waits for another semaphore and gets it
directly, since an interrupt occurred. The task is now able to process the second interrupt.

xSemaphoreTake()

Fig 5.7 A binary semaphore is equivalent to a queue which can


contain one element
Priority inheritance is actually the only difference between a binary semaphore
and a mutex. When several tasks ask for a mutex, the mutex holder's priority is
set to the highest waiting task priority. This mechanism helps against priority
inversion phenomenon although it doesn't absolutely prevent it from happening.
The use of a mutex raises the application global complexity and therefore should
be avoided whenever it is possible.

5.2.7 Interrupts:

Interrupts behave in a way similar to the task switching mechanism available in the
scheduler so that the currently running code is pre-empted if an interrupt occurs,

Interrupts should not be confused with task switching.


 task switching is done by the scheduler through software to make sure that a
lower priority task cannot run while another higher priority task is ready to run.
 Interrupts are produced by hardware events like a user pressing a button.
 Interrupts are used to capture events from the physical world the device is
working in while the task switching is done primarily for multitasking.

5.3 Parameters to measure:

One of our tasks in this project is to determine the execution time and CPU
Utilization for the applications.

5.3.1 Execution Time:

The execution time or CPU time of a given task is defined as the time spent by the
system executing that task, including the time spent executing run-time or system
services on its behalf. The execution time is implementation defined. It is
implementation defined which task, if any, is charged the execution time is
consumed by interrupt handlers and run-time services on behalf of the system.

5.3.2 CPU Utilization:

CPU utilization is the sum of work handled by a Central Processing Unit. It is also
used to estimate system performance. CPU utilization can vary according to the
type and amount of computing task because some tasks require heavy CPU time
while others require less CPU time. Process time is another name for CPU time
and is the amount of time used by a CPU for processing instruction of an operating
system or a computer program. CPU time is quantified in clock ticks or
seconds. CPU utilization shows the burden on a processor in terms of percentage
that indicates if any changes are to be made in the system otherwise it may get
exhausted of capacity.
6. ESP32- A Feature-rich MCU Board ported with
FreeRTOS
The ESP32 is a low-cost system-on-chip (SoC) series created by Espressif
Systems. It is an improvement on the popular ESP8266 that is widely used in IoT
projects. The ESP32 has both Wi-Fi and Bluetooth capabilities, which make it an
all-rounded chip for the development of IoT projects and embedded systems in
general. ESP32 has become the go-to chip for making several devices. They’re
small, powerful, have a ton of onboard features, and they’re relatively easy to
program. Powered by 40 nm technology, ESP32 provides a robust, highly
integrated platform, which helps meet the continuous demands for efficient power
usage, compact design, security, high performance, and reliability.

6.1 ESP32 Specifications:


Coming to ESP32 chip specifications, they are as follows.

 As mentioned previously, it has Wi-Fi and Bluetooth built-in.


 It runs 32-bit programs.
 Single or Dual core Tensilica Xtensa 32-bit LX6 microprocessor.
 The clock frequency can go up to 240MHz and it has a 512 kB RAM.
 This particular board has 30 or 36 pins, 15 in each row.
 It also has wide variety of peripherals available, like: capacitive touch, ADCs,
DACs, UART, SPI, I2C and much more.
 It comes with built-in hall effect sensor and built-in temperature sensor.

Fig 6.1 ESP Board


ESP32 is a very popular chip used for the internet of things applications. The main
part of this module is ESP32-D0WDQ6 chip.

Fig 6.2 ESP32-D0WDQ6 chip

6.2 ESP32 Pin Layout:

There are totally 39 digital Pins on the ESP32 out of which 34 can be used as
GPIO and the remaining are input only pins. The device supports 18-channels for
12-bit ADC and 2-channel for 8-bit DAC. IT also has 16 channels for PWM signal
generation and 10 GPIO pins supports capacitive touch features. The ESP32 has
multiplexing feature, this enables the programmer to configure any GPIO pin for
PWM or other serial communication though program. The ESP32 supports 3 SPI
Interface, 3 UART interface, 2 I2C interface, 2 I2S interface and also supports
CAN protocol.
Pin Pin Name Details
Category

Power Micro-USB, 3.3V, Micro-USB: ESP32 can be powered through


5V, GND
USB port
5V: Regulated 5V can be supplied to this pin
which is we be again regulated to 3.3V by
on board regulator, to power the board.
3.3V: Regulated 3.3V can be supplied to this
pin to power the board.
GND: Ground pins.

Enable En The pin and the button resets


the microcontroller.

Analog Pins ADC1_0 to ADC1_5 Used to measure analog voltage in the range
and ADC2_0 to of 0-3.3V.
ADC2_9
12-bit 18 Channel ADC

DAC pins DAC1 and DAC2 Used for Digital to analog Conversion

Input/Output GPIO0 to GPIO39 Totally 39 GPIO pins, can be used as input or


Pins output pins. 0V (low) and 3.3V (high). But
pins 34 to 39 can be used as input only

Capacitive T0 to T9 These 10 pins can be used a touch pins


Touch pins normally used for capacitive pads

RTC GPIO RTCIO0 to RTCIO17 These 18 GPIO pins can be used to wake up
pins the ESP32 from deep sleep mode.

Serial Rx, Tx Used to receive and transmit TTL serial data.


External All GPIO Any GPIO can be use to trigger an interrupt.
Interrupts

PWM All GPIO 16 independent channel is available for PWM


any GPIO can be made to work as PWM
though software

VSPI GPIO23 (MOSI), Used for SPI-1 communication.


GPIO19(MISO),
GPIO18(CLK) and
GPIO5 (CS)

HSPI GPIO13 (MOSI), Used for SPI-2 communication.


GPIO12(MISO),
GPIO14(CLK) and
GPIO15 (CS)

IIC GPIO21(SDA), Used for I2C communication.


GPIO22(SCL)

AREF AREF To provide reference voltage for input


voltage.

Table 6.1 ESP-32 Pin Specifications

Fig 6.3 ESP32 Pin Layout


In this project, as we shall run programs on to the board, we shall simply connect
the esp32 board to the system using a USB cable.
6.3 Applications:

 Prototyping of IoT devices.


 Low power battery operated applications.
 Network projects.
 Easy to use for beginner level DIYers and makers.
 Projects requiring Multiple I/O interfaces with Wi-Fi and Bluetooth
functionalities.

6.4 ESP32 – An ideal choice:

 On comparing ESP32 with Arduino, we find that both are advantageous and
functional on its own. In terms of power and features obviously the dual
cored microprocessor powered ESP32 will surely take down the
microcontroller powered Arduino UNO. The ESP32 has built in Bluetooth
and Wi-Fi with good number of GPIO pins and communication protocols
for a very cheap price. The Arduino might look a bit handicapped when
competing with ESP32 but it has a large number of shields in the market
which can be readily used, also advanced Arduino boards like Yun has good
processing power as well.
 The ESP32 operates on 3.3V and can be programmed with ESP-IDF or with
Arduino IDE; the Arduino operates at 5V and is known for its easy to use
Arduino IDE and strong community support. So, to conclude, if one has
prior experience with programming and the project really requires some
heavy processing with IoT capabilities then ESP32 can be preferred over
Arduino.
 Both the ESP32 and ESP8266 are Wi-Fi development boards from Espressif
systems. They can be programmed using the ESP-IDF or the Arduino IDE.
The main difference would be that ESP8266 does not have an in-built
Bluetooth module and also does not feature CAN protocol and has no
SRAM. The ESP8266 is inferior in terms of performance compared with the
ESP32. Hence, ESP32 remains as the go-to chip for the project.
6.5 Powering the board:
There are totally three ways topower the ESP32 board.
Micro USB Jack: Connect the mini USB jack to a phone charger or computer
through a cable and it will draw power required for the board to function.
5V Pin: The 5V pin can be supplied with a Regulated 5V, this voltage will again be
regulated to 3.3V through the on-board voltage regulator.
3.3V Pin: If we have a regulated 3.3V supply then we can directly provide this to
the 3.3V pin of the ESP32.

6.6 ESP32 and FreeRTOS:

There are several use cases for wanting to multitask on a microcontroller. For
instance: you might have a microcontroller that reads a temperature sensor, shows
it on an LCD, and send it to the cloud. One can do all three synchronously, one
after the other. But what if one is using an e-ink display that takes a few seconds to
refresh?Luckily the implementation for the ESP32 includes the possibility to
schedule tasks with FreeRTOS. These can run on a single core; many cores and
one can even define which is more important and should get preferential treatment.

6.7 Programming:

Before programming this chip, it’s crucial to understand that, unlike other
embedded systems, the ESP32 comes with a light operating system - FreeRTOS.
The following methods to program this chip don’t replace the FreeRTOS firmware,
but rather deploy applications for it to run.

There are currently two methods to program the ESP32: the ESP-IDF and the
ESP32 Arduino Core.Using the Arduino IDE is easier. On the other hand, using
ESP-IDF gives more power. If we need more control over the hardware, like power
management, wireless power configuration or CPU core management, we’ll need
the ESP-IDF. Hence, we shall opt for ESP-IDF. Expressif included FreeRTOS in
its latest version ESP – IDF. In the subsequent chapters, we shall look into the
setting up of ESP-IDF environment., to be used in the project.
6.8 ESP-IDF RTOS:

The vanilla FreeRTOS is designed to run on a single core. However, the ESP32 is
dual core containing a Protocol CPU (known as CPU 0 or PRO_CPU) and an
Application CPU (known as CPU 1 or APP_CPU). The two cores are identical in
practice and share the same memory. This allows the two cores to run tasks
interchangeably between them.

The ESP-IDF FreeRTOS is a modified version of vanilla FreeRTOS which


supports symmetric multiprocessing (SMP).

6.9 ESP32 for the project:

Given the specifications and features of the ESP32 board, one can see that it is
efficient, lighter and commercially reliable. The advantage that it comes with
FreeRTOS plays a crucial role, and thus, floats out to be the right choice for the
project.
7. SETTING UP ESP-IDF

The native software development framework for the ESP-32 is called the Espressif
IoT Development Framework (ESP-IDF). It provides maximum functionality and
compatibility. It is a bit hard core than some of the other programming options but
it gives a better understanding of what is happening under the hood.

The ESP-IDF functionality includes menu-based configuration, compiling and


firmware download to ESP32 boards.

The following are needed to begin the installation process.

Hardware:

 An ESP32 board.
 USB cable – USB A / micro USB B.
 Computer running Windows, Linux, or mac

OS. Software:

 Toolchain to compile code for ESP32.


 Build tools – CMake and Ninja to build a full application for ESP32.
 ESP-IDF that essentially contains API (software libraries and source code) for
ESP32 and scripts to operate the Toolchain.
 Text editor to write programs (Projects) in C, e.g.., Eclipse.

Fig 7.1 Development of applications for ESP32


The quickest way to start development with ESP32 is by installing a prebuilt
toolchain. For this, we shall head over to the Espressif site and follow the
provided instructions.

7.1 Installation Process:


This is a detailed roadmap for the installation process.
Setting up the Development Environment:
 Step 1: Install prerequisites for Windows.
 Step 2: Get ESP-IDF.
 Step 3: Set up the tools.
 Step 4: Set up the environment variables.

Creating the First Project:


 Step 5: Start a Project.
 Step 6: Connect the device.
 Step 7: Configure.
 Step 8: Build the Project.
 Step 9: Flash onto the device.
 Step 10: Monitor.

Step 1: Install prerequisites for Windows:


Some tools need to be installed on the computer before proceeding to the next step.
Standard Setup of Toolchain for Windows:
Currently, only 64-bit versions of Windows are supported.
The prerequisite tools required to build firmware include Python, Git, cross-
compilers, CMake and Ninja build tools.
For this Getting Started we’re going to use the Command Prompt, but after ESP-
IDF is installed you can use Eclipse or another graphical IDE with CMake support
instead.

ESP-IDF Tools Installer:

The easiest way to install ESP-IDF’s prerequisites is to download the ESP-IDF


Tools installer from this URL:

https://dl.espressif.com/dl/esp-idf-tools-setup-2.3.exe
The installer includes the cross-compilers, OpenOCD, CMake and Ninja build tool.
The installer can also download and run installers for Python 3.7 and Git For
Windows if they are not already installed on the computer.

The installer also offers to download one of the ESP-IDF release versions.

Using the Command Prompt:

For the remaining Getting Started steps, we’re going to use the Windows
Command Prompt.

ESP-IDF Tools Installer creates a shortcut in the Start menu to launch the ESP-IDF
Command Prompt. This shortcut launches the Command Prompt (cmd.exe) and
runs export.bat script to set up the environment variables
( PATH , IDF_PATH and others). Inside this command prompt, all the installed
tools are available.

Note that this shortcut is specific to the ESP-IDF directory selected in the ESP-IDF
Tools Installer. If you have multiple ESP-IDF directories on the computer (for
example, to work with different versions of ESP-IDF), you have two options to use
them:

1. Create a copy of the shortcut created by the ESP-IDF Tools Installer, and
change the working directory of the new shortcut to the ESP-IDF directory you
wish to use.
2. Alternatively, run cmd.exe , then change to the ESP-IDF directory you wish to
use, and run export.bat . Note that unlike the previous option, this way requires
Python and Git to be present in PATH . If you get errors related to Python or
Git not being found, use the first option.

Step 2: Get ESP-IDF:

To build applications for the ESP32, we need the software libraries provided by
Espressif in ESP-IDF repository.

To get ESP-IDF, navigate to the installation directory and clone the repository
with git clone . In addition to installing the tools, ESP-IDF Tools Installer for
Windows introduced in Step 1 can also download a copy of ESP-IDF.

Step 3: Set up the tools:


Aside from the ESP-IDF, you also need to install the tools used by ESP-IDF, such as
the compiler, debugger, Python packages, etc. for Windows introduced in Step 1
installs all the required tools.

In order to install the tools without the help of ESP-IDF Tools Installer, open the
Command Prompt and follow these steps:

cd%userprofile%\esp\esp-idf install.bat

Linux commands are as follows


cd ~/esp/esp-idf
./install.sh
Customizing the tools installation path:

The scripts introduced in this step install compilation tools required by ESP-IDF
%USERPROFILE%\.espressif
inside the user home directory:on Windows and on Linux . To install the tools into
$HOME/.espressif
a different directory, set the
IDF_TOOLS_PATH
environment variablebefore running the installation scripts. Make sure that the
user account has sufficient permissions to read and write this path.

If changing the IDF_TOOLS_PATH , make sure it is set to the same value every
time the Install script install.bat , install.ps1
or install.sh ) and an Export script
( export.bat
(, export.ps1 or export.sh ) are executed.

Step 4: Set up the environment variables:

The installed tools are not yet added to the PATH environment variable. To make
the tools usable from the command line, some environment variables must be set.
ESP-IDF provides another script which does that.

ESP-IDF Tools Installer for Windows creates an “ESP-IDF Command Prompt”


shortcut in the Start Menu. This shortcut opens the Command Prompt and sets up
all the required environment variables. We can open this shortcut and proceed to
the next step.
Alternatively, to use ESP-IDF in an existing Command Prompt window, we can
run:

%userprofile%\esp\esp-idf\export.bat

On Linux, in the terminal where you are going to use ESP-IDF, run:

. $HOME/esp/esp-idf/export.sh
Step 5: Start a Project:
We are now ready to prepare the first application for ESP32.

Let us begin with project from directory in IDF.

Copy to ~/esp directory:

cd%userprofile%\esp
xcopy /e /i %IDF_PATH%\examples\get-started\hello_world hello_world

On Linux,

cd ~/esp
cp -r $IDF_PATH/examples/get-started/hello_world .

Step 6: Connect the device:


Connect the ESP32 board to the computer and check under what serial port the
board is visible. For Windows, it contains names like COM1

Step 7: Configure:
Navigate to the hello_world directory from Step 5. Start a Project, set ESP32 chip
as the target and run the project configuration utility menuconfig.
cd %userprofile%\esp\hello_world
idf.py set-target esp32
idf.py menuconfig
Setting the target with idf.py set-target {IDF_TARGET} should be done once,
after opening a new project. If the project contains some existing builds and
configuration, they will be cleared and initialized.

On Linux,

cd ~/esp/hello_world
idf.py set-target esp32
idf.py menuconfig

The following menu appears:

Fig 7.2 Project configuration - Home window


This menu is used to set up project specific variables, e.g. Wi-Fi network name and
password, the processor speed, etc. Setting up the project with menuconfig may be
skipped for “hello_world”. This example will run with default configuration.

Step 8: Build the Project:


Build the project by running:

idf.py build
This command will compile the application and all ESP-IDF components, then it
will generate the bootloader, partition table, and application binaries.

$ idf.py build
Running cmake in directory /path/to/hello_world/build
Executing "cmake -G Ninja --warn-uninitialized /path/to/hello_world"... Warn
about uninitialized values.
-- Found Git: /usr/bin/git (found version "2.17.0")
-- Building empty aws_iot component due to configuration
-- Component names: ...
-- Component paths: ...

... (more lines of build system output)

[527/527] Generating hello-world.bin esptool.py v2.3.1

Project build complete. To flash, run this command:


../../../components/esptool_py/esptool/esptool.py -p (PORT) -b 921600
write_flash
--flash_mode dio --flash_size detect --flash_freq 40m 0x10000 build/hello-
world.binbuild 0x1000 build/bootloader/bootloader.bin 0x8000
build/partition_table/partition-table.bin
or run 'idf.py
If there are no-perrors,
PORT flash'
the build will finish by generating the firmware binary .bin
file.

Step 9: Flash onto the device:


Flash the binaries that we just built onto your ESP32 board by running:

idf.py -p PORT [-b BAUD] flash


Replace PORT with your ESP32 board’s serial port name from Step 6. Connect
Your Device.

When flashing, we will see the output log similar to the following:

...
esptool.py --chip esp32 -p /dev/ttyUSB0 -b 460800 --before=default_reset --
after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB
0x8000 partition_table/partition-table.bin 0x1000 bootloader/bootloader.bin
0x10000 hello-world.bin
esptool.py v3.0-dev
Serial port
/dev/ttyUSB0
Connecting......._
Chip is ESP32D0WDQ6 (revision 0)
Features: WiFi, BT, Dual Core, Coding Scheme None
Crystal is 40MHz
MAC: 24:0a:c4:05:b9:14
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 460800
Changed.
Configuring flash size...
Compressed 3072 bytes to 103...
Writing at 0x00008000... (100 %)
Wrote 3072 bytes (103 compressed) at 0x00008000 in 0.0 seconds (effective
5962.8 kbit/s)...
Hash of data verified.
Compressed 26096 bytes to 15408...
Writing at 0x00001000... (100 %)
Wrote 26096 bytes (15408 compressed) at 0x00001000 in 0.4 seconds (effective
546.7 kbit/s)...
Hash of data verified.
Compressed 147104 bytes to 77364...
Writing at 0x00010000... (20 %)
Writing at 0x00014000... (40 %)
Writing at 0x00018000... (60 %)
Writing at 0x0001c000... (80 %)
Writing at 0x00020000... (100 %)
Wrote 147104 bytes (77364 compressed) at 0x00010000 in 1.9 seconds (effective
615.5 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...
Done
If there are no issues by the end of the flash process, the board will reboot and start
up the “hello_world” application.

Step 10: Monitor:


To check if “hello_world” is indeed running, type idf.py -p PORT monitor (Do
not forget to replace PORT with your serial port name).

This command launches the IDF Monitor application:

$ idf.py -p /dev/ttyUSB0 monitor


Running idf_monitor in directory [...]/esp/hello_world/build
Executing "python [...]/esp-idf/tools/idf_monitor.py -b 115200
[...]/esp/hello_world/build/hello-world.elf"...
--- idf_monitor on /dev/ttyUSB0 115200 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
ets Jun 8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)


ets Jun 8 2016 00:22:57
...
After startup and diagnostic logs scroll up, you should see “Hello world!” printed
out by the application.

...
Hello world!
Restarting in 10 seconds...
This is esp32 chip with 2 CPU cores, WiFi/BT/BLE, silicon revision 1, 2MB
external flash
Restarting in 9
seconds... Restarting in
8 seconds... Restarting
in 7 seconds...
To exit IDF monitor use the shortcut Ctrl+] .

With this, we are set to execute our applications.

7.2 Hello World code:


Before getting into the software implementations of our project, we run our first
task, “hello world” and later, multitask it with “blink”. The Espressif Internet
Development Framework (ESP-IDF) uses FreeRTOS to make better use of the two
high speed processors and manage the numerous built-in peripherals. It is done by
creating tasks.
This hello world prints the string on UART (eventually on the computer terminal).
We will first look at the ESP-IDF structure. Then look at the hello world example
and modify it blink an LED while still continuously printing the 'hello world'
string.

1.
2. +--esp-idf
3. |
4. |
5. +-- components
6. |
7. +-- docs
8. |
9. +-- examples
10. |
11. +-- make
12. |
13. +-- tools

The components directory holds all the 'C' code for the ESP32. It contains all the
'components' that make up the ESP32. It includes Drivers for numerous
peripherals, the bootloader, bt(bluetooth), freeRTOS etc. If we expand
the components the tree looks like so:

1. +---components
2. |+---app_update
3. ||| component.mk
4. ||| esp_ota_ops.c
5. |||
6. || \---include
7. || esp_ota_ops.h
8. ||
9. |+---bootloader
10.||| component.mk
11.||| README.rst
12. |||
13.||+---include
14.||| esp_image_format.h
15.||| esp_secure_boot.h
16. |||
17.||+---include_priv
18.||| bootloader_flash.h
19. |||
20. || \---src
21.|| bootloader_flash.c
22.|| esp_image_format.c
23.|| secure_boot.c
24.|| secure_boot_signatures.c
25. ||
26. |+---bt
27. ||| bt.c
28...
29. ||
30.|+---driver
31.||| component.mk
32.||| gpio.c
33...
34. ||
35. |+---esp32
36. ||| abi.cpp
37...
38. ||
39.|+---esptool_py
40.|||Kconfig.projbuild
41...
42. ||
43.|+---ethernet
44.||| component.mk
45...
46. ||
47. |+---expat
48...
49. ||
50.|+---freertos
51.||| component.mk
52...
53. ||
54. |+---
idf_test 55...
56. ||
57. |+---newlib
58...
59. ||
60. |+---
nghttp 61...
62. ||
63.|+---nvs_flash
64.|||.gitignore
65. ||
66...
67. ||
68. |+---
openssl 69. ||
70...
71. ||
72. |+---
partition_table 73...
74. ||
75. |+---
spi_flash 76. ||
77...
78. ||
79. |+---
tcpip_adapter 80...
81. ||
82. |+---ulp
83. ||
84...
85. ||
86. |+---vfs
87. ||
88...
89. |+---
wpa_supplicant 90. ||
91.| \---xtensa-debug-module
92.|| component.mk
93...
94.|
95.|
96.+--- docs

7.2.1 The Includes:


As you notice each of the component folders one or more .c and .h files. To include
a component, we need to include the corresponding .h file in our code. Our hello
world example needs four of these components.

1. #include<stdio.h>
2. #include"freertos/FreeRTOS.h"
3. #include"freertos/task.h"
4. #include"esp_system.h"

 freertos/FreeRTOS.h: Inclusion of this set’s configuration required to run


freeRTOS on ESP32.
 freertos/task.h: The tasks provide the multitasking functionality, which we will
explore in the ‘blinky’ with ‘hello world’ example in some time.
 esp_system.h: This inclusion configures the peripherals in the ESP system. It is
similar to system initialization. It's like setting up all the components of your
bike, before we could fire the engine.

7.2.2 The main() function:


app_main(), is just like good old main(). This is the first function that gets called
automatically.

1. void app_main()
2. {
3. nvs_flash_init();
4. xTaskCreate(&hello_task,"hello_task",2048, NULL,5, NULL);
5. }

 Initialize Flash: nvs_flash_init(): As said earlier the ESP32 Modules


run code from an external flash. Unless we are using directly the ESP32 chip,
the module/board maker will taker of the flash initialization and access.
 xTaskCreate(): This function has 5 arguments. The first parameter is address of
the function which execute, when this task is scheduled to run. The second
parameter hello_task is the name given to the task. This could be any name. It
used to obtain handle or while debugging the task. The next parameter 2048 is
the memory allocated to the task in word (2 Bytes).
7.2.3 The hello_task:
The function that is called from the task created above is a simple function as
shown below. It simply prints the string to the UART. The Print stream is
configured to the UART0 of the ESP32.

1. void hello_task(void*pvParameter)
2. {
3. printf("Hello world!\n");
4. for(int i =10; i >=0; i--){
5. printf("Restarting in %d seconds...\n", i);
6. vTaskDelay(1000/
portTICK_RATE_MS); 7. }
8. printf("Restarting now.\n");
9. fflush(stdout);
10. esp_restart();
11.}
Now, let’s look at the complete code.

1. #include<stdio.h>
2. #include"freertos/FreeRTOS.h"
3. #include"freertos/task.h"
4. #include"esp_system.h"
5.
6. void hello_task(void*pvParameter)
7. {
8. printf("Hello world!\n");
9. for(int i =10; i >=0; i--){
10. printf("Restarting in %d seconds...\n", i);
11. vTaskDelay(1000/ portTICK_RATE_MS);
12.}
13. printf("Restarting now.\n");
14. fflush(stdout);
15. esp_restart();
16.}
17.
18. void app_main()
19.{
20. nvs_flash_init();
21. xTaskCreate(&hello_task,"hello_task",2048, NULL,5, NULL);
22.}

7.3 Multitasking Blink with Hello task:


The blinky code is basically turning ON an LED, waiting for a second, turning it
OFF and waiting for another second. If the requirement is to continuously send a
string, say every 100 milliseconds while the LED is still blinking, it can be easily
accomplished using the RTOS. So, the code below, creates two tasks. Whenever
one enters delay state (vTaskDelay function), the other runs and vice-versa. Since
we are slow and ESP32 is fast, it switches between the tasks numerous times and
we see both the tasks happening simultaneously.

1.#include<stdio.h>
2.#include"freertos/FreeRTOS.h"
3.#include"freertos/task.h"
4.#include"esp_system.h"
5.#include"driver/gpio.h"
6.
7.#define BLINK_GPIO 13
8.void hello_task(void*pvParameter)
9.{
10.
11. while(1)
12. {
13. printf("Hello world!\n");
14. vTaskDelay(100/ portTICK_RATE_MS);
15. }
16.}
17.
18. void blinky(void*pvParameter)
19.{
20.
21. gpio_pad_select_gpio(BLINK_GPIO);
22./* Set the GPIO as a push/pull output */
23. gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
24.while(1){
25./* Blink off (output low) */
26. gpio_set_level(BLINK_GPIO,0);
27. vTaskDelay(1000/ portTICK_RATE_MS);
28./* Blink on (output high) */
29. gpio_set_level(BLINK_GPIO,1);
30. vTaskDelay(1000/ portTICK_RATE_MS);
31.}
32.}
33.
34.
35. void app_main()
36.{
37. nvs_flash_init();
38. xTaskCreate(&hello_task,"hello_task",2048, NULL,5, NULL);
39. xTaskCreate(&blinky,"blinky",512,NULL,5,NULL );
40.}
8. PROJECT DEVELOPMENT AND
IMPLEMENTATION
8.1 DEVELOPMENT:

Before getting in to the program development, let us take a look at the folder where
the source code is to be stored and additional documents are required for the
program to function properly.
The folder is as shown below:

Fig 8.1 Folder organisation of the project

From the above picture, one can see that the program folder consists of a main
folder, cmakeLists.txt, makefile, README.md, sdkconfig.defaults.
1.CMakeLists.txt file contains a set of directives and instructions describing the
project's source files and targets (executable, library, or both).

2.A makefile is a special file, containing shell commands, that you create and
name makefile (or Makefile depending upon the system). While in the directory
containing this makefile, you will type make and the commands in the makefile
will be executed.

3.A README file contains information about other files in a directory or archive
of computer software. A form of documentation, it is usually a simple plain text
file called READ.ME, README.TXT, README.md.

4.sdkconfig.defaults contains the default configuration details set in the menu


configuration.

5.The main folder contains the source code for the project.

After building the project, the project folder will be as shown below:
Fig 8.2 Folder organisation of project after building the project

A new folder called as build has been created which stores all the details related to
building the project.
An additional sdkconfig.old document is created which contains the default
configuration details and the sdkconfig folder is updated to the recent
developments.

8.1.1 PROGRAM:
Since we are now familiar with the structure of the program as seen in the Hello
World task earlier, let us proceed to the program development.
The source code of the project for the single-core processor is given below:
#include <stdio.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"

#define pi 3.14159265

void division(void *pvParameter)


{
int a=6,b=3;
while(1)
{
float c;
c=((float)a/(float)b);
printf("Quotient of %d and %d is %f\n",a,b,c);
a+=1;
b+=1;
vTaskDelay(200 / portTICK_RATE_MS);
}
}

void mul(void *pvParameter)


{
int a=6,b=3;
while(1) {
long c;
c=a*b;
printf("Multiplication of %d and %d is %ld\n",a,b,c);
a+=1;
b+=1;
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void add(void *pvParameter)
{
int a=6,b=3;

while(1) {
long c;
c=a+b;
printf("sum of %d and %d is %ld\n",a,b,c);
a+=1;
b+=1;

vTaskDelay(200 / portTICK_RATE_MS);
}
}
void sub(void *pvParameter)
{
int a=6,b=3;

while(1) {
int c;
c=a-b;
printf("subtraction of %d and %d is %d\n",a,b,c);
a+=2;
b+=1;

vTaskDelay(200 / portTICK_RATE_MS);
}
}

void square(void *pvParameter)


{
int a=3;
while(1)
{ long
b=a*a;
printf("The square of %d is %ld\n",a,b);
a+=1;
vTaskDelay(200/portTICK_RATE_MS);
}
}

void sqart(void *pvParameter)


{
int a=4;
float b;
char stats_buffer[4096];
char list_buffer[4096];
while(1){
b=sqrt(a);
printf("The squareroot of %d is %f\n",a,b);
a+=2;
vTaskList(list_buffer);
printf("************************************************************
******\n");
printf("Task\t State\t Prio\t Stack\t Num\t CoreID\n");
printf("************************************************************
******\n");
printf(list_buffer);
printf("************************************************************
******\n\n\n\n");
vTaskGetRunTimeStats(stats_buffer);
printf("**********************************************\n");
printf("Task\t Runtime\t Percentage\n");
printf("**********************************************\n");
printf(stats_buffer);
printf("**********************************************\n");
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void logarithimic(void *pvParameter)
{
double a=2;
while(1)
{ double
res;
res=log(a);
printf("The natural logarithm of %.2lf is %lf\n",a,res);
a+=3;
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void trig(void *pvParameter)
{

double x,rescos,ressine,restan,val;
x=30;
while(1)
{ val=pi/180.0;
rescos=cos(x*val);
ressine=sin(x*val);
restan=tan(x*val);
printf("The cosine of %lf degrees is %lf\n",x,rescos);
printf("The sine of %lf degrees is %lf\n",x,ressine);
printf("The tangent of %lf degrees is %lf\n",x,restan);
x+=15;
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void compadd(void *pvParameter){
int a1=2,b1=4,a2=3,b2=1;
int a3,b3;
while(1)
{
a3=a1+a2;
b3=b1+b2;
printf("The sum of %d+i%d and %d+i%d is %d+i%d\n",a1,b1,a2,b2,a3,b3);
a1+=1;
a2+=2;
b1+=-1;
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void compsub(void *pvParameter){
int a1=2,b1=4,a2=3,b2=1;
int a3,b3;
while(1)
{
a3=a1-a2;
b3=b1-b2;
printf("The difference of %d+i%d and %d+i%d is %d+i%d\n",a1,b1,a2,b2,a3,b3);
a1+=3;
a2+=1;
b1+=2;
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void compmul(void *pvParameter){
int a1=2,b1=4,a2=3,b2=1;
int a3,b3;
while(1)
{
a3=(a1*a2-b1*b2);
b3=(a1*b2+a2*b1);
printf("The multiplication of %d+i%d and %d+i%d is
%d+i%d\n",a1,b1,a2,b2,a3,b3);
a1+=3;
a2+=1;
b1+=2;
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void temp(void *pvParameter){
float c=23;
while(1)
{
float f,k;
f=((c*1.8)+32.0);
k=c+273.15;
printf("The farenheit temperature for given %f celcius is %f farenheit\n",c,f);
printf("The kelvin temperature for given %f celcius is %f kelvin\n",c,k);
c=c+1.5;
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void app_main()
{
xTaskCreatePinnedToCore(&division, "division", 2048, NULL, 10, NULL, 0);
xTaskCreatePinnedToCore(&mul, "mul", 2048,NULL,8,NULL ,0);
xTaskCreatePinnedToCore(&add, "add", 2048,NULL,6,NULL,0);
xTaskCreatePinnedToCore(&sub, "sub", 8192,NULL,5,NULL,0 );
xTaskCreatePinnedToCore(&square, "square", 4096,NULL,3,NULL,0 );
xTaskCreatePinnedToCore(&sqart, "sqart", 16384,NULL,15,NULL,0);
xTaskCreatePinnedToCore(&logarithimic, "logarithimic", 4096,NULL,2,NULL,0
);
xTaskCreatePinnedToCore(&trig, "trig", 8192,NULL,2,NULL,0 );
xTaskCreatePinnedToCore(&temp, "temp", 2048,NULL,2,NULL,0 );
xTaskCreatePinnedToCore(&compadd, "compadd", 2048,NULL,3,NULL,0 );
xTaskCreatePinnedToCore(&compsub, "compsub", 2048,NULL,4,NULL,0 );
xTaskCreatePinnedToCore(&compmul, "compmul", 2048,NULL,5,NULL,0 );

The source code of the project for the dual-core processor is given below:
#include <stdio.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"

#define pi 3.14159265

void division(void *pvParameter)


{
int a=6,b=3;
while(1)
{
float c;
c=((float)a/(float)b);
printf("Quotient of %d and %d is %f\n",a,b,c);
a+=1;
b+=1;
vTaskDelay(200 / portTICK_RATE_MS);
}
}

void mul(void *pvParameter)


{
int a=6,b=3;

while(1) {
long c;
c=a*b;
printf("Multiplication of %d and %d is %ld\n",a,b,c);
a+=1;
b+=1;
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void add(void *pvParameter)
{
int a=6,b=3;
while(1) {
long c;
c=a+b;
printf("sum of %d and %d is %ld\n",a,b,c);
a+=1;
b+=1;

vTaskDelay(200 / portTICK_RATE_MS);
}
}
void sub(void *pvParameter)
{
int a=6,b=3;

while(1) {
int c;
c=a-b;
printf("subtraction of %d and %d is %d\n",a,b,c);
a+=2;
b+=1;

vTaskDelay(200 / portTICK_RATE_MS);
}
}

void square(void *pvParameter)


{
int a=3;
while(1)
{ long
b=a*a;
printf("The square of %d is %ld\n",a,b);
a+=1;
vTaskDelay(200/portTICK_RATE_MS);
}
}

void sqart(void *pvParameter)


{
int a=4;
float b;
char stats_buffer[4096];
char list_buffer[4096];
while(1){
b=sqrt(a);
printf("The squareroot of %d is %f\n",a,b);
a+=2;
vTaskList(list_buffer);
printf("************************************************************
******\n");
printf("Task\t State\t Prio\t Stack\t Num\t CoreID\n");
printf("************************************************************
******\n");
printf(list_buffer);
printf("************************************************************
******\n\n\n\n");
vTaskGetRunTimeStats(stats_buffer);
printf("**********************************************\n");
printf("Task\t Runtime\t Percentage\n");
printf("**********************************************\n");
printf(stats_buffer);
printf("**********************************************\n");
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void logarithimic(void *pvParameter)
{
double a=2;
while(1)
{ double
res;
res=log(a);
printf("The natural logarithm of %.2lf is %lf\n",a,res);
a+=3;
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void trig(void *pvParameter)
{

double x,rescos,ressine,restan,val;
x=30;
while(1)
{ val=pi/180.0;
rescos=cos(x*val);
ressine=sin(x*val);
restan=tan(x*val);
printf("The cosine of %lf degrees is %lf\n",x,rescos);
printf("The sine of %lf degrees is %lf\n",x,ressine);
printf("The tangent of %lf degrees is %lf\n",x,restan);
x+=15;
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void compadd(void *pvParameter){
int a1=2,b1=4,a2=3,b2=1;
int a3,b3;
while(1)
{
a3=a1+a2;
b3=b1+b2;
printf("The sum of %d+i%d and %d+i%d is %d+i%d\n",a1,b1,a2,b2,a3,b3);
a1+=1;
a2+=2;
b1+=-1;
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void compsub(void *pvParameter){
int a1=2,b1=4,a2=3,b2=1;
int a3,b3;
while(1)
{
a3=a1-a2;
b3=b1-b2;
printf("The difference of %d+i%d and %d+i%d is %d+i%d\n",a1,b1,a2,b2,a3,b3);
a1+=3;
a2+=1;
b1+=2;
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void compmul(void *pvParameter){
int a1=2,b1=4,a2=3,b2=1;
int a3,b3;
while(1)
{
a3=(a1*a2-b1*b2);
b3=(a1*b2+a2*b1);
printf("The multiplication of %d+i%d and %d+i%d is
%d+i%d\n",a1,b1,a2,b2,a3,b3);
a1+=3;
a2+=1;
b1+=2;
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void temp(void *pvParameter){
float c=23;
while(1)
{
float f,k;
f=((c*1.8)+32.0);
k=c+273.15;
printf("The farenheit temperature for given %f celcius is %f farenheit\n",c,f);
printf("The kelvin temperature for given %f celcius is %f kelvin\n",c,k);
c=c+1.5;
vTaskDelay(200 / portTICK_RATE_MS);
}
}
void app_main()
{
xTaskCreatePinnedToCore(&division, "division", 2048, NULL, 10, NULL, 1);
xTaskCreatePinnedToCore(&mul, "mul", 2048,NULL,8,NULL ,0);
xTaskCreatePinnedToCore(&add, "add", 2048,NULL,6,NULL,1);
xTaskCreatePinnedToCore(&sub, "sub", 8192,NULL,5,NULL,0 );
xTaskCreatePinnedToCore(&square, "square", 4096,NULL,3,NULL,0 );
xTaskCreatePinnedToCore(&sqart, "sqart", 16384,NULL,15,NULL,1 );
xTaskCreatePinnedToCore(&logarithimic, "logarithimic", 4096,NULL,2,NULL,0
);
xTaskCreatePinnedToCore(&trig, "trig", 8192,NULL,2,NULL,1 );
xTaskCreatePinnedToCore(&temp, "temp", 2048,NULL,2,NULL,0 );
xTaskCreatePinnedToCore(&compadd, "compadd", 2048,NULL,3,NULL,1 );
xTaskCreatePinnedToCore(&compsub, "compsub", 2048,NULL,4,NULL,0 );
xTaskCreatePinnedToCore(&compmul, "compmul", 2048,NULL,5,NULL,1 );}

Now, let us understand the program:

8.1.2THE INCLUDES:
As the program requires usage of more than one component, we include the
corresponding .h files in the header section.
 stdio.h,math.h the C libraries are included so as to facilitate the
input/output and mathematical operations function smoothly.
 freertos/FREERTOS.h facilitates the inclusion of this set's configuration
required to run freeRTOS on ESP32.
 freertos/task.h facilitates the inclusion of multitasking functionality.
 esp_system.h configures the peripherals in the ESP System which is similar
to system initialization.
8.1.3 MAIN FUNCTION:
The program execution starts with the app_main(), just like good old main(). This
is the first function that gets called automatically.

Example:
void app_main()
{

xTaskCreate(&hello_task, "hello_task", 2048, NULL, 5, NULL);


xTaskCreatePinnedToCore(&blink_task, "blink_task", 2048, NULL, 5,
NULL,0);
}

xTaskCreate():

xTaskCreate( TaskFunction_t pvTaskCode,


const char * const pcName,
configSTACK_DEPTH_TYPE usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask
);

Each task requires RAM that is used to hold the task state, and used by the task as
its stack. If a task is created using xTaskCreate() then the required RAM is
automatically allocated from the FreeRTOS heap.
Parameters:
 pvTaskCode: Pointer to the task entry function i.e; the address of the
task.Tasks are normally implemented as an infinite loop, and must never
attempt to return or exit from their implementing function. Tasks can
however delete themselves.

 pcName: A descriptive name for the task. This is mainly used to facilitate
debugging, but can also be used to obtain a task handle. The maximum
length of a task’s name is set using the configMAX_TASK_NAME_LEN
parameter in FreeRTOSConfig.h.

 usStackDepth: The number of words (not bytes!) to allocate for use as the
task’s stack.
 pvParameters: A value that will passed into the created task as the task’s
parameter. If pvParameters is set to the address of a variable then the
variable must still exist when the created task executes – so it is not valid to
pass the address of a stack variable.

 uxPriority : The priority at which the created task will execute.

 pxCreatedTask: Used to pass a handle to the created task out of the


xTaskCreate() function. pxCreatedTask is optional and can be set to NULL.

 Returns:
If the task was created successfully then pdPASS/pdTRUE is returned.
Otherwise errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is returned.

xTaskCreatePinnedToCore():

Create a new task with a specified affinity.

This function is similar to xTaskCreate, but allows setting task affinity in SMP
system.
xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode,
const char * const pcName,
configSTACK_DEPTH_TYPE usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask
const BaseType_t xCoreID );
Parameters:

 pvTaskCode: Pointer to the task entry function simply, the address of the
task. Tasks are normally implemented as an infinite loop, and must never
attempt to return or exit from their implementing function. Tasks can
however delete themselves.

 pcName: A descriptive name for the task. This is mainly used to facilitate
debugging, but can also be used to obtain a task handle. The maximum
length of a task’s name is set using the configMAX_TASK_NAME_LEN
parameter in FreeRTOSConfig.h.

 usStackDepth: The number of words (not bytes!) to allocate for use as the
task’s stack.
 pvParameters: A value that will passed into the created task as the task’s
parameter. If pvParameters is set to the address of a variable then the
variable must still exist when the created task executes – so it is not valid to
pass the address of a stack variable.

 uxPriority: The priority at which the created task will execute.

 pxCreatedTask: Used to pass a handle to the created task out of the


xTaskCreate() function. pxCreatedTask is optional and can be set to NULL.

 xCoreID: If the value is tskNO_AFFINITY, the created task is not pinned


to any CPU, and the scheduler can run it on any core available. Values 0 or
1 indicate the index number of the CPU which the task should be pinned to.
Specifying values larger than (portNUM_PROCESSORS - 1) will cause the
function to fail.
 Returns: If the task was created successfully then pdPASS/pdTRUE is
returned.
 Otherwise, errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is
returned.

8.1.4 THE TASK:


Consider a task from the above program and the structure is as shown below:
void add(void *pvParameter)
{
int a=6,b=3;

while(1) {
long c;
c=a+b;
printf("sum of %d and %d is %ld\n",a,b,c);
a+=1;
b+=1;

vTaskDelay(200 / portTICK_RATE_MS);
}
}
Here, the functionality of the task is written in the while loop that run infinitely as
a task in the freertos should run infinitely and it should never attempt to return or
exit from the implementing function.
Now, in order to facilitate the execution of other tasks, we introduce a delay in the
task through vTaskDelay.

vTaskDelay:
void vTaskDelay( const TickType_t xTicksToDelay );
vTaskDelay() specifies a time at which the task wishes to unblock relative to the
time at which vTaskDelay() is called. For example, specifying a block period of
100 ticks will cause the task to unblock 100 ticks after vTaskDelay() is called.
Parameters:
 xTicksToDelay: The amount of time, in tick periods, that the calling task
should block.

Example usage:

void vTaskFunction( void * pvParameters )


{
/* Block for 500ms. */

for( ;; )
{
/* Simply toggle the LED every 500ms, blocking between each toggle. */
vToggleLED();
vTaskDelay(500 / portTICK_PERIOD_MS;);
}
}

8.1.5 RUNTIME STATS:

In order to get the information about the tasks running and the run time statistics of
the program, two APIs are used. Their functionality is described below:

vTaskList():

void vTaskList( char *pcWriteBuffer );


configUSE_TRACE_FACILITY and
configUSE_STATS_FORMATTING_FUNCTIONS must be defined as 1 in
FreeRTOSConfig.h for this function to be available.
vTaskList() calls uxTaskGetSystemState(), then formats the raw data generated by
uxTaskGetSystemState() into a human readable (ASCII) table that shows the state
of each task, including the task’s stack high water mark.

In the ASCII table the following letters are used to denote the state of a task:

‘B’ – Blocked
‘R’ – Ready
‘D’ – Deleted (waiting clean up)
‘S’ – Suspended, or Blocked without a timeout

Parameters:
 pcWriteBuffer : A buffer into which the above mentioned details will be
written, in ASCII form. This buffer is assumed to be large enough to
contain the generated report. Approximately 40 bytes per task should be
sufficient.

Fig 8.3 Example of Task List

vTaskGetRunTimeStats():

void vTaskGetRunTimeStats( char *pcWriteBuffer );


configGENERATE_RUN_TIME_STATS,
configUSE_STATS_FORMATTING_FUNCTIONS and
configSUPPORT_DYNAMIC_ALLOCATION must all be defined as 1 for this
function to be available.

Fig 8.4 Example of Run Time Stats

vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats the raw data


generated by uxTaskGetSystemState() into a human readable (ASCII) table that
shows the amount of time each task has spent in the Running state (how much
CPU time each task has consumed).The resolution of the absolute value is
dependent on the frequency of the run time stats clock provided by the application.

Parameters:
 pcWriteBuffer: A buffer into which the execution times will be written,
in ASCII form. This buffer is assumed to be large enough to contain the
generated report. Approximately 40 bytes per task should be sufficient.

8.2 IMPLEMENTATION:

The following are the steps to be followed for implementing the project in a
LINUX based system:
1) Adding tools to the environment

Before proceeding to compiling and building the project ensure that all the tools
must be added to the path so as to function. Hence, navigate to the folder where the
ESP-IDF software is stored and then type the command. ./export.sh. This will
ensure that all the tools are added to the path for smooth functioning of the build
process.
Fig 8.5 Adding tools to the path

2) Configuring the options

Now, navigate to the folder where the project is stored through the terminal and
type make menuconfig. The following window appears:

Fig 8.6 Configuration Window


Now, navigate to the serial flasher config(shown below), at the top is the name of
the serial port to which the component is to attached, now set the flash size to
2MHz and the baud rate to 115200bps and the flash SPI speed to 40MHZ(These
are to be done according to the board that one uses).
Fig 8.7 Serial Flasher Config Window

Now, go to freeRTOS under the component config and set the options as shown
below. These are set to enable the APIs vTaskList and vTaskGetRunTimeStats that
are used in the project and also to to do various other funcyions like to limit the
size of the task name, tick rate etc.
After setting the options as per requirement, press S to save the choices and press
Esc to exit from the configuration window.

Fig 8.8 Component Config Window


3) Building the project
Now, type the command make to build the project. This builds the project and
generates the binary file that can be flashed to the device. A folder called as build
is created in the project folder and all the information pertaining to building the
project is stored there.

Fig 8.9 Process of building the project

4) Flashing the program to the device


Now, type the command make flash which flashes the binaries to the device.

Fig 8.10 Flashing binaries to the device

5) Viewing the output


The program eventually prints the data on a UART, we ca see the output on the
terminal by typing the command make monitor. This results in the output being
printed on the console continuously since all the tasks run in an infinite loop. In
order to exit from this, press ctrl+] this will exit from the output and restores the
terminal to normal form.
9. RESULTS AND DISCUSSIONS

The project converges to a study on FreeRTOS, installation of ESP-IDF and


executing two applications on the ESP32As demonstrated in the previous chapter,
we ran two applications whose results are shown below.
The first application is implementing a calculator on a single-core processor.

Fig 9.1 Result obtained for single-core processor

In the above figure, we can see that the expected values of the outcome match with
the obtained results. It shows the list of all the tasks performed.

Fig 9.2 Result obtained for single-core processor


In the above figure, the tasks along with their runtime and percentage of CPU
utilization are determined.
We can now calculate the real-time statistics which are execution time of the
program and percentage of CPU utilization for the application.
The formulae to determine the above parameters are as follows.
Execution time of the program:
The execution time of the program is the sum of execution time of the individual
tasks.
Hence, Execution time(te) = 1201500+ 29546+ 30053+ 19967+ 43097+ 26704+
37843+ 31665+ 20590+ 130097+ 38653+ 119233+
3249935+ 1465122+ 50+ 48+ 9588+ 4624
= 6458315 microseconds
Execution time(te) =6.46 seconds

CPU Utilization:
CPU Utilization is the sum of work handled by a Central Processing Unit.
CPU Utilization = 100- (%time for which the CPU is idle)
From the above results we know that, CPU is idle for 49% for IDLE1 and 22% for
IDLE2.
Hence, CPU Utilization = 100 – (49+22) = 29%
The second application is implementing a calculator on a dual-core processor.
Fig 9.3 Result obtained for dual-core processor
In the above figure, we can see that the expected values of the outcome match with
the obtained results. It shows the list of all the tasks performed.

Fig 9.4 Result obtained for dual-core processor


In the above figure, the tasks along with their runtime and percentage of CPU
utilization are determined.
We can now calculate the real-time statistics which are execution time of the
program and percentage of CPU utilization for the application.
Execution time of the program:
Careful observation of the results above prove the point that when it comes to a
dual-core processor, the execution time for the program is the execution time of
that processor that has the highest execution time.
For core-0,
Execution time(te) = 2820065+26201+33155 + 22061+22656+ 122340+ 26020
+55+48+4624
= 3056834 microseconds
Execution time(te) =3.057 seconds

For core-1,
Execution time(te) =1201577+27131+1646545+39032+28717+108791+18142
+9588
= 3079523 microseconds
Execution time(te) = 3.08 seconds
Since, the core-1 has a higher execution time compared to core-0, the execution
time of the dual-core processor as a whole is taken to be 3.08 seconds.

Hence, Execution time(te) = 3.08 seconds(3079523 microseconds)

CPU Utilization:
CPU Utilization is the sum of work handled by a Central Processing Unit.
CPU Utilization = 100- (%time for which the CPU is idle)
The CPU utilization of a dual-core processor is given by the average of the CPU
utilization times of both the cores.
Core -0
CPU Utilization = (100-45)% = 55%
Core -1
CPU Utilization = (100-26)% = 74%
Hence, for the dual-core processor,
CPU Utilization=( 55+74)/2 = 64.5%

Let us summarize the results.


S.No. Application Number of Execution CPU
Tasks Time Utilization
(in (%)
microseconds)
1. Calculator for single- 12 6458315 29%
core processor
2. Calculator for dual-core 12 3079523 64.5%
processor
Table 9.1 Results
It is crystal clear from the results above that the dual core processor has an
exceedingly efficient execution time. It is almost half of what the single core
processor delivers. And also the CPU Utilization of the dual-core processor has
been considerably increased from that of dual-core processor. The values can be
compared from the table and the inferences drawn are mentioned in the next
chapter.
10. CONCLUSION
In the present project, the performance of the FreeRTOS kernel using ESP32 board
was analysed through different parameters. The explanation about the importance
of using RTOS in critical time systems was also accomplished and proved.
Considering the circumstances of the experiments, it is possible to conclude that
the FreeRTOS kernel really presented determinism and reliability. Such kernel
may be considered useful and flexible with free open source libraries. The
FreeRTOS libraries are written in C programming language, being very easy to
become acquainted with.
The embedded system ESP32 demonstrated a good performance with simplicity,
low cost reliability, and feasibility. The board has a very small size, demonstrating
a good capability of being installed in any further projects, when portability is
necessary. As displayed in the project, this device may be considered useful for the
proposed application and other applications involving RTOS. ESP32 is a dual-core
processor and the project successfully runs the application on one of the two cores
and also on the two cores.

From the results shown above and all the discussion, it is evident that the dual core
processor totally outplays the single core processor. All the limitations pertaining
to the single core processor have been overcome by the dual core processor. The
execution time of the dual core processor is almost half of the single core processor
which reflects the efficient and quick response time of the dual core processor.
Hence, all these results validate the usage of dual core processors in most of the
modern applications.

From this project we conclude that RTOS is very important to handle critical time
systems, which become useless after missing the deadline. Also, we have seen the
capabilities of the ESP32 board, and up to what extent a single-core processor is
reliable. A dual core processor executes all the tasks much faster. It is perfect to
run multiple processors at one time. If a single core processor is assigned with two
difference things, it cannot do them both simultaneously. It switches to all tasks
one by one but a multiple core processor can do both operations at the same time.
Having a dual core processor means one has the power of two computers in one.
But a dual core processor is much cheaper than buying two computers and than
combining them to build a dual-processor unit. The disadvantage is that, a lot of
power is wasted which of course quickly drains the battery. For simpler
applications, the disadvantage overpowers its advantages.

Depending on the needs required to meet the project, one can choose to whether to
go for the single-core or the dual-core processor. From this project, along with
pointing the difference between the single and dual core systems, it is possible to
conclude that there is a very rich field involving RTOS applications for embedded
tasks.

10.1Future Scope
Microprocessors have found their way in almost every electronic device that is
available today. Microprocessors and microcontrollers enable the modern world to
produce devices that comprehend the environment and react to situations
producing a much desired effect. The operating speed of modern day computers
has increased over a thousand times in the past twenty years.
It’s not hard to predict that next-generation processors will have smaller details,
more transistors on each square millimeter of silicon, and do more clever things,
such as powering down under-used sections even between keystrokes. They will
also have many cores or CPUs.
Even now, Intel has given hardware and software developers a peek at a prototype
80-core processor with one teraflop of computational ability. The company,
however, has set no launch date. Nvidia Corp. sells a Quadro Plex graphic
processor sporting 128 cores. And Cisco Systems Inc., sensing a coming surge in
network traffic, is toying with a 188-core router.
References
(1) https://www.freertos.org/FreeRTOS-quick-start-guide.html
(2) https://www.freertos.org/a00019.html
(3) https://www.freertos.org/a00021.html#vTaskList
(4) https://www.freertos.org/a00021.html#vTaskGetRunTimeStats
(5) https://www.freertos.org/RTOS-message-buffer-API.html
(6) ESP-IDF Programming Guide — ESP-IDF Programming Guide documentation
(7) https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/linux-
setup.html
(8) https://docs.aws.amazon.com/freertos/latest/userguide/what-is-freertos.html
(9) https://www.espressif.com/en/products/socs/esp32/overview
(10) Using the FreeRTOS Real Time Kernel: A Practical Guide Richard Barry

You might also like