ESP32入门——freeRTOS

devtools/2025/2/13 8:54:10/

一 任务状态

  1. 运行状态(Running)

    • 任务当前正在处理器上运行。在单核系统中,同一时间只有一个任务处于运行状态

  2. 就绪状态(Ready)

    • 任务已经准备好运行,但由于优先级较低或其它任务正在运行,暂时没有被调度执行。一旦调度器选择该任务,它将进入运行状态。

  3. 阻塞状态(Blocked)

    • 任务正在等待某个事件(如信号量、队列、延时等)发生。在事件发生之前,任务不会进入就绪状态。任务可以通过调用 vTaskDelay()xQueueReceive() 等函数进入阻塞状态。

  4. 挂起状态(Suspended)

    • 任务被显式挂起,调度器不会选择该任务运行。任务可以通过调用 vTaskSuspend() 进入挂起状态,并通过 vTaskResume()xTaskResumeFromISR() 恢复为就绪状态。

  5. 删除状态(Deleted)

    • 任务已被删除,但其资源(如堆栈)可能还未被释放。任务可以通过调用 vTaskDelete() 进入删除状态。

 


 

二. 任务创建

#include "freertos/FreeRTOS.h"

#include "freertos/task.h"

函数原型

BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pxTaskCode,          // 任务函数指针const char * const pcName,          // 任务名称(字符串)const uint32_t usStackDepth,         // 任务堆栈大小(以字为单位)void * const pvParameters,           // 传递给任务函数的参数UBaseType_t uxPriority,              // 任务优先级,数值越大,优先级越高。TaskHandle_t * const pxCreatedTask,  // 任务句柄(用于引用任务)const BaseType_t xCoreID             // 任务绑定的核心编号(0 或 1)
);

        返回值

  • pdPASS:任务创建成功。

  • pdFAIL:任务创建失败

BaseType_t xTaskCreate(TaskFunction_t pxTaskCode,          // 任务函数指针const char * const pcName,          // 任务名称(字符串)const uint32_t usStackDepth,         // 任务堆栈大小(以字为单位)void * const pvParameters,           // 传递给任务函数的参数UBaseType_t uxPriority,              // 任务优先级TaskHandle_t * const pxCreatedTask   // 任务句柄(用于引用任务)
);

        返回值

  • pdPASS:任务创建成功。

  • pdFAIL:任务创建失败

xTaskCreatexTaskCreatePinnedToCore 的区别

特性xTaskCreatexTaskCreatePinnedToCore
适用平台所有 FreeRTOS 平台仅 ESP-IDF(ESP32)
核心绑定任务可以在任意核心上运行可以绑定到指定核心(0 或 1)
多核支持依赖调度器分配核心显式指定核心
灵活性更通用,适合单核和多核系统更适合需要核心绑定的场景
函数来源FreeRTOS 标准 APIESP-IDF 扩展 API

声明功能函数

