目录
1、3个核心文件
2、Task四种输入参数
2.1 整数形
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
void myTask(void * pvParam)
{
int *pInt;
pInt = (int *)pvParam;
while (1)
{
printf("i got a %d\r\n",*pInt);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
int testNum = 1;
void app_main(void)
{
xTaskCreate(myTask,"myTask",2048,(void *)&testNum,1,NULL);
}
2.2 整数形数组
void myTask(void *pvParam)
{
int *pArrayAddr;
pArrayAddr = (int *)pvParam;
while (1)
{
printf("i got a %d,%d,%d\r\n", *pArrayAddr, *(pArrayAddr + 1), *(pArrayAddr + 2));
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
int testNum[] = {1, 2, 3};
void app_main(void)
{
xTaskCreate(myTask, "myTask", 2048, (void *)testNum, 1, NULL);
}
2.3 结构体
typedef struct A_STRUCT
{
int iMem1;
int iMem2;
} xStruct;
xStruct xStrTest = {8, 9};
void myTask(void *pvParam)
{
xStruct *pStrTest;
pStrTest = (xStruct *)pvParam;
while (1)
{
printf("i got a %d,%d\r\n", pStrTest->iMem1, pStrTest->iMem2);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
xTaskCreate(myTask, "myTask", 2048, (void *)&xStrTest, 1, NULL);
}
2.4 字符串
static const char *pcTxt = "little one";
void myTask(void *pvParam)
{
char *pcTxtInTask;
pcTxtInTask = (char *)pvParam;
while (1)
{
printf("i got a %s\r\n", pcTxtInTask);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
xTaskCreate(myTask, "myTask", 2048, (void *)pcTxt, 1, NULL);
}
3、Task优先级
3.1 最大优先级
UBaseType_t ipriority = 0;
TaskHandle_t pxTask = NULL;
xTaskCreate(myTask, "myTask", 2048, (void *)pcTxt,5, &pxTask);
ipriority = uxTaskPriorityGet(pxTask);
printf("ipriority = %d\r\n", ipriority);
- ESP32S3 是Xtens核所以配置文件看
3.2 同等优先级任务
- 谁先创建谁先运行然后时间片轮询
- 句柄就是任务地址
void myTask1(void *pvParam)
{
while (1)
{
printf("myTask1\r\n");
vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s delay
}
}
void myTask2(void *pvParam)
{
while (1)
{
printf("myTask2\r\n");
vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s delay
}
}
void app_main(void)
{
xTaskCreate(myTask1, "myTask1", 2048,NULL, 5, NULL);
xTaskCreate(myTask2, "myTask2", 2048,NULL, 5, NULL);
}
3.3 不等优先级任务
- 高优先级先运行
- ESP32设置优先级无效,是ESP32双核运行导致的。在SDK配置编辑器(menuconfig)中勾选 "Run FreeRTOS only on first core" 就好了。
- 上述方法试了还是不管用,解决办法将两个任务都分配到指定内核
void myTask1(void *pvParam)
{
BaseType_t id;
while (1)
{
id = xPortGetCoreID();
printf("myTask1-coreID:%d\r\n",id);
vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s delay
}
}
void myTask2(void *pvParam)
{
BaseType_t id;
while (1)
{
id = xPortGetCoreID();
printf("myTask2-coreID:%d\r\n",id);
vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s delay
}
}
void app_main(void)
{
UBaseType_t iPriority = 0;
TaskHandle_t pxTask;
// xTaskCreate(myTask1, "myTask1", 2048,NULL, 1,&pxTask) ;
// xTaskCreate(myTask2, "myTask2", 2048,NULL, 2, NULL);
xTaskCreatePinnedToCore(myTask1, "myTask1", 2048,NULL, 1,&pxTask,1) ;
xTaskCreatePinnedToCore(myTask2, "myTask1", 2048,NULL, 2,&pxTask,1) ;
vTaskPrioritySet(pxTask,3);
iPriority = uxTaskPriorityGet(pxTask);
printf("iPriority = %d\n",iPriority);
}
3.4 修改优先级别
vTaskPrioritySet(pxTask,3);
4、Task挂起和恢复
4.1 任务的四态
- Runing、Ready、Blocked、Suspended
4.2 任务挂起恢复
- 一个任务不能独占CPU运行时间过久,会触发看门狗重启系统。
vTaskSuspend(pxTask);
vTaskResume(pxTask);
vTaskSuspened(句柄) 挂起函数
vTaskResume(句柄) 恢复挂起的函数
vTaskSuspenedAll() 挂起所有函数,挂起后不可以执行FreeRTOS自身的函数
vTaskResumeAll 恢复所有挂起函数
4.3 挂起全部任务
void myTask1(void *pvParam) { printf("Test begin\n"); vTaskSuspendAll(); //挂起调度器系统所有任务被挂起停止,CPU全部时间在执行下面的for循环 //并且在vTaskSuspendAll 与 xTaskResumeAll 之间不不能再调用FreeRTOS的API,自己的函数是可以的。 for (int i = 0; i < 9999; i++) { for (int j = 0; j < 5555; j++) { ; } } xTaskResumeAll(); printf("Test end\n"); //恢复调度器 vTaskDelete(NULL); //删除该任务 } void myTask2(void *pvParam) { BaseType_t id; while (1) { id = xPortGetCoreID(); printf("myTask2-coreID:%d\r\n",id); vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s delay } } void app_main(void) { TaskHandle_t pxTask; xTaskCreate(myTask1, "myTask1", 2048,NULL, 1,&pxTask) ; xTaskCreate(myTask2, "myTask2", 2048,NULL, 2, NULL); }
5、Task系统信息显示
void app_main(void) { TaskHandle_t pxTask; xTaskCreate(myTask1, "myTask1", 2048,NULL, 1,&pxTask) ; xTaskCreate(myTask2, "myTask2", 2048,NULL, 2, NULL); static char pcWriteBuffer[512] = {0}; while (1) { vTaskList(pcWriteBuffer); printf("------------------------------------------------\n"); printf("Name State Priority Stack Num\n"); printf("%s\n",pcWriteBuffer); vTaskDelay(3000 / portTICK_PERIOD_MS); // 1s delay } }
For example ESP-IDF system tasks (like ipc, timer tasks etc.) will also have that extra stack space allocated. So this feature should be used with care.
在 ESP32 中,IPC0 和 IPC1 分别指代两个不同的 IPC 总线。
IPC0 是 ESP32 的主 IPC 总线,用于连接主处理器(CPU0)与其他辅助处理器(如协处理器 CPU1)之间的通信。主要用于在不同处理器之间传输数据和命令,以实现协同工作和数据交换。
IPC1 是 ESP32 的备用 IPC 总线,也可用于进行处理器之间的通信。它可以与其他外设进行接口连接用于特定的应用。
这两个 IPC 总线可以实现高效的数据交换和通信,帮助 ESP32 在多处理器系统中实现强大的功能和性能。
6、Task堆栈设置和调试
- 用uxTaskGetStackHighWaterMark()来取得指定Task运行过程中stack剩余的值,相交与上一个方法用到的资源更加少;
- vxTaskGetStackHighWaterMark(句柄) 可以查看当前函数剩余的内存大小(创建任务时分配) ,如果不够会导致程序不断重启
分配的数量每+1,内存+4byte
void app_main(void) { TaskHandle_t pxTask; xTaskCreate(myTask1, "myTask1", 2048,NULL, 1,&pxTask) ; xTaskCreate(myTask2, "myTask2", 2048,NULL, 2, NULL); UBaseType_t iStack; while (1) { iStack = uxTaskGetStackHighWaterMark(pxTask); printf("IDLE Stack %d\n",iStack); vTaskDelay(3000 / portTICK_PERIOD_MS); // 1s delay } }
7、Task看门狗
7.1 中断看门狗
- 防止在中断里面运行时间过长,会触发中断看门狗;
7.2 Task看门狗
- 缺省状态下监控IDLE 任务,如果5s后没被运行会触发看门狗重启系统;
void myTask1(void *pvParam) { BaseType_t id; while (1) { } } void app_main(void) { TaskHandle_t pxTask; xTaskCreate(myTask1, "myTask1", 2048,NULL, 1,&pxTask) ; }
错误信息中提到的任务 “IDLE (CPU 0)” 意味着CPU 0上的空闲任务(也就是系统任务),未能及时重置任务看门狗。而任务 “myTask1” 则是当前在 CPU 0 上正在运行的任务。
将任务优先级改成和空闲任务一样的优先级0,这样两个任务同等优先级,时间片轮训IDLE任务可以得到运行就不会触发。
7.3 将自己的任务加到监控列表
#include <stdio.h> #include <inttypes.h> #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_chip_info.h" #include "esp_flash.h" #include "esp_task_wdt.h" void myTask1(void *pvParam) { while (1) { printf("myTask1\r\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s delay } } void myTask2(void *pvParam) { esp_task_wdt_add(NULL); //task_handle为NULL表示添加本任务到列表 while (1) { printf("myTask2\r\n"); vTaskDelay(6000 / portTICK_PERIOD_MS); // 6s delay esp_task_wdt_reset(); //喂狗 } } void app_main(void) { TaskHandle_t pxTask; xTaskCreate(myTask1, "myTask1", 2048,NULL, 1,&pxTask) ; xTaskCreate(myTask2, "myTask2", 2048,NULL, 1,NULL) ; }
系统默认的触发时间为5s,这里延迟了6s后才喂狗,所以会触发看门狗。
8、队列的三种数据传递
8.1 整形
- freertos的队列是值传递,虽然传进去的指针。
#include <stdio.h> #include <inttypes.h> #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_chip_info.h" #include "esp_flash.h" #include "freertos/queue.h" void sendTask(void *pvParam) { QueueHandle_t QHandle; QHandle = (QueueHandle_t)pvParam; BaseType_t xStatus; int i = 0; while (1) { xStatus = xQueueSend(QHandle, &i, 0); if (pdPASS == xStatus) printf("send done\n"); else printf("send fail\n"); i++; if (i == 8) i = 0; vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s delay } } void recTask(void *pvParam) { QueueHandle_t QHandle; QHandle = (QueueHandle_t)pvParam; BaseType_t xStatus; int j = 0; while (1) { if (0 != uxQueueMessagesWaiting(QHandle)) // 优化:在读取数据时判断队列中有没有数据,有多少个数据在队列中等待 { xStatus = xQueueReceive(QHandle, &j, 0); if (pdPASS == xStatus) printf("rec j = %d\n", j); else printf("rec fail\n"); } else { printf("no data!\n"); } vTaskDelay(1000 / portTICK_PERIOD_MS); // 6s delay } } void app_main(void) { QueueHandle_t QHandle; QHandle = xQueueCreate(5, sizeof(int)); if (NULL != QHandle) { printf("Create queue successfully\n"); xTaskCreate(sendTask, "sendTask", 2048, (void *)QHandle, 0, NULL); xTaskCreate(recTask, "recTask", 2048, (void *)QHandle, 0, NULL); } else { printf("Can't crate queue!\n"); } }
8.2 结构体
typedef struct A_STRUCT { char id; char data; }xStruct; void sendTask(void *pvParam) { QueueHandle_t QHandle; QHandle = (QueueHandle_t)pvParam; BaseType_t xStatus; xStruct xUSB = {1,55}; while (1) { xStatus = xQueueSend(QHandle, &xUSB, 0); if (pdPASS == xStatus) printf("send done\n"); else printf("send fail\n"); xUSB.id++; if (xUSB.id == 8) xUSB.id = 0; vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s delay } } void recTask(void *pvParam) { QueueHandle_t QHandle; QHandle = (QueueHandle_t)pvParam; BaseType_t xStatus; xStruct xUSB = {0,0}; while (1) { if (0 != uxQueueMessagesWaiting(QHandle)) // 优化:在读取数据时判断队列中有没有数据,有多少个数据在队列中等待 { xStatus = xQueueReceive(QHandle, &xUSB, 0); if (pdPASS == xStatus) printf("rec id = %d data = %d \n", xUSB.id , xUSB.data); else printf("rec fail\n"); } else { printf("no data!\n"); } vTaskDelay(1000 / portTICK_PERIOD_MS); // 6s delay } } void app_main(void) { QueueHandle_t QHandle; QHandle = xQueueCreate(5, sizeof(xStruct)); if (NULL != QHandle) { printf("Create queue successfully\n"); xTaskCreate(sendTask, "sendTask", 2048, (void *)QHandle, 0, NULL); xTaskCreate(recTask, "recTask", 2048, (void *)QHandle, 0, NULL); } else { printf("Can't crate queue!\n"); } }
8.3 指针
void sendTask(void *pvParam) { QueueHandle_t QHandle; QHandle = (QueueHandle_t)pvParam; BaseType_t xStatus; char *pStrToSend; int i=0; while (1) { pStrToSend = (char *)malloc(50); // 分配内存,这里在while循环里一直分配内存所以接受任务要收到一个释放一个不然会内存泄漏 snprintf(pStrToSend, 50, "Todat is a good day %d!\r\n", i); // 给内存赋值 xStatus = xQueueSend(QHandle, &pStrToSend, 0); //如果是pStrToSend传递的是指针的值 &pStrToSend传递是指向数据指针的指针 if (pdPASS == xStatus) // printf("send done\n"); else printf("send fail\n"); i++; vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s delay } } void recTask(void *pvParam) { QueueHandle_t QHandle; QHandle = (QueueHandle_t)pvParam; BaseType_t xStatus; char *pStrToRec; while (1) { if (0 != uxQueueMessagesWaiting(QHandle)) // 优化:在读取数据时判断队列中有没有数据,有多少个数据在队列中等待 { xStatus = xQueueReceive(QHandle, &pStrToRec, 0); if (pdPASS == xStatus) printf("rec str %s\n", pStrToRec); else printf("rec fail\n"); free(pStrToRec); //内存释放 } else { printf("no data!\n"); } vTaskDelay(1000 / portTICK_PERIOD_MS); // 6s delay } } void app_main(void) { QueueHandle_t QHandle; QHandle = xQueueCreate(5, sizeof(char *)); if (NULL != QHandle) { printf("Create queue successfully\n"); xTaskCreate(sendTask, "sendTask", 2048, (void *)QHandle, 0, NULL); xTaskCreate(recTask, "recTask", 2048, (void *)QHandle, 0, NULL); } else { printf("Can't crate queue!\n"); } }
- `&pStrToSend` 是将 `pStrToSend` 的地址传递给 `xQueueSend` 函数,以便函数能够在队列中发送 `pStrToSend` 所指向的数据。在这种情况下,`xQueueSend` 函数期望接收一个指向数据的指针的指针(即指向指针的指针),因此需要使用 `&` 操作符获取指针的地址。
- 如果传递的是 `pStrToSend`,则实际上是将指针的值(内存地址)传递给 `xQueueSend` 函数,而不是指向 `pStrToSend` 所指向的数据。这可能会导致函数无法正确地访问和发送数据。所以需要使用 `&` 操作符获取指针的地址,以便函数能够正确地操作数据。
9、 队列模型
9.1 队列的多进单出
void sendTask1(void *pvParam) { QueueHandle_t QHandle; QHandle = (QueueHandle_t)pvParam; BaseType_t xStatus; int i=111; while (1) { xStatus = xQueueSend(QHandle, &i, 0); if (pdPASS == xStatus) printf("Task1 send done\n"); else printf("Task1 send fail\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s delay } } void sendTask2(void *pvParam) { QueueHandle_t QHandle; QHandle = (QueueHandle_t)pvParam; BaseType_t xStatus; int i=222; while (1) { xStatus = xQueueSend(QHandle, &i, 0); if (pdPASS == xStatus) printf("Task2 send done\n"); else printf("Task2 send fail\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s delay } } void recTask(void *pvParam) { QueueHandle_t QHandle; QHandle = (QueueHandle_t)pvParam; BaseType_t xStatus; int i = 0; while (1) { xStatus = xQueueReceive(QHandle, &i, portMAX_DELAY); if (pdPASS == xStatus) printf("rec str %d\n", i); else printf("rec fail\n"); //vTaskDelay(1000 / portTICK_PERIOD_MS); // 6s delay 此处不能有延时可能会导致另外一个任务发送失败, //因为两个发送任务的优先级一样,轮训发送这里再延迟的话会导致另外一个发送失败 } } void app_main(void) { QueueHandle_t QHandle; QHandle = xQueueCreate(5, sizeof(int)); if (NULL != QHandle) { printf("Create queue successfully\n"); xTaskCreate(sendTask1, "sendTask1", 2048, (void *)QHandle, 1, NULL); xTaskCreate(sendTask2, "sendTask2", 2048, (void *)QHandle, 1, NULL); xTaskCreate(recTask, "recTask", 2048, (void *)QHandle, 2, NULL); } else { printf("Can't crate queue!\n"); } }
9.2 队列集合
void sendTask1(void *pvParam) { QueueHandle_t QHandle; QHandle = (QueueHandle_t)pvParam; BaseType_t xStatus; int i = 111; while (1) { xStatus = xQueueSend(QHandle, &i, 0); if (pdPASS == xStatus) printf("Task1 send done\n"); else printf("Task1 send fail\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s delay } } void sendTask2(void *pvParam) { QueueHandle_t QHandle; QHandle = (QueueHandle_t)pvParam; BaseType_t xStatus; int i = 222; while (1) { xStatus = xQueueSend(QHandle, &i, 0); if (pdPASS == xStatus) printf("Task2 send done\n"); else printf("Task2 send fail\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s delay } } void recTask(void *pvParam) { QueueSetHandle_t QueueSet; QueueSet = (QueueHandle_t)pvParam; QueueSetMemberHandle_t QueueDate; BaseType_t xStatus; int i = 0; while (1) { QueueDate = xQueueSelectFromSet(QueueSet,portMAX_DELAY); //获得queueSet中有数据的队列 xStatus = xQueueReceive(QueueDate, &i, portMAX_DELAY); if (pdPASS == xStatus) printf("rec str %d\n", i); else printf("rec fail\n"); } } void app_main(void) { QueueHandle_t QHandle1; QueueHandle_t QHandle2; QHandle1 = xQueueCreate(5, sizeof(int)); QHandle2 = xQueueCreate(5, sizeof(int)); QueueSetHandle_t QueueSet; QueueSet = xQueueCreateSet(10); // 创建Queue xQueueAddToSet(QHandle1, QueueSet); // 加入Queue xQueueAddToSet(QHandle2, QueueSet); if ((NULL != QHandle1) && (NULL != QHandle2) && (NULL != QueueSet)) { printf("Create queue successfully\n"); xTaskCreate(sendTask1, "sendTask1", 2048, (void *)QHandle1, 1, NULL); xTaskCreate(sendTask2, "sendTask2", 2048, (void *)QHandle2, 1, NULL); xTaskCreate(recTask, "recTask", 2048, (void *)QueueSet, 2, NULL); } else { printf("Can't crate queue!\n"); } }
9.3 队列邮箱
void writeTask(void *pvParam) { QueueHandle_t Mailbox; Mailbox = (QueueHandle_t)pvParam; BaseType_t xStatus; int i = 0; while (1) { xStatus = xQueueOverwrite(Mailbox,&i); if (pdPASS == xStatus) printf("writeTask send done\n"); else printf("writeTask send fail\n"); i++; vTaskDelay(6000 / portTICK_PERIOD_MS); // 6s delay } } void readTask(void *pvParam) { QueueHandle_t Mailbox; Mailbox = (QueueHandle_t)pvParam; BaseType_t xStatus; int i = 0; while (1) { xStatus = xQueuePeek(Mailbox, &i,0); if (pdPASS == xStatus) printf("read %d\n", i); else printf("read fail\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s delay } } void app_main(void) { QueueHandle_t Mailbox; Mailbox = xQueueCreate(1, sizeof(int)); if (NULL != Mailbox) { printf("Create queue successfully\n"); xTaskCreate(writeTask, "writeTask", 2048, (void *)Mailbox, 1, NULL); xTaskCreate(readTask, "readTask1", 2048, (void *)Mailbox, 2, NULL); xTaskCreate(readTask, "readTask2", 2048, (void *)Mailbox, 2, NULL); xTaskCreate(readTask, "readTask3", 2048, (void *)Mailbox, 2, NULL); } else { printf("Can't crate queue!\n"); } }
10 、软件定时器
#include "freertos/timers.h" void TimerCallBack(TimerHandle_t xTimer) { const char *strName; int *id; strName = pcTimerGetName(xTimer); id = (int *)pvTimerGetTimerID(xTimer); printf("Timer Name %s ID is %d!\n", strName, *id); } void app_main(void) { static int id1 = 1; // 这里要注意ID值,如果直接传入的是数字(void *)1,这里是报地址1传过去,任务中是(uint32_t)pvTimerGetTimerID(xTimer); static int id2 = 2; // 如果是先定义ID号再去ID地址(void *)&id1传入这里出入的就是ID号的地址,但是这个ID号不能是局部变量会被销毁,要是全局变量或者是用static修饰 TimerHandle_t xTimer1; TimerHandle_t xTimer2; xTimer1 = xTimerCreate("Timer1", pdMS_TO_TICKS(1000), pdTRUE, (void *)&id1, TimerCallBack); xTimer2 = xTimerCreate("Timer2", pdMS_TO_TICKS(2000), pdTRUE, (void *)&id2, TimerCallBack); xTimerStart(xTimer1, 0); xTimerStart(xTimer2, 0); vTaskDelay(pdMS_TO_TICKS(4000)); xTimerChangePeriod(xTimer1, pdMS_TO_TICKS(5000), 0); // 改变运行周期 }
11、 信号量
11.1 二进制信号量
- 信号量创建以后为空状态,在获取信号量之前首先要释放信号量。
#include "freertos/semphr.h" SemaphoreHandle_t semphrHandle; int iCont = 0; void myTask1(void *pvParam) { while (1) { xSemaphoreTake(semphrHandle, portMAX_DELAY); for (int i = 0; i < 10; i++) { iCont++; printf("my task1 iCont = %d\n", iCont); vTaskDelay(pdMS_TO_TICKS(1000)); } xSemaphoreGive(semphrHandle); vTaskDelay(pdMS_TO_TICKS(1000)); } } void myTask2(void *pvParam) { while (1) { xSemaphoreTake(semphrHandle, portMAX_DELAY); for (int i = 0; i < 10; i++) { iCont++; printf("my task2 iCont = %d\n", iCont); vTaskDelay(pdMS_TO_TICKS(1000)); } xSemaphoreGive(semphrHandle); vTaskDelay(pdMS_TO_TICKS(1000)); } } void app_main(void) { semphrHandle = xSemaphoreCreateBinary(); xSemaphoreGive(semphrHandle); xTaskCreate(myTask1, "myTask1", 2048, NULL, 1, NULL); xTaskCreate(myTask2, "myTask2", 2048, NULL, 1, NULL); }
不用信号量
11.2 计数型信号量
SemaphoreHandle_t semphrHandle; int iCont = 0; void carInTask(void *pvParam) { int emptySpace = 0; BaseType_t iResult; while (1) { emptySpace = uxSemaphoreGetCount(semphrHandle); //检测是否有空的车位 printf("emptySpace = %d\n", emptySpace); iResult = xSemaphoreTake(semphrHandle, 0); //获取一个空车位 if (pdTRUE == iResult) { printf("One car in!\n"); } else { printf("No Space!\n"); } vTaskDelay(pdMS_TO_TICKS(1000)); } } void carOutTask(void *pvParam) { while (1) { vTaskDelay(pdMS_TO_TICKS(6000)); xSemaphoreGive(semphrHandle); //释放一个车位 printf("One car out!\n"); } } void app_main(void) { semphrHandle = xSemaphoreCreateCounting(5,5); xSemaphoreGive(semphrHandle); xTaskCreate(carInTask, "carInTask", 2048, NULL, 1, NULL); xTaskCreate(carOutTask, "carOutTask", 2048, NULL, 1, NULL); }
12、互斥量
- 优先级别继承
- 为什么任务1做耗时操作时有延时,这里延时的时候不会切换到空闲任务,导致触发看门狗
当task1在开始阻塞自身的时候,如果task2, task3还在处于while循环之前的阻塞状态, idle task有机会运行,一旦task2进入while循环, idle task就没有机会运行了,因为task2优先级别比task1和idle的都高, 会优先运行. 周期切换只是在task2和task3之间切换.
- 为什么任务3获取信号量后任务1还能运行
分配的核心不一样,pintocore api把这任务放到一个核心上
- xSemaphoreTake(mutexHandle, 1000);的1000意思
1000的tick=10000ms,也就是10s
- 作为互斥量,不需要give,创建后可以直接拿到.
在task3拿到mutex后, 任务只会在task2和task3之间切换,连task1都不会有机会执行. 因为 task2的while循环中没有阻塞自己,只有高优先级别的task3才可以有机会运行. 空闲任务的优先级别是0, 比task1低, 更加不执行. 不过, 如果不是用mutex, 而是用二进制信号量的时候, 在创建二进制信号量的时候,还应该give释放一下信号量才行. 否则,task1拿不到二进制信号量, 但是, 作为互斥量,不需要give,创建后可以直接拿到.
SemaphoreHandle_t mutexHandle; void Task1(void *pvParam) { BaseType_t iRet; while (1) { printf("Task1 begin!\n"); iRet = xSemaphoreTake(mutexHandle,1000); if(iRet == pdPASS) { printf("Task1 take!\n"); for (int i = 0; i < 50; i++) { printf("Task1 i =%d\n", i); vTaskDelay(pdMS_TO_TICKS(1000)); } xSemaphoreGive(mutexHandle); printf("Task1 give!\n"); vTaskDelay(pdMS_TO_TICKS(5000)); } else { printf("Task1 didn't take!\n"); vTaskDelay(pdMS_TO_TICKS(1000)); } } } void Task2(void *pvParam) { printf("Task2 begin!\n"); vTaskDelay(pdMS_TO_TICKS(1000)); //阻塞Task2让Task1运行 while (1) { ; } } void Task3(void *pvParam) { printf("Task3 begin!\n"); vTaskDelay(pdMS_TO_TICKS(1000)); //阻塞Task3让Task2运行 BaseType_t iResult; while (1) { iResult = xSemaphoreTake(mutexHandle, 1000);//1000的tick=10000ms,也就是10s if (pdPASS == iResult) { printf("Task3 take!\n"); for (int i = 0; i < 10; i++) { printf("Task3 i =%d\n", i); vTaskDelay(pdMS_TO_TICKS(1000)); } xSemaphoreGive(mutexHandle); printf("Task3 give!\n"); vTaskDelay(pdMS_TO_TICKS(5000)); } else { printf("Task3 didn't take!\n"); vTaskDelay(pdMS_TO_TICKS(1000)); } } } void app_main(void) { mutexHandle = xSemaphoreCreateMutex(); // mutexHandle = xSemaphoreCreateBinary(); // xSemaphoreGive(mutexHandle); vTaskSuspendAll(); // 任务调度器挂起 xTaskCreatePinnedToCore(Task1, "Task1", 2048, NULL, 1, NULL,0); xTaskCreatePinnedToCore(Task2, "Task2", 2048, NULL, 2, NULL,0); xTaskCreatePinnedToCore(Task3, "Task3", 2048, NULL, 3, NULL,0); xTaskResumeAll(); // 恢复任务调度器 }
13 、递归互斥量
void Task1(void *pvParam) { BaseType_t iRet; while (1) { printf("-------------\n"); printf("task1 begin\n"); xSemaphoreTakeRecursive(mutexHandle, portMAX_DELAY); // 获得一个递归锁\取得资源A printf("task1 task A\n"); for (int i = 0; i < 4; i++) { printf("task1 i =%d for A\n", i); vTaskDelay(pdMS_TO_TICKS(1000)); } xSemaphoreTakeRecursive(mutexHandle, portMAX_DELAY); // 获得一个递归锁\取得资源B printf("task1 task B\n"); for (int i = 0; i < 4; i++) { printf("task1 i =%d for B\n", i); vTaskDelay(pdMS_TO_TICKS(1000)); } printf("task1 give B\n"); xSemaphoreGiveRecursive(mutexHandle); // 释放一个锁 vTaskDelay(pdMS_TO_TICKS(3000)); printf("task1 give A\n"); xSemaphoreGiveRecursive(mutexHandle); // 释放一个锁,即要释放两次锁另外一个任务才能得到锁 vTaskDelay(pdMS_TO_TICKS(3000)); } } void Task2(void *pvParam) { vTaskDelay(pdMS_TO_TICKS(1000)); // 阻塞Task2先让Task1运行得到互斥锁 while (1) { printf("-------------\n"); printf("task2 begin\n"); xSemaphoreTakeRecursive(mutexHandle, portMAX_DELAY); // 获得一个递归锁\取得资源A printf("task2 task A\n"); for (int i = 0; i < 4; i++) { printf("task2 i =%d for A\n", i); vTaskDelay(pdMS_TO_TICKS(1000)); } printf("task2 give A\n"); xSemaphoreGiveRecursive(mutexHandle); vTaskDelay(pdMS_TO_TICKS(3000)); } } void app_main(void) { mutexHandle = xSemaphoreCreateRecursiveMutex(); vTaskSuspendAll(); // 任务调度器挂起 xTaskCreatePinnedToCore(Task1, "Task1", 2048, NULL, 1, NULL, 0); xTaskCreatePinnedToCore(Task2, "Task2", 2048, NULL, 2, NULL, 0); xTaskResumeAll(); // 恢复任务调度器 }
14、 事件组
14.1 事件组的等待
#include "freertos/event_groups.h" #define BIT_0 (1 << 0) #define BIT_4 (1 << 4) EventGroupHandle_t xCreatedEventGroup; void Task1(void *pvParam) { while (1) { printf("Task1 begin to wait!\n"); xEventGroupWaitBits(xCreatedEventGroup, BIT_0 | BIT_4, pdTRUE, pdTRUE, portMAX_DELAY); printf("Task1 BIT_0 AND BIT_4 is set!\n"); vTaskDelay(pdMS_TO_TICKS(1000)); } } void Task2(void *pvParam) { vTaskDelay(pdMS_TO_TICKS(1000)); // 阻塞Task2先让Task1运行得到互斥锁 while (1) { printf("Task2 begin to set bit0!\n"); xEventGroupSetBits(xCreatedEventGroup,BIT_0); vTaskDelay(pdMS_TO_TICKS(5000)); printf("Task2 begin to set bit4!\n"); xEventGroupSetBits(xCreatedEventGroup,BIT_4); vTaskDelay(pdMS_TO_TICKS(5000)); } } void app_main(void) { xCreatedEventGroup = xEventGroupCreate(); if (xCreatedEventGroup == NULL) { printf("Event group creat fail!\n"); } else { vTaskSuspendAll(); xTaskCreatePinnedToCore(Task1, "Task1", 2048, NULL, 1, NULL, 0); xTaskCreatePinnedToCore(Task2, "Task2", 2048, NULL, 2, NULL, 0); xTaskResumeAll(); } }
14.2 事件组的同步
每个任务设置完自身一个位后进入阻塞态,只有但每个任务都设置完后三个任务才会一起开始运行。事件组的等待中是设置任务设置完后就继续执行自己的任务。
#include "freertos/event_groups.h" #define TASK_BIT_0 (1 << 0) #define TASK_BIT_1 (1 << 1) #define TASK_BIT_2 (1 << 2) #define ALL_SYNC_BITS (TASK_BIT_0 | TASK_BIT_1 | TASK_BIT_2) EventGroupHandle_t xEventBits; void Task0(void *pvParam) { while (1) { printf("task0 begin\n"); vTaskDelay(pdMS_TO_TICKS(1000)); printf("task0 set bit0\n"); xEventGroupSync(xEventBits, TASK_BIT_0, ALL_SYNC_BITS, portMAX_DELAY); printf("task0 sync\n"); vTaskDelay(pdMS_TO_TICKS(10000)); } } void Task1(void *pvParam) { while (1) { printf("task1 begin\n"); vTaskDelay(pdMS_TO_TICKS(3000)); printf("task1 set bit1\n"); xEventGroupSync(xEventBits, TASK_BIT_1, ALL_SYNC_BITS, portMAX_DELAY); printf("task1 sync\n"); vTaskDelay(pdMS_TO_TICKS(10000)); } } void Task2(void *pvParam) { while (1) { printf("task2 begin\n"); vTaskDelay(pdMS_TO_TICKS(5000)); printf("task2 set bit1\n"); xEventGroupSync(xEventBits, TASK_BIT_2, ALL_SYNC_BITS, portMAX_DELAY); printf("task2 sync\n"); vTaskDelay(pdMS_TO_TICKS(10000)); } } void app_main(void) { xEventBits = xEventGroupCreate(); if (xEventBits == NULL) { printf("Event group create fail\n"); } else { vTaskSuspendAll(); xTaskCreatePinnedToCore(Task0, "Task0", 2048, NULL, 1, NULL, 0); xTaskCreatePinnedToCore(Task1, "Task1", 2048, NULL, 1, NULL, 0); xTaskCreatePinnedToCore(Task2, "Task2", 2048, NULL, 1, NULL, 0); xTaskResumeAll(); } }
15、通知
15.1 通知同步
TaskHandle_t xTask1 = NULL; void Task1(void *pvParam) { while (1) { printf("--------------\n"); printf("task1 wait notification!\n"); ulTaskNotifyTake(pdTRUE, portMAX_DELAY); printf("--------------\n"); printf("task1 got notification!\n"); vTaskDelay(pdMS_TO_TICKS(3000)); } } void Task2(void *pvParam) { while (1) { vTaskDelay(pdMS_TO_TICKS(5000)); printf("--------------\n"); printf("task2 notify task1!\n"); xTaskNotifyGive(xTask1); } } void app_main(void) { vTaskSuspendAll(); xTaskCreatePinnedToCore(Task1, "Task1", 2048, NULL, 1, &xTask1, 0); xTaskCreatePinnedToCore(Task2, "Task2", 2048, NULL, 1, NULL, 0); xTaskResumeAll(); }
15.2 通知值
TaskHandle_t xTask1 = NULL; void Task1(void *pvParam) { uint32_t ulNotifiedValue; while (1) { printf("--------------\n"); printf("task1 wait notification!\n"); xTaskNotifyWait(0x00, ULONG_MAX, &ulNotifiedValue, portMAX_DELAY); // 进入该函数时不清除任何通知的位,退出该函数时将通知的值全部Reset为0,用于刚保存通知的值,无限循环等待 if ((ulNotifiedValue & 0x01) != 0) { printf("task1 process bit0 event!\n"); } if ((ulNotifiedValue & 0x02) != 0) { printf("task1 process bit1 event!\n"); } if ((ulNotifiedValue & 0x04) != 0) { printf("task1 process bit2 event!\n"); } printf("--------------\n"); vTaskDelay(pdMS_TO_TICKS(3000)); } } void Task2(void *pvParam) { while (1) { vTaskDelay(pdMS_TO_TICKS(5000)); printf("--------------\n"); printf("task2 notify task1 bit0!\n"); xTaskNotify(xTask1, 0x01, eSetValueWithoutOverwrite); vTaskDelay(pdMS_TO_TICKS(5000)); printf("--------------\n"); printf("task2 notify task1 bit1!\n"); xTaskNotify(xTask1, 0x02, eSetValueWithoutOverwrite); vTaskDelay(pdMS_TO_TICKS(5000)); printf("--------------\n"); printf("task2 notify task1 bit2!\n"); xTaskNotify(xTask1, 0x04, eSetValueWithoutOverwrite); vTaskDelay(pdMS_TO_TICKS(5000)); } } void app_main(void) { vTaskSuspendAll(); xTaskCreatePinnedToCore(Task1, "Task1", 2048, NULL, 1, &xTask1, 0); xTaskCreatePinnedToCore(Task2, "Task2", 2048, NULL, 1, NULL, 0); xTaskResumeAll(); }
16、流数据缓冲区
16.1 数据流缓冲区
- 如果把vTaskDelay(pdMS_TO_TICKS(3000));放后面 一发送就会接收到数据,不是等超过100个;
因为在第一次发送数据的时候接收数据任务并没有进入阻塞状态,任务一优先运行把数据发送到StreamBuff中,任务二还没有进入阻塞状态一调用xStreamBufferReceive就会把所有数据接收下来,然后再进入阻塞状态等待发送数据超过100个字节解除阻塞。 把 vTaskDelay(pdMS_TO_TICKS(3000))提前就会让接收数据任务提前运行此时发送任务并还没有发送数据没有要在进入阻塞状态前要接收的数据,然后接收任务进入阻塞态。
16.2 确定数据流缓冲区的大小
- 用一个任务监测空闲buff的大小
void Task1(void *pvParam) { char tx_buf[50]; int i = 0; int srt_len = 0; int send_bytes = 0; while (1) { vTaskDelay(pdMS_TO_TICKS(3000)); i++; srt_len = sprintf(tx_buf, "Hello,I am LittleOne %d!", i); send_bytes = xStreamBufferSend(StreamBuff, (void *)tx_buf, srt_len, portMAX_DELAY); printf("--------------\n"); printf("Send: srt_len = %d,send_bytes = %d!\n", srt_len, send_bytes); } } void Task2(void *pvParam) { char rx_buf[50]; int rec_bytes = 0; while (1) { memset(rx_buf, 0, sizeof(rx_buf)); // 初始化数组内容为0 rec_bytes = xStreamBufferReceive(StreamBuff, (void *)rx_buf, sizeof(rx_buf), portMAX_DELAY); printf("--------------\n"); printf("Receive:%s,rec_bytes = %d!\n", rx_buf, rec_bytes); } } void Task3(void *pvParam) { int buf_space = 0; int min_space = 1000; while (1) { buf_space = xStreamBufferSpacesAvailable(StreamBuff); if (buf_space < min_space ) min_space = buf_space; printf("--------------\n"); printf("buf_space %d,min_space %d!\n", buf_space, min_space); vTaskDelay(pdMS_TO_TICKS(3000)); } } void app_main(void) { StreamBuff = xStreamBufferCreate(1000, 100); // 接收字节超过100个就会解除接收数据的阻塞。 if (NULL != StreamBuff) { vTaskSuspendAll(); xTaskCreatePinnedToCore(Task1, "Task1", 2048, NULL, 1, NULL, 0); xTaskCreatePinnedToCore(Task2, "Task2", 2048, NULL, 1, NULL, 0); xTaskCreatePinnedToCore(Task3, "Task3", 2048, NULL, 1, NULL, 0); xTaskResumeAll(); } else { printf("Fail to creat buff fail!\n"); } }
17、 消息缓冲区
17.1 与数据流的区别1
- 一次只能接收一条完整的Message,没有流数据中触发阈值的概念,尽管接收buff很大一次也只能接收一条Message;
#include "freertos/message_buffer.h" #include <string.h> TaskHandle_t xTask1 = NULL; MessageBufferHandle_t MessageBufferHandle = NULL; void Task1(void *pvParam) { char tx_buf[50]; int srt_len = 0; int send_bytes = 0; for (int i = 0; i < 3; i++) { srt_len = sprintf(tx_buf, "Hello,I am Message %d!", i); send_bytes = xMessageBufferSend(MessageBufferHandle, (void *)tx_buf, srt_len, portMAX_DELAY); printf("--------------\n"); printf("Send: i = %d,send_bytes = %d!\n", i, send_bytes); } vTaskDelete(NULL); } void Task2(void *pvParam) { char rx_buf[100]; int rec_bytes = 0; vTaskDelay(pdMS_TO_TICKS(3000)); while (1) { memset(rx_buf, 0, sizeof(rx_buf)); // 初始化数组内容为0 rec_bytes = xMessageBufferReceive(MessageBufferHandle, (void *)rx_buf, sizeof(rx_buf), portMAX_DELAY); printf("--------------\n"); printf("Receive:%s,rec_bytes = %d!\n", rx_buf, rec_bytes); vTaskDelay(pdMS_TO_TICKS(1000)); } } void app_main(void) { MessageBufferHandle = xMessageBufferCreate(1000); if (NULL != MessageBufferHandle) { vTaskSuspendAll(); xTaskCreatePinnedToCore(Task1, "Task1", 2048, NULL, 1, NULL, 0); xTaskCreatePinnedToCore(Task2, "Task2", 2048, NULL, 1, NULL, 0); xTaskResumeAll(); } else { printf("Fail to creat buff fail!\n"); } }
17.1 与数据流的区别2
- 流数据缓冲区的接收buff即使小于一条消息的数据长度也能打印出接收buff大小的数据,而消息缓冲区会是返回0;
- 流数据
#include "freertos/stream_buffer.h" #include <string.h> TaskHandle_t xTask1 = NULL; StreamBufferHandle_t StreamBufferHandle = NULL; void Task1(void *pvParam) { char tx_buf[50]; int srt_len = 0; int send_bytes = 0; for (int i = 0; i < 3; i++) { srt_len = sprintf(tx_buf, "Hello,I am Stream %d!", i); send_bytes = xStreamBufferSend(StreamBufferHandle, (void *)tx_buf, srt_len, portMAX_DELAY); printf("--------------\n"); printf("Send: i = %d,send_bytes = %d!\n", i, send_bytes); } vTaskDelete(NULL); } void Task2(void *pvParam) { char rx_buf[10]; int rec_bytes = 0; vTaskDelay(pdMS_TO_TICKS(3000)); while (1) { memset(rx_buf, 0, sizeof(rx_buf)); // 初始化数组内容为0 rec_bytes = xStreamBufferReceive(StreamBufferHandle, (void *)rx_buf, sizeof(rx_buf), portMAX_DELAY); printf("--------------\n"); printf("Receive:%s,rec_bytes = %d!\n", rx_buf, rec_bytes); vTaskDelay(pdMS_TO_TICKS(1000)); } } void app_main(void) { StreamBufferHandle = xStreamBufferCreate(1000,10); if (NULL != StreamBufferHandle) { vTaskSuspendAll(); xTaskCreatePinnedToCore(Task1, "Task1", 2048, NULL, 1, NULL, 0); xTaskCreatePinnedToCore(Task2, "Task2", 2048, NULL, 1, NULL, 0); xTaskResumeAll(); } else { printf("Fail to creat buff fail!\n"); } }
- 消息缓冲
#include "freertos/message_buffer.h" #include <string.h> TaskHandle_t xTask1 = NULL; MessageBufferHandle_t MessageBufferHandle = NULL; void Task1(void *pvParam) { char tx_buf[50]; int srt_len = 0; int send_bytes = 0; for (int i = 0; i < 3; i++) { srt_len = sprintf(tx_buf, "Hello,I am Message %d!", i); send_bytes = xMessageBufferSend(MessageBufferHandle, (void *)tx_buf, srt_len, portMAX_DELAY); printf("--------------\n"); printf("Send: i = %d,send_bytes = %d!\n", i, send_bytes); } vTaskDelete(NULL); } void Task2(void *pvParam) { char rx_buf[10]; int rec_bytes = 0; vTaskDelay(pdMS_TO_TICKS(3000)); while (1) { memset(rx_buf, 0, sizeof(rx_buf)); // 初始化数组内容为0 rec_bytes = xMessageBufferReceive(MessageBufferHandle, (void *)rx_buf, sizeof(rx_buf), portMAX_DELAY); printf("--------------\n"); printf("Receive:%s,rec_bytes = %d!\n", rx_buf, rec_bytes); vTaskDelay(pdMS_TO_TICKS(1000)); } } void app_main(void) { MessageBufferHandle = xMessageBufferCreate(1000); if (NULL != MessageBufferHandle) { vTaskSuspendAll(); xTaskCreatePinnedToCore(Task1, "Task1", 2048, NULL, 1, NULL, 0); xTaskCreatePinnedToCore(Task2, "Task2", 2048, NULL, 1, NULL, 0); xTaskResumeAll(); } else { printf("Fail to creat buff fail!\n"); } }