通过阅读本文可以了解到:
一个队列能保存有限数量的固定大小的数据单元,每个队列数据单元的长度与大小是在创建队列时设置的。
队列通常是一个先入先出(FIFO)的缓冲区,即数据在队列末尾被写入,在队列前部移除,也可以写入队列的前端,并覆盖已位于队列前端的数据。下图演示了队列的创建及使用:
有两种方法可以实现队列的数据通信:
队列本身就是对象,任何知道它们存在的任务或 ISR 都可以访问它们。任意数量的任务可以写入同一个队列,任意数量的任务也可以从同一个队列读取。在实践中,队列有多个写入者是非常常见的,但是队列有多个读取者就不那么常见了。
当任务尝试从队列中读取时,可以选择指定阻塞时间。如果队列已经为空,则这是任务将保持在阻塞状态以等待队列中的数据的时间。当一个任务或中断将数据写入队列时,因为等待队列而阻塞的任务移至就绪态,如果指定的阻塞时间在数据可用之前到期,相应的任务也会移至就绪态。
队列可以有多个读取者,因此单个队列可能会阻塞多个任务,在这种情况下,只有一个任务在数据可用时将被解除阻塞。解除阻塞的任务始终是等待数据的最高优先级任务。如果被阻塞的任务具有相同优先级,那么等待数据最久的任务将被阻塞。
与读取队列一样,任务也可以在向队列写入数据时指定阻塞时间。在这种情况下,如果队列已满,则阻塞时间是任务应该保持在阻塞状态的最长时间,在这期间中如果队列写入成功则退出阻塞状态。
队列可被分组到集合中,允许任务进入阻塞状态来等待数据在集合的全部队列中变为可用。
创建队列的函数。创建队列后,可以使用xQueueReset()函数将队列返回其原始的空状态。
创建的队列一次可以容纳的最大项数。
队列的每个项的字节大小。
如果返回NULL,则无法创建队列,因为FreeRTOS没哟足够的堆内存来分配队列数据结构和存储区域。返回的非空值代表创建成功,该返回值是这个队列的句柄,后续操作这个队列时需要用到。
xQueueSendToBack()用于将数据发送到队列的尾部,xQueueSendToFront()用于将数据发送到队列的头部。xQueueSend()与xQueueSendToBack()等价。
xQueueSendToFront() 或 xQueueSendToBack()不能在中断函数中调用。应该使用中断安全转换 xQueueSendToFrontFromISR() 和 xQueueSendToBackFromISR()。
发送数据的队列的句柄。
指向要复制到队列中的数据的指针。
如果队列已满,任务应该保持阻塞状态以等待队列上可用空间的最大时间量。如果为0,则立即返回;如果为portMAX_DELAY,则一直阻塞。
在指定的时间内没有成功写入会返回失败。
从队列中接收一个元素,收到的元素将从队列中删除。
需要接收数据的队列句柄。
指向要将接收到的数据复制到内存的指针。pvBuffer指向的内存必须大于在创建队列设置了每个数据项的大小。
如果队列已空,则任务应保持阻塞状态等待数据的最长时间。如果为0,则立即返回;如果为portMAX_DELAY,则一直阻塞。
在指定的时间内没有成功写入会返回失败。
用于查询当前队列的项数。
如果存储在队列中的数据量很大,最好使用队列将指针传输到数据,而不是将数据本身逐个字节得复制到队列中。引用指针在处理时间和创建队列所需的RAM量方面都更有效。在队列中引用指针时,以下几点需要确保:
目前我们已经知道队列可以发送一个结构或者引用指针,将这两个组合起来就可以允许一个任务使用一个队列接收来自任何数据源的任何数据类型。
首先我们可以先定义一个结构,这个结构是用来描述需要发送的数据的特性的(例如类型、长度、存放数据的内存地址),与此同时要自定义一块内存来放需要发送的数据,里面的数据如何解析可以从定义的结构得知。
应用程序设计通常需要单个任务来接收不同大小、不同含义、不同来源的数据,而且同时需要这些队列都非空,这种情况可以使用“队列集”。
队列集允许任务从多个队列接收数据,而无需依次轮询每个队列是否有数据。与使用接收结构的单个队列实现相同功能的设计相比,使用队列集从多个源接收数据的设计更复杂,效率也更低,所以在设计是非必要不使用。
如何使用队列集:
创建队列集。
创建队列集时指定可以容纳的队列数量。
如果返回NULL,则无法创建队列集,因为没有足够的空间。如果返回非空值,则该返回值为队列集的句柄。
将队列或信号量添加到队列集中。
正在添加到队列集中的队列或信号量的句柄。两种句柄可以互相转化。
要添加队列或信号量的队列集的句柄。
成功或否。队列和二进制信号量只有在为空时才能添加到集合中。计数信号量只能在其计数为零时才能添加到集合中。队列和信号量一次只能是一个队列集的成员。
从队列集中读取队列的句柄。当队列集的成员有接收到数据时,该接口可以返回队列成员的句柄,然后必须直接从队列成员中读取数据。用该接口读取成员句柄时,每次只能读取一个。从使用的感觉来看,有点像任务间的switch语句。
队列集的句柄。
阻塞的超时时间。
如果成功的话该返回值是队列集成员的句柄。
嵌入式社区内部对“邮箱”没有共识,在不同的操作系统中有不同的含义。在本文中,指的是一个长度为1的队列。队列之所以被描述为邮箱,是因为它再应用程序中的使用方式,而不是因为它与队列的功能不同:
像队列发送数据,如果队列已满,则覆盖。
从队列中接收数据,而不从队列中移除项目。