在实际的项目开发中,经常会遇到在任务于任务之间或任务于中断之间需要进行“沟通交 流”,这里的“沟通交流”就是消息传递的过程。在不使用操作系统的情况下,函数与函数,或 函数与中断之间的“沟通交流”一般使用一个或多多个全局变量来完成,但是在操作系统中, 因为会涉及“资源管理”的问题,比方说读写冲突,因此使用全局变量在任务于任务或任务于 中断之间进行消息传递,并不是很好的解决方案。FreeRTOS 为此提供了“队列”的机制。
如图 13.1.1,创建了一个用于任务 A 与任务 B 之间“沟通交流”的队列,这个队列最大可容纳5个队列项目,即队列的长度为 5。刚创建的队列是不包含内容的,因此这个队列为空。
(2) 往队列写入第一个消息
如图 13.1.2,任务 A 将一个私有变量写入队列的尾部。由于在写入队列之前,队列是空的, 因此新写入的消息,既是是队列的头部,也是队列的尾部。
(3) 往队列中写入第二个消息
如图 13.1.3,任务 A 改变了私有变量的值,并将新值写入队列。现在队列中包含了队列 A 写入的两个值,其中第一个写入的值在队列的头部,而新写入的值在队列的尾部。这时队列还 有 3 个空闲的位置。
(4) 从队列读取第一个消息
如图 13.1.4,任务B从队列中读取消息,任务B读取的消息是处于队列头部的消息,这是 任务 A 第一次往队列中写入的消息。在任务B从队列中读取消息后,队列中任务 A 第二次写 入的消息,变成了队列的头部,因此下次任务 B 再次读取消息时,将读取到这个消息。此时队列中剩余4个空闲的位置。
FreeRTOS 队列相关 API 函数
队列创建
函数 xQueueCreate()
此函数用于使用动态方式创建队列,队列所需的内存空间由 FreeRTOS 从 FreeRTOS 管理 的堆中分配。函数 xQueueCreate()实际上是一个宏定义,在 queue.h 文件中有定义,具体的代码如下所示:
函数 xQueueCreate()的形参描述,如下表所示:
可以看到,函数 xQueueCreate()实际上是调用了函数 xQueueGenericCreate(),函数 xQueueGenericCreate()用于使用动态方式创建指定类型的队列,前面说 FreeRTOS 基于队列实现 了多种功能,每一种功能对应一种队列类型,队列类型的 queue.h 文件中有定义,具体的代码如下所示:
在任务中往队列写入消息的函数有函数 xQueueSend() 、 xQueueSendToBack() 、 xQueueSendToFront()、xQueueOverwrite(),这 4 个函数实际上都是宏定义,在 queue.h 文件中有 定义,具体的代码如下所示:
从上面的代码中可以看到,函数 xQueueSend()、函数 xQueueSendToBack()、函数 xQueueSendToFront()和函数 xQueueOverwrite()实际上都是调用了函数 xQueueGenericSend(),只 是指定了不同的写入位置,队列一共有 3 种写入位置,在 queue.h 文件中有定义,具体的代码 如下所示:
要注意的是,覆写方式写入队列,只有在队列的队列长度为 1 时,才能够使用。
函 数 xQueueGenericSend() 用 于 在 任 务 中 往 队 列 的 指 定 位 置 写 入 消 息 。 函 数 xQueueGenericSend()的函数原型如下所示:
函数 xQueueGenericSend()的形参描述,如下表所示:
队列读取消息
FreeRTOS 中用于从队列中读取消息的 API 函数如下表所示:
函数 xQueueReceive()
此函数用于在任务中,从队列中读取消息,并且消息读取成功后,会将消息从队列中移除。 消息的读取是通过拷贝的形式传递的,具体拷贝数据的大小,为队列项目的大小。该函数的函 数原型如下所示:
函数 xQueueReceive()的形参描述,如下表所示:
#define QUEUE_LENGTH 1
#define QUEUE_ITEM_SIZE sizeof(unsigned char)
QueueHandle_t xQueue;
然后再start任务中创建队列:
xQueue = xQueueCreate(QUEUE_LENGTH, QUEUE_ITEM_SIZE);
分别在led灯任务中,实现消息队列发送逻辑:
#define WS2812B_RED_STATE_ON 0XA1
#define WS2812B_RED_STATE_OFF 0XA2
#define WS2812B_GREEN_STATE_ON 0XA3
#define WS2812B_GREEN_STATE_OFF 0XA4
void led_green_task(void *pvParameters)
{
unsigned char led_green_on_flag = 0;
unsigned char ws2812b_color_num = 0;
while(1)
{
if(led_green_on_flag && mpu6050_init_success_flag == 1)
{
LED_GREEN_ON;
ws2812b_color_num = WS2812B_GREEN_STATE_ON;
xQueueSend(xQueue, &ws2812b_color_num, portMAX_DELAY);
}else
{
ws2812b_color_num = WS2812B_GREEN_STATE_OFF;
LED_GREEN_OFF;
xQueueSend(xQueue, &ws2812b_color_num, portMAX_DELAY);
}
led_green_on_flag = ~led_green_on_flag;
vTaskDelay(1000);
}
}
void led_red_task(void *pvParameters)
{
unsigned char led_red_on_flag = 0;
unsigned char ws2812b_color_num = 0;
while(1)
{
if(led_red_on_flag && mpu6050_init_success_flag == 1)
{
LED_RED_ON;
ws2812b_color_num = WS2812B_RED_STATE_ON;
xQueueSend(xQueue, &ws2812b_color_num, portMAX_DELAY);
}else
{
ws2812b_color_num = WS2812B_RED_STATE_OFF;
LED_RED_OFF;
xQueueSend(xQueue, &ws2812b_color_num, portMAX_DELAY);
}
led_red_on_flag = ~led_red_on_flag;
vTaskDelay(2000);
}
}
最后在ws2812b任务中,实现消息队列获取并执行亮灯逻辑:
void ws2812b_task(void *pvParameters)
{
unsigned char queue_recv;
unsigned char ws2812b_red_state = 0;
unsigned char ws2812b_green_state = 0;
while(1)
{
xQueueReceive(xQueue, &queue_recv, portMAX_DELAY);
if(queue_recv == WS2812B_RED_STATE_ON) ws2812b_red_state = 1;
if(queue_recv == WS2812B_RED_STATE_OFF) ws2812b_red_state = 0;
if(queue_recv == WS2812B_GREEN_STATE_ON) ws2812b_green_state = 1;
if(queue_recv == WS2812B_GREEN_STATE_OFF) ws2812b_green_state = 0;
if(ws2812b_red_state == 1 && ws2812b_green_state == 0)
{
ws2812b_write_rgb(255, 0, 0);
}else if(ws2812b_red_state == 0 && ws2812b_green_state == 1)
{
ws2812b_write_rgb(0, 255, 0);
}else if(ws2812b_red_state == 1 && ws2812b_green_state == 1)
{
ws2812b_write_rgb(255, 255, 0);
}else if(ws2812b_red_state == 0 && ws2812b_green_state == 0)
{
ws2812b_write_rgb(0, 0, 0);
}
vTaskDelay(20);
}
}
FreeRTOS 队列集简介
![](https://img-blog.csdnimg.cn/3f6ebe984406448ab639d77ab7f43c80.png)
函数 xQueueCreateSet()
此函数用于创建队列集,该函数在 queue.c 文件中有定义,函数的原型如下所示:
函数 xQueueAddToSet()
此函数用于往队列集中添加队列,要注意的时,队列在被添加到队列集之前,队列中不能 有有效的消息,该函数在 queue.c 文件中有定义,函数的原型如下所示:
函数 xQueueRemoveFromSet()
此函数用于从队列集中移除队列,要注意的时,队列在从队列集移除之前,必须没有有效 的消息,该函数在 queue.c 文件中有定义,函数的原型如下所示:
函数 xQueueSelectFromSet()
此函数用于在任务中获取队列集中有有效消息的队列,该函数在 queue.c 文件中有定义,函 数的原型如下所示:
#define QUEUE_GREEN_LENGTH 1
#define QUEUE_GREEN_ITEM_SIZE sizeof(unsigned char)
QueueHandle_t xQueue_green;
#define QUEUE_RED_LENGTH 1
#define QUEUE_RED_ITEM_SIZE sizeof(unsigned char)
QueueHandle_t xQueue_red;
#define QUEUESET_LENGTH 2
QueueHandle_t xQueueSet;
然后是在start任务中进行队列集的创建,队列的创建,以及队列加入到队列集操作。
xQueueSet = xQueueCreateSet(QUEUESET_LENGTH);
xQueue_green = xQueueCreate(QUEUE_GREEN_LENGTH, QUEUE_GREEN_ITEM_SIZE);
xQueue_red = xQueueCreate(QUEUE_RED_LENGTH, QUEUE_RED_ITEM_SIZE);
xQueueAddToSet(xQueue_green, xQueueSet);
xQueueAddToSet(xQueue_red, xQueueSet);
然后是在led_green和led_red任务中分别进行不同队列的消息传递。
#define WS2812B_RED_STATE_ON 0XA1
#define WS2812B_RED_STATE_OFF 0XA2
#define WS2812B_GREEN_STATE_ON 0XA3
#define WS2812B_GREEN_STATE_OFF 0XA4
void led_green_task(void *pvParameters)
{
unsigned char led_green_on_flag = 0;
unsigned char ws2812b_color_num = 0;
while(1)
{
if(led_green_on_flag && mpu6050_init_success_flag == 1)
{
LED_GREEN_ON;
ws2812b_color_num = WS2812B_GREEN_STATE_ON;
xQueueSend(xQueue_green, &ws2812b_color_num, portMAX_DELAY);
}else
{
ws2812b_color_num = WS2812B_GREEN_STATE_OFF;
LED_GREEN_OFF;
xQueueSend(xQueue_green, &ws2812b_color_num, portMAX_DELAY);
}
led_green_on_flag = ~led_green_on_flag;
vTaskDelay(1000);
}
}
void led_red_task(void *pvParameters)
{
unsigned char led_red_on_flag = 0;
unsigned char ws2812b_color_num = 0;
while(1)
{
if(led_red_on_flag && mpu6050_init_success_flag == 1)
{
LED_RED_ON;
ws2812b_color_num = WS2812B_RED_STATE_ON;
xQueueSend(xQueue_red, &ws2812b_color_num, portMAX_DELAY);
}else
{
ws2812b_color_num = WS2812B_RED_STATE_OFF;
LED_RED_OFF;
xQueueSend(xQueue_red, &ws2812b_color_num, portMAX_DELAY);
}
led_red_on_flag = ~led_red_on_flag;
vTaskDelay(2000);
}
}
最后是在ws2812b任务中对队列集进行消息获取以及不同队列的消息识别执行亮灯逻辑。
void ws2812b_task(void *pvParameters)
{
QueueSetMemberHandle_t activate_member = NULL;
unsigned char queue_recv;
unsigned char ws2812b_red_state = 0;
unsigned char ws2812b_green_state = 0;
while(1)
{
activate_member = xQueueSelectFromSet(xQueueSet, portMAX_DELAY);
if(activate_member == xQueue_green)
{
xQueueReceive(activate_member, &queue_recv, portMAX_DELAY);
if(queue_recv == WS2812B_GREEN_STATE_ON) ws2812b_green_state = 1;
if(queue_recv == WS2812B_GREEN_STATE_OFF) ws2812b_green_state = 0;
}
if(activate_member == xQueue_red)
{
xQueueReceive(activate_member, &queue_recv, portMAX_DELAY);
if(queue_recv == WS2812B_RED_STATE_ON) ws2812b_red_state = 1;
if(queue_recv == WS2812B_RED_STATE_OFF) ws2812b_red_state = 0;
}
if(ws2812b_red_state == 1 && ws2812b_green_state == 0)
{
ws2812b_write_rgb(20, 0, 0);
}else if(ws2812b_red_state == 0 && ws2812b_green_state == 1)
{
ws2812b_write_rgb(0, 20, 0);
}else if(ws2812b_red_state == 1 && ws2812b_green_state == 1)
{
ws2812b_write_rgb(20, 20, 0);
}else if(ws2812b_red_state == 0 && ws2812b_green_state == 0)
{
ws2812b_write_rgb(0, 0, 0);
}
vTaskDelay(20);
}
}
下载验证:与上一个demo能够实现相同的效果,且逻辑更加清楚。NICE!