嵌入式学习 队列

队列 (Queue)​​是最核心的、最常用的任务间通信(IPC)机制之一。它提供了任务之间、任务和中断服务程序(ISR)之间安全传递数据的通道。

1. 队列的本质与特点

        数据结构:本质是一个先进先出(FIFO)的缓刑缓冲区。数据项按写入的顺序存储,按写入的顺序读出。

        数据传递:通过拷贝实现数据传递。发送方将数据复制到队列的存储区,接收方从队列的 存储区复制数据到自己的内存空间。这意味着:

        (1)发送方和接收方不想需要共享内存地址(增强了隔离性)

        (2)传递的是数据的副本,原始数据在发送后可以进行安全修改。

        (3)适用于传递小型、固定大小的数据项(结构体、基本类型、小数组)。传递大型数据(如图像)应使用指针(需小心挂你内存生命周期)。

        线程安全:FREERTOS中的队列是线程安全的。多个任务或ISR可以安全的同时访问同一个队列(通过接收或发送的API),内核内部会处理同步和互斥的问题。

        阻塞机制:队列操作(发送和接收)支持阻塞。当任务尝试从空队列中读取到数据时,它可以阻塞等待直到有数据可用;当任务向满队列写入数据时,他可以阻塞等待直到队列有空间。阻塞时间可以指定(portTICK_PERIOD_MSpdMS_TO_TICKS转换)。

        多任务访问:可以被任意数量的任务或ISR访问。

        长度与项大小:在创建队列时指定:

        uxQueueLength:队列能容纳的最大数据项数量

  uxItemSize:每个数据项的大小(字节数)

2. 队列的核心操作 API (任务上下文)

 创建队列

        QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );

        参数

         uxQueueLength:队列能容纳的最大数据项数量

   uxItemSize:每个数据项的大小(字节数)

        返回值:成功返回句柄,失败返回NULL

发送数据到队尾(入队-Enqueue):

BaseType_t xQueueSend( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );
BaseType_t xQueueSendToBack( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait ); // 同上

参数:

xQueue(输入参数)​

  • 类型:​QueueHandle

pvItemToQueue(输入参数)​

  • 类型:​const void *(指向常量 void 的指针)

  • 含义:​​ 指向要发送到队列的数据项的指针。

xTicksToWait(输入参数)​

  • 类型:​TickType_t

  • 含义:​​ 任务在队列满时,愿意等待队列腾出空间的最大时间(以系统节拍 ticks 为单位)。

  • 作用:​​ 控制当队列满时,调用任务的阻塞行为

  • 0:立即返回 errQUEUE_FULL

  • portMAX_DELAY:无限期阻塞(需 configUSE_MUTEXES配置)。

  • N:阻塞最多 N 个系统节拍 (ticks)。

发送数据到队头(插队):

BaseType_t xQueueSendToFront( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );
//新数据项成为队列头,下次接收操作将首先得到它。其他行为同 xQueueSendToBack

从队列接收数据(出队 -Dequeue):

BaseType_t xQueueReceive( QueueHandle_t xQueue, void * pvBuffer, TickType_t xTicksToWait );
  • 如果队列非空,立即接收成功 (pdPASS)。

  • 从队头移除一个数据项 并复制到pvBuffer

  • 如果队列空,根据 xTicksToWait阻塞:

    • 0:立即返回 errQUEUE_EMPTY

    • portMAX_DELAY:无限期阻塞。

    • N:阻塞最多 N 个系统节拍。

窥视数据(Peek):

BaseType_t xQueuePeek( QueueHandle_t xQueue, void * pvBuffer, TickType_t xTicksToWait );

从队列头部复制一个数据项到 pvBuffer,但不移除该数据项。队列内容不变。阻塞行为同 xQueueReceive

覆盖写入 (用于长度=1的队列):​

