Objective
- Understand how to use the semaphore with FreeRTOS. In this lab, the main focus will be using semaphores to sycnrhonize various tasks. Students must create four different tasks. Task 1 should run every two seconds. Task 2 should run twice as fast as task 1. Task 3 should run every time either task 1 or task 2 run. Lastly, task 4 should run every time task 3 runs. Each task must display "Task X running".
 
| Task | Priority | Description | 
| Task 1 | High | Run every 2 seconds | 
| Task 2 | High | Run twice as fast as Task 1 | 
| Task 3 | Low | Run everytime either Task 1 or Task 2 | 
| Task 4 | Low | Run everytime Task 3 runs | 
 
| Task Note | 
| Low priority numbers denote low priority tasks. | 
 
Bonus
- Undergrad Bonus:
- Turn an led every time Task 3orTask 4runs.
 
- Grad Bonus:
- Create two indepenent tasks: Task 5andTask 6.Task 5will send a semaphore toTask 6which turns on theonboard led.
 
ESP32 Pinout
                                        +-----------------------+
                                        | O      | USB |      O |
                                        |        -------        |
                                    3V3 | [ ]               [ ] | VIN
                                    GND | [ ]               [ ] | GND
    Touch3 / HSPI_CS0 / ADC2_3 / GPIO15 | [ ]               [ ] | GPIO13 / ADC2_4 / HSPI_ID / Touch4
CS / Touch2 / HSPI_WP / ADC2_2 /  GPIO2 | [ ]               [ ] | GPIO12 / ADC2_5 / HSPI_Q / Touch5
     Touch0 / HSPI_HD / ADC2_0 /  GPIO4 | [ ]               [ ] | GPIO14 / ADC2_6 / HSPI_CLK / Touch6
                        U2_RXD / GPIO16 | [ ]               [ ] | GPIO27 / ADC2_7 / Touch7
                        U2_TXD / GPIO17 | [ ]               [ ] | GPIO26 / ADC2_9 / DAC2
                     V_SPI_CS0 /  GPIO5 | [ ]  ___________  [ ] | GPIO25 / ADC2_8 / DAC1
               SCK / V_SPI_CLK / GPIO18 | [ ] |           | [ ] | GPIO33 / ADC1_5 / Touch8 / XTAL32
       U0_CTS / MSIO / V_SPI_Q / GPIO19 | [ ] |           | [ ] | GPIO32 / ADC1_4 / Touch9 / XTAL32
                SDA / V_SPI_HD / GPIO21 | [ ] |           | [ ] | GPIO35 / ADC1_7 
                 CLK2 / U0_RXD /  GPIO3 | [ ] |           | [ ] | GPIO34 / ADC1_6 
                 CLK3 / U0_TXD /  GPIO1 | [ ] |           | [ ] | GPIO39 / ADC1_3 / SensVN 
       SCL / U0_RTS / V_SPI_WP / GPIO22 | [ ] |           | [ ] | GPIO36 / ADC1_0 / SensVP 
               MOSI / V_SPI_WP / GPIO23 | [ ] |___________| [ ] | EN 
                                        |                       |
                                        |  |  |  ____  ____  |  |
                                        |  |  |  |  |  |  |  |  |
                                        |  |__|__|  |__|  |__|  |
                                        | O                   O |
                                        +-----------------------+
Example 1
The following example is a brief demostration of how to use a semaphores. Task 1 will run every 2 second and give a semaphore for Task 2 to run. Task 2 will consistently wait to receive the semaphore which Task 1 will provide every 2 seconds. Once Task 2 receives the semaphore from Task 1 it will run and print "Task 2 is running!!".
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
 
SemaphoreHandle_t mySemaphore = NULL;
 
void exampleTask1(void *pvParameters){
    while(1){
        
        xSemaphoreGive(mySemaphore); 
        vTaskDelay(2000 / portTICK_PERIOD_MS); 
        printf("Task 1 running!\n");
    }
}
 
