ESP32基础知识-多任务 一、前提案例1、单 LED 小灯控制要求 LED灯每秒闪烁一次。设备Esp32开发板、面包板、led灯珠、电阻150欧实现代码#include Arduino.h#define LED_PIN 23 // 23号引脚void setup(){Serial.begin(115200);pinMode(LED_PIN, OUTPUT);}void loop(){digitalWrite(LED_PIN, HIGH);delay(1000);digitalWrite(LED_PIN, LOW);delay(1000);}2、双 LED 小灯控制要求 led1 小灯 每隔1s亮1次led2 小灯 每隔3s亮1次。按照 单 LED 小灯控制方式实现去观察效果。#include Arduino.h#define LED_PIN1 23 // 23号引脚#define LED_PIN2 22 // 22号引脚void setup(){Serial.begin(115200);pinMode(LED_PIN1, OUTPUT);pinMode(LED_PIN2, OUTPUT);}void loop(){// LED1小灯 每隔1s亮一次digitalWrite(LED_PIN1, HIGH);delay(1000);digitalWrite(LED_PIN1, LOW);delay(1000);// LED3小灯 每隔3s亮一次digitalWrite(LED_PIN2, HIGH);delay(3000);digitalWrite(LED_PIN2, LOW);delay(3000);}二、问题分析1、观察现象双 LED 小灯控制中要求 led1 小灯 每隔1s亮1次led2 小灯 每隔3s亮1次。 通过从上面的双 LED 小灯控制实验中可以看出LED1从熄灭到点亮中间经历了7s 【1 3 3】LED2从熄灭到点亮中间经历了5s 【1 1 3】2、原因与解决方案通过观察代码发现原因是在代码的 loop函数中的逻辑它是一行行按顺序往下执行的LED1和LED2的控制逻辑会相互影响最终造成延迟增大。为了实现 led1 小灯 每隔1s亮1次led2 小灯 每隔3s亮1次 的需求我们需要引入多任务即RTOSReal-Time Operating System, 实时操作系统让两个任务相互不干扰“同时”运行。注意RTOS的实现有很多其中开源免费的FreeRTOS广受欢迎。三、FreeRTOSFreeRTOS是市场领先的面向微控制器和小型微处理器的实时操作系统(RTOS)与世界领先的芯片公司合作开发现在每 170 秒下载一次。FreeRTOS 通过 MIT 开源许可免费分发包括一个内核和一组不断丰富的 IoT 库适用于所有行业领域。FreeRTOS 的构建突出可靠性和易用性。FreeRTOS是RTOS中开源免费的。ESP32的Arduino框架里面已经内置了FreeRTOS框架并且对ESP32的双核进行了完美的适配所以我们在使用时无需引入第三方库就可以直接使用。四、FreeRTOS多任务函数1、xTaskCreate函数static inline IRAM_ATTR BaseType_t xTaskCreate(TaskFunction_t pvTaskCode,const char * const pcName,const uint32_t usStackDepth,void * const pvParameters,UBaseType_t uxPriority,TaskHandle_t * const pxCreatedTask)pvTaskCode任务函数(任务要执行的逻辑)返回值必须为void只有一个参数参数类型必须为void*pcName自定义任务的名称一个字符串主要为了方便排查问题usStackDepth给任务分配的最大堆栈大小比如2048单位是字节这个值需要根据任务复杂度来选择一般简单的任务2048~4096范围的值就足够如果该任务需要处理的逻辑确实比较繁重可以适当增大比如8192pvParameters传递给任务的参数类型是void*;uxPriority任务优先级取值范围为[0, 24]数字越大优先级越高pxCreatedTask用于接收该任务的句柄后续对该任务的操作需要基于该句柄完成。2、实现步骤01、定义任务函数pvTaskCode定义 pvTaskCode 任务函数handle_led1 和 handle_led2参数都只有1个参数类型必须为void* 且返回值为void类型。void handle_led1(void *ptr){pinMode(LED1, OUTPUT);char *param (char *)ptr;Serial.print(param);Serial.println( started);while (1){digitalWrite(LED1, HIGH);vTaskDelay(1000);digitalWrite(LED1, LOW);vTaskDelay(1000);}vTaskDelete(NULL);}void handle_led2(void *ptr){pinMode(LED2, OUTPUT);char *param (char *)ptr;Serial.print(param);Serial.println( started);while (1){digitalWrite(LED2, HIGH);vTaskDelay(3000);digitalWrite(LED2, LOW);vTaskDelay(3000);}vTaskDelete(NULL);}02、定义任务名称分别为handle_led1 和 handle_led2任务函数定义任务名称: task1、task203、设置堆栈大小分别为handle_led1 和 handle_led2任务设置堆栈大小2048、204804、设置任务参数分别为handle_led1 和 handle_led2设置任务的参数类型是void*:(void *)led1、(void *)led205、设置优先级分别为handle_led1 和 handle_led2设置任务的优先级分别为1、106、设置任务句柄分别为handle_led1 和 handle_led2设置任务句柄TaskHandle_t task1; TaskHandle_t task2;3、完整xTaskCreate函数xTaskCreate(handle_led1,task1,2048,(void *)led1,1,task1);xTaskCreate(handle_led2,task2,2048,(void *)led2,1,task2);4、双 LED 小灯控制多任务执行#include Arduino.h#define LED1 23 // 控制第一颗LED灯的引脚#define LED2 22 // 控制第二颗LED灯的引脚TaskHandle_t task1;TaskHandle_t task2;void handle_led1(void *ptr){pinMode(LED1, OUTPUT);char *param (char *)ptr;Serial.print(param);Serial.println( started);while (1){digitalWrite(LED1, HIGH);vTaskDelay(1000);digitalWrite(LED1, LOW);vTaskDelay(1000);}vTaskDelete(NULL);}void handle_led2(void *ptr){pinMode(LED2, OUTPUT);char *param (char *)ptr;Serial.print(param);Serial.println( started);while (1){digitalWrite(LED2, HIGH);vTaskDelay(3000);digitalWrite(LED2, LOW);vTaskDelay(3000);}vTaskDelete(NULL);}void setup(){Serial.begin(115200);pinMode(LED_PIN1, OUTPUT);pinMode(LED_PIN2, OUTPUT);xTaskCreate(handle_led1, task1_led, 2048, (void *)led1, 1, task1);xTaskCreate(handle_led2, task2_led, 2048, (void *)led2, 1, task2);}void loop(){}五、其他函数1、vTaskDelete函数xTaskToDelete表示要删除的任务的句柄这个句柄就是上面xTaskCreate函数中传入的最后一个参数~如果参数设置为NULL表示删除当前任务。一般建议该函数在每个任务运行结束时传入NULL调用不建议跨任务删除有些情况下会产生一些不可预知的问题。2、 vTaskDelay函数delay的底层函数也是这个函数。void vTaskDelay( const TickType_t xTicksToDelay )xTicksToDelay是延迟的ticks数对于ESP32一个tick等于1ms所以要延迟3秒就应该调用vTaskDelay(3000)六、任务绑定CPU将创建的任务绑定到固定的cpu执行。BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pvTaskCode,const char * const pcName,const uint32_t usStackDepth,void * const pvParameters,UBaseType_t uxPriority,TaskHandle_t * const pvCreatedTask,const BaseType_t xCoreID);xCoreID 是cpu编号完整案例#include Arduino.h#define LED_PIN1 23 // 23号引脚#define LED_PIN2 22 // 22号引脚TaskHandle_t task1;TaskHandle_t task2;void handle_led1(void *pra){char *param (char *)pra;Serial.print(handle_led1 传入的数据参数是:);Serial.println(param);while (1){Serial.print(handle_led1 绑定的CPU是:);Serial.println(xPortGetCoreID());digitalWrite(LED_PIN1, HIGH);vTaskDelay(1000);digitalWrite(LED_PIN1, LOW);vTaskDelay(1000);}vTaskDelete(NULL);}void handle_led2(void *pra){char *param (char *)pra;Serial.print(handle_led2 传入的数据参数是:);Serial.println(param);while (1){Serial.print(handle_led2 绑定的CPU是:);Serial.println(xPortGetCoreID());digitalWrite(LED_PIN2, HIGH);vTaskDelay(3000);digitalWrite(LED_PIN2, LOW);vTaskDelay(3000);}vTaskDelete(NULL);}void setup(){Serial.begin(115200);pinMode(LED_PIN1, OUTPUT);pinMode(LED_PIN2, OUTPUT);xTaskCreatePinnedToCore(handle_led1, task1_led, 2048, (void *)led1, 1, task1, 0);xTaskCreatePinnedToCore(handle_led2, task2_led, 2048, (void *)led2, 1, task2, 1);}void loop(){}