☛ 功能说明

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