一、环境:
平台:arduino IDE 或 VS Code PlatformIO 皆可。
我的是后者,具体为:
框架:VS + PlatformIO + Arduino
二、硬件准备:
一个esp32s3
本文用到的是U0RXD(GPIO44 )与U0TXD(GPIO43)引脚
一台电脑
一根数据线
三、代码功能:
一个LED一秒亮一次:
另一个LED两秒两一次。
注意:ESP32S3有两个type-C口,一个UART用来下载调试程序,一个USB用来供电。下载完程序后切换到USB,现象会更明显。
四、最简代码
#include <Arduino.h>#define LED_U0RXD 44
#define LED_U0TXD 43// 创建两个任务,
// TaskBlink1任务:一个LED 1s亮一次,
// TaskBlink2任务:另一个LED 2s亮一次。
void TaskBlink1( void *pvParameters );
void TaskBlink2( void *pvParameters );void setup() {Serial.begin(115200);uint32_t blink_delay1 = 1000; // Delay between changing state on LED pinuint32_t blink_delay2 = 2000; // Delay between changing state on LED pinxTaskCreate(TaskBlink1 // 这个任务运行的函数, "Task Blink1" // 给人看的名字, 2048 // 任务栈的大小,用于存储任务运行时的上下文信息。简单来说,就是最多存这么多信息, (void*) &blink_delay1 // 任务参数。要么没有填NULL;要么必须为无类型指针, 2 // 优先级, NULL // 任务的句柄,用于管理和控制任务,NULL相当于0,意味着此处不需要任务句柄);xTaskCreate(TaskBlink2 // 这个任务运行的函数, "Task Blink2" // 给人看的名字, 2048 // 任务栈的大小,用于存储任务运行时的上下文信息。简单来说,就是最多存这么多信息, (void*) &blink_delay2 // 任务参数。要么没有填NULL;要么必须为无类型指针, 2 // 优先级, NULL // 任务的句柄,用于管理和控制任务,NULL相当于0,意味着此处不需要任务句柄);Serial.printf("Basic Multi Threading Arduino Example\n");// Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.
}void loop(){
// 这里什么都不用写
}/*---------------------- Tasks ---------------------*/void TaskBlink1(void *pvParameters){ // This is a task.uint32_t blink_delay = *((uint32_t*)pvParameters);// 初始化LED_U0RXD为output模式pinMode(LED_U0RXD, OUTPUT);
// for死循环for (;;){ // 多线程,每个任务必须是死循环digitalWrite(LED_U0RXD, HIGH); // turn the LED on (HIGH is the voltage level)delay(blink_delay);digitalWrite(LED_U0RXD, LOW); // turn the LED off by making the voltage LOWdelay(blink_delay);}
}void TaskBlink2(void *pvParameters){ // This is a task.uint32_t blink_delay = *((uint32_t*)pvParameters);// 初始化LED_U0RXD为output模式pinMode(LED_U0TXD, OUTPUT);for (;;){ // A Task shall never return or exit.digitalWrite(LED_U0TXD, HIGH); // turn the LED on (HIGH is the voltage level)delay(blink_delay);digitalWrite(LED_U0TXD, LOW); // turn the LED off by making the voltage LOWdelay(blink_delay);}
}
主要代码解析
我们可以看到,多线程其实很简单。
主要就是函数 xTaskCreate( ) :创建一个线程,然后在线程内运行 TaskBlink1( ) 函数。
- TaskBlink1 :这个任务运行的函数
- "Task Blink1":给人看的名字
- 2048 :任务栈的大小,用于存储任务运行时的上下文信息。简单来说,就是最多存这么多信息
- (void*) &blink_delay1 :任务参数。要么没有填NULL;要么必须为无类型指针
- 2 :优先级
- NULL : 任务的句柄,用于管理和控制任务,NULL相当于0,意味着此处不需要任务句柄
五、多核下的多线程
有些板子有多个核,比如:ESP32S3有两个核,本身就可以就可以让两个核单独控制一个LED,实现上文双线程的效果。所以说,对于有两个核的ESP32S3,既可以指定某一个核运行俩线程,也可以指定俩核单独运行一个线程,而实现相同的效果。
这里介绍俩核各自运行一个线程,各自控制一个LED的闪烁。
话不多说,上代码:
#include <Arduino.h>// #if CONFIG_FREERTOS_UNICORE
// #define ARDUINO_RUNNING_CORE 0
// #else
// #define ARDUINO_RUNNING_CORE 1
// #endif#define LED_U0RXD 44
#define LED_U0TXD 43// 创建两个任务,
// TaskBlink1任务:一个LED 1s亮一次,
// TaskBlink2任务:另一个LED 2s亮一次。
void TaskBlink1(void *pvParameters);
void TaskBlink2(void *pvParameters);void setup() {Serial.begin(115200);xTaskCreatePinnedToCore(TaskBlink1, "TaskBlink1" // 任务名, 1024 // This stack size can be checked & adjusted by reading the Stack Highwater, NULL, 2 // 任务优先级, with 3 (configMAX_PRIORITIES - 1) 是最高的,0是最低的., NULL , 0); // 第一个核xTaskCreatePinnedToCore(TaskBlink2, "TaskBlink2" //任务名, 1024 // 栈大小, NULL, 1 // 任务优先级, NULL , 1); // 第二个核//现在,接管单个任务调度控制的任务调度程序将自动启动。
}void loop()
{// Empty. Things are done in Tasks.
}/*---------------------- Tasks ---------------------*/void TaskBlink1(void *pvParameters){ // This is a task.uint32_t blink_delay = 1000;// 初始化LED_U0RXD为output模式pinMode(LED_U0RXD, OUTPUT);
// for死循环for (;;){ // 多线程,每个任务必须是死循环digitalWrite(LED_U0RXD, HIGH); // turn the LED on (HIGH is the voltage level)delay(blink_delay);digitalWrite(LED_U0RXD, LOW); // turn the LED off by making the voltage LOWdelay(blink_delay);}
}void TaskBlink2(void *pvParameters){ // This is a task.uint32_t blink_delay = 2000;// 初始化LED_U0RXD为output模式pinMode(LED_U0TXD, OUTPUT);for (;;){ // A Task shall never return or exit.digitalWrite(LED_U0TXD, HIGH); // turn the LED on (HIGH is the voltage level)delay(blink_delay);digitalWrite(LED_U0TXD, LOW); // turn the LED off by making the voltage LOWdelay(blink_delay);}
}
主要代码解析
我们可以看到,多核、多线程其实也很简单。
主要就是函数 xTaskCreatePinnedToCore ( ) :创建一个线程,并指定哪一个核运行线程,然后在线程内运行 TaskBlink1( ) 函数。与函数 xTaskCreate( ) 相比只多了指定运行的核,这一步。
- TaskBlink1 :这个任务运行的函数
- "Task Blink1":给人看的名字
- 2048 :任务栈的大小,用于存储任务运行时的上下文信息。简单来说,就是最多存这么多信息
- (void*) &blink_delay1 :任务参数。要么没有填NULL;要么必须为无类型指针
- 2 :优先级
- NULL : 任务的句柄,用于管理和控制任务,NULL相当于0,意味着此处不需要任务句柄
- 0 :第一个核。只有俩核,只能填0或1
最后,我有话说:
如果文章对你有帮助,我很开心。有疑问,请留言,看到后,我会回复。