Pertemuan OS - Id.en
Pertemuan OS - Id.en
Pertemuan OS - Id.en
com
OS MEETING SUMMARY
Richard Barry originally developed FreeRTOS around 2003. Real-Time Engineers Ltd,
Richard's company, continued FreeRTOS development in close partnership with the
world's leading chip companies until Amazon Web Services (AWS) took stewardship
of FreeRTOS 2016.
There are many well-established techniques for writing good embedded software
without using a multithreading kernel. If the system under development is simple,
then these techniques might provide the most appropriate solution. Using a kernel
would likely be preferable in more complex cases, but where the crossover point
occurs will always be subjective.
As already described, task prioritization can help ensure an application meets its
processing deadlines, but a kernel can bring other less obvious benefits. Some of
these are listed very briefly below.
The RTOS is responsible for execution timing and provides a time-related API to the
application. That allows the structure of the application code to be more
straightforward and the overall code size to be smaller.
2. Maintainability/Extensibility
3. Modularity
Tasks are independent modules, each of which should have a well-defined purpose.
4. Team development
Tasks should also have well-defined interfaces, allowing easier team development.
5. Easier testing
Tasks that are well-defined independent modules with clean interfaces are easier to
test in isolation.
6. Code reuse
7. Improved efficiency
Countering the efficiency gained from being event driven is the need to process the
RTOS tick interrupt and switch execution from one task to another. However,
applications that don't use an RTOS normally include some form of tick interrupt
anyway.
8. Idle time
The automatically created Idle task executes when there are no application tasks that
require processing. The Idle task can measure spare processing capacity, perform
background checks, or place the processor into a low-power mode.
9. Power Management
The efficiency gains that result from using an RTOS allow the processor to spend
more time in a low power mode.
Simple design patterns can achieve a mix of periodic, continuous, and event-driven
processing within an application. In addition, hard and soft real-time requirements
can be met by selecting appropriate tasks and interrupt priorities.
To help users orient themselves with the FreeRTOS kernel files and directories, this
chapter:
tasks.c and list.c implement the core FreeRTOS kernel functionality and are always
required. They are located directly in the FreeRTOS/Source directory, as shown in
Figure 2.2. The same directory also contains the following optional source files:
1. queue.c
2. timers. c
3. event_groups.c
5. croutine.c
Variable Names
Variables are prefixed with their type: 'c' for char, 's' for int16_t (short), 'l' for int32_t
(long), and 'x' for BaseType_t and any other non-standard types (structures, task
handles, queue handles, etc.).
If a variable is unsigned, it is also prefixed with a 'u'. If a variable is a pointer, it is also
prefixed with a 'p'. For example, a variable of type uint8_t will be prefixed with 'uc',
and a variable of type pointer to char (char *) will be prefixed with 'pc'.
Function Names
Functions are prefixed with both the type they return and the file they are defined
within. For example:
The RTOS kernel needs RAM each time a task, queue, mutex, software timer,
semaphore or event group is created. The RAM can be automatically dynamically
allocated from the RTOS heap within the RTOS API object creation functions, or it
can beprovided by the application writer.
If RTOS objects are created dynamically then the standard C library malloc() and
free() functions can sometimes be used for the purpose, but...
...so more often than not an alternative memory allocation implementation is required.
One embedded / real time system can have very different RAM and timing
requirements to another - so a single RAM allocation algorithm will only ever be
appropriate for a subset of applications.
4) Task Management
Scope
Task Functions
Tasks are implemented as C functions. Tasks must implement the expected function
prototype shown in Listing 4.1. which takes a void pointer parameter and returns
void. Code:void vATaskFunction( void * pvParameters );
The tasks created in Example 4.4 spend most of their time in the Blocked state. While
in this state, they are not able to run, so they cannot be selected by the scheduler.
There must always be at least one task that can enter the Running state 3. To ensure
this is the case, the scheduler automatically creates an Idle task when
vTaskStartScheduler() is called. The idle task does very little more than sitting in a
loop, so, like the task in the first example, it is always able to run.
The idle task has the lowest possible priority (priority zero), to ensure it never
prevents a higher priority application task from entering theRunningstate. However,
there is nothing to prevent application designers from creating tasks at, and
therefore share, the idle task priority, if desired. The configIDLE_SHOULD_YIELD
compile time configuration constant in FreeRTOSConfig.h can be used to prevent the
Idle task from consuming processing time that would be more productively allocated
to application tasks that also have a priority of 0. Section 4.12, Scheduling
Algorithms, describes configIDLE_SHOULD_YIELD.
Running at the lowest priority ensures the Idle task is transitioned out of
theRunningstate as soon as a higher priority task enters the Ready state. This can be
seen at time tn in Figure 4.9, where the Idle task is immediately swapped out to allow
Task 2 to execute at the instant Task 2 leaves theBlockedstate. Task 2 is said to have
preempted the idle task. Preemption occurs automatically, and without the
knowledge of the task being preempted.
It is possible to add application specific functionality directly into the idle task
through the use of an idle hook (or idle callback) function, which is a function that is
called automatically by the idle task once per iteration of the idle task loop.
Measuring the amount of spare processing capacity. (The idle task will run
only when all higher priority application tasks have no work to perform; so
measuring the amount of processing time allocated to the idle task provides a
clear indication of spare processing time.)
Placing the processor into a low power mode, providing an easy and
automatic method of saving power whenever there is no application
processing to be performed (although the achievable power saving is less
than that achieved by tick-less idle mode).
Mutex.ino
To introduce conflicts between tasks, we can modify the parameters passed to the
xTaskCreate() function, in particular the delayTime parameter. We can adjust the delay time of
one of the tasks to use a shorter delay time of 500ms
Code:
void setup() {
Serial.begin(9600);
mutex = xSemaphoreCreateMutex();
if (mutex != NULL) {
Serial.println("Mutex created");
}
for (;;)
{
Serial.print(pcTaskGetName(NULL)); // Get the task name
Serial.print(", Read value Count: ");
Serial.print(globalCount);
globalCount++;
vTaskDelay(delayTime / portTICK_PERIOD_MS);
}
}
With this task modification, both tasks will directly access and modify the globalCount
variable without any synchronization mechanism. But a conflict occurred, so I added code like the
one below
Semaphore is a tool used to organize access to shared resources between different tasks in the
system, before tasks access the globalCount variable, they must request "permission"
(semaphore) by using xSemaphoreTake(mutex, 10). Then, when it has finished accessing
globalCount, the task gives back those "permissions" using xSemaphoreGive(mutex).
when we remove these two operations from the TaskMutex function, it means we remove
the command to ask permission (take) before accessing globalCount and give permission
back (give) after finishing accessing it.
By removing this operation, both tasks no longer have to request permission before
accessing globalCount. They can access it immediately without waiting or coordinating with
other tasks. This can be problematic, because both tasks could attempt to access
globalCount at the same time, which could result in incorrect or inconsistent values. This is
the intended conflict, when tasks compete to access the same resources without
coordination.
If a conflict occurs, add a delay such as:
/**Create tasks*/
xTaskCreate(TaskMutex, // Task function
"Task1", // Task names for humans
128,
2000, // Task parameters
1, // Task priority
NULL);
xTaskCreate(TaskMutex, "Task2", 128, 2000, 1, NULL);
3. Mutex/Semaphore
Mutex is a tool used in parallel programming to ensure that only one process or task
can access a shared resource at any given time. This helps prevent situations where two or
more processes try to access the same resource simultaneously, which could result in
inconsistent or even broken results. By using mutexes, we can ensure proper coordination
and synchronization between these processes, thereby ensuring organized and correct
execution in a parallel environment.
Mutex is implemented using the functions and data structures provided by FreeRTOS.
FreeRTOS provides functions such as xSemaphoreCreateMutex(), xSemaphoreTake(), and
xSemaphoreGive() to create, retrieve, and provide mutexes, which allows users to use the
mutex concept in embedded system development.
- Mutex is a special type of semaphore that has only two values
locked (locked) or not locked (unlocked).
- The main purpose of a mutex is to ensure that only one thread or process can
access a shared resource at any given time.
- Mutex to protect critical zones in a program and Only one thread can hold a
mutex at a time. If a mutex is being used by a particular thread, other threads
must wait until the mutex is released
Semaphore
A semaphore is a special variable used to control access to a shared resource by multiple
threads or processes.
- has values that can be changed according to application needs
- two types of semaphore binary semaphore (or mutex) which has two values (0
or 1) and counting semaphore which has a value greater than zero.
4. Resource Management
MULTITHREADING
TaskAnalogRead: This task reads the analog value from pin A0 and displays the value
to the Serial Monitor using the Serial.println() function. This task also uses the vTaskDelay()
function to provide a delay between reading analog values.
THREADS
Multithreading (also called the concept of threads) => execution of several tasks
simultaneously on the Arduino platform.
The two tasks that are executed, namely TaskBlink and TaskAnalogRead, run independently
of each other. TaskBlink controls the LED to flash, while TaskAnalogRead reads the analog
value from the pin and displays it on the Serial Monitor.
Threads are small units of a process that can execute independently. A program can
have one or more threads running simultaneously. Threads allow programs to perform
multiple tasks simultaneously or in parallel.
Concurrency is the ability of a program to handle multiple orders or requests. Where
every incoming order or request can be stacked/encumbered by one process.