#include "freertos/queue.h"
QueueHandle_t xQueueCreate(
UBaseType_t uxQueueLength,//队列长度,相当于buff深度
UBaseType_t uxItemSize //元素大小(Byte),相当于buff宽度 例如:用 sizeof(int)获取;
);
//向队列最前端写入,等同于先入后出
BaseType_t xQueueSendToFront(
QueueHandle_t xQueue, //队列句柄
const void * pvItemToQueue, //待复制的数据指针,复制的数据大小由创建队列时指定的宽度确定
TickType_t xTicksToWait //等待时间
);
//向队列后面写入,等同于先入先出
BaseType_t xQueueSendToBack(
QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait
);
//返回值:
// pdPASS:成功写入
// errQUEUE_FULL:队列满
等待时间为:
BaseType_t xQueueReceive(
QueueHandle_t xQueue,//队列句柄
void *pvBuffer,//接收数据存放位置
TickType_t xTicksToWait //等待时间,与发送类似
);
//返回值:
//pdPASS: 接收成功
//errQUEUE_EMPTY:队列为空
等待时间为:
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue//队列句柄
);//返回值: 当前队列有效数据个数
将可变参数 (…) 按照 format 格式化成字符串,并将字符串复制到 str 中,实际上后半部分跟 printf() 函数用法相同。
int snprintf(char *str,//目标字符串
size_t size, //拷贝字节数(Bytes) format长度大于该值将被截断,并在(size-1)位置添加“\0”
const char *format, //格式化成字符串
... //可变参数
) ;//返回值: 写入的字符串长度
//创建队列集
QueueSetHandle_t xQueueCreateSet(
const UBaseType_t uxEventQueueLength //队列集长度 = 包含的每个队列的长度之和
);//返回:队列集句柄
//添加队列到队列集
BaseType_t xQueueAddToSet(
QueueSetMemberHandle_t xQueueOrSemaphore,//要添加到队列集的队列或信号量,被强制转换成 QueueSetMemberHandle_t 类型
QueueSetHandle_t xQueueSet //队列集
);//返回值: pdPASS:成功; pdFAIL:无法将队列或信号量添加到队列集中,因为它已经是另一个集的成员
//从队列集移除队列
BaseType_t xQueueRemoveFromSet(
QueueSetMemberHandle_t xQueueOrSemaphore,
QueueSetHandle_t xQueueSet
);
//从队列集的成员中选择包含数据的队列 或 可用于获取的信号量
QueueSetMemberHandle_t xQueueSelectFromSet(
QueueSetHandle_t xQueueSet, //队列集
const TickType_t xTicksToWait //等待超时 设为 portMAX_DELAY时, 若 INCLUDE_vTaskSuspend == 1 ,且一直未收到数据将一直阻塞
);//返回值 NULL:在xTicksToWait参数指定的阻止时间过期之前,该集中包含的队列或信号量不可用。其他值:QueueSetMemberHandle_t 句柄
//写队列邮箱
BaseType_t xQueueOverwrite(
QueueHandle_t xQueue, //队列邮箱句柄
const void *pvItemToQueue //要写入的数据指针
);//返回值:只能是 pdPASS
//清空队列邮箱
BaseType_t xQueueReset( QueueHandle_t xQueue );
//读队列邮箱,读取后不会从队列中移除该数据
BaseType_t xQueuePeek(
QueueHandle_t xQueue,//队列邮箱句柄
void *pvBuffer, //读出的数据指针
TickType_t xTicksToWait //超时,功能与队列读取函数相同
);//返回值:pdPASS:成功;errQUEUE_EMPTY:队列邮箱为空。
#include
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/queue.h"
void mytask1(void *pvParameter)//发送任务
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
int t1 = 5;
while (1)
{
if (xQueueSendToBack(p, &t1, 0) == pdPASS)
{
printf("mytask1: send ok \n");
}
else
{
printf("mytask1: send error \n");
}
t1++;
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
void mytask2(void *pvParameter)//接收任务
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
int t2 = 0;
UBaseType_t cnt;
while (1)
{
cnt = uxQueueMessagesWaiting(p);
if (cnt > 0)
{
xQueueReceive(p, &t2, 0);
printf("mytask2: t2= %d cnt= %d\n", t2, cnt-1);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
QueueHandle_t que1 = xQueueCreate(5, sizeof(int));
if (que1 != NULL)//队列创建成功时再创建收发任务
{
xTaskCreate(mytask1, "mytask1", 1024 * 5, (void *)que1, 1, NULL);//注意堆栈,设小了会导致任务启动/运行异常
xTaskCreate(mytask2, "mytask2", 1024 * 5, (void *)que1, 1, NULL);
}
}
效果
mytask1: send ok
mytask2: t2= 5 cnt= 0
mytask1: send ok
mytask2: t2= 6 cnt= 0
mytask1: send ok
mytask1: send ok
mytask2: t2= 7 cnt= 1
mytask1: send ok
mytask1: send ok
mytask2: t2= 8 cnt= 2
mytask1: send ok
mytask1: send ok
mytask2: t2= 9 cnt= 3
mytask1: send ok
mytask1: send ok
mytask2: t2= 10 cnt= 4
mytask1: send ok
mytask1: send error
mytask2: t2= 11 cnt= 4
mytask1: send ok
mytask1: send error
mytask2: t2= 12 cnt= 4
mytask1: send ok
mytask1: send error
队列总长度为5,发送快,接收慢,队列满时,发送出错。
修改接收任务检测时间为200ms:
void mytask2(void *pvParameter)
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
int t2 = 0;
UBaseType_t cnt;
while (1)
{
cnt = uxQueueMessagesWaiting(p);
if (cnt > 0)
{
xQueueReceive(p, &t2, 0);
printf("mytask2: t2= %d cnt= %d\n", t2, cnt-1);
}
vTaskDelay(200 / portTICK_PERIOD_MS);//修改接收检测时间
}
}
效果
mytask1: send ok
mytask2: t2= 5 cnt= 0
mytask1: send ok
mytask2: t2= 6 cnt= 0
mytask1: send ok
mytask2: t2= 7 cnt= 0
mytask1: send ok
mytask2: t2= 8 cnt= 0
mytask1: send ok
mytask2: t2= 9 cnt= 0
mytask1: send ok
mytask2: t2= 10 cnt= 0
mytask1: send ok
mytask2: t2= 11 cnt= 0
mytask1: send ok
mytask2: t2= 12 cnt= 0
mytask1: send ok
mytask2: t2= 13 cnt= 0
mytask1: send ok
mytask2: t2= 14 cnt= 0
mytask1: send ok
mytask2: t2= 15 cnt= 0
mytask1: send ok
接收快,发送慢,每次发送都正常。
因为在接收任务中添加了uxQueueMessagesWaiting函数,用于统计队列有效元素个数,没有数据时不会去接收,因此未报错。
#include
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/queue.h"
typedef struct a_struct
{
int id;
int data;
} xStruct;
void mytask1(void *pvParameter)
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
xStruct t1;
t1.id = 0;
t1.data = 999;
while (1)
{
if (xQueueSendToBack(p, &t1, 0) == pdPASS)
{
printf("mytask1: send ok \n");
}
else
{
printf("mytask1: send error \n");
}
t1.id++;
t1.data--;
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
void mytask2(void *pvParameter)
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
xStruct t2;
t2.id = 0;
t2.data = 0;
UBaseType_t cnt;
while (1)
{
cnt = uxQueueMessagesWaiting(p);
if (cnt > 0)
{
xQueueReceive(p, &t2, 0);
printf("mytask2: t2= { %d , %d }cnt= %d\n", t2.id, t2.data, cnt - 1);
}
vTaskDelay(200 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
QueueHandle_t que1 = xQueueCreate(5, sizeof(xStruct));
if (que1 != NULL)
{
xTaskCreate(mytask1, "mytask1", 1024 * 5, (void *)que1, 1, NULL);
xTaskCreate(mytask2, "mytask2", 1024 * 5, (void *)que1, 1, NULL);
}
}
效果
mytask1: send ok
mytask2: t2= { 0 , 999 }cnt= 0
mytask1: send ok
mytask2: t2= { 1 , 998 }cnt= 0
mytask1: send ok
mytask2: t2= { 2 , 997 }cnt= 0
mytask1: send ok
mytask2: t2= { 3 , 996 }cnt= 0
mytask1: send ok
mytask2: t2= { 4 , 995 }cnt= 0
mytask1: send ok
mytask2: t2= { 5 , 994 }cnt= 0
mytask1: send ok
mytask2: t2= { 6 , 993 }cnt= 0
mytask1: send ok
mytask2: t2= { 7 , 992 }cnt= 0
mytask1: send ok
mytask2: t2= { 8 , 991 }cnt= 0
#include
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/queue.h"
void mytask1(void *pvParameter)
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
char *pt1;
int i = 0;
while (1)
{
pt1 = (char *)malloc(50); //每次循环都分配新的地址
snprintf(pt1, 50, "hello world %d", i);
if (xQueueSendToBack(p, &pt1, 0) == pdPASS)
{
printf("mytask1: send ok pt1_addr= %p \n", pt1);
}
else
{
printf("mytask1: send error \n");
}
i++;
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void mytask2(void *pvParameter)
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
char *pt2;
UBaseType_t cnt;
while (1)
{
cnt = uxQueueMessagesWaiting(p);
if (cnt > 0)
{
xQueueReceive(p, &pt2, 0);//在队列中接收的实际只是一个char指针,即发送任务中分配的地址
printf("mytask2: pt2_addr= %p pt2= %s | cnt= %d\n", pt2, pt2, cnt - 1);
free(pt2); //释放发送函数分配的内存
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
QueueHandle_t que1 = xQueueCreate(5, sizeof(char *));
if (que1 != NULL)
{
xTaskCreate(mytask1, "mytask1", 1024 * 5, (void *)que1, 1, NULL);
xTaskCreate(mytask2, "mytask2", 1024 * 5, (void *)que1, 1, NULL);
}
}
效果
mytask1: send ok pt1_addr= 0x3ffb94d0
mytask2: pt2_addr= 0x3ffb94d0 pt2= hello world 0 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4a48
mytask2: pt2_addr= 0x3ffb4a48 pt2= hello world 1 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4a48
mytask2: pt2_addr= 0x3ffb4a48 pt2= hello world 2 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4a48
mytask2: pt2_addr= 0x3ffb4a48 pt2= hello world 3 | cnt= 0
屏蔽free()函数效果如下,这样会占用不必要的内存,需谨慎使用。
mytask1: send ok pt1_addr= 0x3ffb94d0
mytask2: pt2_addr= 0x3ffb94d0 pt2= hello world 0 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4a48
mytask2: pt2_addr= 0x3ffb4a48 pt2= hello world 1 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4a80
mytask2: pt2_addr= 0x3ffb4a80 pt2= hello world 2 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4ab8
mytask2: pt2_addr= 0x3ffb4ab8 pt2= hello world 3 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4af0
mytask2: pt2_addr= 0x3ffb4af0 pt2= hello world 4 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4b28
mytask2: pt2_addr= 0x3ffb4b28 pt2= hello world 5 | cnt= 0
#include
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/queue.h"
void mytask1(void *pvParameter) //发送任务1
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
int t1 = 111;
while (1)
{
if (xQueueSendToBack(p, &t1, 0) == pdPASS)
{
printf("mytask1: send ok \n");
}
else
{
printf("mytask1: send error \n");
}
t1++;
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void mytask2(void *pvParameter) //发送任务2
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
int t2 = 222;
while (1)
{
if (xQueueSendToBack(p, &t2, 0) == pdPASS)
{
printf("mytask2: send ok \n");
}
else
{
printf("mytask2: send error \n");
}
t2++;
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void mytask3(void *pvParameter) //接收任务
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
int t3 = 0;
UBaseType_t cnt;
while (1)
{
cnt = uxQueueMessagesWaiting(p);
if (cnt > 0)
{
xQueueReceive(p, &t3, 0);
printf("mytask3: t3= %d cnt= %d\n", t3, cnt - 1);
}
vTaskDelay(200 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
QueueHandle_t que1 = xQueueCreate(5, sizeof(int));
if (que1 != NULL) //队列创建成功时再创建收发任务
{
xTaskCreate(mytask1, "mytask1", 1024 * 5, (void *)que1, 1, NULL); //注意堆栈,设小了会导致任务启动/运行异常
xTaskCreate(mytask2, "mytask2", 1024 * 5, (void *)que1, 1, NULL);
xTaskCreate(mytask3, "mytask3", 1024 * 5, (void *)que1, 2, NULL);
}
}
效果
任务1和2往同一个队列里面写数据,任务3接收。
mytask1: send ok
mytask3: t3= 111 cnt= 0
mytask2: send ok
mytask3: t3= 222 cnt= 0
mytask1: send ok
mytask2: send ok
mytask3: t3= 112 cnt= 1
mytask3: t3= 223 cnt= 0
mytask1: send ok
mytask2: send ok
mytask3: t3= 113 cnt= 1
mytask3: t3= 224 cnt= 0
mytask1: send ok
mytask2: send ok
#include
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/queue.h"
void mytask1(void *pvParameter)//发送任务
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
int t1 = 5;
while (1)
{
//if (xQueueSendToBack(p, &t1, 0) == pdPASS)//先入先出
if (xQueueSendToFront(p, &t1, 0) == pdPASS)//先入后出
{
printf("mytask1: send ok \n");
}
else
{
printf("mytask1: send error \n");
}
t1++;
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
void mytask2(void *pvParameter)//接收任务
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
int t2 = 0;
UBaseType_t cnt;
while (1)
{
cnt = uxQueueMessagesWaiting(p);
if (cnt > 0)
{
xQueueReceive(p, &t2, 0);
printf("mytask2: t2= %d cnt= %d\n", t2, cnt-1);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
QueueHandle_t que1 = xQueueCreate(5, sizeof(int));
if (que1 != NULL)//队列创建成功时再创建收发任务
{
xTaskCreate(mytask1, "mytask1", 1024 * 5, (void *)que1, 1, NULL);//注意堆栈,设小了会导致任务启动/运行异常
xTaskCreate(mytask2, "mytask2", 1024 * 5, (void *)que1, 1, NULL);
}
}
效果
先进入队列的数据反而没有按顺序打印,反而先接收了最新写入队列的数据。
mytask1: send ok
mytask2: t2= 5 cnt= 0
mytask1: send ok
mytask2: t2= 6 cnt= 0
mytask1: send ok
mytask1: send ok
mytask2: t2= 8 cnt= 1
mytask1: send ok
mytask1: send ok
mytask2: t2= 10 cnt= 2
mytask1: send ok
mytask1: send ok
mytask2: t2= 12 cnt= 3
mytask1: send ok
mytask1: send ok
mytask2: t2= 14 cnt= 4
mytask1: send ok
mytask1: send error
先入先出效果对比:
队列溢出前,按先入先出的顺序,依次接收。
mytask1: send ok
mytask2: t2= 5 cnt= 0
mytask1: send ok
mytask2: t2= 6 cnt= 0
mytask1: send ok
mytask1: send ok
mytask2: t2= 7 cnt= 1
mytask1: send ok
mytask1: send ok
mytask2: t2= 8 cnt= 2
mytask1: send ok
mytask1: send ok
mytask2: t2= 9 cnt= 3
mytask1: send ok
mytask1: send ok
mytask2: t2= 10 cnt= 4
mytask1: send ok
mytask1: send error
每个发送任务具有自己独立的队列,而接收任务从多个队列,即“Queue Set”中选择有数据的队列,并接收队列中的数据。
#include
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/queue.h"
void mytask1(void *pvParameter) //发送任务1
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
int t1 = 111;
while (1)
{
if (xQueueSendToBack(p, &t1, 0) == pdPASS)
{
printf("mytask1: send ok \n");
}
else
{
printf("mytask1: send error \n");
}
t1++;
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void mytask2(void *pvParameter) //发送任务2
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
char t2 = 222;
while (1)
{
if (xQueueSendToBack(p, &t2, 0) == pdPASS)
{
printf("mytask2: send ok \n");
}
else
{
printf("mytask2: send error \n");
}
t2++;
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void mytask3(void *pvParameter) //接收任务
{
QueueSetHandle_t pset;
pset = (QueueSetHandle_t)pvParameter;
QueueHandle_t p;
int t3;
while (1)
{
p = (QueueHandle_t)xQueueSelectFromSet(pset, portMAX_DELAY);
xQueueReceive(p, &t3, 0);
printf("mytask3: t3= %d \n", t3);
vTaskDelay(200 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
QueueHandle_t que1 = xQueueCreate(5, sizeof(int));
QueueHandle_t que2 = xQueueCreate(8, sizeof(char));
QueueSetHandle_t que_set1 = xQueueCreateSet(13); //队列集长度 = 包含的每个队列的长度之和
if ((que1 != NULL) && (que1 != NULL)) //队列创建成功时再创建收发任务
{
xQueueAddToSet(que1, que_set1);
xQueueAddToSet(que2, que_set1);
xTaskCreate(mytask1, "mytask1", 1024 * 5, (void *)que1, 1, NULL); //注意堆栈,设小了会导致任务启动/运行异常
xTaskCreate(mytask2, "mytask2", 1024 * 5, (void *)que2, 1, NULL);
xTaskCreate(mytask3, "mytask3", 1024 * 5, (void *)que_set1, 2, NULL);
}
}
效果
mytask1: send ok
mytask3: t3= 111
mytask2: send ok
mytask3: t3= 222
mytask3: t3= 112
mytask1: send ok
mytask2: send ok
mytask3: t3= 223
mytask3: t3= 113
mytask1: send ok
mytask2: send ok
mytask3: t3= 224
从队列集中接收数据正常,但有个遗留问题还不知如何解决,就是从程序中怎么确定当前接收的队列是que1还是que2。
队列是用于数据传递,从一个或多个任务发送数据到另一个任务;
队列邮箱(Mailbox)用于控制程序执行流程,写任务向Mailbox写入控制命令,各个读任务从Mailbox拿到指令并执行不同到操作。
邮箱基于队列创建,与队列主要差异如下:
#include
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/queue.h"
void send_task(void *pvParameter) //发送任务1
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
int t1;
while (1)
{
t1 = 1; // mode A
xQueueOverwrite(p, &t1);
vTaskDelay(2000 / portTICK_PERIOD_MS);
t1 = 5; // mode B
xQueueOverwrite(p, &t1);
vTaskDelay(2000 / portTICK_PERIOD_MS);
t1 = 8; // mode C
xQueueOverwrite(p, &t1);
vTaskDelay(2000 / portTICK_PERIOD_MS);
xQueueReset(p); //清空队列内容
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
void receive_task(void *pvParameter) //接收任务
{
QueueHandle_t p;
p = (QueueHandle_t)pvParameter;
char t2 = 222;
char *task_name;
task_name = pcTaskGetName(NULL); //获取当前任务名
while (1)
{
if (xQueuePeek(p, &t2, 0) == pdPASS) // portMAX_DELAY
{
switch (t2)
{
case 1:
printf("%s: mode A \n", task_name);
break;
case 5:
printf("%s: mode B \n", task_name);
break;
case 8:
printf("%s: mode C \n", task_name);
break;
default:
printf("%s: error \n", task_name);
break;
}
}
else
{
printf("%s: Mailbox empty \n", task_name);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
QueueHandle_t Mailbox1 = xQueueCreate(1, sizeof(int));
if (Mailbox1 != NULL) //邮箱创建成功时再创建收发任务
{
xTaskCreate(send_task, "send_task", 1024 * 5, (void *)Mailbox1, 1, NULL); //注意堆栈,设小了会导致任务启动/运行异常
xTaskCreate(receive_task, "receive_task1", 1024 * 5, (void *)Mailbox1, 2, NULL); //接收优先级高
xTaskCreate(receive_task, "receive_task2", 1024 * 5, (void *)Mailbox1, 2, NULL); //接收任务2和3与任务1共用1个函数体,功能相同
xTaskCreate(receive_task, "receive_task3", 1024 * 5, (void *)Mailbox1, 2, NULL);
}
}
效果
receive_task2: mode A
receive_task1: mode A
receive_task3: mode A
receive_task1: mode A
receive_task2: mode A
receive_task3: mode A
receive_task1: mode A
receive_task2: mode A
receive_task3: mode B
receive_task1: mode B
receive_task2: mode B
receive_task3: mode B
receive_task1: mode B
receive_task2: mode B
receive_task3: mode C
receive_task1: mode C
receive_task2: mode C
receive_task3: mode C
receive_task1: mode C
receive_task2: mode C
receive_task3: Mailbox empty
receive_task1: Mailbox empty
receive_task2: Mailbox empty
receive_task3: Mailbox empty
receive_task1: Mailbox empty
receive_task2: Mailbox empty
receive_task3: mode A
receive_task1: mode A
receive_task2: mode A
receive_task3: mode A
receive_task1: mode A
receive_task2: mode A
receive_task3: mode B
1个发送任务进行系统控制,每隔2s切换模式A、B、C,并在最后2s清除邮箱,3个接收任务公用1个函数体,执行相同的函数,通过打印任务名可做区分。