BaseType_t xQueueOverwrite( QueueHandle_t xQueue, const void * pvItemToQueue );
  • 专为单数据项队列 (uxQueueLength = 1)​​ 设计。

  • 无论队列空或满,​总是写入成功 (pdPASS)。

  • 如果队列满,会覆盖已有的数据项(即唯一的数据项)。

  • 保证队列中始终是最新的数据。

  • 无阻塞​(总是立即成功)。

删除队列:

void vQueueDelete( QueueHandle_t xQueue );

释放队列占用的内存。删除后不能再使用该队列句柄。

3.中断安全API(ISR上下文)

在中断服务程序 (ISR) 中操作队列必须使用以 FromISR结尾的 API。这些 API:

  • 不会阻塞(ISR 中不允许阻塞)。

  • 使用更轻量级的同步机制(如暂时屏蔽中断)。

  • 需要一个额外的 pxHigherPriorityTaskWoken参数。

BaseType_t xQueueSendFromISR( QueueHandle_t xQueue, const void * pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken );
//向队列尾部发送数据

BaseType_t xQueueSendToBackFromISR( QueueHandle_t xQueue, const void * pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken );
//向队列尾部发送数据

BaseType_t xQueueSendToFrontFromISR( QueueHandle_t xQueue, const void * pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken );
//向队列头部发送数据

BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * pvBuffer, BaseType_t *pxHigherPriorityTaskWoken );
//从队列接收并移除数据

BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * pvBuffer ); 
// 注意:PeekFromISR 没有 pxHigherPriorityTaskWoken 参数! 因为只查看不做任何处理。
//查看队列头部数据(不移除)

BaseType_t xQueueOverwriteFromISR( QueueHandle_t xQueue, const void * pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken );
//强制覆盖写入队列(即使队列满) 仅用于长度为1的队列

pxHigherPriorityTaskWoken参数:​

  • 这是一个指向 BaseType_t变量的指针(通常初始化为 pdFALSE)。

  • 如果队列操作(发送或接收)导致一个等待该队列的任务解除阻塞,并且这个被解除阻塞的任务的优先级高于当前被中断的任务,那么该 API 会将 *pxHigherPriorityTaskWoken设置为 pdTRUE

  • 如何使用:​​ 在 ISR 退出前,检查 pxHigherPriorityTaskWoken指向的值。如果是 pdTRUE,必须调用 portYIELD_FROM_ISR()(或移植相关的宏如 taskYIELD()) 来请求一次上下文切换,让更高优先级的任务在中断退出后立即运行。

BaseType_t xHigherPriorityTaskWoken = pdFALSE;
//初始化为 pdFALSE
// ... 在 ISR 中调用队列 API ...
if (xHigherPriorityTaskWoken == pdTRUE)
 //程序结尾检查是否为pdTRUE
 {
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken); // 如为pdTRUE进行一次任务切换
}

4. 队列的常见用途

  1. 任务间数据传输:​​ 最基本的功能,一个任务生产数据,另一个任务消费数据。

  2. 任务间同步:​

    • 事件通知:​​ 发送一个“空”消息(如 0或一个枚举值)表示事件发生。接收任务阻塞在队列上等待事件。

    • 二值信号量 (Binary Semaphore):​​ 创建一个长度为 1,项大小为 0 (uxItemSize = 0) 的队列。xQueueGive()(发送) 相当于释放信号量,xQueueTake()(接收) 相当于获取信号量。队列为空表示信号量不可用,队列满(有一个“令牌”)表示信号量可用。

    • 计数信号量 (Counting Semaphore):​​ 创建一个长度大于 1,项大小为 0 的队列。队列中的数据项数量代表信号量的计数值。xQueueGive()增加计数,xQueueTake()减少计数。

    • 互斥锁 (Mutex):​​ FreeRTOS 的互斥锁 (xSemaphoreCreateMutex()) 内部通常也是基于队列(长度=1,项大小=0)实现的,并增加了优先级继承机制。

  3. 中断到任务通信:​​ ISR 使用 FromISRAPI 将数据或事件通知发送给任务。任务阻塞在队列上等待。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值