☛ 功能說明

利用 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);
     }
}