#include "semphr.h" // Khai báo semphr.h để sử dụng Semaphore #define LED 13 // Định nghĩa chân LED 13 SemaphoreHandle_t xBinarySemaphore; // Tạo Semaphore nhị phân void setup() { Serial.begin(9600); // Giao tiếp serial với tốc độ 9600bps pinMode(LED ,OUTPUT); // Cài đặt chân LED là OUTPUT xBinarySemaphore = xSemaphoreCreateBinary(); xTaskCreate(LedOnTask, "LedON",100,NULL,1,NULL); // Tạo Task LedON xTaskCreate(LedoffTask, "LedOFF", 100,NULL,1,NULL); // Tạo Task LedOFF xSemaphoreGive(xBinarySemaphore); } void loop(){} void LedOnTask(void *pvParameters) { while(1) { xSemaphoreTake(xBinarySemaphore,portMAX_DELAY); // Đợi và lấy Semaphore Serial.println("Inside LedOnTask"); // In ra seiral Inside LedOnTask digitalWrite(LED,LOW); // Bật đèn LED xSemaphoreGive(xBinarySemaphore); // Trả Semaphore sau khi sử dụng xong vTaskDelay(1); } } void LedoffTask(void *pvParameters) { while(1) { xSemaphoreTake(xBinarySemaphore,portMAX_DELAY); // Đợi và lấy Semaphore Serial.println("Inside LedffTask"); // In ra seiral Inside LedffTask digitalWrite(LED,HIGH); // Tắt đèn LED xSemaphoreGive(xBinarySemaphore); // Trả Semaphore sau khi sử dụng xong vTaskDelay(1); } }
GIẢI THÍCH CODE:
- Chương trình bao gồm 2 tác vụ: 1 tác vụ bật đèn LED và 1 tác vụ sẽ tắt đèn LED. Nhưng cả 2 nhiệm vụ không thể thực hiện cùng một lúc, vì cả 2 đều chia sẻ cùng mọt semaphore nhị phân. Cả 2 nhiệm vụ đều có cùng mức độ ưu tiên. Do đó, bộ lập lịch FreeRTOS sẽ lên lịch cho cả 2 tác vụ theo kiểu chia sẻ thời gian hoặc vòng tròn. - Tại thời điểm t1, "LedOnTask" bắt đầu thực thi. Bởi vì cả hai nhiệm vụ đều có mức độ ưu tiên như nhau. Ngoài ra, semaphore nhị phân có sẵn để có được vì chúng tôi đã cung cấp semaphore bên trong chức năng thiết lập. Do đó, "LedOnTask" lấy tín hiệu semaphore nhị phân bằng cách sử dụng xSemaphoreTake () và hoàn thành tất cả việc thực thi nó. - Do kỹ thuật lập lịch chia sẻ thời gian của FreeRTOS cho các tác vụ ưu tiên bằng nhau, "LedoffTask" sẽ cố gắng thực thi bằng cách ưu tiên trước "LedOnTask", nhưng chuyển sang trạng thái chặn do không có sẵn tài nguyên được chia sẻ nhị phân semaphore. Do đó, "LedOnTask" trước tiên hoàn thành việc thực thi và sau đó phát hành semaphore nhị phân. - Ngay sau khi semaphore nhị phân trở nên có sẵn, "LedoffTask" được thực thi bởi vì nó được nhập ở trạng thái khối do không có sẵn tài nguyên được chia sẻ nhị phân semaphore. Tương tự, nó cũng hoàn thành việc thực hiện và phát hành mã thông báo. Sau đó, mã Arduino lặp lại cùng một mẫu để thực thi. TH5.1.b: CODE:
#include <semphr.h> // Khai báo semphr.h để sử dụng Semaphore SemaphoreHandle_t interruptSemaphore; // Khai báo biến Semaphore có tên là interruptSemaphore void setup() { Serial.begin(9600); pinMode(LED_BUILTIN, OUTPUT); // Cài đặt chân LED_BUILTIN là OUTPUT xTaskCreate(TaskLedon, // Task function "Ledon", // Task name 128, // Stack size NULL, 0 ,// Priority NULL ); xTaskCreate(TaskLedoff, // Task function "Ledoff", // Task name 128, // Stack size NULL, 0, // Priority NULL ); interruptSemaphore = xSemaphoreCreateBinary(); // Tạo Semaphore nhị phân if (interruptSemaphore != NULL) { attachInterrupt(digitalPinToInterrupt(2), interruptHandler, HIGH); // Gán ngắt chân số 2, khi ngắt xảy ra, hàm interruptHandler sẽ được gọi } } void loop() {} void interruptHandler() { Serial.println("Semaphore is given"); // In ra Serial "Semaphore is given" BaseType_t xHigherPriorityTaskWoken pdFALSE; xSemaphoreGiveFromISR(interruptSemaphore, &xHigherPriorityTaskWoken); // Gửi Semaphore từ bên trong một hàm ngắt } // TaskLedon thực hiện việc bật LED khi nhận được Semaphore và in ra TaskLedon Received Semaphore void TaskLedon(void *pvParameters) { (void) pvParameters; for (;;) { if (xSemaphoreTake(interruptSemaphore, portMAX_DELAY) == pdPASS) { Serial.println("TaskLedon Received Semaphore"); // TaskLedoff thực hiện việc tắt LED khi nhận được Semaphore và in ra TaskLedoff Received Semaphore digitalWrite(LED_BUILTIN, HIGH); } } } void TaskLedoff(void *pvParameters) { (void) pvParameters; for (;;) { if (xSemaphoreTake(interruptSemaphore, portMAX_DELAY) == pdPASS) { Serial.println("TaskLedoff Received Semaphore"); digitalWrite(LED_BUILTIN, LOW); } } }
GIẢI THÍCH CODE:
- Chương trình tạo 2 tác vụ và ngắt bên ngoài trên chân kỹ thuật số hai của Adruino để đồng bộ hóa các tác vụ này bằng cách sử dụng semaphore nhị phân. Hai tác vụ này điều khiển một đèn LED mà chúng tôi kết nối với chân kỹ thuật số 13 của Adruino. Tác vụ "LedOnTask" bật đèn LED và in chuỗi "Bên trong LedOnTask" trên màn hình Arduino Serial. Tương tự, "LedoffTask" tắt đèn LED và in chuỗi "Inside LedffTask" trên màn hình nối tiếp Arduino. - Cả hai tác vụ này đều có cùng mức độ ưu tiên và sử dụng cùng một semaphore nhị phân. Do đó, FreeRTOS lên lịch cho cả hai tác vụ bằng thuật toán lập lịch chia sẻ thời gian. Hơn nữa, cả hai tác vụ vẫn ở trạng thái chặn trừ khi gián đoạn xảy ra và nó cung cấp semaphore nhị phân. Vì các tác vụ ưu tiên bằng nhau tuân theo thuật toán lập lịch chia sẻ thời gian trong FreeRTOS. Đáp lại, cả hai nhiệm vụ sẽ thu được semaphore nhị phân lần lượt.
#include <semphr.h> // Khai báo semphr.h để sử dụng Semaphore SemaphoreHandle_t xCountingSemaphore; // Khai báo biến Semaphore loại đếm (xCountingSemaphore). void setup() { Serial.begin(9600); pinMode(LED_BUILTIN, OUTPUT); xTaskCreate(Task1, // Task function "Ledon", // Task name 128, // Stack size NULL, 0 ,// Priority NULL ); xTaskCreate(Task2, // Task function "Ledoff", // Task name 128, // Stack size NULL, 0, // Priority NULL ); xCountingSemaphore = xSemaphoreCreateCounting(1,1); // Tạo Semaphore đếm với giá trị khởi tạo là 1 và giá trị tối đa là 1. xSemaphoreGive(xCountingSemaphore); // Gửi Semaphore ban đầu ho phép một trong hai task có thể bắt đầu thực hiện ngay lập tức. } void loop() {} // Cả hai task đều thực hiện công việc bật (Task1) và tắt (Task2) đèn LED cùng với việc ghi thông điệp vào Serial Monitor khi có Semaphore được lấy. void Task1(void *pvParameters) { (void) pvParameters; for (;;) { xSemaphoreTake(xCountingSemaphore, portMAX_DELAY); Serial.println("Inside Task1 and Serial monitor Resource Taken"); digitalWrite(LED_BUILTIN, HIGH); xSemaphoreGive(xCountingSemaphore); vTaskDelay(1); } } void Task2(void *pvParameters) { (void) pvParameters; for (;;) { xSemaphoreTake(xCountingSemaphore, portMAX_DELAY); Serial.println("Inside Task2 and Serial monitor Resource Taken"); digitalWrite(LED_BUILTIN, LOW); xSemaphoreGive(xCountingSemaphore); vTaskDelay(1); } }
GIẢI THÍCH CODE:
- Tạo núm điều khiển kiểu SemaphoreHandle_t - Bước tiếp theo là tạo hai tác vụ như Task1 và Task2. Cả hai nhiệm vụ được tạo ra với mức độ ưu tiên như nhau. Do đó, bộ lập lịch FreeRTOS sẽ lên lịch cho chúng theo thuật toán cắt thời gian. Khi bắt đầu, cả hai nhiệm vụ sẽ sẵn sàng ở trạng thái. Nhưng chỉ có một nhiệm vụ sẽ có thể thực hiện sau khi đếm semaphore. Một tác vụ khác sẽ chuyển sang trạng thái chặn do không có sẵn các tài nguyên như cổng giao tiếp nối tiếp và màn hình nối tiếp Arduino. - Task1 và Task2 sử dụng giao tiếp nối tiếp để in dữ liệu trên màn hình nối tiếp. Do đó, bạn phải kích hoạt cổng nối tiếp của Arduino với bất kỳ tốc độ truyền nào có thể. Do đó, chúng tôi kích hoạt nó với tốc độ truyền là 9600. Cả hai tác vụ cũng điều khiển đèn LED tích hợp của bo mạch phát triển Arduino. Task1 bật và Task2 tắt đèn LED.
#include "semphr.h" // Khai báo semphr.h để sử dụng Semaphore Mutex SemaphoreHandle_t xMutex; // Khai báo biến Semaphore Mutex có tên là xMutex void setup() { Serial.begin(9600); xMutex = xSemaphoreCreateMutex(); // Tạo Semaphore Mutex xTaskCreate(OutputTask,"PrinterTask1",100,"Task1#####################Task1 \r\ n",1,NULL); // Tạo một task có tên là "PrinterTask1" để gọi hàm OutputTask. Task này có độ ưu tiên là 1. xTaskCreate(OutputTask,"Printer Task 2", 100,"Task 2 ---------------------Task2 \r\ n",2,NULL); // Tạo một task có tên là "Printer Task 2" để gọi hàm OutputTask. Task này có độ ưu tiên là 2. } // Nhận thông điệp cần in (pcStringToPrint) thông qua tham số pvParameters. Trong vòng lặp vô hạn, task sẽ gọi hàm printer() để ghi thông điệp ra Serial Monitor và sau đó chờ một khoảng thời gian bằng vTaskDelay(pdMS_TO_TICKS(100)) void OutputTask(void *pvParameters) { char *pcStringToPrint; pcStringToPrint = (char *)pvParameters; while(1) { printer(pcStringToPrint); vTaskDelay(pdMS_TO_TICKS(100)); } } // Ghi thông điệp ra Serial Monitor. Trước khi ghi, task sử dụng Semaphore Mutex để đảm bảo rằng chỉ có một task được phép ghi vào Serial Monitor tại một thời điểm. Sau khi ghi, task trả lại Semaphore Mutex bằng cách gọi xSemaphoreGive(xMutex). void printer(const char* pcString) { xSemaphoreTake(xMutex, portMAX_DELAY); Serial.println(pcString); xSemaphoreGive(xMutex); } void loop(){ }