☛ 功能說明
利用 Arduino UNO 開發板、 FreeRTOS 函式庫及 semphr 函式庫控制兩個 LED 與一個按鍵開關,當按鍵開關按下後,啟動中斷程式,並且改變 LED2 的明滅狀態,而 LED1 則保持閃爍狀態。
☛ 使用材料
Arduino UNO R3 開發板 × 1、LED × 2、按鍵開關 × 1。
☛ 電路圖及麵包板接線圖
☛ 程式碼
#include <Arduino_FreeRTOS.h> //引用 FreeRTOS 函式庫 #include <semphr.h> //引用 semphr.h 信號旗標函式庫 SemaphoreHandle_t interruptSemaphore; //宣告 SemaphoreHandle_t 類型的變數來存儲信號旗標的值 long debouncing_time = 150; //消除按鈕抖動的時間 150ms volatile unsigned long last_micros; void setup() { pinMode(2, INPUT_PULLUP); //啟用內部上拉電阻並連接中斷引腳 xTaskCreate(TaskLed, "Led", 128, NULL, 0, NULL ); //創建 TaskLed 任務 xTaskCreate(TaskBlink, "LedBlink", 128, NULL, 0, NULL ); //創建 TaskBlink 任務 interruptSemaphore = xSemaphoreCreateBinary(); //創建 interruptSemaphore 信號旗標 if(interruptSemaphore != NULL) //開關按下后,調用 debounceInterrupt() { attachInterrupt(digitalPinToInterrupt(2), debounceInterrupt, LOW); } } void loop() { } void debounceInterrupt() //消除按鈕開關抖動 { if((long)(micros() - last_micros) >= debouncing_time * 1000) { interruptHandler(); last_micros = micros(); } } void interruptHandler() { xSemaphoreGiveFromISR(interruptSemaphore, NULL); //釋放一個信號機,帶中斷保護 } void TaskLed(void *pvParameters) { (void) pvParameters; pinMode(8, OUTPUT); while(1) //獲取信號旗標,當 pdPASS =1,切換 LED 明滅狀態 { if (xSemaphoreTake(interruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite(8, !digitalRead(8)); } } } void TaskBlink(void *pvParameters) //LED 閃爍副程式 { (void) pvParameters; pinMode(7, OUTPUT); while(1) { digitalWrite(7, HIGH); vTaskDelay(200 / portTICK_PERIOD_MS); digitalWrite(7, LOW); vTaskDelay(200 / portTICK_PERIOD_MS); } }
☛ 程式碼說明
⑴ 在 void setup() 中,使用 xTaskCreate 創建兩個任務 ( TaskLED 和 TaskBlink ),然後使用 xSemaphoreCreateBinary() 创建一個信號旗標。 創建一個具有相同優先順序的任務,稍後嘗試使用此任務。 另外,將引腳 2 配置為輸入,並啟用內部上拉電阻並連接中斷引腳。 最後,啟動調度程式。
void setup() { pinMode(2, INPUT_PULLUP); xTaskCreate(TaskLed, "Led", 128, NULL, 0, NULL ); xTaskCreate(TaskBlink, "LedBlink", 128, NULL, 0, NULL ); interruptSemaphore = xSemaphoreCreateBinary(); if (interruptSemaphore != NULL) { attachInterrupt(digitalPinToInterrupt(2), debounceInterrupt, LOW); } }
⑵ 實現 ISR 函數。 創建一個函數,並將其命名為與 attachInterrupt() 函數的第二個參數相同的名稱。 為了使中斷正常工作,需要使用毫秒函數並通過調整時間來消除按鈕的抖動問題。 在此函數中,調用 interruptHandler() 函數。 並在 interruptHandler() 函數中,調用 xSemaphoreGiveFromISR() 函數。 此函數將向 TaskLed 提供信號旗標以打開 LED。
long debouncing_time = 150; volatile unsigned long last_micros; void debounceInterrupt() { if((long)(micros() - last_micros) >= debouncing_time * 1000) { interruptHandler(); last_micros = micros(); } }
void interruptHandler() { xSemaphoreGiveFromISR(interruptSemaphore, NULL); }
⑶ 創建一個 TaskLed 函數,並在 while 循環內,調用 xSemaphoreTake() API 並檢查信號旗標是否成功獲取。 如果等於 pdPASS ( 即 1 ),則使 LED 做明滅狀態的切換。
void TaskLed(void *pvParameters) { (void) pvParameters; pinMode(8, OUTPUT); while(1) { if (xSemaphoreTake(interruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite(8, !digitalRead(8)); } } }
⑷ 創建一個函數來使連接到引腳 7 的 LED 閃爍。
void TaskLed1(void *pvParameters) { (void) pvParameters; pinMode(7, OUTPUT); while(1) { digitalWrite(7, HIGH); vTaskDelay(200 / portTICK_PERIOD_MS); digitalWrite(7, LOW); vTaskDelay(200 / portTICK_PERIOD_MS); } }