ESP32 使用FreeRTOS的多线程

ESP32中S和S3系列是双核,也就是一个CPU在工作的时候同时运行另一个CPU。ESP32的Arduino core在使用多任务的前提下具备Arduino功能。Arduino 核心程序是使用称为 FreeRTOS 的实时操作系统创建的。
在双核模式下,一个核心运行FreeRTOS调度程序,另一个核心则可用于运行用户应用程序。这种方式可以提高系统性能和响应速度。

ESP32 FreeRTOS 文档
https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32/api-reference/system/freertos.html

最简单的多线程例子

TaskHandle_t th_p[1]; // 任务句柄,对xTaskCreate的调用返回。可用作参数到vTaskDelete以删除任务。
long a_Task = 0;
long b_Task = 0;
void Core0task(void *args) {
while(1){ // 多线程中必须使用一个死循环
b_Task++;
delay(1); // 添加延迟1ms,可以有效防止卡死,爆错,提高效率。
}
}
void setup() {
Serial.begin(115200);
xTaskCreatePinnedToCore(Core0task, “Core0task”, 4096, NULL, 3, &th_p[0], 0);
}
void loop() {
a_Task++;
Serial.print(“a_Task = “)
Serial.println(a_Task);
Serial.print(“b_Task = “)
Serial.println(b_Task);
delay(1000);
}

执行结果:
-> a_Task = 1
-> b_Task = 2
-> a_Task = 2
-> b_Task = 1002
-> a_Task = 3
-> b_Task = 2002
-> a_Task = 4
-> b_Task = 3002
-> a_Task = 5
-> b_Task = 4002
-> a_Task = 6
-> b_Task = 5002
-> a_Task = 7
-> b_Task = 6002
-> a_Task = 8
-> b_Task = 7002

代码说明:

1,定义句柄

必须定义用于存储多线程的任务句柄。任务句柄需要通过地址传递,所以在这里被设置为数组会更方便管理。

TaskHandle_t th_p[1];

2,定义多线程的程序

定义一个void(无类型)的函数,用作多线程的任务

void Core0task(void *args) {
while(1){ // 多线程中必须使用一个死循环
b_Task++;
delay(1); // 添加延迟1ms,可以有效防止卡死,爆错,提高效率。
}
}

3,启动多线程

在 void setup 中启动多线程

void setup() {

xTaskCreatePinnedToCore(Core0task, “Core0task”, 4096, NULL, 3, &thp[0], 0);

}

xTaskCreatePinnedToCore 说明

创建一个有特定关联的线程,此函数类似于 xTaskCreate,但允许在 SMP 系统中设置任务关联。

BaseType_t xTaskCreatePinnedToCore(
TaskFunction_t pvTaskCode, // 指向任务入口函数的指针。任务必须实现永不返回(即连续循环),或者应该使用 vTaskDelete 函数终止。
const char *constpcName, // 任务的描述性名称。这个主要是用来方便调试的。
const uint32_t usStackDepth, // 指定为字节数的任务堆栈的大小。请注意,这与普通的 FreeRTOS 不同。
void *constpvParameters, // 将用作正在创建的任务的参数的指针。

UBaseType_t uxPriority, // 任务运行的优先级。数字越大,优先级越高。
TaskHandle_t *constpvCreatedTask, // 用于传回一个句柄,创建的任务可以通过该句柄引用
const BaseType_t xCoreID // 值 0 或 1 表示任务应固定到的 CPU 的索引号。指定大于 (portNUM_PROCESSORS – 1) 的值将导致函数失败。
)

创建更多的线程的例子

TaskHandle_t th_p[2];
long a_Task = 0;
long b_Task = 0;
long c_Task = 0;
void Core0task(void *args) {
while(1){ // 多线程中必须使用一个死循环
b_Task++;
delay(1); // 添加延迟1ms,可以有效防止卡死,爆错,提高效率。
}
}
void Core1task(void *args) {
while(1){ // 多线程中必须使用一个死循环
c_Task++;
delay(2);
}
}
void setup() {
Serial.begin(115200);
xTaskCreatePinnedToCore(Core0task, “Core0task”, 4096, NULL, 3, &th_p[0], 0);
xTaskCreatePinnedToCore(Core1task, “Core1task”, 4096, NULL, 4, &th_p[1], 1);
}
void loop() {
a_Task++;
Serial.print(“a_Task = “)
Serial.println(a_Task);
Serial.print(“b_Task = “)
Serial.println(b_Task);
Serial.print(“c_Task = “)
Serial.println(c_Task);
delay(1000);
}

参考:

12 行在 ESP32 上尝试多核
https://qiita.com/Ninagawa_Izumi/items/5c3a9d40996836bd825f

ESP32 FreeRTOS 文档
https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32/api-reference/system/freertos.html


评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注