Using xTaskNotifyGive() as a Lightweight Semaphore in FreeRTOS
📖 Scenario: You are working on a FreeRTOS embedded system where two tasks need to synchronize access to a shared resource. Instead of using a full semaphore, you want to use xTaskNotifyGive() and ulTaskNotifyTake() as a lightweight semaphore to signal between tasks.
🎯 Goal: Build a simple FreeRTOS program where one task signals another task using xTaskNotifyGive() as a lightweight semaphore. The second task waits for the notification using ulTaskNotifyTake() before proceeding.
📋 What You'll Learn
Create two tasks: Task1 and Task2
Use xTaskNotifyGive() in Task1 to notify Task2
Use ulTaskNotifyTake() in Task2 to wait for the notification
Print messages to indicate when Task2 is waiting and when it receives the notification
💡 Why This Matters
🌍 Real World
Lightweight task synchronization is common in embedded systems where resources are limited and tasks need to signal events efficiently.
💼 Career
Understanding task notifications as lightweight semaphores is valuable for embedded software engineers working with FreeRTOS or similar real-time operating systems.
Progress0 / 4 steps
1
Create two FreeRTOS tasks named Task1 and Task2
Write code to create two tasks called Task1 and Task2 using xTaskCreate(). Use the function prototypes void Task1(void *pvParameters) and void Task2(void *pvParameters). Use stack size 1000 and priority 1 for both tasks.
FreeRTOS
Hint
Use xTaskCreate() twice, once for each task, with the exact names Task1 and Task2.
2
Add a notification counter variable for Task2
Declare a variable called ulNotificationValue of type uint32_t inside Task2 to hold the notification count returned by ulTaskNotifyTake().
FreeRTOS
Hint
Inside Task2, declare uint32_t ulNotificationValue; before the infinite loop.
3
Use xTaskNotifyGive() in Task1 and ulTaskNotifyTake() in Task2
Inside Task1, call xTaskNotifyGive() to notify Task2 every 2 seconds. Inside Task2, use ulTaskNotifyTake(pdTRUE, portMAX_DELAY) to wait for the notification and store the result in ulNotificationValue. Remove the vTaskDelay() calls inside both tasks.
FreeRTOS
Hint
Use xTaskNotifyGive(xTask2Handle); in Task1 and ulNotificationValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY); in Task2.
4
Print messages in Task2 to show waiting and notification received
Inside Task2, before calling ulTaskNotifyTake(), print "Task2 waiting for notification...". After receiving the notification (when ulNotificationValue is greater than 0), print "Task2 received notification!".
FreeRTOS
Hint
Use printf("Task2 waiting for notification...\n"); before ulTaskNotifyTake() and printf("Task2 received notification!\n"); after checking ulNotificationValue > 0.
Practice
(1/5)
1. What is the primary purpose of xTaskNotifyGive() in FreeRTOS?
easy
A. To send a simple signal from one task to another as a lightweight semaphore
B. To create a new task in the system
C. To delete a task permanently
D. To allocate memory dynamically
Solution
Step 1: Understand the function role
xTaskNotifyGive() is designed to send a notification signal from one task to another.
Step 2: Compare with other options
Creating or deleting tasks and memory allocation are unrelated to xTaskNotifyGive().
Final Answer:
To send a simple signal from one task to another as a lightweight semaphore -> Option A
Quick Check:
Signal sending = To send a simple signal from one task to another as a lightweight semaphore [OK]
Hint: Remember: xTaskNotifyGive() signals tasks like a lightweight semaphore [OK]
Common Mistakes:
Confusing it with task creation or deletion functions
Thinking it manages memory
Assuming it blocks tasks directly
2. Which of the following is the correct syntax to send a notification using xTaskNotifyGive() to a task handle named xTaskHandle?
easy
A. xTaskNotifyGive(xTaskHandle, 1);
B. xTaskNotifyGive(&xTaskHandle);
C. xTaskNotifyGive(xTaskHandle);
D. xTaskNotifyGive();
Solution
Step 1: Recall function signature
xTaskNotifyGive() takes a task handle as a parameter, not a pointer or extra arguments.
Step 2: Check each option
xTaskNotifyGive(xTaskHandle); correctly passes the task handle. xTaskNotifyGive(&xTaskHandle); passes address (wrong), C adds an extra argument (wrong), D has no argument (wrong).
A. Passing address of task handle instead of task handle to xTaskNotifyGive()
B. Using pdFALSE in ulTaskNotifyTake() causes immediate return
C. Missing delay in TaskB before notify
D. printf statement is incorrect syntax
Solution
Step 1: Check parameter passed to xTaskNotifyGive()
The function expects a task handle, but &TaskAHandle passes its address, which is incorrect.
Step 2: Validate other parts
Using pdFALSE is valid to clear notification count, no syntax error in printf, and delay is optional.
Final Answer:
Passing address of task handle instead of task handle to xTaskNotifyGive() -> Option A
Quick Check:
Correct parameter type needed = Passing address of task handle instead of task handle to xTaskNotifyGive() [OK]
Hint: Pass task handle directly, not its address [OK]
Common Mistakes:
Passing pointer to handle instead of handle
Misunderstanding pdFALSE effect
Assuming printf syntax error
5. You want to use xTaskNotifyGive() as a lightweight semaphore to synchronize two tasks: Producer and Consumer. The Consumer waits for the notification before processing data. Which sequence correctly implements this synchronization?
hard
A. Producer calls ulTaskNotifyTake(pdTRUE, portMAX_DELAY);, Consumer calls xTaskNotifyGive(ProducerHandle);
B. Consumer calls xTaskNotifyGive() and Producer calls ulTaskNotifyTake()
C. Both tasks call xTaskNotifyGive() simultaneously without waiting
D. Consumer calls ulTaskNotifyTake(pdTRUE, portMAX_DELAY); to wait, Producer calls xTaskNotifyGive(ConsumerHandle); to signal
Solution
Step 1: Identify roles of tasks
The Consumer should wait for a signal before processing, so it uses ulTaskNotifyTake() to block until notified.
Step 2: Producer signals Consumer
The Producer sends the notification using xTaskNotifyGive() with the Consumer's task handle.
Final Answer:
Consumer calls ulTaskNotifyTake(pdTRUE, portMAX_DELAY); to wait, Producer calls xTaskNotifyGive(ConsumerHandle); to signal -> Option D
Quick Check:
Wait then signal = Consumer calls ulTaskNotifyTake(pdTRUE, portMAX_DELAY); to wait, Producer calls xTaskNotifyGive(ConsumerHandle); to signal [OK]
Hint: Consumer waits, Producer signals with xTaskNotifyGive() [OK]
Common Mistakes:
Reversing roles of Producer and Consumer
Both tasks signaling without waiting
Using wrong task handles in calls
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.