提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
队列(Queue) 是一种用于任务间通信和同步的重要机制。它允许不同任务或者中断服务例程之间交换数据,而无需直接共享变量。这种机制不仅确保数据在任务之间传递的安全性,还解决了多任务系统中的并发和同步问题。
一、队列的基本内容
1.1 队列的引入
在以前的代码中,如果我们想让一个变量在不同函数中进行传递操作,下意识可能会想到全局变量,但是这种方式存在一个问题:当我们使用freertos时,低优先级的任务会被高优先级的任务打断,这意味着可能会导致变量的结果不可控。为了解决这个问题,我们引入了队列,他自带临界保护,我们只要进行相关的API操作即可。在队列中可以存储固定大小且有限的数据,其中每一个数据叫“队列项目”,而所能存储的最大“队列项目”的数量称为队列的长度。除了可以被多任务访问之外,队列还有以下的特性:
- FreeRTOS 中的队列是先进先出的结构,即最先插入队列的数据会最先被读取;
- 队列的长度和每个队列项的大小在创建时由用户指定;
- FreeRTOS 提供了阻塞模式和非阻塞模式的队列操作。如果任务需要立即返回,可以使用超时为 0 的非阻塞模式;如果任务愿意等待队列有空位或数据,可以设置阻塞时间(如 0~portMAX_DELAY)让任务进入阻塞状态;
- FreeRTOS 中的队列在数据传递时通常会直接拷贝数据进行实值传输,当然也可以传递指针。
1.2 FreeRTOS 队列的功能与作用
- 任务间通信:FreeRTOS 的队列可以让多个任务之间安全地传递数据。例如,一个任务可以向队列发送数据,另一个任务从队列读取数据。这使得不同任务之间可以高效地共享信息,而不需要频繁访问全局变量。
- 任务与中断间通信:队列不仅适用于任务间通信,还适用于中断和任务之间的通信。FreeRTOS 提供了特定的 ISR(中断服务例程)函数,比如 xQueueSendFromISR() 和 xQueueReceiveFromISR(),可以让中断处理函数安全地与任务交换数据。
- 同步与调度:当一个任务尝试从空队列中读取数据时,任务可以选择进入阻塞状态,直到有数据可读。当另一个任务或中断向队列中发送数据时,等待的任务会被唤醒并立即处理数据。这样,队列不仅传递数据,还能用作同步机制,协调任务间的执行顺序。
- 队列引发的任务切换: 在 FreeRTOS 中,如果一个任务向队列发送数据,并唤醒了一个更高优先级的任务,该任务会立刻抢占当前任务进行执行。这使得队列不仅用于数据交换,还起到了一定的任务调度作用。
1.3 队列的结构体
在 FreeRTOS 中,队列是通过一个结构体来实现的。虽然具体实现可能根据不同平台有所调整,但通常队列的结构体包含了队列长度、队列项大小、存储数据的缓冲区指针、队列的头尾指针、队列的同步机制(如等待队列的任务列表)等信息。在 FreeRTOS 的源码中,定义队列的结构体一般为 QueueDefinition,其在 queue.c 中实现,通常通过类型定义 QueueHandle_t 来间接引用该结构体。
struct QueueDefinition
{
int8_t *pcHead; /* 指向队列存储区的指针(队列缓冲区的头部) */
int8_t *pcTail; /* 指向队列存储区的尾部 */
int8_t *pcWriteTo; /* 指向队列中下一个写入位置 */
int8_t *pcReadFrom; /* 指向队列中下一个读取位置 */
UBaseType_t uxMessagesWaiting; /* 队列中的消息数 */
UBaseType_t uxLength