void exampleTask2(void *pvParameters){
    while(1){
         
         if(xSemaphoreTake(mySemaphore, 1000/portTICK_RATE_MS) == pdTRUE){ 
             printf("Task 2 is running!!\n");
             vTaskDelay(100/portTICK_RATE_MS); 
         }else{
             vTaskDelay(100/portTICK_RATE_MS); 
         }
    }
}
 
 
    
    mySemaphore = xSemaphoreCreateBinary(); 
 
    
    xTaskCreate(&exampleTask1, "example task 1", 2048, NULL, 4, NULL); 
    xTaskCreate(&exampleTask2, "example task 2", 2048, NULL, 4, NULL);
 
}
void app_main()
Definition main.c:261
Example 2
The following example is a more advance demostration of how to use semaphore. Task 1 will wait for mySemaphore1 and once it receives it, it will give mySeamphore2. Task 2 will receive the semaphore from Task 1 and run. Once Task 2 task is done, mySemaphore1 will be given back to Task 1 and run again. In simple terms, task 1 will run task 2 and task 2 will run task 1. Therefore, task 1 and task 2 synchronize which demostrates the true purpose of semaphores. 
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
 
SemaphoreHandle_t mySemaphore1 = NULL; 
SemaphoreHandle_t mySemaphore2 = NULL;
 
void exampleTask1(void *pvParameters){
    while(1){
        
        if(xSemaphoreTake(mySemaphore1, (TickType_t)100 ) == pdTRUE){ /
              printf("Task 1 sent flag to task 2!\n");
              vTaskDelay(2000 / portTICK_PERIOD_MS); 
              xSemaphoreGive(mySemaphore2); 
        }else{
              vTaskDelay(100 / portTICK_PERIOD_MS); 
        }
    }
}
 
void exampleTask2(void *pvParameters){
    while(1){
         
         if(xSemaphoreTake(mySemaphore2, 100/portTICK_PERIOD_MS) == pdTRUE){ 
             printf("Task 2 is running!!!!\n");
             vTaskDelay(100/portTICK_PERIOD_MS); 
             xSemaphoreGive(mySemaphore1); 
         }else{
             vTaskDelay(100/portTICK_PERIOD_MS); 
         }
    }
}
 
 
    
    mySemaphore1 = xSemaphoreCreateBinary(); 
    mySemaphore2 = xSemaphoreCreateBinary(); 
 
    
    xTaskCreate(&exampleTask1, "example task 1", 2048, NULL, 4, NULL); 
    xTaskCreate(&exampleTask2, "example task 2", 2048, NULL, 4, NULL); 
    
    
    xSemaphoreGive(mySemaphore1); 
}
Lab Template
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
 
 
void task1(
void *pvParameters)
 
{
    while(1)
    {
        printf("Task 1\n");
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
 
void task2(
void *pvParameters)
 
{
    while(1)
    {
        printf("Task 2\n");
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}
 
{
    
 
    
    xTaskCreate(&
task1, 
"task1", 4096, NULL, 5, NULL);
    xTaskCreate(&
task2, 
"task2", 4096, NULL, 5, NULL);
 
}
SemaphoreHandle_t xSemaphore
Definition main.c:40
void task1(void *pvParameters)
Definition main.c:43
void task2(void *pvParameters)
Definition main.c:53
C helpful functions
For this lab, there are few important function calls that are going to be used throughout the lab. Semaphores are created by using SemaphoreHandle_t data type. For instance, SemaphoreHandle_t mySemaphore is how you would create a semaphore.
To be able to allocate an instance of a semaphore, you will need the following function: xSemaphoreCreateBinary. There are different types of semaphores, however, we will only look at the binary semaphore for now. A binary semaphore has a behavior that holds a value of 0 or 1. 
SemaphoreHandle_t xSemaphoreCreateBinary( void );
 Next, xSemaphoreGive will set a 0 in the semaphore. Therefore, by setting a 0 it will state that the semaphore is ready to be used. 
void xSemaphoreGive( SemaphoreHandle_t 
xSemaphore );
 
Lastly, xSemaphoreTake is the opposite from the previous function, as it will set a 1 and used the semaphore. However, xSemaphoreTake will require two parameters SemaphoreHandle_t xSemaphore and TickType_t xTicksToWait. You must pass by value the instance of a semaphore and how long to wait for the semaphore. If you receive the semaphore during the provided time frame, xSemaphoreTake will return pdTRUE otherwise pdFALSE. 
xSemaphoreTake( SemaphoreHandle_t 
xSemaphore, TickType_t xTicksToWait );
Additional Links
Authors
GitHub
 Read Next: Lab 5