FreeRTOS
入门文档
2025-05-12
ESP32的FreeRTOS入门
写在前边
弄这个文档的初衷是为了更快地入门ESP3RTOS2中的FreeRTOS,一寻思自己弄太慢了,就大家一起来完善吧。
文档内容肯定会有错误,在群里说一下改正即可,如果进度比较快or有地方需要完善,按照已有格式进行完善即可,众人拾柴火焰高,对吧。
文档说明
下边的函数或者某些参数如果没有看懂,可以参考《FreeRTOS基本函数大全》,需要结合PDF和AI了解。
文档的基本格式如下↓
某个大类1(使用“标题1”格式)
然后是这个大类的简短介绍,包括具体使用流程等等。
1**.具体流程1(使用“副标题”格式)(缩进一个TAB)**
包括具体的代码和介绍,介绍放代码前。(代码使用代码块填写)
2**.**具体流程2(格式同上)
包括具体的代码和介绍,介绍放代码前。
某个大类****2
1.****………………如此往下
目录
一些需要注意的
在ESP32中,没有unsigned char,被uint32替代。
注意任务创建时顺序问题,若任务A未完全创建,而此时B已经创建完成且调用了A中的数据,则程序会在运行时崩溃。
任务(Task)
任务换个说法,就是程序所运行在的那个基本框架。
具体使用流程:创建任务->调用任务
1**.**创建任务
TaskA是任务的名字,可任意更改
(任务也有句柄,为TaskHandle,直达任务通知用。)
void TaskA(void* param)
2**.**在运行中调用任务
通过xTaskCreate添加任务到内核中,这样就可以运行这个任务的程序了
void app_main(void)
{
xTaskCreatePinnedToCore(TaskA,”Read_from_queue”,2048,NULL,3,NULL,1);
//创建任务
xTaskCreatePinnedToCore(TaskB,”Write_to_queue”,2048,NULL,3,NULL,1);
}
BaseType_t xTaskCreatePinnedToCore(
TaskFunction_t pxTaskCode, // 任务函数指针
const char *const pcName, // 任务名称(字符串标识)
const uint32_t usStackDepth, // 任务堆栈大小(单位:字,1字=4字节)
void *const pvParameters, // 任务参数指针(传递给任务的参数)
UBaseType_t uxPriority, // 任务优先级(0~configMAX_PRIORITIES-1)
TaskHandle_t *const pxCreatedTask, // 任务句柄输出(可为NULL;保存创建的任务句柄,可用于后续操作)
const BaseType_t xCoreID // 绑定运行的CPU核心(0或1,或tskNO_AFFINITY)
);
队列(Queue)
队列,是一种先进先出(FIFO)的数据结构,用于在任务、中断或模块之间有序传递数据。
使用流程:
创建队列->向队列中发送/读取数据->不使用使删除队列
**1. ** 创建队列
先创建队列句柄,再使用xQueueCreate创建动态队列(必须可以被app_main调用)
定义队列句柄并初始化为空(需app_main中通过xQueueCreate创建队列后赋值)
QueueHandle_t queue_handle = NULL;
queue_handle = xQueueCreate (5,sizeof(queue_data_t));
**2. ** 向队列中发送/接受数据
xQueueReceive函数接收后返回pdTrue/ pdFALSE。
pdTRUE == xQueueReceive (queue_handle,&data,100)
通过队列句柄(queue_handle)向队列发送数据
xQueueSend(queue_handle,&data,100);
信号量(Semaphore)
信号量是一种同步机制,通过计数器控制多任务/线程对共享资源的访问。本质是一个非负整数,支持两种原子操作:获取(Take/P)和释放(Give/V),需手动释放(减少)信号量。
使用流程:
①创建信号量(成功后返回信号量句柄)->获取信号量->(若不用则删除信号量)
②单独释放信号量(相当于改变信号量)
**1. ** 创建信号量
类似创建队列,先使用创建信号量句柄再创建信号量
同队列,xSemaphoreCreate必须可以被app_main调用()
SemaphoreHandle_t bin_sem; //创建信号量句柄
bin_sem = xSemaphoreCreateBinary(); //通过之前定义的信号量句柄创建二进制信号量
**2. ** 释放信号量
此处只演示二进制信号量
xSemaphoreGive(bin_sem); //每隔1s释放一个信号量
vTaskDelay(pdMS_TO_TICKS(1000));
**3. ** 获取信号量(使用信号量)
if(pdTRUE == xSemaphoreTake(bin_sem,portMAX_DELAY))
//因为是二进制信号量,所以只有两个状态0/1
ESP_LOGI(“bin_sem”,”You got the SEM_BIN!”);
**互斥锁(**互斥量Mutex)
实际上使用信号量控制访问权限的,确保同一时间只有一个任务可以访问资源,避免数据竞争。
谁访问谁释放。当一个任务获取了锁(Take),其他任务就无法再获取该锁,直到原任务释放锁(Give)。
使用流程:获取->进行硬件/参数访问->释放
**1. ** 获取信号量
xSemaphoreTake(bin_sem,portMAX_DELAY)
**2. ** 释放信号量
xSemaphoreGive(bin_sem);
事件组(Event Groups)
这是一位事件位↓
0
0
0
1
0
↑↑↑这是一个五位事件组↑↑↑
通过事件组,同时记录多个事件的发生状态,0无1有。
使用流程:
创建事件组->等待事件组中某个标志位->设置标志位->清除标志位
**1. ** 创建事件组
同样,需要先创建事件组的句柄,再定义两事件位。
static EventGroupHandle_t test_event_handle;
//创建事件组句柄,名字为test_event_handle
test_event_handle = xEventGroupCreate();
//给test_event_handle创建事件组
**2. ** 读取&处理事件组
读取:xEventGroupWaitBits这个函数有返回值为事件组的16进制值,也就是表格中的00010B=>0x02,第三位表示是否清除标志位,第四位表示是否等待的标志位都成功了才返回,选择pdTURE时为与条件,选择pdFALSE时为或条件
event_group_data xEventGroupWaitBits(test_event_handle,BIT0BIT11,pdTRUE,pdFALSE,pdMS_TO_TICKS(5000));
//设置标志位
xEventGroupSetBits(
EventGroupHandle_t xEventGroup,//事件组句柄
const EventBits_t uxBitsToSet ); //设置哪个位
//清除标志位
xEventGroupClearBits(
EventGroupHandle_t xEventGroup,//事件组句柄
const EventBits_t uxBitsToClear ); //设置哪个位
直达任务通知(Task Notify)
直接向任务发送通知并携带数据,而无需通过队列/信号量等中间象(单向通信,一发一收)。
使用流程:发送通知->接收通知
**1. ** 发送通知
先创建任务句柄,再通过函数发送
static TaskHandle_t TaskA_Handle;
static TaskHandle_t TaskB_Handle;
xTaskNotify(TaskA_Handle,value,eSetValueWithOverwrite);
//把一个32位的值value发送到“TaskA_Handle”任务中,发送的模式位第三个参数“eSetValue”
**2. ** 等待接收通知
先创建一个值用来接收数据,再使用xTaskNotifyWait接收
xTaskNotifyWait接收自身任务的句柄中的数据,传到TaskNotify_data中。
xTaskNotifyWait(
uint32_t ulBitsToClearOnEntry, // 进入时清除的位掩码
uint32_t ulBitsToClearOnExit, // 退出时清除的位掩码
uint32_t *pulNotificationValue, // 输出参数:接收通知值
TickType_t xTicksToWait // 超时时间(单位:Tick)
);
例:
uint32_t TaskBNotify_data = 0;
xTaskNotifyWait(0x00,ULONG_MAX,&TaskNotify_data,portMAX_DELAY);

