☛ 功能说明
利用 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); } }