void taskA(void *param)
{while(1){//每隔500ms打印ESP_LOGI(TAG,"this is taskA");vTaskDelay(pdMS_TO_TICKS(500));}
}

运行任务

 xTaskCreatePinnedToCore(taskA,"taskA",2048,NULL,3,NULL,1);
 xTaskCreate(taskA, "taskA", 2048, NULL, 1, Null);

 


 

三.任务间同步

#include "freertos/semphr.h"

#include "freertos/queue.h"

#include "freertos/event_groups.h"

 

1. 信号量(Semaphore)

信号量用于任务间的同步或资源管理。FreeRTOS 提供了两种信号量:

  • 二进制信号量(Binary Semaphore)

  • 只能取值 0 或 1,用于任务间的简单同步。

  • xSemaphoreCreateBinary()//创建二进制信号量

SemaphoreHandle_t xSemaphore = NULL;void vTask1(void *pvParameters) {while (1) {// 任务 1 执行一些操作printf("Task 1 is running\n");xSemaphoreGive(xSemaphore);  // 释放信号量vTaskDelay(pdMS_TO_TICKS(1000));  // 延时 1 秒}
}void vTask2(void *pvParameters) {BaseType_t ret = 0;while(1){//无限等待二进制信号量,直到获取成功才返回ret = xSemaphoreTake(s_testBinSem,portMAX_DELAY);if(ret == pdTRUE){ESP_LOGI(TAG,"take binary semaphore");}}
}void app_main() {xSemaphore = xSemaphoreCreateBinary();  // 创建二进制信号量xTaskCreate(vTask1, "Task 1", 2048, NULL, 1, NULL);xTaskCreate(vTask2, "Task 2", 2048, NULL, 1, NULL);
}
  • 计数信号量(Counting Semaphore)

  • 可以取值大于 1,用于管理多个资源。

  • xSemaphoreCreateCounting(2,2);//输入最大值,及初始值

SemaphoreHandle_t xCountingSemaphore;void vTaskWorker(void *pvParameters) {while (1) {// 获取信号量(限制同时运行的任务数量)xSemaphoreTake(xCountingSemaphore, portMAX_DELAY);printf("Task %d is running\n", (int)pvParameters);vTaskDelay(pdMS_TO_TICKS(2000));  // 模拟任务运行xSemaphoreGive(xCountingSemaphore);  // 释放信号量printf("Task %d finished\n", (int)pvParameters);vTaskDelay(pdMS_TO_TICKS(1000));  // 延时 1 秒}
}void app_main() {// 创建计数信号量,最大值为 2,初始值为 2xCountingSemaphore = xSemaphoreCreateCounting(2, 2);if (xCountingSemaphore != NULL) {// 创建 4 个任务for (int i = 1; i <= 4; i++) {xTaskCreate(vTaskWorker, "Worker", 2048, (void *)i, 1, NULL);}} else {printf("Failed to create counting semaphore\n");}
}

 同一时间只有两个任务在运行

 

2. 互斥量(Mutex)

互斥量用于保护共享资源,确保同一时间只有一个任务可以访问资源。互斥量具有优先级继承机制,可以防止优先级反转问题。

  • xSemaphoreCreateMutex()//创建互斥量
SemaphoreHandle_t xMutex = NULL;
int sharedResource = 0;void vTask1(void *pvParameters) {while (1) {xSemaphoreTake(xMutex, portMAX_DELAY);  // 获取互斥量sharedResource++;  // 修改共享资源printf("Task 1: sharedResource = %d\n", sharedResource);xSemaphoreGive(xMutex);  // 释放互斥量vTaskDelay(pdMS_TO_TICKS(1000));}
}void vTask2(void *pvParameters) {while (1) {xSemaphoreTake(xMutex, portMAX_DELAY);  // 获取互斥量sharedResource--;  // 修改共享资源printf("Task 2: sharedResource = %d\n", sharedResource);xSemaphoreGive(xMutex);  // 释放互斥量vTaskDelay(pdMS_TO_TICKS(1000));}
}void app_main() {xMutex = xSemaphoreCreateMutex();  // 创建互斥量xTaskCreate(vTask1, "Task 1", 2048, NULL, 1, NULL);xTaskCreate(vTask2, "Task 2", 2048, NULL, 1, NULL);
}

 

3. 事件组(Event Group)

事件组用于任务间的复杂同步,允许任务等待多个事件的发生。

任务需要等待多个事件(如传感器数据就绪、网络连接成功等)同时发生。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"// 定义事件位
#define BIT_0 (1 << 0)  // 事件位 0
#define BIT_1 (1 << 1)  // 事件位 1
#define BIT_2 (1 << 2)  // 事件位 2EventGroupHandle_t xEventGroup;
static TaskHandle_t task1_handle = NULL; // 任务1句柄
static TaskHandle_t task2_handle = NULL; // 任务2句柄
void vTask1(void *pvParameters) {while (1) {printf("Task 1 is running\n");xEventGroupSetBits(xEventGroup, BIT_0);  // 设置事件位 0vTaskDelay(pdMS_TO_TICKS(1000));  // 延时 1 秒}
}void vTask2(void *pvParameters) {while (1) {printf("Task 2 is running\n");xEventGroupSetBits(xEventGroup, BIT_1);  // 设置事件位 1vTaskDelay(pdMS_TO_TICKS(2000));  // 延时 2 秒}
}void vTask3(void *pvParameters) {while (1) {// 等待事件位 0 和 1 都被设置EventBits_t uxBits = xEventGroupWaitBits(xEventGroup,  // 事件组句柄BIT_0 | BIT_1,  // 等待事件位 0 和 1pdTRUE,  // 在退出时清除事件位pdTRUE,  // 等待所有事件位portMAX_DELAY  // 无限等待);if ((uxBits & (BIT_0 | BIT_1))) {printf("Task 3: Both BIT_0 and BIT_1 are set\n");vTaskDelete(task1_handle);  // 删除任务vTaskDelete(task2_handle);  // 删除任务}}
}void app_main() {// 创建事件组xEventGroup = xEventGroupCreate();if (xEventGroup != NULL) {// 创建任务xTaskCreate(vTask1, "Task 1", 2048, NULL, 1, &task1_handle);xTaskCreate(vTask2, "Task 2", 2048, NULL, 1, &task2_handle);xTaskCreate(vTask3, "Task 3", 2048, NULL, 1, NULL);} else {printf("Failed to create event group\n");}
}

 

4. 队列(Queue)

队列用于任务间的数据传递,支持 FIFO(先进先出)或 LIFO(后进先出)模式。

//队列句柄
static QueueHandle_t s_testQueue;
//定义一个队列数据内容结构体
typedef struct
{int num;    //里面只有一个num成员,用来记录一下数据
}queue_packet;/** 队列任务A,用于定时向队列发送queue_packet数据* @param 无* @return 无
*/
void queue_taskA(void *param)
{int test_cnt = 0;while(1){queue_packet packet;packet.num = test_cnt++;//发送queue_packet数据xQueueSend(s_testQueue,&packet,pdMS_TO_TICKS(200));ESP_LOGI(TAG,"taskA send packet,num:%d",packet.num);//延时1000msvTaskDelay(pdMS_TO_TICKS(1000));}
}/** 队列任务B,用于从队列接收数据* @param 无* @return 无
*/
void queue_taskB(void *param)
{while(1){queue_packet packet;BaseType_t ret = xQueueReceive(s_testQueue,&packet,pdMS_TO_TICKS(200));if(ret == pdTRUE){//如果收到数据就打印出来ESP_LOGI(TAG,"taskB receive packet,num:%d",packet.num);}}
}/** 初始化队列例程* @param 无* @return 无
*/
void rtos_queue_sample(void)
{//初始化一个队列,队列单元内容是queue_packet结构体,最大长度是5s_testQueue = xQueueCreate(5,sizeof(queue_packet));//队列任务A,定时向队列发送数据xTaskCreatePinnedToCore(queue_taskA,"queue_taskA",2048,NULL,3,NULL,1);//队列任务B,从队列中接收数据xTaskCreatePinnedToCore(queue_taskB,"queue_taskB",2048,NULL,3,NULL,1);
}

 

5.任务通知(Task Notification)

//要使用任务通知,需要记录任务句柄
static TaskHandle_t s_notifyTaskAHandle;
static TaskHandle_t s_notifyTaskBHandle;/** 任务通知A,用于定时向任务通知B直接传输数据* @param 无* @return 无
*/
void notify_taskA(void* param)
{uint32_t rec_val = 0;while(1){if (xTaskNotifyWait(0x00, ULONG_MAX, &rec_val, pdMS_TO_TICKS(1000)) == pdTRUE){ESP_LOGI(TAG,"receive notify value:%lu",rec_val);}}
}/** 任务通知B,实时接收任务通知A的数据* @param 无* @return 无
*/
void notify_taskB(void* param)
{int notify_val = 0;while(1){xTaskNotify(s_notifyTaskAHandle, notify_val, eSetValueWithOverwrite);notify_val++;vTaskDelay(pdMS_TO_TICKS(1000));}
}/** 任务通知例程初始化* @param 无* @return 无
*/
void rtos_notify_sample(void)
{xTaskCreatePinnedToCore(notify_taskA,"notify_taskA",2048,NULL,3,&s_notifyTaskAHandle,1);xTaskCreatePinnedToCore(notify_taskB,"notify_taskB",2048,NULL,3,&s_notifyTaskBHandle,1);
}

 


注意:

1. 核心分配

  • 任务绑定到核心:FreeRTOS 默认将任务分配到任意核心,通过 xTaskCreatePinnedToCore() 将任务绑定到特定的核心。避免核心之间的资源争用,提高并发处理的效率。
  • 注意点:避免将所有任务都绑定到同一个核心,尽量平衡两个核心的负载。ESP32 的双核模式可以提高并行处理能力。通常,负责处理无线网络的任务(WIFI或者蓝牙)固定到CPU0上,而处理应用程序其余部分的任务将被固定到CPU1

2. 任务优先级

  • 优先级设置:FreeRTOS 使用优先级来决定任务调度的顺序,任务优先级范围是 0 到 configMAX_PRIORITIES - 1。优先级高的任务会优先执行。
  • 合理设置优先级:避免使用过高或过低的优先级。过高的优先级会导致低优先级任务长时间无法执行,造成系统不响应。过低的优先级可能导致高优先级任务得不到及时调度。
  • 注意实时性:对于实时性要求高的任务,应该分配较高的优先级,避免将所有任务都设置为高优先级。

3. 中断管理

  • 中断处理与任务:ESP32 中的中断处理函数(ISR)需要注意与 FreeRTOS 任务的协作。中断处理函数不能执行耗时的操作,应尽量简短。
  • 禁止在 ISR 中使用 FreeRTOS API:在中断服务程序(ISR)中不能直接调用大部分 FreeRTOS API(如 xSemaphoreTake()),可能会导致任务调度或等待,产生死锁或阻塞。
  • 使用 ISR 安全的 API:FreeRTOS 提供了针对 ISR 的 API,比如 xSemaphoreGiveFromISR()xQueueSendFromISR() 等,它们能够安全地在中断上下文中被调用。

4. 延时管理

 vTaskDelay 会让当前任务进入阻塞状态,直到延时的时间到达为止。它延时的单位是 系统时钟滴答(tick),每个滴答时长通过 configTICK_RATE_HZ 配置

  • 延时指定的时间(以 tick 为单位),然后将任务挂起(阻塞)。
  • 这个函数是基于 任务执行时间 的,也就是说任务延迟的时间是从它调用 vTaskDelay() 的那一刻开始算起的。

 vTaskDelayUntil() 的作用是让任务按照固定的周期进行延时。它可以确保任务在每次延时之后,按照指定的周期来执行。适合那些需要定期执行的任务,如定时器任务。

  • 根据传入的参数延迟任务的执行,使得任务的执行时间能够周期性地固定在某个时间点上。
  • 它是基于 绝对时间 的,任务的延时是从某个固定的时间点开始算起的,而不是从任务调用函数的时间开始计算。

http://www.ppmy.cn/devtools/158429.html

相关文章

Java进阶面试八股文

1、Java SE和Java EE区别&#xff1f; Java SE 是 Java 的基础版本&#xff0c;Java EE 是 Java 的高级版本。Java SE 更适合开发桌面应用程序或简单的服务器应用程序&#xff0c;Java EE 更适合开发复杂的企业级应用程序或 Web 应用程序。 2、JVM和JRE和JDK区别&#xff1f;…

Tailwind CSS 的核心理念

实用优先&#xff08;Utility-First&#xff09; Tailwind CSS 的最核心理念是"实用优先"。这种方法颠覆了传统的 CSS 开发方式&#xff0c;不再编写自定义的类名和样式规则&#xff0c;而是通过组合预定义的工具类来构建界面。这种方式带来了以下优势&#xff1a; …

自动化测试、压力测试、持续集成

因为项目的原因&#xff0c;前段时间研究并使用了 SoapUI 测试工具进行自测开发的 api。下面将研究的成果展示给大家&#xff0c;希望对需要的人有所帮助。 SoapUI 是什么&#xff1f; SoapUI 是一个开源测试工具&#xff0c;通过 soap/http 来检查、调用、实现 Web Service 的…

推荐算法实践:movielens数据集

MovieLens 数据集介绍 MovieLens 数据集是由明尼苏达大学的GroupLens研究小组维护的一个广泛使用的电影评分数据集&#xff0c;主要用于推荐系统的研究。该数据集包含用户对电影的评分、标签以及其他相关信息&#xff0c;是电影推荐系统开发与研究的常用数据源。 数据集版本 …

FPGA简介|结构、组成和应用

Field Programmable Gate Arrays&#xff08;FPGA&#xff0c;现场可编程逻辑门阵列&#xff09;&#xff0c;是在PAL、GAL、CPLD等可编程器件的基础上进一步发展的产物&#xff0c; 是作为专用集成电路&#xff08;ASIC&#xff09;领域中的一种半定制电路而出现的&#xff0c…

小蓝相机启动阶段trace学习笔记

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、启动阶段拆解概览 1. 启动阶段拆解表格 Camera 启动阶段拆机表格 S0 :System deliverInputEvent > activityStart 开始 S1 :Camera APP Camera…

singleTaskAndroid的Activity启动模式知识点总结

一. 前提知识 1.1. 任务栈知识 二. Activity启动模式的学习 2.1 standard 2.2 singleTop 2.3.singleTask 2.4.singleInstance 引言&#xff1a; Activity作为四大组件之一&#xff0c;也可以说Activity是其中最重要的一个组件&#xff0c;其负责调节APP的视图&#xff…

缓存组件<keep-alive>

缓存组件<keep-alive> 1.组件作用 组件, 默认会缓存内部的所有组件实例&#xff0c;当组件需要缓存时首先考虑使用此组件。 2.使用场景 场景1&#xff1a;tab切换时&#xff0c;对应的组件保持原状态&#xff0c;使用keep-alive组件 使用&#xff1a;KeepAlive | Vu…