基本多线程Arduino示例
配置
#if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_RUNNING_CORE 1
#endif
当FreeRTOS配置为单核模式时,ARDUINO_RUNNING_CORE宏被定义为0,表示应用程序在主核心上运行。而当FreeRTOS配置为双核模式时,ARDUINO_RUNNING_CORE宏被定义为1,表示应用程序在第二个核心上运行。
在ESP32上,可以使用两个独立的处理器核心来运行应用程序和操作系统。在双核模式下,一个核心运行FreeRTOS调度程序,另一个核心则可用于运行用户应用程序。这种方式可以提高系统性能和响应速度。
创建任务
#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
void task1(void *pvParameters) {
while (1) {
// 任务1的代码
vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时1秒
}
}
void task2(void *pvParameters) {
while (1) {
// 任务2的代码
vTaskDelay(2000 / portTICK_PERIOD_MS); // 延时2秒
}
}
void setup() {
// 初始化代码可以放在这里
Serial.begin(115200);
// 创建FreeRTOS任务
xTaskCreate(task1, "task1", 4096, NULL, 1, NULL);
xTaskCreate(task2, "task2", 4096, NULL, 2, NULL);
}
void loop() {
// 由于FreeRTOS自行管理任务,loop()中一般不需要添加额外的代码
}
-
例子中,setup()函数用于初始化代码,而loop()函数为空。FreeRTOS的任务(task1和task2)被创建在setup()函数中,因为它们会在整个程序运行期间持续执行。
-
请注意,在ESP32上,FreeRTOS自身负责任务的调度,因此在loop()中通常不需要编写额外的代码。任务的具体实现应该在各自的任务函数中。这样设计的好处是能够更灵活地管理任务的执行和调度。
消息队列
#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
QueueHandle_t xQueue;
void sender_task(void *pvParameters) {
while (1) {
// 生产数据
int data = esp_random();
xQueueSend(xQueue, &data, portMAX_DELAY);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void receiver_task(void *pvParameters) {
while (1) {
int data;
xQueueReceive(xQueue, &data, portMAX_DELAY);
// 处理接收到的数据
Serial.println("Received data: " + String(data));
}
}
void setup() {
Serial.begin(115200);
// 创建消息队列
xQueue = xQueueCreate(5, sizeof(int));
// 创建FreeRTOS任务
xTaskCreate(sender_task, "sender", 4096, NULL, 1, NULL);
xTaskCreate(receiver_task, "receiver", 4096, NULL, 2, NULL);
}
void loop() {
// FreeRTOS任务的具体执行在各自的任务函数中,因此loop()中无需额外的代码
}
-
例子中,sender_task任务负责产生随机数据并将其发送到消息队列中,而receiver_task任务则负责从消息队列中接收数据并进行处理。在setup()函数中,首先创建了一个消息队列 (xQueue = xQueueCreate(5, sizeof(int))😉,然后创建了两个FreeRTOS任务,分别执行sender_task和receiver_task。
-
请注意,vTaskDelay函数用于任务之间的延时,以防止任务频繁执行。此外,在Arduino中使用Serial对象输出调试信息。
信号量
#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h>
SemaphoreHandle_t xSemaphore;
void task1(void *pvParameters) {
while (1) {
// 任务1等待信号量
if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) {
// 共享资源的访问和操作
Serial.println("Task 1 is running");
// 释放信号量
xSemaphoreGive(xSemaphore);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void task2(void *pvParameters) {
while (1) {
// 任务2等待信号量
if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) {
// 共享资源的访问和操作
Serial.println("Task 2 is running");
// 释放信号量
xSemaphoreGive(xSemaphore);
}
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
void setup() {
Serial.begin(115200);
// 创建信号量
xSemaphore = xSemaphoreCreateMutex();
// 创建FreeRTOS任务
xTaskCreate(task1, "task1", 4096, NULL, 1, NULL);
xTaskCreate(task2, "task2", 4096, NULL, 2, NULL);
}
void loop() {
// FreeRTOS任务的具体执行在各自的任务函数中,因此loop()中无需额外的代码
}
-
例子中,task1和task2任务都尝试获取一个互斥信号量 (xSemaphore)。如果信号量可用,任务就会获得对共享资源的访问权限,执行一些操作,然后释放信号量。如果信号量当前被其他任务占用,任务将等待,直到信号量可用。
-
请注意,在实际应用中,信号量可用于保护共享资源,以确保多个任务之间对资源的访问是同步的,避免竞态条件。
定时器
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/timers.h>
TimerHandle_t xTimer;
void timer_callback(TimerHandle_t xTimer) {
// 定时器到期时的处理代码
}
void setup() {
xTimer = xTimerCreate("MyTimer", pdMS_TO_TICKS(1000), pdTRUE, 0, timer_callback);
xTimerStart(xTimer, 0);
// 创建其他任务等
}
void loop() {
// FreeRTOS任务的具体执行在各自的任务函数中,因此loop()中无需额外的代码
}
- 定时器可以用于定时执行特定的任务或操作。
互拆量(Mutex)
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h>
SemaphoreHandle_t xMutex;
void task1(void *pvParameters) {
while (1) {
xSemaphoreTake(xMutex, portMAX_DELAY);
// 临界区的代码
xSemaphoreGive(xMutex);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void task2(void *pvParameters) {
while (1) {
xSemaphoreTake(xMutex, portMAX_DELAY);
// 临界区的代码
xSemaphoreGive(xMutex);
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
void setup() {
xMutex = xSemaphoreCreateMutex();
xTaskCreate(task1, "task1", 4096, NULL, 1, NULL);
xTaskCreate(task2, "task2", 4096, NULL, 2, NULL);
}
void loop() {
// FreeRTOS任务的具体执行在各自的任务函数中,因此loop()中无需额外的代码
}
- 互斥量用于保护共享资源,确保在任何时刻只有一个任务能够访问临界区的代码。
事件组(Event Group)
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/event_groups.h>
EventGroupHandle_t xEventGroup;
#define BIT_0 (1 << 0)
#define BIT_1 (1 << 1)
void task1(void *pvParameters) {
while (1) {
xEventGroupSetBits(xEventGroup, BIT_0);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void task2(void *pvParameters) {
while (1) {
if (xEventGroupWaitBits(xEventGroup, BIT_0, pdTRUE, pdTRUE, portMAX_DELAY)) {
// 处理事件 BIT_0
xEventGroupSetBits(xEventGroup, BIT_1);
}
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
void setup() {
xEventGroup = xEventGroupCreate();
xTaskCreate(task1, "task1", 4096, NULL, 1, NULL);
xTaskCreate(task2, "task2", 4096, NULL, 2, NULL);
}
void loop() {
// FreeRTOS任务的具体执行在各自的任务函数中,因此loop()中无需额外的代码
}
- 事件组允许任务等待和设置事件标志,用于任务间的同步和通信。