This article is a continuation of the Series on RT-Thread STM32 Tutorials and carries the discussion on RT-Thread RTOS and implementation with STM32. The aim of this series is to provide easy and practical examples that anyone can understand. In our last post, we have seen Timers in the RT-Thread RTOS. In this post, we will see the RT-Thread Thread Synchronization.
You can see the video explanation of Getting started the RT-Thread RTOS.
Prerequisites
This is the continuation of the below tutorials. If you don’t read it, please read that also before this.
- RT-Thread RTOS Introduction
- Getting started with RT-Thread RTOS – Part 1
- Thread management of RT-Thread RTOS – Part 2
- Timer management of RT-Thread RTOS – Part 3
Tools and Components Required
- STM32F411 Dev Board (You can use any STM32F4 controller)
- RT-Thread Studio
RT-Thread Interthread Synchronization Tutorial
Thread Synchronization
When you work in a multithread or multitasking environment, you need to synchronize the thread or task. That is really needed. Why is it? We have already discussed this concept in this post. So we are not stepping into this topic.
Types of thread synchronization
There are many ways to synchronize threads.
- Critical Section
- Semaphores
- Mutex
- Event
- In some scenarios, we can use Mailbox, and Queue also for synchronization.
In this post, we will see the critical section and Semaphore. In our next post, we will see Mutex and Events.
Critical Section
In two ways we can enter into the critical section.
- By disabling the hardware interrupt
- By using the APIs
Disabling the hardware interrupt
We can call the rt_hw_interrupt_disable()
to enter the critical section and call rt_hw_interrupt_enable()
to exit the critical section. When we disable the hardware interrupts, the context switch will not happen. So, the other task won’t get the chance to run. So only one thread can run at a time and access the common resource. Once that thread is done, then it should re-enable the hardware interrupt.
By using the APIs
RT-Thread has special APIs to enter and exit the critical section. Call rt_enter_critical()
to enter the critical section and call rt_exit_critical()
to exit the critical section.
Example Source code
In the below code, we have created the two threads. Both are accessing the same resource called global_var
. So, both thread has to be synchronized. In this example, we have used rt_enter_critical()
and rt_exit_critical()
.
You can get the entire project source code from GitHub.
#include <rtthread.h> #define DBG_TAG "main" #define DBG_LVL DBG_LOG #include <rtdbg.h> #include <drv_common.h> #include <rtdevice.h> uint32_t global_var = 10; static rt_thread_t threadId1 = RT_NULL; static rt_thread_t threadId2 = RT_NULL; /* Thread 1 */ static void thread1_function(void *parameter) { while (1) { //rt_hw_interrupt_disable(); rt_enter_critical(); global_var++; //Accessing common resource LOG_D("Thread 1: global_var = %d\n", global_var); //rt_hw_interrupt_enable(0); rt_exit_critical(); rt_thread_mdelay(1000); } } /* Thread 2 */ static void thread2_function(void *parameter) { while (1) { //rt_hw_interrupt_disable(); rt_enter_critical(); global_var--; //Accessing common resource LOG_D("Thread 2: global_var = %d\n", global_var); //rt_hw_interrupt_enable(0); rt_exit_critical(); rt_thread_mdelay(1000); } } int main(void) { /* Create thread 1 */ threadId1 = rt_thread_create("thread1_fn", //Name thread1_function, //Function address RT_NULL, //Thread function parameter 1024, //Stack Size 1, //Thread priority 2000); //Time slice in ticks /* Create thread 2 */ threadId2 = rt_thread_create("thread2_fn", //Name thread2_function, //Function address RT_NULL, //Thread function parameter 1024, //Stack Size 1, //Thread priority 2000); //Time slice in ticks /* Start both threads */ rt_thread_startup(threadId1); rt_thread_startup(threadId2); while(1); return RT_EOK; }
Output
\ | / - RT - Thread Operating System / | \ 4.1.1 build Feb 18 2023 17:43:20 2006 - 2022 Copyright by RT-Thread team [D/main] Thread 1: global_var = 11 [D/main] Thread 2: global_var = 10 [D/main] Thread 1: global_var = 11 [D/main] Thread 2: global_var = 10 [D/main] Thread 1: global_var = 11 [D/main] Thread 2: global_var = 10 [D/main] Thread 1: global_var = 11 [D/main] Thread 2: global_var = 10 [D/main] Thread 1: global_var = 11 [D/main] Thread 2: global_var = 10 [D/main] Thread 1: global_var = 11 [D/main] Thread 2: global_var = 10 [D/main] Thread 1: global_var = 11 [D/main] Thread 2: global_var = 10 [D/main] Thread 1: global_var = 11 [D/main] Thread 2: global_var = 10 [D/main] Thread 1: global_var = 11 [D/main] Thread 2: global_var = 10
Semaphores
You can see the complete details about the semaphores here.
Create and Delete the semaphores
We can use the below API to create the semaphore dynamically.
rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag);
When this function is called, the system will first allocate a semaphore object from the object manager and initialize the object.
Parameters | Description |
---|---|
name | Semaphore Name |
value | Semaphore Initial Value |
flag | Semaphore flag, which can be the following values: RT_IPC_FLAG_FIFO or RT_IPC_FLAG_PRIO |
Return | —— |
RT_NULL | Creation failed |
semaphore control block pointer | Creation successful |
The semaphore flag
parameter determines the queuing way of how multiple threads wait when the semaphore is not available. When the RT_IPC_FLAG_FIFO (first in, first out) mode is selected, the waiting thread queue will be queued in a first in first out manner. The first thread that goes in first obtains the waiting semaphore. When the RT_IPC_FLAG_PRIO
(priority waiting) mode is selected, the waiting threads will be queued in order of priority. Threads waiting with the highest priority will get the wait semaphore first.
You can use the rt_err_t rt_sem_delete(rt_sem_t sem)
function to delete the semaphore, if you have created the semaphore using the rt_sem_create()
.
You can create the static semaphore using the below function interface. When this function is called, the system will initialize the semaphore object.
rt_err_t rt_sem_init(rt_sem_t sem, const char *name, rt_uint32_t value, rt_uint8_t flag)
Parameters | Description |
---|---|
sem | Semaphore object handle |
name | Semaphore name |
value | Semaphore initial value |
flag | Semaphore flag, which can be the following values: RT_IPC_FLAG_FIFO or RT_IPC_FLAG_PRIO |
Return | —— |
RT_EOK | Initialization successful |
You can use the rt_err_t rt_sem_detach(rt_sem_t sem)
function to detach the semaphore, if you have initialized it using the rt_sem_init()
function.
Take and Release the Semaphore
When the thread wants to access the common resource, then it has to take the semaphore. The below function interface is used to do that.
rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time);
When the semaphore value is greater than zero, the thread will get the semaphore, and the corresponding semaphore value will be reduced by 1. When calling this function, if the value of the semaphore is zero, it means the current semaphore resource instance is not available, and the thread applying for the semaphore will wait based on the time parameter. While waiting, if other threads or ISR released the semaphore, then the thread will stop the waiting. If the semaphore is still not available after parameter specified time, the thread will time out and return, the return value is -RT_ETIMEOUT
.
If you don’t want to wait, then you can call the function with time parameters as 0 like rt_sem_take(sem, 0)
. You can use this rt_sem_trytake(rt_sem_t sem)
function also if you don’t want to wait.
Once you are done with the resources, then the thread can release the Semaphore. You can release the semaphore using the below function interface.
rt_err_t rt_sem_release(rt_sem_t sem);
When you do that, it will wake up the other threads who is waiting or blocking for this semaphore. If no thread is waiting for this, it will increment the semaphore count by 1.
Example Program
In the below program, I have created two threads. Thread 1 will read the Button state which is connected to the PA0. If the button is pressed, then it will release the semaphore. In the meantime, thread 2 will be waiting for the semaphore. Once thread 1 releases the semaphore, thread 2 will blink the LED which is connected to the PC13.
You can get this complete project from GitHub.
#include <rtthread.h> #define DBG_TAG "main" #define DBG_LVL DBG_LOG #include <rtdbg.h> #include <drv_common.h> #include <rtdevice.h> #define INPUT_PIN GET_PIN(A, 0) //PA0 #define LED_PIN GET_PIN(C, 13) //PC13 static rt_thread_t threadId1 = RT_NULL; static rt_thread_t threadId2 = RT_NULL; static rt_sem_t semaphore_sync = RT_NULL; /* Thread 1 */ static void thread1_function(void *parameter) { rt_pin_mode( INPUT_PIN , PIN_MODE_INPUT_PULLUP); while (1) { if( rt_pin_read(INPUT_PIN) == 0 ) { LOG_D("Button Pressed - Released the Semaphore!!!\n"); /* Release the semaphore */ rt_sem_release(semaphore_sync); } rt_thread_mdelay(200); } } /* Thread 2 */ static void thread2_function(void *parameter) { rt_pin_mode( LED_PIN , PIN_MODE_OUTPUT); rt_pin_write(LED_PIN, PIN_HIGH); while (1) { rt_sem_take(semaphore_sync, RT_WAITING_FOREVER); LOG_D("Got Semaphore - Blink the LED!!!\n"); rt_pin_write(LED_PIN, PIN_LOW); rt_thread_mdelay(2000); rt_pin_write(LED_PIN, PIN_HIGH); rt_thread_mdelay(1000); } } int main(void) { /* Create thread 1 */ threadId1 = rt_thread_create("thread1_fn", //Name thread1_function, //Function address RT_NULL, //Thread function parameter 1024, //Stack Size 5, //Thread priority 10); //Time slice in ticks /* Create thread 2 */ threadId2 = rt_thread_create("thread2_fn", //Name thread2_function, //Function address RT_NULL, //Thread function parameter 1024, //Stack Size 5, //Thread priority 10); //Time slice in ticks /* Create the Semaphore */ semaphore_sync = rt_sem_create( "Semaphore_Sync", //Name 0, //Initial value RT_IPC_FLAG_FIFO ); //Flag if( semaphore_sync == RT_NULL ) { LOG_D("Semaphore Creation Failed!!!\n"); return RT_EOK; } /* Start both threads */ rt_thread_startup(threadId1); rt_thread_startup(threadId2); while(1); return RT_EOK; }
Output
What’s Next
In our next tutorial, we will see another method of RT-Thread Inter thread synchronization called a mutex and event.
You can also read the below tutorials.

Embedded Software | Firmware | Linux Devic Deriver | RTOS
Hi, I’m SLR. I am a tech blogger and an Embedded Engineer. I am always eager to learn and explore tech-related concepts. And also, I wanted to share my knowledge with everyone in a more straightforward way with easy practical examples. I strongly believe that learning by doing is more powerful than just learning by reading. I love to do experiments. If you want to help or support me on my journey, consider sharing my articles, or Buy me a Coffee! Thank you for reading my blog! Happy learning!