# STM32 - FreeRTOS
###### tags: `firmware_hardware` `electrical_system` `NTURT`
#### Author: @andre-liang

### Preface
Conventionally, functions related to sensor measurements and actuator operations are grouped together within a loop and executed sequentially. However, this conventional method is suboptimal due to varying time requirements for specific operations, such as communication tasks that can exhibit inconsistent durations. Consequently, ensuring the execution of critical operations within designated time constraints becomes challenging.
To address this issue, a more efficient strategy involves partitioning the primary loop into discrete tasks, each assigned different priorities. This partitioning allows for the independent execution of tasks, enabling better management of critical functions within predefined timeframes. Accomplishing precise task execution timing necessitates the implementation of a real-time operating system (RTOS). In our solution, we've adopted FreeRTOS as the foundational framework. Notably, FreeRTOS seamlessly integrates with CubeIDE, simplifying the integration of real-time task scheduling into existing projects.
### Multithreading and Race Condition
While our microcontroller features a sole physical core, capable of executing only one instruction at a time, a clever mechanism allows us to seemingly achieve parallelism. By swiftly alternating between different tasks, our microcontroller can simulate the execution of multiple tasks concurrently. This illusion of simultaneous execution is achieved through a dynamic process known as scheduling, which involves determining the order and timing of task execution.

Round Robin Scheduling Algorithm, source: [link](http://cs341.cs.illinois.edu/coursebook/Scheduling)
Nonetheless, due to the potentially random execution sequence among multiple tasks, a situation may arise where multiple processes attempt to access the same data, often referred to as shared memory, without any safeguards. This scenario gives rise to a critical issue known as a race condition, where unintended and erroneous outcomes can occur. To mitigate this problem, protective mechanisms like mutual locks and semaphores are implemented. These mechanisms act as safeguards, ensuring orderly and controlled access to shared memory, thereby averting the pitfalls of race conditions and promoting the stability and reliability of the system.

Mutex Locking Mechanism Illustration, source:[link](https://www.geeksforgeeks.org/mutex-lock-for-linux-thread-synchronization/)
### Setup
To begin, initiate the setup of a new STM32 project, meticulously configuring the hardware settings to suit your requirements. Next, navigate to the Middleware and Software Pack section, and proceed to enable FreeRTOS, a crucial component for effective multitasking. Within the tasks and queue subsection, establish two distinct tasks: hello and fib. As you embark on this task, feel free to experiment with varying priority settings to fine-tune task execution. While customizing the priority settings, remember to retain the default configurations for other settings.

### Define Tasks
CubeIDE will automatically generate empty functions for the tasks you've created. Consequently, it becomes essential for us to delve into these functions and intricately define the specific details of our custom tasks. By injecting the requisite functionality into these tasks, we breathe life into our application, enabling it to execute precisely as intended.
```c=
void fib_cal(void const * argument)
{
/* USER CODE BEGIN fib_cal */
/* Infinite loop */
int index;
for(;;)
{
osDelay(1);
for (index=0;index<20;index++){
printf("fib(%d)=%d\r\n",index,fib(index));
osDelay(500);
}
}
/* USER CODE END fib_cal */
}
```
```c=
void hi_funct(void const * argument)
{
/* USER CODE BEGIN hi_funct */
/* Infinite loop */
for(;;)
{
printf("Hello from STM32!\r\n");
osDelay(1000);
}
/* USER CODE END hi_funct */
}
```
It's important to emphasize that we opt for the use of `osDelay` over `HAL_Delay` to temporarily suspend individual tasks. This distinction arises from the fact that HAL_Delay employs a loop-based mechanism, known as busy waiting, which consumes valuable computational resources. In contrast, osDelay offers a more efficient and accurate approach by enabling the CPU to execute other tasks during the suspension of a particular task.
### Output Interface
As we harness the printf function to produce output messages within the STM32 environment, it's worth noting that STM32 lacks native support for standard output. To address this, we adopt a strategic approach: we channel the content of the stdout stream to the UART connector using a specific mechanism. This tactful maneuver enables us to efficiently route the output messages to the designated UART interface, effectively bridging the gap between printf functionality and the STM32 environment.
```c=
int __io_putchar(int ch) {
HAL_UART_Transmit(&huart3, (uint8_t*)&ch, 1, HAL_MAX_DELAY);
return ch;
}
```
### Result
Upon executing the program, initiate the process of opening a serial monitor for observing the messages emanating from the STM32. For this purpose, I recommend employing the CuteCom program, which proves particularly suitable for Linux platforms. As the serial monitor interface comes to life, you will gain insight into the messages the STM32 meticulously produces. This visual exploration will distinctly reveal the concurrent execution of the two custom tasks, confirming their parallel operation in action.

### Conclusion
In conclusion, the incorporation of an RTOS along with a task-based methodology offers a streamlined means of structuring our application. The compartmentalization of functions into distinct tasks yields tangible benefits, enhancing both productivity and system stability. For those seeking a more profound grasp of operating systems concepts, I wholeheartedly recommend exploring the IM2003 Operating Systems course. Delving into this course will undoubtedly provide a richer understanding of the intricate realm of operating systems.