├── .gitignore ├── 01-what-is-an-rtos └── rtos-part-01.pptx ├── 02-getting-started-with-freertos ├── esp32-freertos-02-demo-blinky │ └── esp32-freertos-02-demo-blinky.ino ├── esp32-freertos-02-solution-blinky │ └── esp32-freertos-02-solution-blinky.ino └── rtos-part-02.pptx ├── 03-task-scheduling-and-management ├── esp32-freertos-03-demo-prioritization │ └── esp32-freertos-03-demo-prioritization.ino ├── esp32-freertos-03-solution-led │ └── esp32-freertos-03-solution-led.ino └── rtos-part-03.pptx ├── 04-memory-allocation ├── esp32-freertos-04-demo-stack-overflow │ └── esp32-freertos-04-demo-stack-overflow.ino ├── esp32-freertos-04-solution-heap │ └── esp32-freertos-04-solution-heap.ino └── rtos-part-04.pptx ├── 05-queue ├── esp32-freertos-05-demo-queue │ └── esp32-freertos-05-demo-queue.ino ├── esp32-freertos-05-solution-queue │ └── esp32-freertos-05-solution-queue.ino └── rtos-part-05.pptx ├── 06-mutex ├── esp32-freertos-06-challenge-task-parameters │ └── esp32-freertos-06-challenge-task-parameters.ino ├── esp32-freertos-06-demo-mutex │ └── esp32-freertos-06-demo-mutex.ino ├── esp32-freertos-06-demo-race-condition │ └── esp32-freertos-06-demo-race-condition.ino ├── esp32-freertos-06-solution-task-parameters │ └── esp32-freertos-06-solution-task-parameters.ino └── rtos-part-06.pptx ├── 07-semaphore ├── esp32-freertos-07-challenge-counting-semaphore │ └── esp32-freertos-07-challenge-counting-semaphore.ino ├── esp32-freertos-07-demo-binary-semaphore │ └── esp32-freertos-07-demo-binary-semaphore.ino ├── esp32-freertos-07-demo-counting-semaphore │ └── esp32-freertos-07-demo-counting-semaphore.ino ├── esp32-freertos-07-solution-alt-queue │ └── esp32-freertos-07-solution-alt-queue.ino ├── esp32-freertos-07-solution-counting-semaphore │ └── esp32-freertos-07-solution-counting-semaphore.ino └── rtos-part-07.pptx ├── 08-software-timer ├── esp32-freertos-08-demo-software-timer │ └── esp32-freertos-08-demo-software-timer.ino ├── esp32-freertos-08-solution-led-dimmer │ └── esp32-freertos-08-solution-led-dimmer.ino └── rtos-part-08.pptx ├── 09-hardware-interrupts ├── esp32-freertos-09-demo-isr-critical-section │ └── esp32-freertos-09-demo-isr-critical-section.ino ├── esp32-freertos-09-demo-isr-semaphore │ └── esp32-freertos-09-demo-isr-semaphore.ino ├── esp32-freertos-09-demo-timer-interrupt │ └── esp32-freertos-09-demo-timer-interrupt.ino ├── esp32-freertos-09-solution-isr-audio │ └── esp32-freertos-09-solution-isr-audio.ino ├── esp32-freertos-09-solution-isr-sample │ └── esp32-freertos-09-solution-isr-sample.ino └── rtos-part-09.pptx ├── 10-deadlock ├── esp32-freertos-10-challenge-dining-philosophers │ └── esp32-freertos-10-challenge-dining-philosophers.ino ├── esp32-freertos-10-demo-deadlock-hierarchy │ └── esp32-freertos-10-demo-deadlock-hierarchy.ino ├── esp32-freertos-10-demo-deadlock-timeout │ └── esp32-freertos-10-demo-deadlock-timeout.ino ├── esp32-freertos-10-demo-deadlock │ └── esp32-freertos-10-demo-deadlock.ino ├── esp32-freertos-10-solution-dining-philosophers-arbitrator │ └── esp32-freertos-10-solution-dining-philosophers-arbitrator.ino ├── esp32-freertos-10-solution-dining-philosophers-hierarchy │ └── esp32-freertos-10-solution-dining-philosophers-hierarchy.ino └── rtos-part-10.pptx ├── 11-priority-inversion ├── esp32-freertos-11-demo-priority-inheritance │ └── esp32-freertos-11-demo-priority-inheritance.ino ├── esp32-freertos-11-demo-priority-inversion │ └── esp32-freertos-11-demo-priority-inversion.ino ├── esp32-freertos-11-solution-critical-section │ └── esp32-freertos-11-solution-critical-section.ino └── rtos-part-11.pptx ├── 12-multicore ├── esp32-freertos-12-demo-multicore-isr │ └── esp32-freertos-12-demo-multicore-isr.ino ├── esp32-freertos-12-demo-multicore-semaphore │ └── esp32-freertos-12-demo-multicore-semaphore.ino ├── esp32-freertos-12-demo-multicore-spinlock │ └── esp32-freertos-12-demo-multicore-spinlock.ino ├── esp32-freertos-12-demo-multicore │ └── esp32-freertos-12-demo-multicore.ino ├── esp32-freertos-12-solution-isr-sample │ └── esp32-freertos-12-solution-isr-sample.ino └── rtos-part-12.pptx ├── README.md └── images └── intro-to-rtos.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /01-what-is-an-rtos/rtos-part-01.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasir-shahzad/introduction-to-rtos/f2ae37914fa61eca4f7f9e7e11a6895fde7e6cf3/01-what-is-an-rtos/rtos-part-01.pptx -------------------------------------------------------------------------------- /02-getting-started-with-freertos/esp32-freertos-02-demo-blinky/esp32-freertos-02-demo-blinky.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Demo for 02 - Blinky 3 | * 4 | * Toggles the LED on and off in its own task/thread. 5 | * 6 | * Date: December 3, 2020 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // Use only core 1 for demo purposes 12 | #if CONFIG_FREERTOS_UNICORE 13 | static const BaseType_t app_cpu = 0; 14 | #else 15 | static const BaseType_t app_cpu = 1; 16 | #endif 17 | 18 | // Pins 19 | static const int led_pin = LED_BUILTIN; 20 | 21 | // Our task: blink an LED 22 | void toggleLED(void *parameter) { 23 | while(1) { 24 | digitalWrite(led_pin, HIGH); 25 | vTaskDelay(500 / portTICK_PERIOD_MS); 26 | digitalWrite(led_pin, LOW); 27 | vTaskDelay(500 / portTICK_PERIOD_MS); 28 | } 29 | } 30 | 31 | void setup() { 32 | 33 | // Configure pin 34 | pinMode(led_pin, OUTPUT); 35 | 36 | // Task to run forever 37 | xTaskCreatePinnedToCore( // Use xTaskCreate() in vanilla FreeRTOS 38 | toggleLED, // Function to be called 39 | "Toggle LED", // Name of task 40 | 1024, // Stack size (bytes in ESP32, words in FreeRTOS) 41 | NULL, // Parameter to pass to function 42 | 1, // Task priority (0 to configMAX_PRIORITIES - 1) 43 | NULL, // Task handle 44 | app_cpu); // Run on one core for demo purposes (ESP32 only) 45 | 46 | // If this was vanilla FreeRTOS, you'd want to call vTaskStartScheduler() in 47 | // main after setting up your tasks. 48 | } 49 | 50 | void loop() { 51 | // Do nothing 52 | 53 | // setup() and loop() run in their own task with priority 1 in core 1 54 | // on ESP32 55 | } 56 | -------------------------------------------------------------------------------- /02-getting-started-with-freertos/esp32-freertos-02-solution-blinky/esp32-freertos-02-solution-blinky.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Solution to 02 - Blinky Challenge 3 | * 4 | * Toggles LED at different rates using separate tasks. 5 | * 6 | * Date: December 3, 2020 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // Use only core 1 for demo purposes 12 | #if CONFIG_FREERTOS_UNICORE 13 | static const BaseType_t app_cpu = 0; 14 | #else 15 | static const BaseType_t app_cpu = 1; 16 | #endif 17 | 18 | // LED rates 19 | static const int rate_1 = 500; // ms 20 | static const int rate_2 = 323; // ms 21 | 22 | // Pins 23 | static const int led_pin = LED_BUILTIN; 24 | 25 | // Our task: blink an LED at one rate 26 | void toggleLED_1(void *parameter) { 27 | while(1) { 28 | digitalWrite(led_pin, HIGH); 29 | vTaskDelay(rate_1 / portTICK_PERIOD_MS); 30 | digitalWrite(led_pin, LOW); 31 | vTaskDelay(rate_1 / portTICK_PERIOD_MS); 32 | } 33 | } 34 | 35 | // Our task: blink an LED at another rate 36 | void toggleLED_2(void *parameter) { 37 | while(1) { 38 | digitalWrite(led_pin, HIGH); 39 | vTaskDelay(rate_2 / portTICK_PERIOD_MS); 40 | digitalWrite(led_pin, LOW); 41 | vTaskDelay(rate_2 / portTICK_PERIOD_MS); 42 | } 43 | } 44 | 45 | void setup() { 46 | 47 | // Configure pin 48 | pinMode(led_pin, OUTPUT); 49 | 50 | // Task to run forever 51 | xTaskCreatePinnedToCore( // Use xTaskCreate() in vanilla FreeRTOS 52 | toggleLED_1, // Function to be called 53 | "Toggle 1", // Name of task 54 | 1024, // Stack size (bytes in ESP32, words in FreeRTOS) 55 | NULL, // Parameter to pass to function 56 | 1, // Task priority (0 to configMAX_PRIORITIES - 1) 57 | NULL, // Task handle 58 | app_cpu); // Run on one core for demo purposes (ESP32 only) 59 | 60 | // Task to run forever 61 | xTaskCreatePinnedToCore( // Use xTaskCreate() in vanilla FreeRTOS 62 | toggleLED_2, // Function to be called 63 | "Toggle 2", // Name of task 64 | 1024, // Stack size (bytes in ESP32, words in FreeRTOS) 65 | NULL, // Parameter to pass to function 66 | 1, // Task priority (0 to configMAX_PRIORITIES - 1) 67 | NULL, // Task handle 68 | app_cpu); // Run on one core for demo purposes (ESP32 only) 69 | 70 | // If this was vanilla FreeRTOS, you'd want to call vTaskStartScheduler() in 71 | // main after setting up your tasks. 72 | } 73 | 74 | void loop() { 75 | // Do nothing 76 | // setup() and loop() run in their own task with priority 1 in core 1 77 | // on ESP32 78 | } 79 | -------------------------------------------------------------------------------- /02-getting-started-with-freertos/rtos-part-02.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasir-shahzad/introduction-to-rtos/f2ae37914fa61eca4f7f9e7e11a6895fde7e6cf3/02-getting-started-with-freertos/rtos-part-02.pptx -------------------------------------------------------------------------------- /03-task-scheduling-and-management/esp32-freertos-03-demo-prioritization/esp32-freertos-03-demo-prioritization.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * FreeRTOS Task Demo 3 | * 4 | * Toggles LED and prints "Hello, World" to console in separate tasks. 5 | * 6 | * Date: December 3, 2020 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // Use only core 1 for demo purposes 12 | #if CONFIG_FREERTOS_UNICORE 13 | static const BaseType_t app_cpu = 0; 14 | #else 15 | static const BaseType_t app_cpu = 1; 16 | #endif 17 | 18 | // Some string to print 19 | const char msg[] = "Barkadeer brig Arr booty rum."; 20 | 21 | // Task handles 22 | static TaskHandle_t task_1 = NULL; 23 | static TaskHandle_t task_2 = NULL; 24 | 25 | //***************************************************************************** 26 | // Tasks 27 | 28 | // Task: print to Serial Terminal with lower priority 29 | void startTask1(void *parameter) { 30 | 31 | // Count number of characters in string 32 | int msg_len = strlen(msg); 33 | 34 | // Print string to Terminal 35 | while (1) { 36 | Serial.println(); 37 | for (int i = 0; i < msg_len; i++) { 38 | Serial.print(msg[i]); 39 | } 40 | Serial.println(); 41 | vTaskDelay(1000 / portTICK_PERIOD_MS); 42 | } 43 | } 44 | 45 | // Task: print to Serial Terminal with higher priority 46 | void startTask2(void *parameter) { 47 | while (1) { 48 | Serial.print('*'); 49 | vTaskDelay(100 / portTICK_PERIOD_MS); 50 | } 51 | } 52 | 53 | //***************************************************************************** 54 | // Main (runs as its own task with priority 1 on core 1) 55 | 56 | void setup() { 57 | 58 | // Configure Serial (go slow so we can watch the preemption) 59 | Serial.begin(300); 60 | 61 | // Wait a moment to start (so we don't miss Serial output) 62 | vTaskDelay(1000 / portTICK_PERIOD_MS); 63 | Serial.println(); 64 | Serial.println("---FreeRTOS Task Demo---"); 65 | 66 | // Print self priority 67 | Serial.print("Setup and loop task running on core "); 68 | Serial.print(xPortGetCoreID()); 69 | Serial.print(" with priority "); 70 | Serial.println(uxTaskPriorityGet(NULL)); 71 | 72 | // Task to run forever 73 | xTaskCreatePinnedToCore(startTask1, 74 | "Task 1", 75 | 1024, 76 | NULL, 77 | 1, 78 | &task_1, 79 | app_cpu); 80 | 81 | // Task to run once with higher priority 82 | xTaskCreatePinnedToCore(startTask2, 83 | "Task 2", 84 | 1024, 85 | NULL, 86 | 2, 87 | &task_2, 88 | app_cpu); 89 | } 90 | 91 | void loop() { 92 | 93 | // Suspend the higher priority task for some intervals 94 | for (int i = 0; i < 3; i++) { 95 | vTaskSuspend(task_2); 96 | vTaskDelay(2000 / portTICK_PERIOD_MS); 97 | vTaskResume(task_2); 98 | vTaskDelay(2000 / portTICK_PERIOD_MS); 99 | } 100 | 101 | // Delete the lower priority task 102 | if (task_1 != NULL) { 103 | vTaskDelete(task_1); 104 | task_1 = NULL; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /03-task-scheduling-and-management/esp32-freertos-03-solution-led/esp32-freertos-03-solution-led.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Solution to 03 - LED Challenge 3 | * 4 | * One task flashes an LED at a rate specified by a value set in another task. 5 | * 6 | * Date: December 4, 2020 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // Needed for atoi() 12 | #include 13 | 14 | // Use only core 1 for demo purposes 15 | #if CONFIG_FREERTOS_UNICORE 16 | static const BaseType_t app_cpu = 0; 17 | #else 18 | static const BaseType_t app_cpu = 1; 19 | #endif 20 | 21 | // Settings 22 | static const uint8_t buf_len = 20; 23 | 24 | // Pins 25 | static const int led_pin = LED_BUILTIN; 26 | 27 | // Globals 28 | static int led_delay = 500; // ms 29 | 30 | //***************************************************************************** 31 | // Tasks 32 | 33 | // Task: Blink LED at rate set by global variable 34 | void toggleLED(void *parameter) { 35 | while (1) { 36 | digitalWrite(led_pin, HIGH); 37 | vTaskDelay(led_delay / portTICK_PERIOD_MS); 38 | digitalWrite(led_pin, LOW); 39 | vTaskDelay(led_delay / portTICK_PERIOD_MS); 40 | } 41 | } 42 | 43 | // Task: Read from serial terminal 44 | // Feel free to use Serial.readString() or Serial.parseInt(). I'm going to show 45 | // it with atoi() in case you're doing this in a non-Arduino environment. You'd 46 | // also need to replace Serial with your own UART code for non-Arduino. 47 | void readSerial(void *parameters) { 48 | 49 | char c; 50 | char buf[buf_len]; 51 | uint8_t idx = 0; 52 | 53 | // Clear whole buffer 54 | memset(buf, 0, buf_len); 55 | 56 | // Loop forever 57 | while (1) { 58 | 59 | // Read characters from serial 60 | if (Serial.available() > 0) { 61 | c = Serial.read(); 62 | 63 | // Update delay variable and reset buffer if we get a newline character 64 | if (c == '\n') { 65 | led_delay = atoi(buf); 66 | Serial.print("Updated LED delay to: "); 67 | Serial.println(led_delay); 68 | memset(buf, 0, buf_len); 69 | idx = 0; 70 | } else { 71 | 72 | // Only append if index is not over message limit 73 | if (idx < buf_len - 1) { 74 | buf[idx] = c; 75 | idx++; 76 | } 77 | } 78 | } 79 | } 80 | } 81 | 82 | //***************************************************************************** 83 | // Main 84 | 85 | void setup() { 86 | 87 | // Configure pin 88 | pinMode(led_pin, OUTPUT); 89 | 90 | // Configure serial and wait a second 91 | Serial.begin(115200); 92 | vTaskDelay(1000 / portTICK_PERIOD_MS); 93 | Serial.println("Multi-task LED Demo"); 94 | Serial.println("Enter a number in milliseconds to change the LED delay."); 95 | 96 | // Start blink task 97 | xTaskCreatePinnedToCore( // Use xTaskCreate() in vanilla FreeRTOS 98 | toggleLED, // Function to be called 99 | "Toggle LED", // Name of task 100 | 1024, // Stack size (bytes in ESP32, words in FreeRTOS) 101 | NULL, // Parameter to pass 102 | 1, // Task priority 103 | NULL, // Task handle 104 | app_cpu); // Run on one core for demo purposes (ESP32 only) 105 | 106 | // Start serial read task 107 | xTaskCreatePinnedToCore( // Use xTaskCreate() in vanilla FreeRTOS 108 | readSerial, // Function to be called 109 | "Read Serial", // Name of task 110 | 1024, // Stack size (bytes in ESP32, words in FreeRTOS) 111 | NULL, // Parameter to pass 112 | 1, // Task priority (must be same to prevent lockup) 113 | NULL, // Task handle 114 | app_cpu); // Run on one core for demo purposes (ESP32 only) 115 | 116 | // Delete "setup and loop" task 117 | vTaskDelete(NULL); 118 | } 119 | 120 | void loop() { 121 | // Execution should never get here 122 | } 123 | -------------------------------------------------------------------------------- /03-task-scheduling-and-management/rtos-part-03.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasir-shahzad/introduction-to-rtos/f2ae37914fa61eca4f7f9e7e11a6895fde7e6cf3/03-task-scheduling-and-management/rtos-part-03.pptx -------------------------------------------------------------------------------- /04-memory-allocation/esp32-freertos-04-demo-stack-overflow/esp32-freertos-04-demo-stack-overflow.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * FreeRTOS Stack Overflow Demo 3 | * 4 | * Generate a stack overflow on purpose. 5 | * 6 | * Date: December 5, 2020 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // Use only core 1 for demo purposes 12 | #if CONFIG_FREERTOS_UNICORE 13 | static const BaseType_t app_cpu = 0; 14 | #else 15 | static const BaseType_t app_cpu = 1; 16 | #endif 17 | 18 | // Task: Perform some mundane task 19 | void testTask(void *parameter) { 20 | while (1) { 21 | int a = 1; 22 | int b[100]; 23 | 24 | // Do something with array so it's not optimized out by the compiler 25 | for (int i = 0; i < 100; i++) { 26 | b[i] = a + 1; 27 | } 28 | //Serial.println(b[0]); 29 | 30 | // Print out remaining stack memory (words) 31 | Serial.print("High water mark (words): "); 32 | Serial.println(uxTaskGetStackHighWaterMark(NULL)); 33 | 34 | // Print out number of free heap memory bytes before malloc 35 | Serial.print("Heap before malloc (bytes): "); 36 | Serial.println(xPortGetFreeHeapSize()); 37 | int *ptr = (int*)pvPortMalloc(1024 * sizeof(int)); 38 | 39 | // One way to prevent heap overflow is to check the malloc output 40 | if (ptr == NULL) { 41 | Serial.println("Not enough heap."); 42 | vPortFree(NULL); 43 | } else { 44 | 45 | // Do something with the memory so it's not optimized out by the compiler 46 | for (int i = 0; i < 1024; i++) { 47 | ptr[i] = 3; 48 | } 49 | } 50 | 51 | // Print out number of free heap memory bytes after malloc 52 | Serial.print("Heap after malloc (bytes): "); 53 | Serial.println(xPortGetFreeHeapSize()); 54 | 55 | // Free up our allocated memory 56 | //vPortFree(ptr); 57 | 58 | // Wait for a while 59 | vTaskDelay(100 / portTICK_PERIOD_MS); 60 | } 61 | } 62 | 63 | void setup() { 64 | 65 | // Configure Serial 66 | Serial.begin(115200); 67 | 68 | // Wait a moment to start (so we don't miss Serial output) 69 | vTaskDelay(1000 / portTICK_PERIOD_MS); 70 | Serial.println(); 71 | Serial.println("---FreeRTOS Memory Demo---"); 72 | 73 | // Start the only other task 74 | xTaskCreatePinnedToCore(testTask, 75 | "Test Task", 76 | 1500, 77 | NULL, 78 | 1, 79 | NULL, 80 | app_cpu); 81 | 82 | // Delete "setup and loop" task 83 | vTaskDelete(NULL); 84 | } 85 | 86 | void loop() { 87 | // Execution should never get here 88 | } 89 | -------------------------------------------------------------------------------- /04-memory-allocation/esp32-freertos-04-solution-heap/esp32-freertos-04-solution-heap.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Solution to 04 - Heap Challenge 3 | * 4 | * One task reads from Serial, constructs a message buffer, and the second 5 | * prints the message to the console. 6 | * 7 | * Date: December 5, 2020 8 | * Author: Shawn Hymel 9 | * License: 0BSD 10 | */ 11 | 12 | // If your Arduino board does not have LED_BUILTIN, set it here to LED pin 13 | //#define LED_BUILTIN 13 14 | 15 | // Use only core 1 for demo purposes 16 | #if CONFIG_FREERTOS_UNICORE 17 | static const BaseType_t app_cpu = 0; 18 | #else 19 | static const BaseType_t app_cpu = 1; 20 | #endif 21 | 22 | // Settings 23 | static const uint8_t buf_len = 255; 24 | 25 | // Globals 26 | static char *msg_ptr = NULL; 27 | static volatile uint8_t msg_flag = 0; 28 | 29 | //***************************************************************************** 30 | // Tasks 31 | 32 | // Task: read message from Serial buffer 33 | void readSerial(void *parameters) { 34 | 35 | char c; 36 | char buf[buf_len]; 37 | uint8_t idx = 0; 38 | 39 | // Clear whole buffer 40 | memset(buf, 0, buf_len); 41 | 42 | // Loop forever 43 | while (1) { 44 | 45 | // Read characters from serial 46 | if (Serial.available() > 0) { 47 | c = Serial.read(); 48 | 49 | // Store received character to buffer if not over buffer limit 50 | if (idx < buf_len - 1) { 51 | buf[idx] = c; 52 | idx++; 53 | } 54 | 55 | // Create a message buffer for print task 56 | if (c == '\n') { 57 | 58 | // The last character in the string is '\n', so we need to replace 59 | // it with '\0' to make it null-terminated 60 | buf[idx - 1] = '\0'; 61 | 62 | // Try to allocate memory and copy over message. If message buffer is 63 | // still in use, ignore the entire message. 64 | if (msg_flag == 0) { 65 | msg_ptr = (char *)pvPortMalloc(idx * sizeof(char)); 66 | 67 | // If malloc returns 0 (out of memory), throw an error and reset 68 | configASSERT(msg_ptr); 69 | 70 | // Copy message 71 | memcpy(msg_ptr, buf, idx); 72 | 73 | // Notify other task that message is ready 74 | msg_flag = 1; 75 | } 76 | 77 | // Reset receive buffer and index counter 78 | memset(buf, 0, buf_len); 79 | idx = 0; 80 | } 81 | } 82 | } 83 | } 84 | 85 | // Task: print message whenever flag is set and free buffer 86 | void printMessage(void *parameters) { 87 | while (1) { 88 | 89 | // Wait for flag to be set and print message 90 | if (msg_flag == 1) { 91 | Serial.println(msg_ptr); 92 | 93 | // Give amount of free heap memory (uncomment if you'd like to see it) 94 | // Serial.print("Free heap (bytes): "); 95 | // Serial.println(xPortGetFreeHeapSize()); 96 | 97 | // Free buffer, set pointer to null, and clear flag 98 | vPortFree(msg_ptr); 99 | msg_ptr = NULL; 100 | msg_flag = 0; 101 | } 102 | } 103 | } 104 | 105 | //***************************************************************************** 106 | // Main (runs as its own task with priority 1 on core 1) 107 | 108 | void setup() { 109 | 110 | // Configure Serial 111 | Serial.begin(115200); 112 | 113 | // Wait a moment to start (so we don't miss Serial output) 114 | vTaskDelay(1000 / portTICK_PERIOD_MS); 115 | Serial.println(); 116 | Serial.println("---FreeRTOS Heap Demo---"); 117 | Serial.println("Enter a string"); 118 | 119 | // Start Serial receive task 120 | xTaskCreatePinnedToCore(readSerial, 121 | "Read Serial", 122 | 1024, 123 | NULL, 124 | 1, 125 | NULL, 126 | app_cpu); 127 | 128 | // Start Serial print task 129 | xTaskCreatePinnedToCore(printMessage, 130 | "Print Message", 131 | 1024, 132 | NULL, 133 | 1, 134 | NULL, 135 | app_cpu); 136 | 137 | // Delete "setup and loop" task 138 | vTaskDelete(NULL); 139 | } 140 | 141 | void loop() { 142 | // Execution should never get here 143 | } 144 | -------------------------------------------------------------------------------- /04-memory-allocation/rtos-part-04.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasir-shahzad/introduction-to-rtos/f2ae37914fa61eca4f7f9e7e11a6895fde7e6cf3/04-memory-allocation/rtos-part-04.pptx -------------------------------------------------------------------------------- /05-queue/esp32-freertos-05-demo-queue/esp32-freertos-05-demo-queue.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * FreeRTOS Queue Demo 3 | * 4 | * Send data to the serial terminal using a queue. 5 | * 6 | * Date: January 19, 2020 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // Use only core 1 for demo purposes 12 | #if CONFIG_FREERTOS_UNICORE 13 | static const BaseType_t app_cpu = 0; 14 | #else 15 | static const BaseType_t app_cpu = 1; 16 | #endif 17 | 18 | // Settings 19 | static const uint8_t msg_queue_len = 5; 20 | 21 | // Globals 22 | static QueueHandle_t msg_queue; 23 | 24 | //***************************************************************************** 25 | // Tasks 26 | 27 | // Task: wait for item on queue and print it 28 | void printMessages(void *parameters) { 29 | 30 | int item; 31 | 32 | // Loop forever 33 | while (1) { 34 | 35 | // See if there's a message in the queue (do not block) 36 | if (xQueueReceive(msg_queue, (void *)&item, 0) == pdTRUE) { 37 | //Serial.println(item); 38 | } 39 | Serial.println(item); 40 | 41 | // Wait before trying again 42 | vTaskDelay(500 / portTICK_PERIOD_MS); 43 | } 44 | } 45 | 46 | //***************************************************************************** 47 | // Main (runs as its own task with priority 1 on core 1) 48 | 49 | void setup() { 50 | 51 | // Configure Serial 52 | Serial.begin(115200); 53 | 54 | // Wait a moment to start (so we don't miss Serial output) 55 | vTaskDelay(1000 / portTICK_PERIOD_MS); 56 | Serial.println(); 57 | Serial.println("---FreeRTOS Queue Demo---"); 58 | 59 | // Create queue 60 | msg_queue = xQueueCreate(msg_queue_len, sizeof(int)); 61 | 62 | // Start print task 63 | xTaskCreatePinnedToCore(printMessages, 64 | "Print Messages", 65 | 1024, 66 | NULL, 67 | 1, 68 | NULL, 69 | app_cpu); 70 | } 71 | 72 | void loop() { 73 | 74 | static int num = 0; 75 | 76 | // Try to add item to queue for 10 ticks, fail if queue is full 77 | if (xQueueSend(msg_queue, (void *)&num, 10) != pdTRUE) { 78 | Serial.println("Queue full"); 79 | } 80 | num++; 81 | 82 | // Wait before trying again 83 | vTaskDelay(1000 / portTICK_PERIOD_MS); 84 | } 85 | -------------------------------------------------------------------------------- /05-queue/esp32-freertos-05-solution-queue/esp32-freertos-05-solution-queue.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Solution to 05 - Queue Challenge 3 | * 4 | * One task performs basic echo on Serial. If it sees "delay" followed by a 5 | * number, it sends the number (in a queue) to the second task. If it receives 6 | * a message in a second queue, it prints it to the console. The second task 7 | * blinks an LED. When it gets a message from the first queue (number), it 8 | * updates the blink delay to that number. Whenever the LED blinks 100 times, 9 | * the second task sends a message to the first task to be printed. 10 | * 11 | * Date: January 18, 2021 12 | * Author: Shawn Hymel 13 | * License: 0BSD 14 | */ 15 | 16 | // Use only core 1 for demo purposes 17 | #if CONFIG_FREERTOS_UNICORE 18 | static const BaseType_t app_cpu = 0; 19 | #else 20 | static const BaseType_t app_cpu = 1; 21 | #endif 22 | 23 | // Settings 24 | static const uint8_t buf_len = 255; // Size of buffer to look for command 25 | static const char command[] = "delay "; // Note the space! 26 | static const int delay_queue_len = 5; // Size of delay_queue 27 | static const int msg_queue_len = 5; // Size of msg_queue 28 | static const uint8_t blink_max = 100; // Num times to blink before message 29 | 30 | // Pins (change this if your Arduino board does not have LED_BUILTIN defined) 31 | static const int led_pin = LED_BUILTIN; 32 | 33 | // Message struct: used to wrap strings (not necessary, but it's useful to see 34 | // how to use structs here) 35 | typedef struct Message { 36 | char body[20]; 37 | int count; 38 | } Message; 39 | 40 | // Globals 41 | static QueueHandle_t delay_queue; 42 | static QueueHandle_t msg_queue; 43 | 44 | //***************************************************************************** 45 | // Tasks 46 | 47 | // Task: command line interface (CLI) 48 | void doCLI(void *parameters) { 49 | 50 | Message rcv_msg; 51 | char c; 52 | char buf[buf_len]; 53 | uint8_t idx = 0; 54 | uint8_t cmd_len = strlen(command); 55 | int led_delay; 56 | 57 | // Clear whole buffer 58 | memset(buf, 0, buf_len); 59 | 60 | // Loop forever 61 | while (1) { 62 | 63 | // See if there's a message in the queue (do not block) 64 | if (xQueueReceive(msg_queue, (void *)&rcv_msg, 0) == pdTRUE) { 65 | Serial.print(rcv_msg.body); 66 | Serial.println(rcv_msg.count); 67 | } 68 | 69 | // Read characters from serial 70 | if (Serial.available() > 0) { 71 | c = Serial.read(); 72 | 73 | // Store received character to buffer if not over buffer limit 74 | if (idx < buf_len - 1) { 75 | buf[idx] = c; 76 | idx++; 77 | } 78 | 79 | // Print newline and check input on 'enter' 80 | if ((c == '\n') || (c == '\r')) { 81 | 82 | // Print newline to terminal 83 | Serial.print("\r\n"); 84 | 85 | // Check if the first 6 characters are "delay " 86 | if (memcmp(buf, command, cmd_len) == 0) { 87 | 88 | // Convert last part to positive integer (negative int crashes) 89 | char* tail = buf + cmd_len; 90 | led_delay = atoi(tail); 91 | led_delay = abs(led_delay); 92 | 93 | // Send integer to other task via queue 94 | if (xQueueSend(delay_queue, (void *)&led_delay, 10) != pdTRUE) { 95 | Serial.println("ERROR: Could not put item on delay queue."); 96 | } 97 | } 98 | 99 | // Reset receive buffer and index counter 100 | memset(buf, 0, buf_len); 101 | idx = 0; 102 | 103 | // Otherwise, echo character back to serial terminal 104 | } else { 105 | Serial.print(c); 106 | } 107 | } 108 | } 109 | } 110 | 111 | // Task: flash LED based on delay provided, notify other task every 100 blinks 112 | void blinkLED(void *parameters) { 113 | 114 | Message msg; 115 | int led_delay = 500; 116 | uint8_t counter = 0; 117 | 118 | // Set up pin 119 | pinMode(LED_BUILTIN, OUTPUT); 120 | 121 | // Loop forever 122 | while (1) { 123 | 124 | // See if there's a message in the queue (do not block) 125 | if (xQueueReceive(delay_queue, (void *)&led_delay, 0) == pdTRUE) { 126 | 127 | // Best practice: use only one task to manage serial comms 128 | strcpy(msg.body, "Message received "); 129 | msg.count = 1; 130 | xQueueSend(msg_queue, (void *)&msg, 10); 131 | } 132 | 133 | // Blink 134 | digitalWrite(led_pin, HIGH); 135 | vTaskDelay(led_delay / portTICK_PERIOD_MS); 136 | digitalWrite(led_pin, LOW); 137 | vTaskDelay(led_delay / portTICK_PERIOD_MS); 138 | 139 | // If we've blinked 100 times, send a message to the other task 140 | counter++; 141 | if (counter >= blink_max) { 142 | 143 | // Construct message and send 144 | strcpy(msg.body, "Blinked: "); 145 | msg.count = counter; 146 | xQueueSend(msg_queue, (void *)&msg, 10); 147 | 148 | // Reset counter 149 | counter = 0; 150 | } 151 | } 152 | } 153 | 154 | //***************************************************************************** 155 | // Main (runs as its own task with priority 1 on core 1) 156 | 157 | void setup() { 158 | 159 | // Configure Serial 160 | Serial.begin(115200); 161 | 162 | // Wait a moment to start (so we don't miss Serial output) 163 | vTaskDelay(1000 / portTICK_PERIOD_MS); 164 | Serial.println(); 165 | Serial.println("---FreeRTOS Queue Solution---"); 166 | Serial.println("Enter the command 'delay xxx' where xxx is your desired "); 167 | Serial.println("LED blink delay time in milliseconds"); 168 | 169 | // Create queues 170 | delay_queue = xQueueCreate(delay_queue_len, sizeof(int)); 171 | msg_queue = xQueueCreate(msg_queue_len, sizeof(Message)); 172 | 173 | // Start CLI task 174 | xTaskCreatePinnedToCore(doCLI, 175 | "CLI", 176 | 1024, 177 | NULL, 178 | 1, 179 | NULL, 180 | app_cpu); 181 | 182 | // Start blink task 183 | xTaskCreatePinnedToCore(blinkLED, 184 | "Blink LED", 185 | 1024, 186 | NULL, 187 | 1, 188 | NULL, 189 | app_cpu); 190 | 191 | // Delete "setup and loop" task 192 | vTaskDelete(NULL); 193 | } 194 | 195 | void loop() { 196 | // Execution should never get here 197 | } 198 | -------------------------------------------------------------------------------- /05-queue/rtos-part-05.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasir-shahzad/introduction-to-rtos/f2ae37914fa61eca4f7f9e7e11a6895fde7e6cf3/05-queue/rtos-part-05.pptx -------------------------------------------------------------------------------- /06-mutex/esp32-freertos-06-challenge-task-parameters/esp32-freertos-06-challenge-task-parameters.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * FreeRTOS Mutex Challenge 3 | * 4 | * Pass a parameter to a task using a mutex. 5 | * 6 | * Date: January 20, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // You'll likely need this on vanilla FreeRTOS 12 | //#include 13 | 14 | // Use only core 1 for demo purposes 15 | #if CONFIG_FREERTOS_UNICORE 16 | static const BaseType_t app_cpu = 0; 17 | #else 18 | static const BaseType_t app_cpu = 1; 19 | #endif 20 | 21 | // Pins (change this if your Arduino board does not have LED_BUILTIN defined) 22 | static const int led_pin = LED_BUILTIN; 23 | 24 | //***************************************************************************** 25 | // Tasks 26 | 27 | // Blink LED based on rate passed by parameter 28 | void blinkLED(void *parameters) { 29 | 30 | // Copy the parameter into a local variable 31 | int num = *(int *)parameters; 32 | 33 | // Print the parameter 34 | Serial.print("Received: "); 35 | Serial.println(num); 36 | 37 | // Configure the LED pin 38 | pinMode(led_pin, OUTPUT); 39 | 40 | // Blink forever and ever 41 | while (1) { 42 | digitalWrite(led_pin, HIGH); 43 | vTaskDelay(num / portTICK_PERIOD_MS); 44 | digitalWrite(led_pin, LOW); 45 | vTaskDelay(num / portTICK_PERIOD_MS); 46 | } 47 | } 48 | 49 | //***************************************************************************** 50 | // Main (runs as its own task with priority 1 on core 1) 51 | 52 | void setup() { 53 | 54 | long int delay_arg; 55 | 56 | // Configure Serial 57 | Serial.begin(115200); 58 | 59 | // Wait a moment to start (so we don't miss Serial output) 60 | vTaskDelay(1000 / portTICK_PERIOD_MS); 61 | Serial.println(); 62 | Serial.println("---FreeRTOS Mutex Challenge---"); 63 | Serial.println("Enter a number for delay (milliseconds)"); 64 | 65 | // Wait for input from Serial 66 | while (Serial.available() <= 0); 67 | 68 | // Read integer value 69 | delay_arg = Serial.parseInt(); 70 | Serial.print("Sending: "); 71 | Serial.println(delay_arg); 72 | 73 | // Start task 1 74 | xTaskCreatePinnedToCore(blinkLED, 75 | "Blink LED", 76 | 1024, 77 | (void *)&delay_arg, 78 | 1, 79 | NULL, 80 | app_cpu); 81 | 82 | // Show that we accomplished our task of passing the stack-based argument 83 | Serial.println("Done!"); 84 | } 85 | 86 | void loop() { 87 | 88 | // Do nothing but allow yielding to lower-priority tasks 89 | vTaskDelay(1000 / portTICK_PERIOD_MS); 90 | } 91 | -------------------------------------------------------------------------------- /06-mutex/esp32-freertos-06-demo-mutex/esp32-freertos-06-demo-mutex.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * FreeRTOS Mutex Demo 3 | * 4 | * Increment a shared global variable with mutex protection. 5 | * 6 | * Date: January 20, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // You'll likely need this on vanilla FreeRTOS 12 | //#include 13 | 14 | // Use only core 1 for demo purposes 15 | #if CONFIG_FREERTOS_UNICORE 16 | static const BaseType_t app_cpu = 0; 17 | #else 18 | static const BaseType_t app_cpu = 1; 19 | #endif 20 | 21 | // Globals 22 | static int shared_var = 0; 23 | static SemaphoreHandle_t mutex; 24 | 25 | //***************************************************************************** 26 | // Tasks 27 | 28 | // Increment shared variable (the wrong way) 29 | void incTask(void *parameters) { 30 | 31 | int local_var; 32 | 33 | // Loop forever 34 | while (1) { 35 | 36 | // Take mutex prior to critical section 37 | if (xSemaphoreTake(mutex, 0) == pdTRUE) { 38 | 39 | // Critical section (poor demonstration of "shared_var++") 40 | local_var = shared_var; 41 | local_var++; 42 | vTaskDelay(random(100, 500) / portTICK_PERIOD_MS); 43 | shared_var = local_var; 44 | 45 | // Print out new shared variable 46 | // This is different than in the video--print shared_var inside the 47 | // critical section to avoid having it be changed by the other task. 48 | Serial.println(shared_var); 49 | 50 | // Give mutex after critical section 51 | xSemaphoreGive(mutex); 52 | 53 | } else { 54 | // Do something else 55 | } 56 | } 57 | } 58 | 59 | //***************************************************************************** 60 | // Main (runs as its own task with priority 1 on core 1) 61 | 62 | void setup() { 63 | 64 | // Hack to kinda get randomness 65 | randomSeed(analogRead(0)); 66 | 67 | // Configure Serial 68 | Serial.begin(115200); 69 | 70 | // Wait a moment to start (so we don't miss Serial output) 71 | vTaskDelay(1000 / portTICK_PERIOD_MS); 72 | Serial.println(); 73 | Serial.println("---FreeRTOS Race Condition Demo---"); 74 | 75 | // Create mutex before starting tasks 76 | mutex = xSemaphoreCreateMutex(); 77 | 78 | // Start task 1 79 | xTaskCreatePinnedToCore(incTask, 80 | "Increment Task 1", 81 | 1024, 82 | NULL, 83 | 1, 84 | NULL, 85 | app_cpu); 86 | 87 | // Start task 2 88 | xTaskCreatePinnedToCore(incTask, 89 | "Increment Task 2", 90 | 1024, 91 | NULL, 92 | 1, 93 | NULL, 94 | app_cpu); 95 | 96 | // Delete "setup and loop" task 97 | vTaskDelete(NULL); 98 | } 99 | 100 | void loop() { 101 | // Execution should never get here 102 | } 103 | -------------------------------------------------------------------------------- /06-mutex/esp32-freertos-06-demo-race-condition/esp32-freertos-06-demo-race-condition.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * FreeRTOS Race Condition Demo 3 | * 4 | * Increment a shared global variable. 5 | * 6 | * Date: January 20, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // Use only core 1 for demo purposes 12 | #if CONFIG_FREERTOS_UNICORE 13 | static const BaseType_t app_cpu = 0; 14 | #else 15 | static const BaseType_t app_cpu = 1; 16 | #endif 17 | 18 | // Globals 19 | static int shared_var = 0; 20 | 21 | //***************************************************************************** 22 | // Tasks 23 | 24 | // Increment shared variable (the wrong way) 25 | void incTask(void *parameters) { 26 | 27 | int local_var; 28 | 29 | // Loop forever 30 | while (1) { 31 | 32 | // Roundabout way to do "shared_var++" randomly and poorly 33 | local_var = shared_var; 34 | local_var++; 35 | vTaskDelay(random(100, 500) / portTICK_PERIOD_MS); 36 | shared_var = local_var; 37 | 38 | // Print out new shared variable 39 | Serial.println(shared_var); 40 | } 41 | } 42 | 43 | //***************************************************************************** 44 | // Main (runs as its own task with priority 1 on core 1) 45 | 46 | void setup() { 47 | 48 | // Hack to kinda get randomness 49 | randomSeed(analogRead(0)); 50 | 51 | // Configure Serial 52 | Serial.begin(115200); 53 | 54 | // Wait a moment to start (so we don't miss Serial output) 55 | vTaskDelay(1000 / portTICK_PERIOD_MS); 56 | Serial.println(); 57 | Serial.println("---FreeRTOS Race Condition Demo---"); 58 | 59 | // Start task 1 60 | xTaskCreatePinnedToCore(incTask, 61 | "Increment Task 1", 62 | 1024, 63 | NULL, 64 | 1, 65 | NULL, 66 | app_cpu); 67 | 68 | // Start task 2 69 | xTaskCreatePinnedToCore(incTask, 70 | "Increment Task 2", 71 | 1024, 72 | NULL, 73 | 1, 74 | NULL, 75 | app_cpu); 76 | 77 | // Delete "setup and loop" task 78 | vTaskDelete(NULL); 79 | } 80 | 81 | void loop() { 82 | // Execution should never get here 83 | } 84 | -------------------------------------------------------------------------------- /06-mutex/esp32-freertos-06-solution-task-parameters/esp32-freertos-06-solution-task-parameters.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * FreeRTOS Mutex Solution 3 | * 4 | * Pass a parameter to a task using a mutex. 5 | * 6 | * Date: January 20, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // You'll likely need this on vanilla FreeRTOS 12 | //#include 13 | 14 | // Use only core 1 for demo purposes 15 | #if CONFIG_FREERTOS_UNICORE 16 | static const BaseType_t app_cpu = 0; 17 | #else 18 | static const BaseType_t app_cpu = 1; 19 | #endif 20 | 21 | // Pins (change this if your Arduino board does not have LED_BUILTIN defined) 22 | static const int led_pin = LED_BUILTIN; 23 | 24 | // Globals 25 | static SemaphoreHandle_t mutex; 26 | 27 | //***************************************************************************** 28 | // Tasks 29 | 30 | // Blink LED based on rate passed by parameter 31 | void blinkLED(void *parameters) { 32 | 33 | // Copy the parameter into a local variable 34 | int num = *(int *)parameters; 35 | 36 | // Release the mutex so that the creating function can finish 37 | xSemaphoreGive(mutex); 38 | 39 | // Print the parameter 40 | Serial.print("Received: "); 41 | Serial.println(num); 42 | 43 | // Configure the LED pin 44 | pinMode(led_pin, OUTPUT); 45 | 46 | // Blink forever and ever 47 | while (1) { 48 | digitalWrite(led_pin, HIGH); 49 | vTaskDelay(num / portTICK_PERIOD_MS); 50 | digitalWrite(led_pin, LOW); 51 | vTaskDelay(num / portTICK_PERIOD_MS); 52 | } 53 | } 54 | 55 | //***************************************************************************** 56 | // Main (runs as its own task with priority 1 on core 1) 57 | 58 | void setup() { 59 | 60 | long int delay_arg; 61 | 62 | // Configure Serial 63 | Serial.begin(115200); 64 | 65 | // Wait a moment to start (so we don't miss Serial output) 66 | vTaskDelay(1000 / portTICK_PERIOD_MS); 67 | Serial.println(); 68 | Serial.println("---FreeRTOS Mutex Solution---"); 69 | Serial.println("Enter a number for delay (milliseconds)"); 70 | 71 | // Wait for input from Serial 72 | while (Serial.available() <= 0); 73 | 74 | // Read integer value 75 | delay_arg = Serial.parseInt(); 76 | Serial.print("Sending: "); 77 | Serial.println(delay_arg); 78 | 79 | // Create mutex before starting tasks 80 | mutex = xSemaphoreCreateMutex(); 81 | 82 | // Take the mutex 83 | xSemaphoreTake(mutex, portMAX_DELAY); 84 | 85 | // Start task 1 86 | xTaskCreatePinnedToCore(blinkLED, 87 | "Blink LED", 88 | 1024, 89 | (void *)&delay_arg, 90 | 1, 91 | NULL, 92 | app_cpu); 93 | 94 | // Do nothing until mutex has been returned (maximum delay) 95 | xSemaphoreTake(mutex, portMAX_DELAY); 96 | 97 | // Show that we accomplished our task of passing the stack-based argument 98 | Serial.println("Done!"); 99 | } 100 | 101 | void loop() { 102 | 103 | // Do nothing but allow yielding to lower-priority tasks 104 | vTaskDelay(1000 / portTICK_PERIOD_MS); 105 | } 106 | -------------------------------------------------------------------------------- /06-mutex/rtos-part-06.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasir-shahzad/introduction-to-rtos/f2ae37914fa61eca4f7f9e7e11a6895fde7e6cf3/06-mutex/rtos-part-06.pptx -------------------------------------------------------------------------------- /07-semaphore/esp32-freertos-07-challenge-counting-semaphore/esp32-freertos-07-challenge-counting-semaphore.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * FreeRTOS Counting Semaphore Challenge 3 | * 4 | * Challenge: use a mutex and counting semaphores to protect the shared buffer 5 | * so that each number (0 throguh 4) is printed exactly 3 times to the Serial 6 | * monitor (in any order). Do not use queues to do this! 7 | * 8 | * Hint: you will need 2 counting semaphores in addition to the mutex, one for 9 | * remembering number of filled slots in the buffer and another for 10 | * remembering the number of empty slots in the buffer. 11 | * 12 | * Date: January 24, 2021 13 | * Author: Shawn Hymel 14 | * License: 0BSD 15 | */ 16 | 17 | // You'll likely need this on vanilla FreeRTOS 18 | //#include 19 | 20 | // Use only core 1 for demo purposes 21 | #if CONFIG_FREERTOS_UNICORE 22 | static const BaseType_t app_cpu = 0; 23 | #else 24 | static const BaseType_t app_cpu = 1; 25 | #endif 26 | 27 | // Settings 28 | enum {BUF_SIZE = 5}; // Size of buffer array 29 | static const int num_prod_tasks = 5; // Number of producer tasks 30 | static const int num_cons_tasks = 2; // Number of consumer tasks 31 | static const int num_writes = 3; // Num times each producer writes to buf 32 | 33 | // Globals 34 | static int buf[BUF_SIZE]; // Shared buffer 35 | static int head = 0; // Writing index to buffer 36 | static int tail = 0; // Reading index to buffer 37 | static SemaphoreHandle_t bin_sem; // Waits for parameter to be read 38 | 39 | //***************************************************************************** 40 | // Tasks 41 | 42 | // Producer: write a given number of times to shared buffer 43 | void producer(void *parameters) { 44 | 45 | // Copy the parameters into a local variable 46 | int num = *(int *)parameters; 47 | 48 | // Release the binary semaphore 49 | xSemaphoreGive(bin_sem); 50 | 51 | // Fill shared buffer with task number 52 | for (int i = 0; i < num_writes; i++) { 53 | 54 | // Critical section (accessing shared buffer) 55 | buf[head] = num; 56 | head = (head + 1) % BUF_SIZE; 57 | } 58 | 59 | // Delete self task 60 | vTaskDelete(NULL); 61 | } 62 | 63 | // Consumer: continuously read from shared buffer 64 | void consumer(void *parameters) { 65 | 66 | int val; 67 | 68 | // Read from buffer 69 | while (1) { 70 | 71 | // Critical section (accessing shared buffer and Serial) 72 | val = buf[tail]; 73 | tail = (tail + 1) % BUF_SIZE; 74 | Serial.println(val); 75 | } 76 | } 77 | 78 | //***************************************************************************** 79 | // Main (runs as its own task with priority 1 on core 1) 80 | 81 | void setup() { 82 | 83 | char task_name[12]; 84 | 85 | // Configure Serial 86 | Serial.begin(115200); 87 | 88 | // Wait a moment to start (so we don't miss Serial output) 89 | vTaskDelay(1000 / portTICK_PERIOD_MS); 90 | Serial.println(); 91 | Serial.println("---FreeRTOS Semaphore Alternate Solution---"); 92 | 93 | // Create mutexes and semaphores before starting tasks 94 | bin_sem = xSemaphoreCreateBinary(); 95 | 96 | // Start producer tasks (wait for each to read argument) 97 | for (int i = 0; i < num_prod_tasks; i++) { 98 | sprintf(task_name, "Producer %i", i); 99 | xTaskCreatePinnedToCore(producer, 100 | task_name, 101 | 1024, 102 | (void *)&i, 103 | 1, 104 | NULL, 105 | app_cpu); 106 | xSemaphoreTake(bin_sem, portMAX_DELAY); 107 | } 108 | 109 | // Start consumer tasks 110 | for (int i = 0; i < num_cons_tasks; i++) { 111 | sprintf(task_name, "Consumer %i", i); 112 | xTaskCreatePinnedToCore(consumer, 113 | task_name, 114 | 1024, 115 | NULL, 116 | 1, 117 | NULL, 118 | app_cpu); 119 | } 120 | 121 | // Notify that all tasks have been created 122 | Serial.println("All tasks created"); 123 | } 124 | 125 | void loop() { 126 | 127 | // Do nothing but allow yielding to lower-priority tasks 128 | vTaskDelay(1000 / portTICK_PERIOD_MS); 129 | } 130 | -------------------------------------------------------------------------------- /07-semaphore/esp32-freertos-07-demo-binary-semaphore/esp32-freertos-07-demo-binary-semaphore.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * FreeRTOS Binary Semaphore Demo 3 | * 4 | * Pass a parameter to a task using a binary semaphore. 5 | * 6 | * Date: January 23, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // You'll likely need this on vanilla FreeRTOS 12 | //#include 13 | 14 | // Use only core 1 for demo purposes 15 | #if CONFIG_FREERTOS_UNICORE 16 | static const BaseType_t app_cpu = 0; 17 | #else 18 | static const BaseType_t app_cpu = 1; 19 | #endif 20 | 21 | // Pins (change this if your Arduino board does not have LED_BUILTIN defined) 22 | static const int led_pin = LED_BUILTIN; 23 | 24 | // Globals 25 | static SemaphoreHandle_t bin_sem; 26 | 27 | //***************************************************************************** 28 | // Tasks 29 | 30 | // Blink LED based on rate passed by parameter 31 | void blinkLED(void *parameters) { 32 | 33 | // Copy the parameter into a local variable 34 | int num = *(int *)parameters; 35 | 36 | // Release the binary semaphore so that the creating function can finish 37 | xSemaphoreGive(bin_sem); 38 | 39 | // Print the parameter 40 | Serial.print("Received: "); 41 | Serial.println(num); 42 | 43 | // Configure the LED pin 44 | pinMode(led_pin, OUTPUT); 45 | 46 | // Blink forever and ever 47 | while (1) { 48 | digitalWrite(led_pin, HIGH); 49 | vTaskDelay(num / portTICK_PERIOD_MS); 50 | digitalWrite(led_pin, LOW); 51 | vTaskDelay(num / portTICK_PERIOD_MS); 52 | } 53 | } 54 | 55 | //***************************************************************************** 56 | // Main (runs as its own task with priority 1 on core 1) 57 | 58 | void setup() { 59 | 60 | long int delay_arg; 61 | 62 | // Configure Serial 63 | Serial.begin(115200); 64 | 65 | // Wait a moment to start (so we don't miss Serial output) 66 | vTaskDelay(1000 / portTICK_PERIOD_MS); 67 | Serial.println(); 68 | Serial.println("---FreeRTOS Mutex Solution---"); 69 | Serial.println("Enter a number for delay (milliseconds)"); 70 | 71 | // Wait for input from Serial 72 | while (Serial.available() <= 0); 73 | 74 | // Read integer value 75 | delay_arg = Serial.parseInt(); 76 | Serial.print("Sending: "); 77 | Serial.println(delay_arg); 78 | 79 | // Create binary semaphore before starting tasks 80 | bin_sem = xSemaphoreCreateBinary(); 81 | 82 | // Start task 1 83 | xTaskCreatePinnedToCore(blinkLED, 84 | "Blink LED", 85 | 1024, 86 | (void *)&delay_arg, 87 | 1, 88 | NULL, 89 | app_cpu); 90 | 91 | // Do nothing until binary semaphore has been returned 92 | xSemaphoreTake(bin_sem, portMAX_DELAY); 93 | 94 | // Show that we accomplished our task of passing the stack-based argument 95 | Serial.println("Done!"); 96 | } 97 | 98 | void loop() { 99 | 100 | // Do nothing but allow yielding to lower-priority tasks 101 | vTaskDelay(1000 / portTICK_PERIOD_MS); 102 | } 103 | -------------------------------------------------------------------------------- /07-semaphore/esp32-freertos-07-demo-counting-semaphore/esp32-freertos-07-demo-counting-semaphore.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * FreeRTOS Counting Semaphore Demo 3 | * 4 | * Demonstrate a counting semaphore by creating several tasks with the same 5 | * parameters. 6 | * 7 | * Date: January 24, 2021 8 | * Author: Shawn Hymel 9 | * License: 0BSD 10 | */ 11 | 12 | // You'll likely need this on vanilla FreeRTOS 13 | //#include 14 | 15 | // Use only core 1 for demo purposes 16 | #if CONFIG_FREERTOS_UNICORE 17 | static const BaseType_t app_cpu = 0; 18 | #else 19 | static const BaseType_t app_cpu = 1; 20 | #endif 21 | 22 | // Settings 23 | static const int num_tasks = 5; // Number of tasks to create 24 | 25 | // Example struct for passing a string as a parameter 26 | typedef struct Message { 27 | char body[20]; 28 | uint8_t len; 29 | } Message; 30 | 31 | // Globals 32 | static SemaphoreHandle_t sem_params; // Counts down when parameters read 33 | 34 | //***************************************************************************** 35 | // Tasks 36 | 37 | void myTask(void *parameters) { 38 | 39 | // Copy the message struct from the parameter to a local variable 40 | Message msg = *(Message *)parameters; 41 | 42 | // Increment semaphore to indicate that the parameter has been read 43 | xSemaphoreGive(sem_params); 44 | 45 | // Print out message contents 46 | Serial.print("Received: "); 47 | Serial.print(msg.body); 48 | Serial.print(" | len: "); 49 | Serial.println(msg.len); 50 | 51 | // Wait for a while and delete self 52 | vTaskDelay(1000 / portTICK_PERIOD_MS); 53 | vTaskDelete(NULL); 54 | } 55 | 56 | //***************************************************************************** 57 | // Main (runs as its own task with priority 1 on core 1) 58 | 59 | void setup() { 60 | 61 | char task_name[12]; 62 | Message msg; 63 | char text[20] = "All your base"; 64 | 65 | // Configure Serial 66 | Serial.begin(115200); 67 | 68 | // Wait a moment to start (so we don't miss Serial output) 69 | vTaskDelay(1000 / portTICK_PERIOD_MS); 70 | Serial.println(); 71 | Serial.println("---FreeRTOS Counting Semaphore Demo---"); 72 | 73 | // Create semaphores (initialize at 0) 74 | sem_params = xSemaphoreCreateCounting(num_tasks, 0); 75 | 76 | // Create message to use as argument common to all tasks 77 | strcpy(msg.body, text); 78 | msg.len = strlen(text); 79 | 80 | // Start tasks 81 | for (int i = 0; i < num_tasks; i++) { 82 | 83 | // Generate unique name string for task 84 | sprintf(task_name, "Task %i", i); 85 | 86 | // Start task and pass argument (common Message struct) 87 | xTaskCreatePinnedToCore(myTask, 88 | task_name, 89 | 1024, 90 | (void *)&msg, 91 | 1, 92 | NULL, 93 | app_cpu); 94 | } 95 | 96 | // Wait for all tasks to read shared memory 97 | for (int i = 0; i < num_tasks; i++) { 98 | xSemaphoreTake(sem_params, portMAX_DELAY); 99 | } 100 | 101 | // Notify that all tasks have been created 102 | Serial.println("All tasks created"); 103 | } 104 | 105 | void loop() { 106 | 107 | // Do nothing but allow yielding to lower-priority tasks 108 | vTaskDelay(1000 / portTICK_PERIOD_MS); 109 | } 110 | -------------------------------------------------------------------------------- /07-semaphore/esp32-freertos-07-solution-alt-queue/esp32-freertos-07-solution-alt-queue.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * FreeRTOS Queue Alternate Solution 3 | * 4 | * Demonstrate how it's often easier to use queues instead of counting 5 | * semaphores to pass information between tasks. 6 | * 7 | * Date: January 24, 2021 8 | * Author: Shawn Hymel 9 | * License: 0BSD 10 | */ 11 | 12 | // You'll likely need this on vanilla FreeRTOS 13 | //#include 14 | 15 | // Use only core 1 for demo purposes 16 | #if CONFIG_FREERTOS_UNICORE 17 | static const BaseType_t app_cpu = 0; 18 | #else 19 | static const BaseType_t app_cpu = 1; 20 | #endif 21 | 22 | // Settings 23 | static const uint8_t queue_len = 10; // Size of queue 24 | static const int num_prod_tasks = 5; // Number of producer tasks 25 | static const int num_cons_tasks = 2; // Number of consumer tasks 26 | static const int num_writes = 3; // Num times each producer writes to buf 27 | 28 | // Globals 29 | static SemaphoreHandle_t bin_sem; // Waits for parameter to be read 30 | static SemaphoreHandle_t mutex; // Lock access to Serial resource 31 | static QueueHandle_t msg_queue; // Send data from producer to consumer 32 | 33 | //***************************************************************************** 34 | // Tasks 35 | 36 | // Producer: write a given number of times to shared buffer 37 | void producer(void *parameters) { 38 | 39 | // Copy the parameters into a local variable 40 | int num = *(int *)parameters; 41 | 42 | // Release the binary semaphore 43 | xSemaphoreGive(bin_sem); 44 | 45 | // Fill queue with task number (wait max time if queue is full) 46 | for (int i = 0; i < num_writes; i++) { 47 | xQueueSend(msg_queue, (void *)&num, portMAX_DELAY); 48 | } 49 | 50 | // Delete self task 51 | vTaskDelete(NULL); 52 | } 53 | 54 | // Consumer: continuously read from shared buffer 55 | void consumer(void *parameters) { 56 | 57 | int val; 58 | 59 | // Read from buffer 60 | while (1) { 61 | 62 | // Read from queue (wait max time if queue is empty) 63 | xQueueReceive(msg_queue, (void *)&val, portMAX_DELAY); 64 | 65 | // Lock Serial resource with a mutex 66 | xSemaphoreTake(mutex, portMAX_DELAY); 67 | Serial.println(val); 68 | xSemaphoreGive(mutex); 69 | } 70 | } 71 | 72 | //***************************************************************************** 73 | // Main (runs as its own task with priority 1 on core 1) 74 | 75 | void setup() { 76 | 77 | char task_name[12]; 78 | 79 | // Configure Serial 80 | Serial.begin(115200); 81 | 82 | // Wait a moment to start (so we don't miss Serial output) 83 | vTaskDelay(1000 / portTICK_PERIOD_MS); 84 | Serial.println(); 85 | Serial.println("---FreeRTOS Semaphore Solution---"); 86 | 87 | // Create mutexes and semaphores before starting tasks 88 | bin_sem = xSemaphoreCreateBinary(); 89 | mutex = xSemaphoreCreateMutex(); 90 | 91 | // Create queue 92 | msg_queue = xQueueCreate(queue_len, sizeof(int)); 93 | 94 | // Start producer tasks (wait for each to read argument) 95 | for (int i = 0; i < num_prod_tasks; i++) { 96 | sprintf(task_name, "Producer %i", i); 97 | xTaskCreatePinnedToCore(producer, 98 | task_name, 99 | 1024, 100 | (void *)&i, 101 | 1, 102 | NULL, 103 | app_cpu); 104 | xSemaphoreTake(bin_sem, portMAX_DELAY); 105 | } 106 | 107 | // Start consumer tasks 108 | for (int i = 0; i < num_cons_tasks; i++) { 109 | sprintf(task_name, "Consumer %i", i); 110 | xTaskCreatePinnedToCore(consumer, 111 | task_name, 112 | 1024, 113 | NULL, 114 | 1, 115 | NULL, 116 | app_cpu); 117 | } 118 | 119 | // Notify that all tasks have been created (lock Serial with mutex) 120 | xSemaphoreTake(mutex, portMAX_DELAY); 121 | Serial.println("All tasks created"); 122 | xSemaphoreGive(mutex); 123 | } 124 | 125 | void loop() { 126 | 127 | // Do nothing but allow yielding to lower-priority tasks 128 | vTaskDelay(1000 / portTICK_PERIOD_MS); 129 | } 130 | -------------------------------------------------------------------------------- /07-semaphore/esp32-freertos-07-solution-counting-semaphore/esp32-freertos-07-solution-counting-semaphore.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * FreeRTOS Counting Semaphore Solution 3 | * 4 | * Use producer tasks (writing to shared memory) and consumer tasks (reading 5 | * from shared memory) to demonstrate counting semaphores. 6 | * 7 | * Date: January 24, 2021 8 | * Author: Shawn Hymel 9 | * License: 0BSD 10 | */ 11 | 12 | // You'll likely need this on vanilla FreeRTOS 13 | //#include 14 | 15 | // Use only core 1 for demo purposes 16 | #if CONFIG_FREERTOS_UNICORE 17 | static const BaseType_t app_cpu = 0; 18 | #else 19 | static const BaseType_t app_cpu = 1; 20 | #endif 21 | 22 | // Settings 23 | enum {BUF_SIZE = 5}; // Size of buffer array 24 | static const int num_prod_tasks = 5; // Number of producer tasks 25 | static const int num_cons_tasks = 2; // Number of consumer tasks 26 | static const int num_writes = 3; // Num times each producer writes to buf 27 | 28 | // Globals 29 | static int buf[BUF_SIZE]; // Shared buffer 30 | static int head = 0; // Writing index to buffer 31 | static int tail = 0; // Reading index to buffer 32 | static SemaphoreHandle_t bin_sem; // Waits for parameter to be read 33 | static SemaphoreHandle_t mutex; // Lock access to buffer and Serial 34 | static SemaphoreHandle_t sem_empty; // Counts number of empty slots in buf 35 | static SemaphoreHandle_t sem_filled; // Counts number of filled slots in buf 36 | 37 | //***************************************************************************** 38 | // Tasks 39 | 40 | // Producer: write a given number of times to shared buffer 41 | void producer(void *parameters) { 42 | 43 | // Copy the parameters into a local variable 44 | int num = *(int *)parameters; 45 | 46 | // Release the binary semaphore 47 | xSemaphoreGive(bin_sem); 48 | 49 | // Fill shared buffer with task number 50 | for (int i = 0; i < num_writes; i++) { 51 | 52 | // Wait for empty slot in buffer to be available 53 | xSemaphoreTake(sem_empty, portMAX_DELAY); 54 | 55 | // Lock critical section with a mutex 56 | xSemaphoreTake(mutex, portMAX_DELAY); 57 | buf[head] = num; 58 | head = (head + 1) % BUF_SIZE; 59 | xSemaphoreGive(mutex); 60 | 61 | // Signal to consumer tasks that a slot in the buffer has been filled 62 | xSemaphoreGive(sem_filled); 63 | } 64 | 65 | // Delete self task 66 | vTaskDelete(NULL); 67 | } 68 | 69 | // Consumer: continuously read from shared buffer 70 | void consumer(void *parameters) { 71 | 72 | int val; 73 | 74 | // Read from buffer 75 | while (1) { 76 | 77 | // Wait for at least one slot in buffer to be filled 78 | xSemaphoreTake(sem_filled, portMAX_DELAY); 79 | 80 | // Lock critical section with a mutex 81 | xSemaphoreTake(mutex, portMAX_DELAY); 82 | val = buf[tail]; 83 | tail = (tail + 1) % BUF_SIZE; 84 | Serial.println(val); 85 | xSemaphoreGive(mutex); 86 | 87 | // Signal to producer thread that a slot in the buffer is free 88 | xSemaphoreGive(sem_empty); 89 | } 90 | } 91 | 92 | //***************************************************************************** 93 | // Main (runs as its own task with priority 1 on core 1) 94 | 95 | void setup() { 96 | 97 | char task_name[12]; 98 | 99 | // Configure Serial 100 | Serial.begin(115200); 101 | 102 | // Wait a moment to start (so we don't miss Serial output) 103 | vTaskDelay(1000 / portTICK_PERIOD_MS); 104 | Serial.println(); 105 | Serial.println("---FreeRTOS Semaphore Solution---"); 106 | 107 | // Create mutexes and semaphores before starting tasks 108 | bin_sem = xSemaphoreCreateBinary(); 109 | mutex = xSemaphoreCreateMutex(); 110 | sem_empty = xSemaphoreCreateCounting(BUF_SIZE, BUF_SIZE); 111 | sem_filled = xSemaphoreCreateCounting(BUF_SIZE, 0); 112 | 113 | // Start producer tasks (wait for each to read argument) 114 | for (int i = 0; i < num_prod_tasks; i++) { 115 | sprintf(task_name, "Producer %i", i); 116 | xTaskCreatePinnedToCore(producer, 117 | task_name, 118 | 1024, 119 | (void *)&i, 120 | 1, 121 | NULL, 122 | app_cpu); 123 | xSemaphoreTake(bin_sem, portMAX_DELAY); 124 | } 125 | 126 | // Start consumer tasks 127 | for (int i = 0; i < num_cons_tasks; i++) { 128 | sprintf(task_name, "Consumer %i", i); 129 | xTaskCreatePinnedToCore(consumer, 130 | task_name, 131 | 1024, 132 | NULL, 133 | 1, 134 | NULL, 135 | app_cpu); 136 | } 137 | 138 | // Notify that all tasks have been created (lock Serial with mutex) 139 | xSemaphoreTake(mutex, portMAX_DELAY); 140 | Serial.println("All tasks created"); 141 | xSemaphoreGive(mutex); 142 | } 143 | 144 | void loop() { 145 | 146 | // Do nothing but allow yielding to lower-priority tasks 147 | vTaskDelay(1000 / portTICK_PERIOD_MS); 148 | } 149 | -------------------------------------------------------------------------------- /07-semaphore/rtos-part-07.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasir-shahzad/introduction-to-rtos/f2ae37914fa61eca4f7f9e7e11a6895fde7e6cf3/07-semaphore/rtos-part-07.pptx -------------------------------------------------------------------------------- /08-software-timer/esp32-freertos-08-demo-software-timer/esp32-freertos-08-demo-software-timer.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * FreeRTOS Software Timer Demo 3 | * 4 | * Demonstrate basic timer usage. 5 | * 6 | * Date: February 1, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // You'll likely need this on vanilla FreeRTOS 12 | //#include 13 | 14 | // Use only core 1 for demo purposes 15 | #if CONFIG_FREERTOS_UNICORE 16 | static const BaseType_t app_cpu = 0; 17 | #else 18 | static const BaseType_t app_cpu = 1; 19 | #endif 20 | 21 | // Globals 22 | static TimerHandle_t one_shot_timer = NULL; 23 | static TimerHandle_t auto_reload_timer = NULL; 24 | 25 | //***************************************************************************** 26 | // Callbacks 27 | 28 | // Called when one of the timers expires 29 | void myTimerCallback(TimerHandle_t xTimer) { 30 | 31 | // Print message if timer 0 expired 32 | if ((uint32_t)pvTimerGetTimerID(xTimer) == 0) { 33 | Serial.println("One-shot timer expired"); 34 | } 35 | 36 | // Print message if timer 1 expired 37 | if ((uint32_t)pvTimerGetTimerID(xTimer) == 1) { 38 | Serial.println("Auto-reload timer expired"); 39 | } 40 | } 41 | 42 | //***************************************************************************** 43 | // Main (runs as its own task with priority 1 on core 1) 44 | 45 | void setup() { 46 | 47 | // Configure Serial 48 | Serial.begin(115200); 49 | 50 | // Wait a moment to start (so we don't miss Serial output) 51 | vTaskDelay(1000 / portTICK_PERIOD_MS); 52 | Serial.println(); 53 | Serial.println("---FreeRTOS Timer Demo---"); 54 | 55 | // Create a one-shot timer 56 | one_shot_timer = xTimerCreate( 57 | "One-shot timer", // Name of timer 58 | 2000 / portTICK_PERIOD_MS, // Period of timer (in ticks) 59 | pdFALSE, // Auto-reload 60 | (void *)0, // Timer ID 61 | myTimerCallback); // Callback function 62 | 63 | // Create an auto-reload timer 64 | auto_reload_timer = xTimerCreate( 65 | "Auto-reload timer", // Name of timer 66 | 1000 / portTICK_PERIOD_MS, // Period of timer (in ticks) 67 | pdTRUE, // Auto-reload 68 | (void *)1, // Timer ID 69 | myTimerCallback); // Callback function 70 | 71 | // Check to make sure timers were created 72 | if (one_shot_timer == NULL || auto_reload_timer == NULL) { 73 | Serial.println("Could not create one of the timers"); 74 | } else { 75 | 76 | // Wait and then print out a message that we're starting the timers 77 | vTaskDelay(1000 / portTICK_PERIOD_MS); 78 | Serial.println("Starting timers..."); 79 | 80 | // Start timers (max block time if command queue is full) 81 | xTimerStart(one_shot_timer, portMAX_DELAY); 82 | xTimerStart(auto_reload_timer, portMAX_DELAY); 83 | } 84 | 85 | // Delete self task to show that timers will work with no user tasks 86 | vTaskDelete(NULL); 87 | } 88 | 89 | 90 | void loop() { 91 | // Execution should never get here 92 | } 93 | -------------------------------------------------------------------------------- /08-software-timer/esp32-freertos-08-solution-led-dimmer/esp32-freertos-08-solution-led-dimmer.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * FreeRTOS Solution to LED Dimmer 3 | * 4 | * Turn on LED when entering serial commands. Turn it off if serial is inactive 5 | * for 5 seconds. 6 | * 7 | * Date: February 1, 2021 8 | * Author: Shawn Hymel 9 | * License: 0BSD 10 | */ 11 | 12 | // You'll likely need this on vanilla FreeRTOS 13 | //#include 14 | 15 | // Use only core 1 for demo purposes 16 | #if CONFIG_FREERTOS_UNICORE 17 | static const BaseType_t app_cpu = 0; 18 | #else 19 | static const BaseType_t app_cpu = 1; 20 | #endif 21 | 22 | // Settings 23 | static const TickType_t dim_delay = 5000 / portTICK_PERIOD_MS; 24 | 25 | // Pins (change this if your Arduino board does not have LED_BUILTIN defined) 26 | static const int led_pin = LED_BUILTIN; 27 | 28 | // Globals 29 | static TimerHandle_t one_shot_timer = NULL; 30 | 31 | //***************************************************************************** 32 | // Callbacks 33 | 34 | // Turn off LED when timer expires 35 | void autoDimmerCallback(TimerHandle_t xTimer) { 36 | digitalWrite(led_pin, LOW); 37 | } 38 | 39 | //***************************************************************************** 40 | // Tasks 41 | 42 | // Echo things back to serial port, turn on LED when while entering input 43 | void doCLI(void *parameters) { 44 | 45 | char c; 46 | 47 | // Configure LED pin 48 | pinMode(led_pin, OUTPUT); 49 | 50 | while (1) { 51 | 52 | // See if there are things in the input serial buffer 53 | if (Serial.available() > 0) { 54 | 55 | // If so, echo everything back to the serial port 56 | c = Serial.read(); 57 | Serial.print(c); 58 | 59 | // Turn on the LED 60 | digitalWrite(led_pin, HIGH); 61 | 62 | // Start timer (if timer is already running, this will act as 63 | // xTimerReset() instead) 64 | xTimerStart(one_shot_timer, portMAX_DELAY); 65 | } 66 | } 67 | } 68 | 69 | //***************************************************************************** 70 | // Main (runs as its own task with priority 1 on core 1) 71 | 72 | void setup() { 73 | 74 | // Configure Serial 75 | Serial.begin(115200); 76 | 77 | // Wait a moment to start (so we don't miss Serial output) 78 | vTaskDelay(1000 / portTICK_PERIOD_MS); 79 | Serial.println(); 80 | Serial.println("---FreeRTOS Timer Solution---"); 81 | 82 | // Create a one-shot timer 83 | one_shot_timer = xTimerCreate( 84 | "One-shot timer", // Name of timer 85 | dim_delay, // Period of timer (in ticks) 86 | pdFALSE, // Auto-reload 87 | (void *)0, // Timer ID 88 | autoDimmerCallback); // Callback function 89 | 90 | // Start command line interface (CLI) task 91 | xTaskCreatePinnedToCore(doCLI, 92 | "Do CLI", 93 | 1024, 94 | NULL, 95 | 1, 96 | NULL, 97 | app_cpu); 98 | 99 | // Delete "setup and loop" task 100 | vTaskDelete(NULL); 101 | } 102 | 103 | void loop() { 104 | // Execution should never get here 105 | } 106 | -------------------------------------------------------------------------------- /08-software-timer/rtos-part-08.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasir-shahzad/introduction-to-rtos/f2ae37914fa61eca4f7f9e7e11a6895fde7e6cf3/08-software-timer/rtos-part-08.pptx -------------------------------------------------------------------------------- /09-hardware-interrupts/esp32-freertos-09-demo-isr-critical-section/esp32-freertos-09-demo-isr-critical-section.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 ISR Critical Section Demo 3 | * 4 | * Increment global variable in ISR. 5 | * 6 | * Date: February 3, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // Use only core 1 for demo purposes 12 | #if CONFIG_FREERTOS_UNICORE 13 | static const BaseType_t app_cpu = 0; 14 | #else 15 | static const BaseType_t app_cpu = 1; 16 | #endif 17 | 18 | // Settings 19 | static const uint16_t timer_divider = 8; 20 | static const uint64_t timer_max_count = 1000000; 21 | static const TickType_t task_delay = 2000 / portTICK_PERIOD_MS; 22 | 23 | // Globals 24 | static hw_timer_t *timer = NULL; 25 | static volatile int isr_counter; 26 | static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; 27 | 28 | //***************************************************************************** 29 | // Interrupt Service Routines (ISRs) 30 | 31 | // This function executes when timer reaches max (and resets) 32 | void IRAM_ATTR onTimer() { 33 | 34 | // ESP-IDF version of a critical section (in an ISR) 35 | portENTER_CRITICAL_ISR(&spinlock); 36 | isr_counter++; 37 | portEXIT_CRITICAL_ISR(&spinlock); 38 | 39 | // Vanilla FreeRTOS version of a critical section (in an ISR) 40 | //UBaseType_t saved_int_status; 41 | //saved_int_status = taskENTER_CRITICAL_FROM_ISR(); 42 | //isr_counter++; 43 | //taskEXIT_CRITICAL_FROM_ISR(saved_int_status); 44 | } 45 | 46 | //***************************************************************************** 47 | // Tasks 48 | 49 | // Wait for semaphore and print out ADC value when received 50 | void printValues(void *parameters) { 51 | 52 | // Loop forever 53 | while (1) { 54 | 55 | // Count down and print out counter value 56 | while (isr_counter > 0) { 57 | 58 | // Print value of counter 59 | Serial.println(isr_counter); 60 | 61 | // ESP-IDF version of a critical section (in a task) 62 | portENTER_CRITICAL(&spinlock); 63 | isr_counter--; 64 | portEXIT_CRITICAL(&spinlock); 65 | 66 | // Vanilla FreeRTOS version of a critical section (in a task) 67 | //taskENTER_CRITICAL(); 68 | //isr_counter--; 69 | //taskEXIT_CRITICAL(); 70 | } 71 | 72 | // Wait 2 seconds while ISR increments counter a few times 73 | vTaskDelay(task_delay); 74 | } 75 | } 76 | 77 | //***************************************************************************** 78 | // Main (runs as its own task with priority 1 on core 1) 79 | 80 | void setup() { 81 | 82 | // Configure Serial 83 | Serial.begin(115200); 84 | 85 | // Wait a moment to start (so we don't miss Serial output) 86 | vTaskDelay(1000 / portTICK_PERIOD_MS); 87 | Serial.println(); 88 | Serial.println("---FreeRTOS ISR Critical Section Demo---"); 89 | 90 | // Start task to print out results 91 | xTaskCreatePinnedToCore(printValues, 92 | "Print values", 93 | 1024, 94 | NULL, 95 | 1, 96 | NULL, 97 | app_cpu); 98 | 99 | // Create and start timer (num, divider, countUp) 100 | timer = timerBegin(0, timer_divider, true); 101 | 102 | // Provide ISR to timer (timer, function, edge) 103 | timerAttachInterrupt(timer, &onTimer, true); 104 | 105 | // At what count should ISR trigger (timer, count, autoreload) 106 | timerAlarmWrite(timer, timer_max_count, true); 107 | 108 | // Allow ISR to trigger 109 | timerAlarmEnable(timer); 110 | 111 | // Delete "setup and loop" task 112 | vTaskDelete(NULL); 113 | } 114 | 115 | void loop() { 116 | // Execution should never get here 117 | } 118 | -------------------------------------------------------------------------------- /09-hardware-interrupts/esp32-freertos-09-demo-isr-semaphore/esp32-freertos-09-demo-isr-semaphore.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 ISR Semaphore Demo 3 | * 4 | * Read ADC values in ISR at 1 Hz and defer printing them out in a task. 5 | * 6 | * Date: February 3, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // You'll likely need this on vanilla FreeRTOS 12 | //#include 13 | 14 | // Use only core 1 for demo purposes 15 | #if CONFIG_FREERTOS_UNICORE 16 | static const BaseType_t app_cpu = 0; 17 | #else 18 | static const BaseType_t app_cpu = 1; 19 | #endif 20 | 21 | // Settings 22 | static const uint16_t timer_divider = 80; // Count at 1 MHz 23 | static const uint64_t timer_max_count = 1000000; 24 | 25 | // Pins 26 | static const int adc_pin = A0; 27 | 28 | // Globals 29 | static hw_timer_t *timer = NULL; 30 | static volatile uint16_t val; 31 | static SemaphoreHandle_t bin_sem = NULL; 32 | 33 | //***************************************************************************** 34 | // Interrupt Service Routines (ISRs) 35 | 36 | // This function executes when timer reaches max (and resets) 37 | void IRAM_ATTR onTimer() { 38 | 39 | BaseType_t task_woken = pdFALSE; 40 | 41 | // Perform action (read from ADC) 42 | val = analogRead(adc_pin); 43 | 44 | // Give semaphore to tell task that new value is ready 45 | xSemaphoreGiveFromISR(bin_sem, &task_woken); 46 | 47 | // Exit from ISR (Vanilla FreeRTOS) 48 | //portYIELD_FROM_ISR(task_woken); 49 | 50 | // Exit from ISR (ESP-IDF) 51 | if (task_woken) { 52 | portYIELD_FROM_ISR(); 53 | } 54 | } 55 | 56 | //***************************************************************************** 57 | // Tasks 58 | 59 | // Wait for semaphore and print out ADC value when received 60 | void printValues(void *parameters) { 61 | 62 | // Loop forever, wait for semaphore, and print value 63 | while (1) { 64 | xSemaphoreTake(bin_sem, portMAX_DELAY); 65 | Serial.println(val); 66 | } 67 | } 68 | 69 | //***************************************************************************** 70 | // Main (runs as its own task with priority 1 on core 1) 71 | 72 | void setup() { 73 | 74 | // Configure Serial 75 | Serial.begin(115200); 76 | 77 | // Wait a moment to start (so we don't miss Serial output) 78 | vTaskDelay(1000 / portTICK_PERIOD_MS); 79 | Serial.println(); 80 | Serial.println("---FreeRTOS ISR Buffer Demo---"); 81 | 82 | // Create semaphore before it is used (in task or ISR) 83 | bin_sem = xSemaphoreCreateBinary(); 84 | 85 | // Force reboot if we can't create the semaphore 86 | if (bin_sem == NULL) { 87 | Serial.println("Could not create semaphore"); 88 | ESP.restart(); 89 | } 90 | 91 | // Start task to print out results (higher priority!) 92 | xTaskCreatePinnedToCore(printValues, 93 | "Print values", 94 | 1024, 95 | NULL, 96 | 2, 97 | NULL, 98 | app_cpu); 99 | 100 | // Create and start timer (num, divider, countUp) 101 | timer = timerBegin(0, timer_divider, true); 102 | 103 | // Provide ISR to timer (timer, function, edge) 104 | timerAttachInterrupt(timer, &onTimer, true); 105 | 106 | // At what count should ISR trigger (timer, count, autoreload) 107 | timerAlarmWrite(timer, timer_max_count, true); 108 | 109 | // Allow ISR to trigger 110 | timerAlarmEnable(timer); 111 | } 112 | 113 | void loop() { 114 | // Do nothing, forever 115 | } 116 | -------------------------------------------------------------------------------- /09-hardware-interrupts/esp32-freertos-09-demo-timer-interrupt/esp32-freertos-09-demo-timer-interrupt.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Timer Interrupt Demo 3 | * 4 | * Blink LED with hardware timer interrupt. 5 | * 6 | * Date: February 3, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // Settings 12 | static const uint16_t timer_divider = 80; 13 | static const uint64_t timer_max_count = 1000000; 14 | 15 | // Pins (change this if your Arduino board does not have LED_BUILTIN defined) 16 | static const int led_pin = LED_BUILTIN; 17 | 18 | // Globals 19 | static hw_timer_t *timer = NULL; 20 | 21 | //***************************************************************************** 22 | // Interrupt Service Routines (ISRs) 23 | 24 | // This function executes when timer reaches max (and resets) 25 | void IRAM_ATTR onTimer() { 26 | 27 | // Toggle LED 28 | int pin_state = digitalRead(led_pin); 29 | digitalWrite(led_pin, !pin_state); 30 | } 31 | 32 | //***************************************************************************** 33 | // Main (runs as its own task with priority 1 on core 1) 34 | 35 | void setup() { 36 | 37 | // Configure LED pin 38 | pinMode(led_pin, OUTPUT); 39 | 40 | // Create and start timer (num, divider, countUp) 41 | timer = timerBegin(0, timer_divider, true); 42 | 43 | // Provide ISR to timer (timer, function, edge) 44 | timerAttachInterrupt(timer, &onTimer, true); 45 | 46 | // At what count should ISR trigger (timer, count, autoreload) 47 | timerAlarmWrite(timer, timer_max_count, true); 48 | 49 | // Allow ISR to trigger 50 | timerAlarmEnable(timer); 51 | } 52 | 53 | void loop() { 54 | // Do nothing 55 | } 56 | -------------------------------------------------------------------------------- /09-hardware-interrupts/esp32-freertos-09-solution-isr-audio/esp32-freertos-09-solution-isr-audio.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Sample and Process Solution 3 | * 4 | * Sample ADC in an ISR, process in a task. 5 | * 6 | * Date: February 23, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // You'll likely need this on vanilla FreeRTOS 12 | //#include 13 | 14 | // Use only core 1 for demo purposes 15 | #if CONFIG_FREERTOS_UNICORE 16 | static const BaseType_t app_cpu = 0; 17 | #else 18 | static const BaseType_t app_cpu = 1; 19 | #endif 20 | 21 | // Settings 22 | static const char command[] = "rms"; // Command 23 | static const uint16_t timer_divider = 2; // Divide 80 MHz by this 24 | static const uint64_t timer_max_count = 2500; // 16kHz sample rate 25 | static const uint32_t cli_delay = 10; // ms delay 26 | static const float adc_voltage = 3.3; // Max ADC voltage 27 | static const uint16_t adc_max = 4095; // Max ADC value (12-bit) 28 | static const uint8_t pwm_ch = 0; // PWM channel 29 | enum { BUF_LEN = 1600 }; // Number of elements in sample buffer 30 | enum { MSG_LEN = 100 }; // Max characters in message body 31 | enum { MSG_QUEUE_LEN = 5 }; // Number of slots in message queue 32 | enum { CMD_BUF_LEN = 255}; // Number of characters in command buffer 33 | 34 | // Pins 35 | static const int adc_pin = A0; 36 | static const int led_pin = 15; 37 | 38 | // Message struct to wrap strings for queue 39 | typedef struct Message { 40 | char body[MSG_LEN]; 41 | } Message; 42 | 43 | // Globals 44 | static hw_timer_t *timer = NULL; 45 | static TaskHandle_t processing_task = NULL; 46 | static SemaphoreHandle_t sem_done_reading = NULL; 47 | static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; 48 | static QueueHandle_t msg_queue; 49 | static volatile uint16_t buf_0[BUF_LEN]; // One buffer in the pair 50 | static volatile uint16_t buf_1[BUF_LEN]; // The other buffer in the pair 51 | static volatile uint16_t* write_to = buf_0; // Double buffer write pointer 52 | static volatile uint16_t* read_from = buf_1; // Double buffer read pointer 53 | static volatile uint8_t buf_overrun = 0; // Double buffer overrun flag 54 | static float adc_rms; 55 | 56 | //***************************************************************************** 57 | // Functions that can be called from anywhere (in this file) 58 | 59 | // Swap the write_to and read_from pointers in the double buffer 60 | // Only ISR calls this at the moment, so no need to make it thread-safe 61 | void IRAM_ATTR swap() { 62 | volatile uint16_t* temp_ptr = write_to; 63 | write_to = read_from; 64 | read_from = temp_ptr; 65 | } 66 | 67 | //***************************************************************************** 68 | // Interrupt Service Routines (ISRs) 69 | 70 | // This function executes when timer reaches max (and resets) 71 | void IRAM_ATTR onTimer() { 72 | 73 | static uint16_t idx = 0; 74 | BaseType_t task_woken = pdFALSE; 75 | 76 | // If buffer is not overrun, read ADC to next buffer element. If buffer is 77 | // overrun, drop the sample. 78 | if ((idx < BUF_LEN) && (buf_overrun == 0)) { 79 | write_to[idx] = analogRead(adc_pin); 80 | idx++; 81 | } 82 | 83 | // Check if the buffer is full 84 | if (idx >= BUF_LEN) { 85 | 86 | // If reading is not done, set overrun flag. We don't need to set this 87 | // as a critical section, as nothing can interrupt and change either value. 88 | if (xSemaphoreTakeFromISR(sem_done_reading, &task_woken) == pdFALSE) { 89 | buf_overrun = 1; 90 | } 91 | 92 | // Only swap buffers and notify task if overrun flag is cleared 93 | if (buf_overrun == 0) { 94 | 95 | // Reset index and swap buffer pointers 96 | idx = 0; 97 | swap(); 98 | 99 | // A task notification works like a binary semaphore but is faster 100 | vTaskNotifyGiveFromISR(processing_task, &task_woken); 101 | } 102 | } 103 | 104 | // Exit from ISR (Vanilla FreeRTOS) 105 | //portYIELD_FROM_ISR(task_woken); 106 | 107 | // Exit from ISR (ESP-IDF) 108 | if (task_woken) { 109 | portYIELD_FROM_ISR(); 110 | } 111 | } 112 | 113 | //***************************************************************************** 114 | // Tasks 115 | 116 | // Serial terminal task 117 | void doCLI(void *parameters) { 118 | 119 | Message rcv_msg; 120 | char c; 121 | char cmd_buf[CMD_BUF_LEN]; 122 | uint8_t idx = 0; 123 | uint8_t cmd_len = strlen(command); 124 | 125 | // Clear whole buffer 126 | memset(cmd_buf, 0, CMD_BUF_LEN); 127 | 128 | // Loop forever 129 | while (1) { 130 | 131 | // Look for any error messages that need to be printed 132 | if (xQueueReceive(msg_queue, (void *)&rcv_msg, 0) == pdTRUE) { 133 | Serial.println(rcv_msg.body); 134 | } 135 | 136 | // Read characters from serial 137 | if (Serial.available() > 0) { 138 | c = Serial.read(); 139 | 140 | // Store received character to buffer if not over buffer limit 141 | if (idx < CMD_BUF_LEN - 1) { 142 | cmd_buf[idx] = c; 143 | idx++; 144 | } 145 | 146 | // Print newline and check input on 'enter' 147 | if ((c == '\n') || (c == '\r')) { 148 | 149 | // Print newline to terminal 150 | Serial.print("\r\n"); 151 | 152 | // Print average value if command given is "avg" 153 | cmd_buf[idx - 1] = '\0'; 154 | if (strcmp(cmd_buf, command) == 0) { 155 | Serial.print("RMS Voltage: "); 156 | Serial.println(adc_rms); 157 | } 158 | 159 | // Reset receive buffer and index counter 160 | memset(cmd_buf, 0, CMD_BUF_LEN); 161 | idx = 0; 162 | 163 | // Otherwise, echo character back to serial terminal 164 | } else { 165 | Serial.print(c); 166 | } 167 | } 168 | 169 | // Don't hog the CPU. Yield to other tasks for a while 170 | vTaskDelay(cli_delay / portTICK_PERIOD_MS); 171 | } 172 | } 173 | 174 | // Wait for semaphore and calculate average of ADC values 175 | void calcRMS(void *parameters) { 176 | 177 | Message msg; 178 | float val; 179 | float avg; 180 | float rms; 181 | float brightness; 182 | 183 | // Loop forever, wait for semaphore, and print value 184 | while (1) { 185 | 186 | // Wait for notification from ISR (similar to binary semaphore) 187 | ulTaskNotifyTake(pdTRUE, portMAX_DELAY); 188 | 189 | // Calculate average (as floating point value) 190 | avg = 0.0; 191 | for (int i = 0; i < BUF_LEN; i++) { 192 | avg += (float)read_from[i]; 193 | //vTaskDelay(105 / portTICK_PERIOD_MS); // Uncomment to test overrun flag 194 | } 195 | avg /= BUF_LEN; 196 | 197 | // Convert average to voltage 198 | avg = (avg * adc_voltage) / (float)adc_max; 199 | 200 | // Calculate volts-RMS value (filter out DC component) 201 | rms = 0.0; 202 | for (int i = 0; i < BUF_LEN; i++) { 203 | val = ((float)read_from[i] * adc_voltage) / (float)adc_max; 204 | rms += powf((val - avg), 2); 205 | } 206 | rms = sqrtf(rms / BUF_LEN); 207 | 208 | // Udate LED brightness 209 | brightness = (rms * UINT16_MAX) / adc_voltage; 210 | ledcWrite(pwm_ch, brightness); 211 | 212 | // Updating the shared float may or may not take multiple isntructions, so 213 | // we protect it with a mutex or critical section. The ESP-IDF critical 214 | // section is the easiest for this application. 215 | portENTER_CRITICAL(&spinlock); 216 | adc_rms = rms; 217 | portEXIT_CRITICAL(&spinlock); 218 | 219 | // If we took too long to process, buffer writing will have overrun. So, 220 | // we send a message to be printed out to the serial terminal. 221 | if (buf_overrun == 1) { 222 | strcpy(msg.body, "Error: Buffer overrun. Samples have been dropped."); 223 | xQueueSend(msg_queue, (void *)&msg, 10); 224 | } 225 | 226 | // Clearing the overrun flag and giving the "done reading" semaphore must 227 | // be done together without being interrupted. 228 | portENTER_CRITICAL(&spinlock); 229 | buf_overrun = 0; 230 | xSemaphoreGive(sem_done_reading); 231 | portEXIT_CRITICAL(&spinlock); 232 | } 233 | } 234 | 235 | //***************************************************************************** 236 | // Main (runs as its own task with priority 1 on core 1) 237 | 238 | void setup() { 239 | 240 | // Configure PWM pin 241 | pinMode(led_pin, OUTPUT); 242 | ledcAttachPin(led_pin, pwm_ch); // Assign pin to PWM channel 0 243 | ledcSetup(pwm_ch, 4000, 16); // channel 0, 12kHz, 16-bit resolution 244 | 245 | // Configure Serial 246 | Serial.begin(115200); 247 | 248 | // Wait a moment to start (so we don't miss Serial output) 249 | vTaskDelay(1000 / portTICK_PERIOD_MS); 250 | Serial.println(); 251 | Serial.println("---FreeRTOS Sample and Process Demo---"); 252 | 253 | // Create semaphore before it is used (in task or ISR) 254 | sem_done_reading = xSemaphoreCreateBinary(); 255 | 256 | // Force reboot if we can't create the semaphore 257 | if (sem_done_reading == NULL) { 258 | Serial.println("Could not create one or more semaphores"); 259 | ESP.restart(); 260 | } 261 | 262 | // We want the done reading semaphore to initialize to 1 263 | xSemaphoreGive(sem_done_reading); 264 | 265 | // Create message queue before it is used 266 | msg_queue = xQueueCreate(MSG_QUEUE_LEN, sizeof(Message)); 267 | 268 | // Start task to handle command line interface events. Let's set it at a 269 | // higher priority but only run it once every 20 ms. 270 | xTaskCreatePinnedToCore(doCLI, 271 | "Do CLI", 272 | 2048, 273 | NULL, 274 | 2, 275 | NULL, 276 | app_cpu); 277 | 278 | // Start task to calculate average. Save handle for use with notifications. 279 | xTaskCreatePinnedToCore(calcRMS, 280 | "Calculate RMS", 281 | 2048, 282 | NULL, 283 | 1, 284 | &processing_task, 285 | app_cpu); 286 | 287 | // Start a timer to run ISR every 100 ms 288 | timer = timerBegin(0, timer_divider, true); 289 | timerAttachInterrupt(timer, &onTimer, true); 290 | timerAlarmWrite(timer, timer_max_count, true); 291 | timerAlarmEnable(timer); 292 | 293 | // Delete "setup and loop" task 294 | vTaskDelete(NULL); 295 | } 296 | 297 | void loop() { 298 | // Execution should never get here 299 | } 300 | -------------------------------------------------------------------------------- /09-hardware-interrupts/esp32-freertos-09-solution-isr-sample/esp32-freertos-09-solution-isr-sample.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Sample and Process Solution 3 | * 4 | * Sample ADC in an ISR, process in a task. 5 | * 6 | * Date: February 3, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // You'll likely need this on vanilla FreeRTOS 12 | //#include 13 | 14 | // Use only core 1 for demo purposes 15 | #if CONFIG_FREERTOS_UNICORE 16 | static const BaseType_t app_cpu = 0; 17 | #else 18 | static const BaseType_t app_cpu = 1; 19 | #endif 20 | 21 | // Settings 22 | static const char command[] = "avg"; // Command 23 | static const uint16_t timer_divider = 8; // Divide 80 MHz by this 24 | static const uint64_t timer_max_count = 1000000; // Timer counts to this value 25 | static const uint32_t cli_delay = 20; // ms delay 26 | enum { BUF_LEN = 10 }; // Number of elements in sample buffer 27 | enum { MSG_LEN = 100 }; // Max characters in message body 28 | enum { MSG_QUEUE_LEN = 5 }; // Number of slots in message queue 29 | enum { CMD_BUF_LEN = 255}; // Number of characters in command buffer 30 | 31 | // Pins 32 | static const int adc_pin = A0; 33 | 34 | // Message struct to wrap strings for queue 35 | typedef struct Message { 36 | char body[MSG_LEN]; 37 | } Message; 38 | 39 | // Globals 40 | static hw_timer_t *timer = NULL; 41 | static TaskHandle_t processing_task = NULL; 42 | static SemaphoreHandle_t sem_done_reading = NULL; 43 | static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; 44 | static QueueHandle_t msg_queue; 45 | static volatile uint16_t buf_0[BUF_LEN]; // One buffer in the pair 46 | static volatile uint16_t buf_1[BUF_LEN]; // The other buffer in the pair 47 | static volatile uint16_t* write_to = buf_0; // Double buffer write pointer 48 | static volatile uint16_t* read_from = buf_1; // Double buffer read pointer 49 | static volatile uint8_t buf_overrun = 0; // Double buffer overrun flag 50 | static float adc_avg; 51 | 52 | //***************************************************************************** 53 | // Functions that can be called from anywhere (in this file) 54 | 55 | // Swap the write_to and read_from pointers in the double buffer 56 | // Only ISR calls this at the moment, so no need to make it thread-safe 57 | void IRAM_ATTR swap() { 58 | volatile uint16_t* temp_ptr = write_to; 59 | write_to = read_from; 60 | read_from = temp_ptr; 61 | } 62 | 63 | //***************************************************************************** 64 | // Interrupt Service Routines (ISRs) 65 | 66 | // This function executes when timer reaches max (and resets) 67 | void IRAM_ATTR onTimer() { 68 | 69 | static uint16_t idx = 0; 70 | BaseType_t task_woken = pdFALSE; 71 | 72 | // If buffer is not overrun, read ADC to next buffer element. If buffer is 73 | // overrun, drop the sample. 74 | if ((idx < BUF_LEN) && (buf_overrun == 0)) { 75 | write_to[idx] = analogRead(adc_pin); 76 | idx++; 77 | } 78 | 79 | // Check if the buffer is full 80 | if (idx >= BUF_LEN) { 81 | 82 | // If reading is not done, set overrun flag. We don't need to set this 83 | // as a critical section, as nothing can interrupt and change either value. 84 | if (xSemaphoreTakeFromISR(sem_done_reading, &task_woken) == pdFALSE) { 85 | buf_overrun = 1; 86 | } 87 | 88 | // Only swap buffers and notify task if overrun flag is cleared 89 | if (buf_overrun == 0) { 90 | 91 | // Reset index and swap buffer pointers 92 | idx = 0; 93 | swap(); 94 | 95 | // A task notification works like a binary semaphore but is faster 96 | vTaskNotifyGiveFromISR(processing_task, &task_woken); 97 | } 98 | } 99 | 100 | // Exit from ISR (Vanilla FreeRTOS) 101 | //portYIELD_FROM_ISR(task_woken); 102 | 103 | // Exit from ISR (ESP-IDF) 104 | if (task_woken) { 105 | portYIELD_FROM_ISR(); 106 | } 107 | } 108 | 109 | //***************************************************************************** 110 | // Tasks 111 | 112 | // Serial terminal task 113 | void doCLI(void *parameters) { 114 | 115 | Message rcv_msg; 116 | char c; 117 | char cmd_buf[CMD_BUF_LEN]; 118 | uint8_t idx = 0; 119 | uint8_t cmd_len = strlen(command); 120 | 121 | // Clear whole buffer 122 | memset(cmd_buf, 0, CMD_BUF_LEN); 123 | 124 | // Loop forever 125 | while (1) { 126 | 127 | // Look for any error messages that need to be printed 128 | if (xQueueReceive(msg_queue, (void *)&rcv_msg, 0) == pdTRUE) { 129 | Serial.println(rcv_msg.body); 130 | } 131 | 132 | // Read characters from serial 133 | if (Serial.available() > 0) { 134 | c = Serial.read(); 135 | 136 | // Store received character to buffer if not over buffer limit 137 | if (idx < CMD_BUF_LEN - 1) { 138 | cmd_buf[idx] = c; 139 | idx++; 140 | } 141 | 142 | // Print newline and check input on 'enter' 143 | if ((c == '\n') || (c == '\r')) { 144 | 145 | // Print newline to terminal 146 | Serial.print("\r\n"); 147 | 148 | // Print average value if command given is "avg" 149 | cmd_buf[idx - 1] = '\0'; 150 | if (strcmp(cmd_buf, command) == 0) { 151 | Serial.print("Average: "); 152 | Serial.println(adc_avg); 153 | } 154 | 155 | // Reset receive buffer and index counter 156 | memset(cmd_buf, 0, CMD_BUF_LEN); 157 | idx = 0; 158 | 159 | // Otherwise, echo character back to serial terminal 160 | } else { 161 | Serial.print(c); 162 | } 163 | } 164 | 165 | // Don't hog the CPU. Yield to other tasks for a while 166 | vTaskDelay(cli_delay / portTICK_PERIOD_MS); 167 | } 168 | } 169 | 170 | // Wait for semaphore and calculate average of ADC values 171 | void calcAverage(void *parameters) { 172 | 173 | Message msg; 174 | float avg; 175 | 176 | // Loop forever, wait for semaphore, and print value 177 | while (1) { 178 | 179 | // Wait for notification from ISR (similar to binary semaphore) 180 | ulTaskNotifyTake(pdTRUE, portMAX_DELAY); 181 | 182 | // Calculate average (as floating point value) 183 | avg = 0.0; 184 | for (int i = 0; i < BUF_LEN; i++) { 185 | avg += (float)read_from[i]; 186 | //vTaskDelay(105 / portTICK_PERIOD_MS); // Uncomment to test overrun flag 187 | } 188 | avg /= BUF_LEN; 189 | 190 | // Updating the shared float may or may not take multiple isntructions, so 191 | // we protect it with a mutex or critical section. The ESP-IDF critical 192 | // section is the easiest for this application. 193 | portENTER_CRITICAL(&spinlock); 194 | adc_avg = avg; 195 | portEXIT_CRITICAL(&spinlock); 196 | 197 | // If we took too long to process, buffer writing will have overrun. So, 198 | // we send a message to be printed out to the serial terminal. 199 | if (buf_overrun == 1) { 200 | strcpy(msg.body, "Error: Buffer overrun. Samples have been dropped."); 201 | xQueueSend(msg_queue, (void *)&msg, 10); 202 | } 203 | 204 | // Clearing the overrun flag and giving the "done reading" semaphore must 205 | // be done together without being interrupted. 206 | portENTER_CRITICAL(&spinlock); 207 | buf_overrun = 0; 208 | xSemaphoreGive(sem_done_reading); 209 | portEXIT_CRITICAL(&spinlock); 210 | } 211 | } 212 | 213 | //***************************************************************************** 214 | // Main (runs as its own task with priority 1 on core 1) 215 | 216 | void setup() { 217 | 218 | // Configure Serial 219 | Serial.begin(115200); 220 | 221 | // Wait a moment to start (so we don't miss Serial output) 222 | vTaskDelay(1000 / portTICK_PERIOD_MS); 223 | Serial.println(); 224 | Serial.println("---FreeRTOS Sample and Process Demo---"); 225 | 226 | // Create semaphore before it is used (in task or ISR) 227 | sem_done_reading = xSemaphoreCreateBinary(); 228 | 229 | // Force reboot if we can't create the semaphore 230 | if (sem_done_reading == NULL) { 231 | Serial.println("Could not create one or more semaphores"); 232 | ESP.restart(); 233 | } 234 | 235 | // We want the done reading semaphore to initialize to 1 236 | xSemaphoreGive(sem_done_reading); 237 | 238 | // Create message queue before it is used 239 | msg_queue = xQueueCreate(MSG_QUEUE_LEN, sizeof(Message)); 240 | 241 | // Start task to handle command line interface events. Let's set it at a 242 | // higher priority but only run it once every 20 ms. 243 | xTaskCreatePinnedToCore(doCLI, 244 | "Do CLI", 245 | 1024, 246 | NULL, 247 | 2, 248 | NULL, 249 | app_cpu); 250 | 251 | // Start task to calculate average. Save handle for use with notifications. 252 | xTaskCreatePinnedToCore(calcAverage, 253 | "Calculate average", 254 | 1024, 255 | NULL, 256 | 1, 257 | &processing_task, 258 | app_cpu); 259 | 260 | // Start a timer to run ISR every 100 ms 261 | timer = timerBegin(0, timer_divider, true); 262 | timerAttachInterrupt(timer, &onTimer, true); 263 | timerAlarmWrite(timer, timer_max_count, true); 264 | timerAlarmEnable(timer); 265 | 266 | // Delete "setup and loop" task 267 | vTaskDelete(NULL); 268 | } 269 | 270 | void loop() { 271 | // Execution should never get here 272 | } 273 | -------------------------------------------------------------------------------- /09-hardware-interrupts/rtos-part-09.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasir-shahzad/introduction-to-rtos/f2ae37914fa61eca4f7f9e7e11a6895fde7e6cf3/09-hardware-interrupts/rtos-part-09.pptx -------------------------------------------------------------------------------- /10-deadlock/esp32-freertos-10-challenge-dining-philosophers/esp32-freertos-10-challenge-dining-philosophers.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Dining Philosophers 3 | * 4 | * The classic "Dining Philosophers" problem in FreeRTOS form. 5 | * 6 | * Based on http://www.cs.virginia.edu/luther/COA2/S2019/pa05-dp.html 7 | * 8 | * Date: February 8, 2021 9 | * Author: Shawn Hymel 10 | * License: 0BSD 11 | */ 12 | 13 | // You'll likely need this on vanilla FreeRTOS 14 | //#include 15 | 16 | // Use only core 1 for demo purposes 17 | #if CONFIG_FREERTOS_UNICORE 18 | static const BaseType_t app_cpu = 0; 19 | #else 20 | static const BaseType_t app_cpu = 1; 21 | #endif 22 | 23 | // Settings 24 | enum { NUM_TASKS = 5 }; // Number of tasks (philosophers) 25 | enum { TASK_STACK_SIZE = 2048 }; // Bytes in ESP32, words in vanilla FreeRTOS 26 | 27 | // Globals 28 | static SemaphoreHandle_t bin_sem; // Wait for parameters to be read 29 | static SemaphoreHandle_t done_sem; // Notifies main task when done 30 | static SemaphoreHandle_t chopstick[NUM_TASKS]; 31 | 32 | //***************************************************************************** 33 | // Tasks 34 | 35 | // The only task: eating 36 | void eat(void *parameters) { 37 | 38 | int num; 39 | char buf[50]; 40 | 41 | // Copy parameter and increment semaphore count 42 | num = *(int *)parameters; 43 | xSemaphoreGive(bin_sem); 44 | 45 | // Take left chopstick 46 | xSemaphoreTake(chopstick[num], portMAX_DELAY); 47 | sprintf(buf, "Philosopher %i took chopstick %i", num, num); 48 | Serial.println(buf); 49 | 50 | // Add some delay to force deadlock 51 | vTaskDelay(1 / portTICK_PERIOD_MS); 52 | 53 | // Take right chopstick 54 | xSemaphoreTake(chopstick[(num+1)%NUM_TASKS], portMAX_DELAY); 55 | sprintf(buf, "Philosopher %i took chopstick %i", num, (num+1)%NUM_TASKS); 56 | Serial.println(buf); 57 | 58 | // Do some eating 59 | sprintf(buf, "Philosopher %i is eating", num); 60 | Serial.println(buf); 61 | vTaskDelay(10 / portTICK_PERIOD_MS); 62 | 63 | // Put down right chopstick 64 | xSemaphoreGive(chopstick[(num+1)%NUM_TASKS]); 65 | sprintf(buf, "Philosopher %i returned chopstick %i", num, (num+1)%NUM_TASKS); 66 | Serial.println(buf); 67 | 68 | // Put down left chopstick 69 | xSemaphoreGive(chopstick[num]); 70 | sprintf(buf, "Philosopher %i returned chopstick %i", num, num); 71 | Serial.println(buf); 72 | 73 | // Notify main task and delete self 74 | xSemaphoreGive(done_sem); 75 | vTaskDelete(NULL); 76 | } 77 | 78 | //***************************************************************************** 79 | // Main (runs as its own task with priority 1 on core 1) 80 | 81 | void setup() { 82 | 83 | char task_name[20]; 84 | 85 | // Configure Serial 86 | Serial.begin(115200); 87 | 88 | // Wait a moment to start (so we don't miss Serial output) 89 | vTaskDelay(1000 / portTICK_PERIOD_MS); 90 | Serial.println(); 91 | Serial.println("---FreeRTOS Dining Philosophers Challenge---"); 92 | 93 | // Create kernel objects before starting tasks 94 | bin_sem = xSemaphoreCreateBinary(); 95 | done_sem = xSemaphoreCreateCounting(NUM_TASKS, 0); 96 | for (int i = 0; i < NUM_TASKS; i++) { 97 | chopstick[i] = xSemaphoreCreateMutex(); 98 | } 99 | 100 | // Have the philosphers start eating 101 | for (int i = 0; i < NUM_TASKS; i++) { 102 | sprintf(task_name, "Philosopher %i", i); 103 | xTaskCreatePinnedToCore(eat, 104 | task_name, 105 | TASK_STACK_SIZE, 106 | (void *)&i, 107 | 1, 108 | NULL, 109 | app_cpu); 110 | xSemaphoreTake(bin_sem, portMAX_DELAY); 111 | } 112 | 113 | 114 | // Wait until all the philosophers are done 115 | for (int i = 0; i < NUM_TASKS; i++) { 116 | xSemaphoreTake(done_sem, portMAX_DELAY); 117 | } 118 | 119 | // Say that we made it through without deadlock 120 | Serial.println("Done! No deadlock occurred!"); 121 | } 122 | 123 | void loop() { 124 | // Do nothing in this task 125 | } 126 | -------------------------------------------------------------------------------- /10-deadlock/esp32-freertos-10-demo-deadlock-hierarchy/esp32-freertos-10-demo-deadlock-hierarchy.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Deadlock Demo - Hierarchy Solution 3 | * 4 | * Demonstrate the heirarchy solution to preventing deadlock. Assign a priority 5 | * rank to each mutex. Tasks must take multiple mutexes (or semaphores) in 6 | * order of priority every time, and release them in reverse order. 7 | * 8 | * Date: February 8, 2021 9 | * Author: Shawn Hymel 10 | * License: 0BSD 11 | */ 12 | 13 | // You'll likely need this on vanilla FreeRTOS 14 | //#include 15 | 16 | // Use only core 1 for demo purposes 17 | #if CONFIG_FREERTOS_UNICORE 18 | static const BaseType_t app_cpu = 0; 19 | #else 20 | static const BaseType_t app_cpu = 1; 21 | #endif 22 | 23 | // Globals 24 | static SemaphoreHandle_t mutex_1; 25 | static SemaphoreHandle_t mutex_2; 26 | 27 | //***************************************************************************** 28 | // Tasks 29 | 30 | // Task A (high priority) 31 | void doTaskA(void *parameters) { 32 | 33 | // Loop forever 34 | while (1) { 35 | 36 | // Take mutex 1 (introduce wait to force deadlock) 37 | xSemaphoreTake(mutex_1, portMAX_DELAY); 38 | Serial.println("Task A took mutex 1"); 39 | vTaskDelay(1 / portTICK_PERIOD_MS); 40 | 41 | // Take mutex 2 42 | xSemaphoreTake(mutex_2, portMAX_DELAY); 43 | Serial.println("Task A took mutex 2"); 44 | 45 | // Critical section protected by 2 mutexes 46 | Serial.println("Task A doing some work"); 47 | vTaskDelay(500 / portTICK_PERIOD_MS); 48 | 49 | // Give back mutexes (in reverse order that we took them) 50 | xSemaphoreGive(mutex_2); 51 | xSemaphoreGive(mutex_1); 52 | 53 | // Wait to let the other task execute 54 | Serial.println("Task A going to sleep"); 55 | vTaskDelay(500 / portTICK_PERIOD_MS); 56 | } 57 | } 58 | 59 | // Task B (low priority) 60 | void doTaskB(void *parameters) { 61 | 62 | // Loop forever 63 | while (1) { 64 | 65 | // Take mutex 1 (introduce wait to force deadlock) 66 | xSemaphoreTake(mutex_1, portMAX_DELAY); 67 | Serial.println("Task B took mutex 1"); 68 | vTaskDelay(1 / portTICK_PERIOD_MS); 69 | 70 | // Take mutex 2 71 | xSemaphoreTake(mutex_2, portMAX_DELAY); 72 | Serial.println("Task B took mutex 2"); 73 | 74 | // Critical section protected by 2 mutexes 75 | Serial.println("Task B doing some work"); 76 | vTaskDelay(500 / portTICK_PERIOD_MS); 77 | 78 | // Give back mutexes (in reverse order that we took them) 79 | xSemaphoreGive(mutex_2); 80 | xSemaphoreGive(mutex_1); 81 | 82 | // Wait to let the other task execute 83 | Serial.println("Task A going to sleep"); 84 | vTaskDelay(500 / portTICK_PERIOD_MS); 85 | } 86 | } 87 | 88 | //***************************************************************************** 89 | // Main (runs as its own task with priority 1 on core 1) 90 | 91 | void setup() { 92 | 93 | // Configure Serial 94 | Serial.begin(115200); 95 | 96 | // Wait a moment to start (so we don't miss Serial output) 97 | vTaskDelay(1000 / portTICK_PERIOD_MS); 98 | Serial.println(); 99 | Serial.println("---FreeRTOS Deadlock Demo---"); 100 | 101 | // Create mutexes before starting tasks 102 | mutex_1 = xSemaphoreCreateMutex(); 103 | mutex_2 = xSemaphoreCreateMutex(); 104 | 105 | // Start Task A (high priority) 106 | xTaskCreatePinnedToCore(doTaskA, 107 | "Task A", 108 | 1024, 109 | NULL, 110 | 2, 111 | NULL, 112 | app_cpu); 113 | 114 | // Start Task B (low priority) 115 | xTaskCreatePinnedToCore(doTaskB, 116 | "Task B", 117 | 1024, 118 | NULL, 119 | 1, 120 | NULL, 121 | app_cpu); 122 | 123 | // Delete "setup and loop" task 124 | vTaskDelete(NULL); 125 | } 126 | 127 | void loop() { 128 | // Execution should never get here 129 | } 130 | -------------------------------------------------------------------------------- /10-deadlock/esp32-freertos-10-demo-deadlock-timeout/esp32-freertos-10-demo-deadlock-timeout.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Deadlock Demo - Timeout 3 | * 4 | * Demonstrate why kernel object timeouts are important to alleviate 5 | * deadlock. Note that this still can cause "livelock." 6 | * 7 | * Date: February 8, 2021 8 | * Author: Shawn Hymel 9 | * License: 0BSD 10 | */ 11 | 12 | // You'll likely need this on vanilla FreeRTOS 13 | //#include 14 | 15 | // Use only core 1 for demo purposes 16 | #if CONFIG_FREERTOS_UNICORE 17 | static const BaseType_t app_cpu = 0; 18 | #else 19 | static const BaseType_t app_cpu = 1; 20 | #endif 21 | 22 | // Settings 23 | TickType_t mutex_timeout = 1000 / portTICK_PERIOD_MS; 24 | 25 | // Globals 26 | static SemaphoreHandle_t mutex_1; 27 | static SemaphoreHandle_t mutex_2; 28 | 29 | //***************************************************************************** 30 | // Tasks 31 | 32 | // Task A (high priority) 33 | void doTaskA(void *parameters) { 34 | 35 | // Loop forever 36 | while (1) { 37 | 38 | // Take mutex 1 39 | if (xSemaphoreTake(mutex_1, mutex_timeout) == pdTRUE) { 40 | 41 | // Say we took mutex 1 and wait (to force deadlock) 42 | Serial.println("Task A took mutex 1"); 43 | vTaskDelay(1 / portTICK_PERIOD_MS); 44 | 45 | // Take mutex 2 46 | if (xSemaphoreTake(mutex_2, mutex_timeout) == pdTRUE) { 47 | 48 | // Say we took mutex 2 49 | Serial.println("Task A took mutex 2"); 50 | 51 | // Critical section protected by 2 mutexes 52 | Serial.println("Task A doing some work"); 53 | vTaskDelay(500 / portTICK_PERIOD_MS); 54 | } else { 55 | Serial.println("Task A timed out waiting for mutex 2"); 56 | } 57 | } else { 58 | Serial.println("Task A timed out waiting for mutex 1"); 59 | } 60 | 61 | // Give back mutexes 62 | xSemaphoreGive(mutex_2); 63 | xSemaphoreGive(mutex_1); 64 | 65 | // Wait to let the other task execute 66 | Serial.println("Task A going to sleep"); 67 | vTaskDelay(500 / portTICK_PERIOD_MS); 68 | } 69 | } 70 | 71 | // Task B (low priority) 72 | void doTaskB(void *parameters) { 73 | 74 | // Loop forever 75 | while (1) { 76 | 77 | // Take mutex 2 78 | if (xSemaphoreTake(mutex_2, mutex_timeout) == pdTRUE) { 79 | 80 | // Say we took mutex 2 and wait (to force deadlock) 81 | Serial.println("Task B took mutex 2"); 82 | vTaskDelay(1 / portTICK_PERIOD_MS); 83 | 84 | // Take mutex 1 85 | if (xSemaphoreTake(mutex_1, mutex_timeout) == pdTRUE) { 86 | 87 | // Say we took mutex 1 88 | Serial.println("Task B took mutex 1"); 89 | 90 | // Critical section protected by 2 mutexes 91 | Serial.println("Task B doing some work"); 92 | vTaskDelay(500 / portTICK_PERIOD_MS); 93 | } else { 94 | Serial.println("Task B timed out waiting for mutex 1"); 95 | } 96 | } else { 97 | Serial.println("Task B timed out waiting for mutex 2"); 98 | } 99 | 100 | // Give back mutexes 101 | xSemaphoreGive(mutex_1); 102 | xSemaphoreGive(mutex_2); 103 | 104 | // Wait to let the other task execute 105 | Serial.println("Task B going to sleep"); 106 | vTaskDelay(500 / portTICK_PERIOD_MS); 107 | } 108 | } 109 | 110 | //***************************************************************************** 111 | // Main (runs as its own task with priority 1 on core 1) 112 | 113 | void setup() { 114 | 115 | // Configure Serial 116 | Serial.begin(115200); 117 | 118 | // Wait a moment to start (so we don't miss Serial output) 119 | vTaskDelay(1000 / portTICK_PERIOD_MS); 120 | Serial.println(); 121 | Serial.println("---FreeRTOS Deadlock Demo---"); 122 | 123 | // Create mutexes before starting tasks 124 | mutex_1 = xSemaphoreCreateMutex(); 125 | mutex_2 = xSemaphoreCreateMutex(); 126 | 127 | // Start Task A (high priority) 128 | xTaskCreatePinnedToCore(doTaskA, 129 | "Task A", 130 | 1024, 131 | NULL, 132 | 2, 133 | NULL, 134 | app_cpu); 135 | 136 | // Start Task B (low priority) 137 | xTaskCreatePinnedToCore(doTaskB, 138 | "Task B", 139 | 1024, 140 | NULL, 141 | 1, 142 | NULL, 143 | app_cpu); 144 | 145 | // Delete "setup and loop" task 146 | vTaskDelete(NULL); 147 | } 148 | 149 | void loop() { 150 | // Execution should never get here 151 | } 152 | -------------------------------------------------------------------------------- /10-deadlock/esp32-freertos-10-demo-deadlock/esp32-freertos-10-demo-deadlock.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Deadlock Demo 3 | * 4 | * Demonstrate deadlock with 2 tasks. 5 | * 6 | * Date: February 8, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // You'll likely need this on vanilla FreeRTOS 12 | //#include 13 | 14 | // Use only core 1 for demo purposes 15 | #if CONFIG_FREERTOS_UNICORE 16 | static const BaseType_t app_cpu = 0; 17 | #else 18 | static const BaseType_t app_cpu = 1; 19 | #endif 20 | 21 | // Globals 22 | static SemaphoreHandle_t mutex_1; 23 | static SemaphoreHandle_t mutex_2; 24 | 25 | //***************************************************************************** 26 | // Tasks 27 | 28 | // Task A (high priority) 29 | void doTaskA(void *parameters) { 30 | 31 | // Loop forever 32 | while (1) { 33 | 34 | // Take mutex 1 (introduce wait to force deadlock) 35 | xSemaphoreTake(mutex_1, portMAX_DELAY); 36 | Serial.println("Task A took mutex 1"); 37 | vTaskDelay(1 / portTICK_PERIOD_MS); 38 | 39 | // Take mutex 2 40 | xSemaphoreTake(mutex_2, portMAX_DELAY); 41 | Serial.println("Task A took mutex 2"); 42 | 43 | // Critical section protected by 2 mutexes 44 | Serial.println("Task A doing some work"); 45 | vTaskDelay(500 / portTICK_PERIOD_MS); 46 | 47 | // Give back mutexes 48 | xSemaphoreGive(mutex_2); 49 | xSemaphoreGive(mutex_1); 50 | 51 | // Wait to let the other task execute 52 | Serial.println("Task A going to sleep"); 53 | vTaskDelay(500 / portTICK_PERIOD_MS); 54 | } 55 | } 56 | 57 | // Task B (low priority) 58 | void doTaskB(void *parameters) { 59 | 60 | // Loop forever 61 | while (1) { 62 | 63 | // Take mutex 2 (introduce wait to force deadlock) 64 | xSemaphoreTake(mutex_2, portMAX_DELAY); 65 | Serial.println("Task B took mutex 2"); 66 | vTaskDelay(1 / portTICK_PERIOD_MS); 67 | 68 | // Take mutex 1 69 | xSemaphoreTake(mutex_1, portMAX_DELAY); 70 | Serial.println("Task B took mutex 1"); 71 | 72 | // Critical section protected by 2 mutexes 73 | Serial.println("Task B doing some work"); 74 | vTaskDelay(500 / portTICK_PERIOD_MS); 75 | 76 | // Give back mutexes 77 | xSemaphoreGive(mutex_1); 78 | xSemaphoreGive(mutex_2); 79 | 80 | // Wait to let the other task execute 81 | Serial.println("Task A going to sleep"); 82 | vTaskDelay(500 / portTICK_PERIOD_MS); 83 | } 84 | } 85 | 86 | //***************************************************************************** 87 | // Main (runs as its own task with priority 1 on core 1) 88 | 89 | void setup() { 90 | 91 | // Configure Serial 92 | Serial.begin(115200); 93 | 94 | // Wait a moment to start (so we don't miss Serial output) 95 | vTaskDelay(1000 / portTICK_PERIOD_MS); 96 | Serial.println(); 97 | Serial.println("---FreeRTOS Deadlock Demo---"); 98 | 99 | // Create mutexes before starting tasks 100 | mutex_1 = xSemaphoreCreateMutex(); 101 | mutex_2 = xSemaphoreCreateMutex(); 102 | 103 | // Start Task A (high priority) 104 | xTaskCreatePinnedToCore(doTaskA, 105 | "Task A", 106 | 1024, 107 | NULL, 108 | 2, 109 | NULL, 110 | app_cpu); 111 | 112 | // Start Task B (low priority) 113 | xTaskCreatePinnedToCore(doTaskB, 114 | "Task B", 115 | 1024, 116 | NULL, 117 | 1, 118 | NULL, 119 | app_cpu); 120 | 121 | // Delete "setup and loop" task 122 | vTaskDelete(NULL); 123 | } 124 | 125 | void loop() { 126 | // Execution should never get here 127 | } 128 | -------------------------------------------------------------------------------- /10-deadlock/esp32-freertos-10-solution-dining-philosophers-arbitrator/esp32-freertos-10-solution-dining-philosophers-arbitrator.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Dining Philosophers Solution: Arbitrator/Waiter 3 | * 4 | * One possible solution to the Dining Philosophers problem. 5 | * 6 | * Based on http://www.cs.virginia.edu/luther/COA2/S2019/pa05-dp.html 7 | * 8 | * Date: February 8, 2021 9 | * Author: Shawn Hymel 10 | * License: 0BSD 11 | */ 12 | 13 | // You'll likely need this on vanilla FreeRTOS 14 | //#include 15 | 16 | // Use only core 1 for demo purposes 17 | #if CONFIG_FREERTOS_UNICORE 18 | static const BaseType_t app_cpu = 0; 19 | #else 20 | static const BaseType_t app_cpu = 1; 21 | #endif 22 | 23 | // Settings 24 | enum { NUM_TASKS = 5 }; // Number of tasks (philosophers) 25 | enum { TASK_STACK_SIZE = 2048 }; // Bytes in ESP32, words in vanilla FreeRTOS 26 | 27 | // Globals 28 | static SemaphoreHandle_t bin_sem; // Wait for parameters to be read 29 | static SemaphoreHandle_t done_sem; // Notifies main task when done 30 | static SemaphoreHandle_t chopstick[NUM_TASKS]; 31 | static SemaphoreHandle_t arbitrator; // Controls who eats when (e.g. waiter) 32 | 33 | //***************************************************************************** 34 | // Tasks 35 | 36 | // The only task: eating 37 | void eat(void *parameters) { 38 | 39 | int num; 40 | char buf[50]; 41 | 42 | // Copy parameter and increment semaphore count 43 | num = *(int *)parameters; 44 | xSemaphoreGive(bin_sem); 45 | 46 | // We have to wait our turn to eat 47 | xSemaphoreTake(arbitrator, portMAX_DELAY); 48 | 49 | // Take left chopstick 50 | xSemaphoreTake(chopstick[num], portMAX_DELAY); 51 | sprintf(buf, "Philosopher %i took chopstick %i", num, num); 52 | Serial.println(buf); 53 | 54 | // Add some delay to force deadlock 55 | vTaskDelay(1 / portTICK_PERIOD_MS); 56 | 57 | // Take right chopstick 58 | xSemaphoreTake(chopstick[(num+1)%NUM_TASKS], portMAX_DELAY); 59 | sprintf(buf, "Philosopher %i took chopstick %i", num, (num+1)%NUM_TASKS); 60 | Serial.println(buf); 61 | 62 | // Do some eating 63 | sprintf(buf, "Philosopher %i is eating", num); 64 | Serial.println(buf); 65 | vTaskDelay(10 / portTICK_PERIOD_MS); 66 | 67 | // Put down right chopstick 68 | xSemaphoreGive(chopstick[(num+1)%NUM_TASKS]); 69 | sprintf(buf, "Philosopher %i returned chopstick %i", num, (num+1)%NUM_TASKS); 70 | Serial.println(buf); 71 | 72 | // Put down left chopstick 73 | xSemaphoreGive(chopstick[num]); 74 | sprintf(buf, "Philosopher %i returned chopstick %i", num, num); 75 | Serial.println(buf); 76 | 77 | // Tell the arbitrator (waiter) that we're done 78 | xSemaphoreGive(arbitrator); 79 | 80 | // Notify main task and delete self 81 | xSemaphoreGive(done_sem); 82 | vTaskDelete(NULL); 83 | } 84 | 85 | //***************************************************************************** 86 | // Main (runs as its own task with priority 1 on core 1) 87 | 88 | void setup() { 89 | 90 | char task_name[20]; 91 | 92 | // Configure Serial 93 | Serial.begin(115200); 94 | 95 | // Wait a moment to start (so we don't miss Serial output) 96 | vTaskDelay(1000 / portTICK_PERIOD_MS); 97 | Serial.println(); 98 | Serial.println("---FreeRTOS Dining Philosophers Arbitrator Solution---"); 99 | 100 | // Create kernel objects before starting tasks 101 | bin_sem = xSemaphoreCreateBinary(); 102 | done_sem = xSemaphoreCreateCounting(NUM_TASKS, 0); 103 | for (int i = 0; i < NUM_TASKS; i++) { 104 | chopstick[i] = xSemaphoreCreateMutex(); 105 | } 106 | arbitrator = xSemaphoreCreateMutex(); 107 | 108 | // Have the philosphers start eating 109 | for (int i = 0; i < NUM_TASKS; i++) { 110 | sprintf(task_name, "Philosopher %i", i); 111 | xTaskCreatePinnedToCore(eat, 112 | task_name, 113 | TASK_STACK_SIZE, 114 | (void *)&i, 115 | 1, 116 | NULL, 117 | app_cpu); 118 | xSemaphoreTake(bin_sem, portMAX_DELAY); 119 | } 120 | 121 | 122 | // Wait until all the philosophers are done 123 | for (int i = 0; i < NUM_TASKS; i++) { 124 | xSemaphoreTake(done_sem, portMAX_DELAY); 125 | } 126 | 127 | // Say that we made it through without deadlock 128 | Serial.println("Done! No deadlock occurred!"); 129 | } 130 | 131 | void loop() { 132 | // Do nothing in this task 133 | } 134 | -------------------------------------------------------------------------------- /10-deadlock/esp32-freertos-10-solution-dining-philosophers-hierarchy/esp32-freertos-10-solution-dining-philosophers-hierarchy.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Dining Philosophers Solution: Hierarchy 3 | * 4 | * One possible solution to the Dining Philosophers problem. 5 | * 6 | * Based on http://www.cs.virginia.edu/luther/COA2/S2019/pa05-dp.html 7 | * 8 | * Date: February 8, 2021 9 | * Author: Shawn Hymel 10 | * License: 0BSD 11 | */ 12 | 13 | // You'll likely need this on vanilla FreeRTOS 14 | //#include 15 | 16 | // Use only core 1 for demo purposes 17 | #if CONFIG_FREERTOS_UNICORE 18 | static const BaseType_t app_cpu = 0; 19 | #else 20 | static const BaseType_t app_cpu = 1; 21 | #endif 22 | 23 | // Settings 24 | enum { NUM_TASKS = 5 }; // Number of tasks (philosophers) 25 | enum { TASK_STACK_SIZE = 2048 }; // Bytes in ESP32, words in vanilla FreeRTOS 26 | 27 | // Globals 28 | static SemaphoreHandle_t bin_sem; // Wait for parameters to be read 29 | static SemaphoreHandle_t done_sem; // Notifies main task when done 30 | static SemaphoreHandle_t chopstick[NUM_TASKS]; 31 | 32 | //***************************************************************************** 33 | // Tasks 34 | 35 | // The only task: eating 36 | void eat(void *parameters) { 37 | 38 | int num; 39 | char buf[50]; 40 | int idx_1; 41 | int idx_2; 42 | 43 | // Copy parameter and increment semaphore count 44 | num = *(int *)parameters; 45 | xSemaphoreGive(bin_sem); 46 | 47 | // Assign priority: pick up lower-numbered chopstick first 48 | if (num < (num + 1) % NUM_TASKS) { 49 | idx_1 = num; 50 | idx_2 = (num + 1) % NUM_TASKS; 51 | } else { 52 | idx_1 = (num + 1) % NUM_TASKS; 53 | idx_2 = num; 54 | } 55 | 56 | // Take lower-numbered chopstick 57 | xSemaphoreTake(chopstick[idx_1], portMAX_DELAY); 58 | sprintf(buf, "Philosopher %i took chopstick %i", num, num); 59 | Serial.println(buf); 60 | 61 | // Add some delay to force deadlock 62 | vTaskDelay(1 / portTICK_PERIOD_MS); 63 | 64 | // Take higher-numbered chopstick 65 | xSemaphoreTake(chopstick[idx_2], portMAX_DELAY); 66 | sprintf(buf, "Philosopher %i took chopstick %i", num, (num+1)%NUM_TASKS); 67 | Serial.println(buf); 68 | 69 | // Do some eating 70 | sprintf(buf, "Philosopher %i is eating", num); 71 | Serial.println(buf); 72 | vTaskDelay(10 / portTICK_PERIOD_MS); 73 | 74 | // Put down higher-numbered chopstick 75 | xSemaphoreGive(chopstick[idx_2]); 76 | sprintf(buf, "Philosopher %i returned chopstick %i", num, (num+1)%NUM_TASKS); 77 | Serial.println(buf); 78 | 79 | // Put down lower-numbered chopstick 80 | xSemaphoreGive(chopstick[idx_1]); 81 | sprintf(buf, "Philosopher %i returned chopstick %i", num, num); 82 | Serial.println(buf); 83 | 84 | // Notify main task and delete self 85 | xSemaphoreGive(done_sem); 86 | vTaskDelete(NULL); 87 | } 88 | 89 | //***************************************************************************** 90 | // Main (runs as its own task with priority 1 on core 1) 91 | 92 | void setup() { 93 | 94 | char task_name[20]; 95 | 96 | // Configure Serial 97 | Serial.begin(115200); 98 | 99 | // Wait a moment to start (so we don't miss Serial output) 100 | vTaskDelay(1000 / portTICK_PERIOD_MS); 101 | Serial.println(); 102 | Serial.println("---FreeRTOS Dining Philosophers Hierarchy Solution---"); 103 | 104 | // Create kernel objects before starting tasks 105 | bin_sem = xSemaphoreCreateBinary(); 106 | done_sem = xSemaphoreCreateCounting(NUM_TASKS, 0); 107 | for (int i = 0; i < NUM_TASKS; i++) { 108 | chopstick[i] = xSemaphoreCreateMutex(); 109 | } 110 | 111 | // Have the philosphers start eating 112 | for (int i = 0; i < NUM_TASKS; i++) { 113 | sprintf(task_name, "Philosopher %i", i); 114 | xTaskCreatePinnedToCore(eat, 115 | task_name, 116 | TASK_STACK_SIZE, 117 | (void *)&i, 118 | 1, 119 | NULL, 120 | app_cpu); 121 | xSemaphoreTake(bin_sem, portMAX_DELAY); 122 | } 123 | 124 | 125 | // Wait until all the philosophers are done 126 | for (int i = 0; i < NUM_TASKS; i++) { 127 | xSemaphoreTake(done_sem, portMAX_DELAY); 128 | } 129 | 130 | // Say that we made it through without deadlock 131 | Serial.println("Done! No deadlock occurred!"); 132 | } 133 | 134 | void loop() { 135 | // Do nothing in this task 136 | } 137 | -------------------------------------------------------------------------------- /10-deadlock/rtos-part-10.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasir-shahzad/introduction-to-rtos/f2ae37914fa61eca4f7f9e7e11a6895fde7e6cf3/10-deadlock/rtos-part-10.pptx -------------------------------------------------------------------------------- /11-priority-inversion/esp32-freertos-11-demo-priority-inheritance/esp32-freertos-11-demo-priority-inheritance.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Priority Inheritance Demo 3 | * 4 | * Demonstrate priority inheritance. 5 | * 6 | * Date: February 12, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // You'll likely need this on vanilla FreeRTOS 12 | //#include 13 | 14 | // Use only core 1 for demo purposes 15 | #if CONFIG_FREERTOS_UNICORE 16 | static const BaseType_t app_cpu = 0; 17 | #else 18 | static const BaseType_t app_cpu = 1; 19 | #endif 20 | 21 | // Settings 22 | TickType_t cs_wait = 250; // Time spent in critical section (ms) 23 | TickType_t med_wait = 5000; // Time medium task spends working (ms) 24 | 25 | // Globals 26 | static SemaphoreHandle_t lock; 27 | 28 | //***************************************************************************** 29 | // Tasks 30 | 31 | // Task L (low priority) 32 | void doTaskL(void *parameters) { 33 | 34 | TickType_t timestamp; 35 | 36 | // Do forever 37 | while (1) { 38 | 39 | // Take lock 40 | Serial.println("Task L trying to take lock..."); 41 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 42 | xSemaphoreTake(lock, portMAX_DELAY); 43 | 44 | // Say how long we spend waiting for a lock 45 | Serial.print("Task L got lock. Spent "); 46 | Serial.print((xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp); 47 | Serial.println(" ms waiting for lock. Doing some work..."); 48 | 49 | // Hog the processor for a while doing nothing 50 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 51 | while ( (xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp < cs_wait); 52 | 53 | // Release lock 54 | Serial.println("Task L releasing lock."); 55 | xSemaphoreGive(lock); 56 | 57 | // Go to sleep 58 | vTaskDelay(500 / portTICK_PERIOD_MS); 59 | } 60 | } 61 | 62 | // Task M (medium priority) 63 | void doTaskM(void *parameters) { 64 | 65 | TickType_t timestamp; 66 | 67 | // Do forever 68 | while (1) { 69 | 70 | // Hog the processor for a while doing nothing 71 | Serial.println("Task M doing some work..."); 72 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 73 | while ( (xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp < med_wait); 74 | 75 | // Go to sleep 76 | Serial.println("Task M done!"); 77 | vTaskDelay(500 / portTICK_PERIOD_MS); 78 | } 79 | } 80 | 81 | // Task H (high priority) 82 | void doTaskH(void *parameters) { 83 | 84 | TickType_t timestamp; 85 | 86 | // Do forever 87 | while (1) { 88 | 89 | // Take lock 90 | Serial.println("Task H trying to take lock..."); 91 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 92 | xSemaphoreTake(lock, portMAX_DELAY); 93 | 94 | // Say how long we spend waiting for a lock 95 | Serial.print("Task H got lock. Spent "); 96 | Serial.print((xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp); 97 | Serial.println(" ms waiting for lock. Doing some work..."); 98 | 99 | // Hog the processor for a while doing nothing 100 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 101 | while ( (xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp < cs_wait); 102 | 103 | // Release lock 104 | Serial.println("Task H releasing lock."); 105 | xSemaphoreGive(lock); 106 | 107 | // Go to sleep 108 | vTaskDelay(500 / portTICK_PERIOD_MS); 109 | } 110 | } 111 | 112 | //***************************************************************************** 113 | // Main (runs as its own task with priority 1 on core 1) 114 | 115 | void setup() { 116 | 117 | // Configure Serial 118 | Serial.begin(115200); 119 | 120 | // Wait a moment to start (so we don't miss Serial output) 121 | vTaskDelay(1000 / portTICK_PERIOD_MS); 122 | Serial.println(); 123 | Serial.println("---FreeRTOS Priority Inheritance Demo---"); 124 | 125 | // Create semaphores and mutexes before starting tasks 126 | lock = xSemaphoreCreateMutex(); 127 | 128 | // The order of starting the tasks matters to force priority inversion 129 | 130 | // Start Task L (low priority) 131 | xTaskCreatePinnedToCore(doTaskL, 132 | "Task L", 133 | 1024, 134 | NULL, 135 | 1, 136 | NULL, 137 | app_cpu); 138 | 139 | // Introduce a delay to force priority inversion 140 | vTaskDelay(1 / portTICK_PERIOD_MS); 141 | 142 | // Start Task H (high priority) 143 | xTaskCreatePinnedToCore(doTaskH, 144 | "Task H", 145 | 1024, 146 | NULL, 147 | 3, 148 | NULL, 149 | app_cpu); 150 | 151 | // Start Task M (medium priority) 152 | xTaskCreatePinnedToCore(doTaskM, 153 | "Task M", 154 | 1024, 155 | NULL, 156 | 2, 157 | NULL, 158 | app_cpu); 159 | 160 | // Delete "setup and loop" task 161 | vTaskDelete(NULL); 162 | } 163 | 164 | void loop() { 165 | // Execution should never get here 166 | } 167 | -------------------------------------------------------------------------------- /11-priority-inversion/esp32-freertos-11-demo-priority-inversion/esp32-freertos-11-demo-priority-inversion.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Priority Inversion Demo 3 | * 4 | * Demonstrate priority inversion. 5 | * 6 | * Date: February 12, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // You'll likely need this on vanilla FreeRTOS 12 | //#include 13 | 14 | // Use only core 1 for demo purposes 15 | #if CONFIG_FREERTOS_UNICORE 16 | static const BaseType_t app_cpu = 0; 17 | #else 18 | static const BaseType_t app_cpu = 1; 19 | #endif 20 | 21 | // Settings 22 | TickType_t cs_wait = 250; // Time spent in critical section (ms) 23 | TickType_t med_wait = 5000; // Time medium task spends working (ms) 24 | 25 | // Globals 26 | static SemaphoreHandle_t lock; 27 | 28 | //***************************************************************************** 29 | // Tasks 30 | 31 | // Task L (low priority) 32 | void doTaskL(void *parameters) { 33 | 34 | TickType_t timestamp; 35 | 36 | // Do forever 37 | while (1) { 38 | 39 | // Take lock 40 | Serial.println("Task L trying to take lock..."); 41 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 42 | xSemaphoreTake(lock, portMAX_DELAY); 43 | 44 | // Say how long we spend waiting for a lock 45 | Serial.print("Task L got lock. Spent "); 46 | Serial.print((xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp); 47 | Serial.println(" ms waiting for lock. Doing some work..."); 48 | 49 | // Hog the processor for a while doing nothing 50 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 51 | while ( (xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp < cs_wait); 52 | 53 | // Release lock 54 | Serial.println("Task L releasing lock."); 55 | xSemaphoreGive(lock); 56 | 57 | // Go to sleep 58 | vTaskDelay(500 / portTICK_PERIOD_MS); 59 | } 60 | } 61 | 62 | // Task M (medium priority) 63 | void doTaskM(void *parameters) { 64 | 65 | TickType_t timestamp; 66 | 67 | // Do forever 68 | while (1) { 69 | 70 | // Hog the processor for a while doing nothing 71 | Serial.println("Task M doing some work..."); 72 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 73 | while ( (xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp < med_wait); 74 | 75 | // Go to sleep 76 | Serial.println("Task M done!"); 77 | vTaskDelay(500 / portTICK_PERIOD_MS); 78 | } 79 | } 80 | 81 | // Task H (high priority) 82 | void doTaskH(void *parameters) { 83 | 84 | TickType_t timestamp; 85 | 86 | // Do forever 87 | while (1) { 88 | 89 | // Take lock 90 | Serial.println("Task H trying to take lock..."); 91 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 92 | xSemaphoreTake(lock, portMAX_DELAY); 93 | 94 | // Say how long we spend waiting for a lock 95 | Serial.print("Task H got lock. Spent "); 96 | Serial.print((xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp); 97 | Serial.println(" ms waiting for lock. Doing some work..."); 98 | 99 | // Hog the processor for a while doing nothing 100 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 101 | while ( (xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp < cs_wait); 102 | 103 | // Release lock 104 | Serial.println("Task H releasing lock."); 105 | xSemaphoreGive(lock); 106 | 107 | // Go to sleep 108 | vTaskDelay(500 / portTICK_PERIOD_MS); 109 | } 110 | } 111 | 112 | //***************************************************************************** 113 | // Main (runs as its own task with priority 1 on core 1) 114 | 115 | void setup() { 116 | 117 | // Configure Serial 118 | Serial.begin(115200); 119 | 120 | // Wait a moment to start (so we don't miss Serial output) 121 | vTaskDelay(1000 / portTICK_PERIOD_MS); 122 | Serial.println(); 123 | Serial.println("---FreeRTOS Priority Inversion Demo---"); 124 | 125 | // Create semaphores and mutexes before starting tasks 126 | lock = xSemaphoreCreateBinary(); 127 | xSemaphoreGive(lock); // Make sure binary semaphore starts at 1 128 | 129 | // The order of starting the tasks matters to force priority inversion 130 | 131 | // Start Task L (low priority) 132 | xTaskCreatePinnedToCore(doTaskL, 133 | "Task L", 134 | 1024, 135 | NULL, 136 | 1, 137 | NULL, 138 | app_cpu); 139 | 140 | // Introduce a delay to force priority inversion 141 | vTaskDelay(1 / portTICK_PERIOD_MS); 142 | 143 | // Start Task H (high priority) 144 | xTaskCreatePinnedToCore(doTaskH, 145 | "Task H", 146 | 1024, 147 | NULL, 148 | 3, 149 | NULL, 150 | app_cpu); 151 | 152 | // Start Task M (medium priority) 153 | xTaskCreatePinnedToCore(doTaskM, 154 | "Task M", 155 | 1024, 156 | NULL, 157 | 2, 158 | NULL, 159 | app_cpu); 160 | 161 | // Delete "setup and loop" task 162 | vTaskDelete(NULL); 163 | } 164 | 165 | void loop() { 166 | // Execution should never get here 167 | } 168 | -------------------------------------------------------------------------------- /11-priority-inversion/esp32-freertos-11-solution-critical-section/esp32-freertos-11-solution-critical-section.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Critical Section Demo 3 | * 4 | * Demonstrate how priority inversion can be avoided through the use of 5 | * critical sections. 6 | * 7 | * Date: February 12, 2021 8 | * Author: Shawn Hymel 9 | * License: 0BSD 10 | */ 11 | 12 | // You'll likely need this on vanilla FreeRTOS 13 | //#include 14 | 15 | // Use only core 1 for demo purposes 16 | #if CONFIG_FREERTOS_UNICORE 17 | static const BaseType_t app_cpu = 0; 18 | #else 19 | static const BaseType_t app_cpu = 1; 20 | #endif 21 | 22 | // Settings 23 | TickType_t cs_wait = 250; // Time spent in critical section (ms) 24 | TickType_t med_wait = 5000; // Time medium task spends working (ms) 25 | 26 | // Globals 27 | static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; 28 | 29 | //***************************************************************************** 30 | // Tasks 31 | 32 | // Task L (low priority) 33 | void doTaskL(void *parameters) { 34 | 35 | TickType_t timestamp; 36 | 37 | // Do forever 38 | while (1) { 39 | 40 | // Take lock 41 | Serial.println("Task L trying to take lock..."); 42 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 43 | portENTER_CRITICAL(&spinlock); // taskENTER_CRITICAL() in vanilla FreeRTOS 44 | 45 | // Say how long we spend waiting for a lock 46 | Serial.print("Task L got lock. Spent "); 47 | Serial.print((xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp); 48 | Serial.println(" ms waiting for lock. Doing some work..."); 49 | 50 | // Hog the processor for a while doing nothing 51 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 52 | while ( (xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp < cs_wait); 53 | 54 | // Release lock 55 | Serial.println("Task L releasing lock."); 56 | portEXIT_CRITICAL(&spinlock); // taskEXIT_CRITICAL() in vanilla FreeRTOS 57 | 58 | // Go to sleep 59 | vTaskDelay(500 / portTICK_PERIOD_MS); 60 | } 61 | } 62 | 63 | // Task M (medium priority) 64 | void doTaskM(void *parameters) { 65 | 66 | TickType_t timestamp; 67 | 68 | // Do forever 69 | while (1) { 70 | 71 | // Hog the processor for a while doing nothing 72 | Serial.println("Task M doing some work..."); 73 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 74 | while ( (xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp < med_wait); 75 | 76 | // Go to sleep 77 | Serial.println("Task M done!"); 78 | vTaskDelay(500 / portTICK_PERIOD_MS); 79 | } 80 | } 81 | 82 | // Task H (high priority) 83 | void doTaskH(void *parameters) { 84 | 85 | TickType_t timestamp; 86 | 87 | // Do forever 88 | while (1) { 89 | 90 | // Take lock 91 | Serial.println("Task H trying to take lock..."); 92 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 93 | portENTER_CRITICAL(&spinlock); // taskENTER_CRITICAL() in vanilla FreeRTOS 94 | 95 | // Say how long we spend waiting for a lock 96 | Serial.print("Task H got lock. Spent "); 97 | Serial.print((xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp); 98 | Serial.println(" ms waiting for lock. Doing some work..."); 99 | 100 | // Hog the processor for a while doing nothing 101 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 102 | while ( (xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp < cs_wait); 103 | 104 | // Release lock 105 | Serial.println("Task H releasing lock."); 106 | portEXIT_CRITICAL(&spinlock); // taskEXIT_CRITICAL() in vanilla FreeRTOS 107 | 108 | // Go to sleep 109 | vTaskDelay(500 / portTICK_PERIOD_MS); 110 | } 111 | } 112 | 113 | //***************************************************************************** 114 | // Main (runs as its own task with priority 1 on core 1) 115 | 116 | void setup() { 117 | 118 | // Configure Serial 119 | Serial.begin(115200); 120 | 121 | // Wait a moment to start (so we don't miss Serial output) 122 | vTaskDelay(1000 / portTICK_PERIOD_MS); 123 | Serial.println(); 124 | Serial.println("---FreeRTOS Critical Section Demo---"); 125 | 126 | // The order of starting the tasks matters to force priority inversion 127 | 128 | // Start Task L (low priority) 129 | xTaskCreatePinnedToCore(doTaskL, 130 | "Task L", 131 | 1024, 132 | NULL, 133 | 1, 134 | NULL, 135 | app_cpu); 136 | 137 | // Introduce a delay to force priority inversion 138 | vTaskDelay(1 / portTICK_PERIOD_MS); 139 | 140 | // Start Task H (high priority) 141 | xTaskCreatePinnedToCore(doTaskH, 142 | "Task H", 143 | 1024, 144 | NULL, 145 | 3, 146 | NULL, 147 | app_cpu); 148 | 149 | // Start Task M (medium priority) 150 | xTaskCreatePinnedToCore(doTaskM, 151 | "Task M", 152 | 1024, 153 | NULL, 154 | 2, 155 | NULL, 156 | app_cpu); 157 | 158 | // Delete "setup and loop" task 159 | vTaskDelete(NULL); 160 | } 161 | 162 | void loop() { 163 | // Execution should never get here 164 | } 165 | -------------------------------------------------------------------------------- /11-priority-inversion/rtos-part-11.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasir-shahzad/introduction-to-rtos/f2ae37914fa61eca4f7f9e7e11a6895fde7e6cf3/11-priority-inversion/rtos-part-11.pptx -------------------------------------------------------------------------------- /12-multicore/esp32-freertos-12-demo-multicore-isr/esp32-freertos-12-demo-multicore-isr.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Multicore ISR Demo 3 | * 4 | * Demonstration of critical sections and ISRs with multicore processor. 5 | * 6 | * Date: March 3, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // Core definitions (assuming you have dual-core ESP32) 12 | static const BaseType_t pro_cpu = 0; 13 | static const BaseType_t app_cpu = 1; 14 | 15 | // Settings 16 | static const uint16_t timer_divider = 80; // 1 MHz hardware timer 17 | static const uint64_t timer_max_count = 100000; // 1 second ISR interval 18 | static const TickType_t task_wait = 10; // Time (ms) hogging the CPU 19 | static const TickType_t isr_wait = 20; // Time (ms) hogging the CPU 20 | 21 | // Globals 22 | static SemaphoreHandle_t bin_sem; 23 | static hw_timer_t *timer = NULL; 24 | static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; 25 | 26 | //***************************************************************************** 27 | // Interrupt Service Routines (ISRs) 28 | 29 | // This function executes when timer reaches max (and resets) 30 | void IRAM_ATTR onTimer() { 31 | 32 | TickType_t timestamp; 33 | 34 | // Hog the CPU in the ISR (remember: this is a terrible idea) 35 | Serial.print("ISR..."); 36 | portENTER_CRITICAL_ISR(&spinlock); 37 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 38 | while ((xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp < isr_wait); 39 | portEXIT_CRITICAL_ISR(&spinlock); 40 | Serial.println("Done"); 41 | } 42 | 43 | //***************************************************************************** 44 | // Tasks 45 | 46 | // Task L (low priority) 47 | void doTaskL(void *parameters) { 48 | 49 | TickType_t timestamp; 50 | char str[20]; 51 | 52 | // Do forever 53 | while (1) { 54 | 55 | // Say something 56 | Serial.println("L"); 57 | 58 | // Hog the processor for a while doing nothing 59 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 60 | while ((xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp < task_wait); 61 | } 62 | } 63 | 64 | // Task H (high priority) 65 | void doTaskH(void *parameters) { 66 | 67 | TickType_t timestamp; 68 | char str[20]; 69 | 70 | // Do forever 71 | while (1) { 72 | 73 | // Say something 74 | Serial.print("spinning..."); 75 | portENTER_CRITICAL(&spinlock); 76 | Serial.println("H"); 77 | portEXIT_CRITICAL(&spinlock); 78 | 79 | // Hog the processor for a while doing nothing 80 | timestamp = xTaskGetTickCount() * portTICK_PERIOD_MS; 81 | while ((xTaskGetTickCount() * portTICK_PERIOD_MS) - timestamp < task_wait); 82 | } 83 | } 84 | 85 | //***************************************************************************** 86 | // Main (runs as its own task with priority 1 on core 1) 87 | 88 | void setup() { 89 | 90 | // Configure Serial 91 | Serial.begin(115200); 92 | 93 | // Wait a moment to start (so we don't miss Serial output) 94 | vTaskDelay(1000 / portTICK_PERIOD_MS); 95 | Serial.println(); 96 | Serial.println("---FreeRTOS Priority Inheritance Demo---"); 97 | 98 | // Hardware timer (core 1) triggers ISR every 2 seconds 99 | timer = timerBegin(0, timer_divider, true); 100 | timerAttachInterrupt(timer, &onTimer, true); 101 | timerAlarmWrite(timer, timer_max_count, true); 102 | timerAlarmEnable(timer); 103 | 104 | // Start Task L (low priority) 105 | xTaskCreatePinnedToCore(doTaskL, 106 | "Task L", 107 | 1024, 108 | NULL, 109 | 1, 110 | NULL, 111 | app_cpu); 112 | 113 | // Start Task H (low priority) 114 | xTaskCreatePinnedToCore(doTaskH, 115 | "Task H", 116 | 1024, 117 | NULL, 118 | 2, 119 | NULL, 120 | pro_cpu); 121 | 122 | // Delete "setup and loop" task 123 | vTaskDelete(NULL); 124 | } 125 | 126 | void loop() { 127 | // Execution should never get here 128 | } 129 | -------------------------------------------------------------------------------- /12-multicore/esp32-freertos-12-demo-multicore-semaphore/esp32-freertos-12-demo-multicore-semaphore.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Multicore Semaphore Demo 3 | * 4 | * How to use semaphores with multicore tasks. 5 | * 6 | * Date: March 3, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // Core definitions (assuming you have dual-core ESP32) 12 | static const BaseType_t pro_cpu = 0; 13 | static const BaseType_t app_cpu = 1; 14 | 15 | // Settings 16 | static const uint32_t task_0_delay = 500; // Time (ms) Task 0 blocks itself 17 | 18 | // Pins 19 | static const int pin_1 = 13; // LED pin 20 | 21 | // Globals 22 | static SemaphoreHandle_t bin_sem; 23 | 24 | //***************************************************************************** 25 | // Tasks 26 | 27 | // Task in Core 0 28 | void doTask0(void *parameters) { 29 | 30 | // Configure pin 31 | pinMode(pin_1, OUTPUT); 32 | 33 | // Do forever 34 | while (1) { 35 | 36 | // Notify other task 37 | xSemaphoreGive(bin_sem); 38 | 39 | // Yield processor for a while 40 | vTaskDelay(task_0_delay / portTICK_PERIOD_MS); 41 | } 42 | } 43 | 44 | // Task in Core 1 45 | void doTask1(void *parameters) { 46 | 47 | // Do forever 48 | while (1) { 49 | 50 | // Wait for semaphore 51 | xSemaphoreTake(bin_sem, portMAX_DELAY); 52 | 53 | // Toggle LED 54 | digitalWrite(pin_1, !digitalRead(pin_1)); 55 | } 56 | } 57 | 58 | //***************************************************************************** 59 | // Main (runs as its own task with priority 1 on core 1) 60 | 61 | void setup() { 62 | 63 | // Create binary semaphore before starting tasks 64 | bin_sem = xSemaphoreCreateBinary(); 65 | 66 | // Start Task 0 (in Core 0) 67 | xTaskCreatePinnedToCore(doTask0, 68 | "Task 0", 69 | 1024, 70 | NULL, 71 | 1, 72 | NULL, 73 | pro_cpu); 74 | 75 | // Start Task 1 (in Core 1) 76 | xTaskCreatePinnedToCore(doTask1, 77 | "Task 1", 78 | 1024, 79 | NULL, 80 | 1, 81 | NULL, 82 | app_cpu); 83 | 84 | // Delete "setup and loop" task 85 | vTaskDelete(NULL); 86 | } 87 | 88 | void loop() { 89 | // Execution should never get here 90 | } 91 | -------------------------------------------------------------------------------- /12-multicore/esp32-freertos-12-demo-multicore-spinlock/esp32-freertos-12-demo-multicore-spinlock.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Multicore Spinlock Demo 3 | * 4 | * Demonstration of critical sections and ISRs with multicore processor. 5 | * 6 | * Date: March 3, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // Core definitions (assuming you have dual-core ESP32) 12 | static const BaseType_t pro_cpu = 0; 13 | static const BaseType_t app_cpu = 1; 14 | 15 | // Settings 16 | static const TickType_t time_hog = 1; // Time (ms) hogging the CPU in Task 1 17 | static const TickType_t task_0_delay = 30; // Time (ms) Task 0 blocks itself 18 | static const TickType_t task_1_delay = 100; // Time (ms) Task 1 blocks itself 19 | 20 | // Pins 21 | static const int pin_0 = 12; 22 | static const int pin_1 = 13; 23 | 24 | // Globals 25 | static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; 26 | 27 | //***************************************************************************** 28 | // Functions 29 | 30 | // Hogs the processor. Accurate to about 1 second (no promises). 31 | static void hog_delay(uint32_t ms) { 32 | for (uint32_t i = 0; i < ms; i++) { 33 | for (uint32_t j = 0; j < 40000; j++) { 34 | asm("nop"); 35 | } 36 | } 37 | } 38 | 39 | //***************************************************************************** 40 | // Tasks 41 | 42 | // Task in Core 0 43 | void doTask0(void *parameters) { 44 | 45 | // Configure pin 46 | pinMode(pin_0, OUTPUT); 47 | 48 | // Do forever 49 | while (1) { 50 | 51 | // Toggle pin 52 | portENTER_CRITICAL(&spinlock); 53 | digitalWrite(pin_0, !digitalRead(pin_0)); 54 | portEXIT_CRITICAL(&spinlock); 55 | 56 | // Yield processor for a while 57 | vTaskDelay(task_0_delay / portTICK_PERIOD_MS); 58 | } 59 | } 60 | 61 | // Task in Core 1 62 | void doTask1(void *parameters) { 63 | 64 | // Configure pin 65 | pinMode(pin_1, OUTPUT); 66 | 67 | // Do forever 68 | while (1) { 69 | 70 | // Do some long critical section (this is bad practice) 71 | portENTER_CRITICAL(&spinlock); 72 | digitalWrite(pin_1, HIGH); 73 | hog_delay(time_hog); 74 | digitalWrite(pin_1, LOW); 75 | portEXIT_CRITICAL(&spinlock); 76 | 77 | // Yield processor for a while 78 | vTaskDelay(task_1_delay / portTICK_PERIOD_MS); 79 | } 80 | } 81 | 82 | //***************************************************************************** 83 | // Main (runs as its own task with priority 1 on core 1) 84 | 85 | void setup() { 86 | 87 | // Start Task 0 (in Core 0) 88 | xTaskCreatePinnedToCore(doTask0, 89 | "Task 0", 90 | 1024, 91 | NULL, 92 | 1, 93 | NULL, 94 | pro_cpu); 95 | 96 | // Start Task 1 (in Core 1) 97 | xTaskCreatePinnedToCore(doTask1, 98 | "Task 1", 99 | 1024, 100 | NULL, 101 | 1, 102 | NULL, 103 | app_cpu); 104 | 105 | // Delete "setup and loop" task 106 | vTaskDelete(NULL); 107 | } 108 | 109 | void loop() { 110 | // Execution should never get here 111 | } 112 | -------------------------------------------------------------------------------- /12-multicore/esp32-freertos-12-demo-multicore/esp32-freertos-12-demo-multicore.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Multicore Demo 3 | * 4 | * How to run two tasks on different cores 5 | * 6 | * Date: March 3, 2021 7 | * Author: Shawn Hymel 8 | * License: 0BSD 9 | */ 10 | 11 | // Core definitions (assuming you have dual-core ESP32) 12 | static const BaseType_t pro_cpu = 0; 13 | static const BaseType_t app_cpu = 1; 14 | 15 | // Settings 16 | static const TickType_t time_hog = 200; // Time (ms) hogging the CPU 17 | 18 | //***************************************************************************** 19 | // Functions 20 | 21 | // Hogs the processor. Accurate to about 1 second (no promises). 22 | static void hog_delay(uint32_t ms) { 23 | for (uint32_t i = 0; i < ms; i++) { 24 | for (uint32_t j = 0; j < 40000; j++) { 25 | asm("nop"); 26 | } 27 | } 28 | } 29 | 30 | //***************************************************************************** 31 | // Tasks 32 | 33 | // Task L (low priority) 34 | void doTaskL(void *parameters) { 35 | 36 | TickType_t timestamp; 37 | char str[20]; 38 | 39 | // Do forever 40 | while (1) { 41 | 42 | // Say something 43 | sprintf(str, "Task L, Core %i\r\n", xPortGetCoreID()); 44 | Serial.print(str); 45 | 46 | // Hog the processor for a while doing nothing (this is a bad idea) 47 | hog_delay(time_hog); 48 | } 49 | } 50 | 51 | // Task H (high priority) 52 | void doTaskH(void *parameters) { 53 | 54 | TickType_t timestamp; 55 | char str[20]; 56 | 57 | // Do forever 58 | while (1) { 59 | 60 | // Say something 61 | sprintf(str, "Task H, Core %i\r\n", xPortGetCoreID()); 62 | Serial.print(str); 63 | 64 | // Hog the processor for a while doing nothing (this is a bad idea) 65 | hog_delay(time_hog); 66 | } 67 | } 68 | 69 | //***************************************************************************** 70 | // Main (runs as its own task with priority 1 on core 1) 71 | 72 | void setup() { 73 | 74 | // Configure Serial 75 | Serial.begin(115200); 76 | 77 | // Wait a moment to start (so we don't miss Serial output) 78 | vTaskDelay(1000 / portTICK_PERIOD_MS); 79 | Serial.println(); 80 | Serial.println("---FreeRTOS Priority Inheritance Demo---"); 81 | 82 | // Start Task L (low priority) 83 | xTaskCreatePinnedToCore(doTaskL, 84 | "Task L", 85 | 2048, 86 | NULL, 87 | 1, 88 | NULL, 89 | // pro_cpu); 90 | tskNO_AFFINITY); 91 | 92 | // Start Task H (low priority) 93 | xTaskCreatePinnedToCore(doTaskH, 94 | "Task H", 95 | 2048, 96 | NULL, 97 | 2, 98 | NULL, 99 | // pro_cpu); 100 | tskNO_AFFINITY); 101 | 102 | // Delete "setup and loop" task 103 | vTaskDelete(NULL); 104 | } 105 | 106 | void loop() { 107 | // Execution should never get here 108 | } 109 | -------------------------------------------------------------------------------- /12-multicore/esp32-freertos-12-solution-isr-sample/esp32-freertos-12-solution-isr-sample.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * ESP32 Sample and Process Solution 3 | * 4 | * Sample ADC in an ISR, process on one core, handle CLI on the other. 5 | * 6 | * Note: Changes from original, single-core version are marked with a 7 | * "%%%" in the comment. 8 | * 9 | * Date: March 3, 2021 10 | * Author: Shawn Hymel 11 | * License: 0BSD 12 | */ 13 | 14 | // You'll likely need this on vanilla FreeRTOS 15 | //#include 16 | 17 | // Core definitions (assuming you have dual-core ESP32) 18 | // %%% We can use both cores now 19 | static const BaseType_t pro_cpu = 0; 20 | static const BaseType_t app_cpu = 1; 21 | 22 | // Settings 23 | static const char command[] = "avg"; // Command 24 | static const uint16_t timer_divider = 8; // Divide 80 MHz by this 25 | static const uint64_t timer_max_count = 1000000; // Timer counts to this value 26 | static const uint32_t cli_delay = 10; // ms delay 27 | enum { BUF_LEN = 10 }; // Number of elements in sample buffer 28 | enum { MSG_LEN = 100 }; // Max characters in message body 29 | enum { MSG_QUEUE_LEN = 5 }; // Number of slots in message queue 30 | enum { CMD_BUF_LEN = 255}; // Number of characters in command buffer 31 | 32 | // Pins 33 | static const int adc_pin = A0; 34 | 35 | // Message struct to wrap strings for queue 36 | typedef struct Message { 37 | char body[MSG_LEN]; 38 | } Message; 39 | 40 | // Globals 41 | static hw_timer_t *timer = NULL; 42 | static TaskHandle_t processing_task = NULL; 43 | static SemaphoreHandle_t sem_done_reading = NULL; 44 | static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; 45 | static QueueHandle_t msg_queue; 46 | static volatile uint16_t buf_0[BUF_LEN]; // One buffer in the pair 47 | static volatile uint16_t buf_1[BUF_LEN]; // The other buffer in the pair 48 | static volatile uint16_t* write_to = buf_0; // Double buffer write pointer 49 | static volatile uint16_t* read_from = buf_1; // Double buffer read pointer 50 | static volatile uint8_t buf_overrun = 0; // Double buffer overrun flag 51 | static float adc_avg; 52 | 53 | //***************************************************************************** 54 | // Functions that can be called from anywhere (in this file) 55 | 56 | // Swap the write_to and read_from pointers in the double buffer 57 | // Only ISR calls this at the moment, so no need to make it thread-safe 58 | void IRAM_ATTR swap() { 59 | volatile uint16_t* temp_ptr = write_to; 60 | write_to = read_from; 61 | read_from = temp_ptr; 62 | } 63 | 64 | //***************************************************************************** 65 | // Interrupt Service Routines (ISRs) 66 | 67 | // This function executes when timer reaches max (and resets) 68 | void IRAM_ATTR onTimer() { 69 | 70 | static uint16_t idx = 0; 71 | BaseType_t task_woken = pdFALSE; 72 | 73 | // If buffer is not overrun, read ADC to next buffer element. If buffer is 74 | // overrun, drop the sample. 75 | if ((idx < BUF_LEN) && (buf_overrun == 0)) { 76 | write_to[idx] = analogRead(adc_pin); 77 | idx++; 78 | } 79 | 80 | // Check if the buffer is full 81 | if (idx >= BUF_LEN) { 82 | 83 | // If reading is not done, set overrun flag. We don't need to set this 84 | // as a critical section, as nothing can interrupt and change either value. 85 | if (xSemaphoreTakeFromISR(sem_done_reading, &task_woken) == pdFALSE) { 86 | buf_overrun = 1; 87 | } 88 | 89 | // Only swap buffers and notify task if overrun flag is cleared 90 | if (buf_overrun == 0) { 91 | 92 | // Reset index and swap buffer pointers 93 | idx = 0; 94 | swap(); 95 | 96 | // A task notification works like a binary semaphore but is faster 97 | vTaskNotifyGiveFromISR(processing_task, &task_woken); 98 | } 99 | } 100 | 101 | // Exit from ISR (Vanilla FreeRTOS) 102 | //portYIELD_FROM_ISR(task_woken); 103 | 104 | // Exit from ISR (ESP-IDF) 105 | if (task_woken) { 106 | portYIELD_FROM_ISR(); 107 | } 108 | } 109 | 110 | //***************************************************************************** 111 | // Tasks 112 | 113 | // Serial terminal task 114 | void doCLI(void *parameters) { 115 | 116 | Message rcv_msg; 117 | char c; 118 | char cmd_buf[CMD_BUF_LEN]; 119 | uint8_t idx = 0; 120 | uint8_t cmd_len = strlen(command); 121 | 122 | // Clear whole buffer 123 | memset(cmd_buf, 0, CMD_BUF_LEN); 124 | 125 | // Loop forever 126 | while (1) { 127 | 128 | // Look for any error messages that need to be printed 129 | if (xQueueReceive(msg_queue, (void *)&rcv_msg, 0) == pdTRUE) { 130 | Serial.println(rcv_msg.body); 131 | } 132 | 133 | // Read characters from serial 134 | if (Serial.available() > 0) { 135 | c = Serial.read(); 136 | 137 | // Store received character to buffer if not over buffer limit 138 | if (idx < CMD_BUF_LEN - 1) { 139 | cmd_buf[idx] = c; 140 | idx++; 141 | } 142 | 143 | // Print newline and check input on 'enter' 144 | if ((c == '\n') || (c == '\r')) { 145 | 146 | // Print newline to terminal 147 | Serial.print("\r\n"); 148 | 149 | // Print average value if command given is "avg" 150 | cmd_buf[idx - 1] = '\0'; 151 | if (strcmp(cmd_buf, command) == 0) { 152 | Serial.print("Average: "); 153 | Serial.println(adc_avg); 154 | } 155 | 156 | // Reset receive buffer and index counter 157 | memset(cmd_buf, 0, CMD_BUF_LEN); 158 | idx = 0; 159 | 160 | // Otherwise, echo character back to serial terminal 161 | } else { 162 | Serial.print(c); 163 | } 164 | } 165 | 166 | // Don't hog the CPU. Yield to other tasks for a while 167 | vTaskDelay(cli_delay / portTICK_PERIOD_MS); 168 | } 169 | } 170 | 171 | // Wait for semaphore and calculate average of ADC values 172 | void calcAverage(void *parameters) { 173 | 174 | Message msg; 175 | float avg; 176 | 177 | // Start a timer to run ISR every 100 ms 178 | // %%% We move this here so it runsin core 0 179 | timer = timerBegin(0, timer_divider, true); 180 | timerAttachInterrupt(timer, &onTimer, true); 181 | timerAlarmWrite(timer, timer_max_count, true); 182 | timerAlarmEnable(timer); 183 | 184 | // Loop forever, wait for semaphore, and print value 185 | while (1) { 186 | 187 | // Wait for notification from ISR (similar to binary semaphore) 188 | ulTaskNotifyTake(pdTRUE, portMAX_DELAY); 189 | 190 | // Calculate average (as floating point value) 191 | avg = 0.0; 192 | for (int i = 0; i < BUF_LEN; i++) { 193 | avg += (float)read_from[i]; 194 | //vTaskDelay(105 / portTICK_PERIOD_MS); // Uncomment to test overrun flag 195 | } 196 | avg /= BUF_LEN; 197 | 198 | // Updating the shared float may or may not take multiple isntructions, so 199 | // we protect it with a mutex or critical section. The ESP-IDF critical 200 | // section is the easiest for this application. 201 | portENTER_CRITICAL(&spinlock); 202 | adc_avg = avg; 203 | portEXIT_CRITICAL(&spinlock); 204 | 205 | // If we took too long to process, buffer writing will have overrun. So, 206 | // we send a message to be printed out to the serial terminal. 207 | if (buf_overrun == 1) { 208 | strcpy(msg.body, "Error: Buffer overrun. Samples have been dropped."); 209 | xQueueSend(msg_queue, (void *)&msg, 10); 210 | } 211 | 212 | // Clearing the overrun flag and giving the "done reading" semaphore must 213 | // be done together without being interrupted. 214 | portENTER_CRITICAL(&spinlock); 215 | buf_overrun = 0; 216 | xSemaphoreGive(sem_done_reading); 217 | portEXIT_CRITICAL(&spinlock); 218 | } 219 | } 220 | 221 | //***************************************************************************** 222 | // Main (runs as its own task with priority 1 on core 1) 223 | 224 | void setup() { 225 | 226 | // Configure Serial 227 | Serial.begin(115200); 228 | 229 | // Wait a moment to start (so we don't miss Serial output) 230 | vTaskDelay(1000 / portTICK_PERIOD_MS); 231 | Serial.println(); 232 | Serial.println("---FreeRTOS Sample and Process Demo---"); 233 | 234 | // Create semaphore before it is used (in task or ISR) 235 | sem_done_reading = xSemaphoreCreateBinary(); 236 | 237 | // Force reboot if we can't create the semaphore 238 | if (sem_done_reading == NULL) { 239 | Serial.println("Could not create one or more semaphores"); 240 | ESP.restart(); 241 | } 242 | 243 | // We want the done reading semaphore to initialize to 1 244 | xSemaphoreGive(sem_done_reading); 245 | 246 | // Create message queue before it is used 247 | msg_queue = xQueueCreate(MSG_QUEUE_LEN, sizeof(Message)); 248 | 249 | // Start task to handle command line interface events. Let's set it at a 250 | // higher priority but only run it once every 10 ms. 251 | xTaskCreatePinnedToCore(doCLI, 252 | "Do CLI", 253 | 1024, 254 | NULL, 255 | 2, 256 | NULL, 257 | app_cpu); 258 | 259 | // Start task to calculate average. Save handle for use with notifications. 260 | // %%% We set this task to run in Core 0 261 | xTaskCreatePinnedToCore(calcAverage, 262 | "Calculate average", 263 | 1024, 264 | NULL, 265 | 1, 266 | &processing_task, 267 | pro_cpu); 268 | 269 | // Delete "setup and loop" task 270 | vTaskDelete(NULL); 271 | } 272 | 273 | void loop() { 274 | // Execution should never get here 275 | } 276 | -------------------------------------------------------------------------------- /12-multicore/rtos-part-12.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasir-shahzad/introduction-to-rtos/f2ae37914fa61eca4f7f9e7e11a6895fde7e6cf3/12-multicore/rtos-part-12.pptx -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction to Real Time Operating Systems (RTOS) 2 | 3 | Welcome to the demo code and solutions section of my Introduction to RTOS course! This repository houses all of the example code and solutions that you may use as references when working through the RTOS examples for FreeRTOS. 4 | 5 | Intro to RTOS course logo 6 | 7 | This course is hosted on YouTube that you may take for free. All you need to do is watch the videos and complete the challenge issued at the end of each video. I highly recommend you try each challenge before peeking at the solutions here. The PowerPoint slides are also made available (under the CC BY 4.0 license) if you wish to use or modify them for review or teaching your own RTOS class. If you use the slides, please give credit to **Shawn Hymel** and **Digi-Key Electronics** in your slides (as per the [CC BY 4.0 requirements](https://creativecommons.org/licenses/by/4.0/)). 8 | 9 | | Chapter | Title | Video | Solution | Slides | 10 | |---------|-------|-------|----------|--------| 11 | | 01 | What is an RTOS | [[video]](https://www.youtube.com/watch?v=F321087yYy4&list=PLEBQazB0HUyQ4hAPU1cJED6t3DU0h34bz&index=1) | [[solution]](https://www.digikey.com/en/maker/projects/what-is-a-realtime-operating-system-rtos/28d8087f53844decafa5000d89608016) | [[slides]](01-what-is-an-rtos/rtos-part-01.pptx?raw=true) | 12 | | 02 | Getting Started with freeRTOS | [[video]](https://www.youtube.com/watch?v=JIr7Xm_riRs&list=PLEBQazB0HUyQ4hAPU1cJED6t3DU0h34bz&index=2) | [[solution]](https://www.digikey.com/en/maker/projects/introduction-to-rtos-solution-to-part-2-freertos/b3f84c9c9455439ca2dcb8ccfce9dec5) | [[slides]](02-getting-started-with-freertos/rtos-part-02.pptx?raw=true) | 13 | | 03 | Task Scheduling and Management | [[video]](https://www.youtube.com/watch?v=95yUbClyf3E&list=PLEBQazB0HUyQ4hAPU1cJED6t3DU0h34bz&index=3) | [[solution]](https://www.digikey.com/en/maker/projects/introduction-to-rtos-solution-to-part-3-task-scheduling/8fbb9e0b0eed4279a2dd698f02ce125f) | [[slides]](03-task-scheduling-and-management/rtos-part-03.pptx?raw=true) | 14 | | 04 | Memory Allocation | [[video]](https://www.youtube.com/watch?v=Qske3yZRW5I&list=PLEBQazB0HUyQ4hAPU1cJED6t3DU0h34bz&index=4) | [[solution]](https://www.digikey.com/en/maker/projects/introduction-to-rtos-solution-to-part-4-memory-management/6d4dfcaa1ff84f57a2098da8e6401d9c) | [[slides]](04-memory-allocation/rtos-part-04.pptx?raw=true) | 15 | | 05 | Queue | [[video]](https://www.youtube.com/watch?v=pHJ3lxOoWeI&list=PLEBQazB0HUyQ4hAPU1cJED6t3DU0h34bz&index=5) | [[solution]](https://www.digikey.com/en/maker/projects/introduction-to-rtos-solution-to-part-5-freertos-queue-example/72d2b361f7b94e0691d947c7c29a03c9) | [[slides]](05-queue/rtos-part-05.pptx?raw=true) | 16 | | 06 | Mutex | [[video]](https://www.youtube.com/watch?v=I55auRpbiTs&list=PLEBQazB0HUyQ4hAPU1cJED6t3DU0h34bz&index=6) | [[solution]](https://www.digikey.com/en/maker/projects/introduction-to-rtos-solution-to-part-6-freertos-mutex-example/c6e3581aa2204f1380e83a9b4c3807a6) | [[slides]](06-mutex/rtos-part-06.pptx?raw=true) | 17 | | 07 | Semaphore | [[video]](https://www.youtube.com/watch?v=5JcMtbA9QEE&list=PLEBQazB0HUyQ4hAPU1cJED6t3DU0h34bz&index=7) | [[solution]](https://www.digikey.com/en/maker/projects/introduction-to-rtos-solution-to-part-7-freertos-semaphore-example/51aa8660524c4daba38cba7c2f5baba7) | [[slides]](07-semaphore/rtos-part-07.pptx?raw=true) | 18 | | 08 | Software Timer | [[video]](https://www.youtube.com/watch?v=b1f1Iex0Tso&list=PLEBQazB0HUyQ4hAPU1cJED6t3DU0h34bz&index=8) | [[solution]](https://www.digikey.com/en/maker/projects/introduction-to-rtos-solution-to-part-8-software-timers/0f64cf758da440a29476165a5b2e577e) | [[slides]](08-software-timer/rtos-part-08.pptx?raw=true) | 19 | | 09 | Hardware Interrupts | [[video]](https://www.youtube.com/watch?v=qsflCf6ahXU&list=PLEBQazB0HUyQ4hAPU1cJED6t3DU0h34bz&index=9) | [[solution]](https://www.digikey.com/en/maker/projects/introduction-to-rtos-solution-to-part-9-hardware-interrupts/3ae7a68462584e1eb408e1638002e9ed) | [[slides]](09-hardware-interrupts/rtos-part-09.pptx?raw=true) | 20 | | 10 | Deadlock and Starvation | [[video]](https://www.youtube.com/watch?v=hRsWi4HIENc&list=PLEBQazB0HUyQ4hAPU1cJED6t3DU0h34bz&index=10) | [[solution]](https://www.digikey.com/en/maker/projects/introduction-to-rtos-solution-to-part-10-deadlock-and-starvation/872c6a057901432e84594d79fcb2cc5d) | [[slides]](10-deadlock/rtos-part-10.pptx?raw=true) | 21 | | 11 | Priority Inversion | [[video]](https://www.youtube.com/watch?v=C2xKhxROmhA&list=PLEBQazB0HUyQ4hAPU1cJED6t3DU0h34bz&index=11) | [[solution]](https://www.digikey.com/en/maker/projects/introduction-to-rtos-solution-to-part-11-priority-inversion/abf4b8f7cd4a4c70bece35678d178321) | [[slides]](11-priority-inversion/rtos-part-11.pptx?raw=true) | 22 | | 12 | Multicore Systems | [[video]](https://www.youtube.com/watch?v=LPSHUcH5aQc&list=PLEBQazB0HUyQ4hAPU1cJED6t3DU0h34bz&index=12) | [[solution]](https://www.digikey.com/en/maker/projects/introduction-to-rtos-solution-to-part-12-multicore-systems/369936f5671d4207a2c954c0637e7d50) | [[slides]](12-multicore/rtos-part-12.pptx?raw=true) | 23 | 24 | ## Directory Structure 25 | 26 | Examples and solutions are housed in dirctories that correspond to each chapter or video number. For example, if you watch "Intro to RTOS Part 3 - Task Scheduling," you should refer to the directory *03-task-scheduling-and-management.* In it, you will find 2 directories, one marked "demo" that gives the finished demo code used during the video (so you may run it and examine it at your own pace) and another marked "solution" that provides one possible solution to the challenge issued at the end of the video. 27 | 28 | If a challenge is issued in a video that starts with some code, it will be listed as a *challenge* Arduino sketch in the naming scheme shown below. 29 | 30 | Directories are in the following structure (where xx is the part or chapter number): 31 | 32 | ``` 33 | xx-/ 34 | |- esp32-freertos-xx-challenge-/ 35 | |- esp32-freertos-xx-demo-/ 36 | |- esp32-freertos-xx-solution-/ 37 | |- rtos-part-xx.pptx 38 | ``` 39 | 40 | PowerPoint slides used in each video can be found within the respective *xx-* directory. 41 | 42 | The only exception to this is the *images* directory, which is where I keep images for this repository. 43 | 44 | ## License 45 | 46 | PowerPoint slides are licensed under the [Creative Commons CC BY 4.0 license](https://creativecommons.org/licenses/by/4.0/). You are welcome to use and modify them for your own review and teaching. If you use them, please give credit to **Shawn Hymel** and **Digi-Key Electronics**. 47 | 48 | All code in this repository, unless otherwise noted, is licensed under the [Zero-Clause BSD / Free Public License 1.0.0 (0BSD)](https://opensource.org/licenses/0BSD). 49 | 50 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. 51 | 52 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /images/intro-to-rtos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yasir-shahzad/introduction-to-rtos/f2ae37914fa61eca4f7f9e7e11a6895fde7e6cf3/images/intro-to-rtos.png --------------------------------------------------------------------------------