17.1 消息队列的基本概念
- 消息队列又称消息队列,是一种常用于任务间通信的数据结构, 队列可以在任务与任务间、中断和任务间传递信息,实现了任务接收来自其他任务或中断的不固定长度的消息。
- 任务能够从队列里面读取消息,当队列中的消息是空时,读取消息的任务将被阻塞,用户还可以指定阻塞的任务时间 xTicksToWait,在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。 当队列中有新消息时, 被阻塞的任务会被唤醒并处理新消息;当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转为就绪态。
- 消息队列是一种异步的通信方式。
任务或中断服务例程可以将一条或多条消息放入消息队列中,同样,一个或多个任务可以从消息队列中获得消息。当有多个消息发送到消息队列时,通常是将先进入消息队列的消息先传给任务,也就是说,任务先得到的是最先进入消息队列的消息,即先进先出原则(FIFO),但是也支持后进先出原则(LIFO)
FreeRTOS 中使用队列数据结构实现任务异步通信工作,具有如下特性:
- 消息支持先进先出方式排队,支持异步读写工作方式。
- 读写队列均支持超时机制。
- 消息支持后进先出方式排队, 往队首发送消息(LIFO) 。
- 可以允许不同长度(不超过队列节点最大值)的任意类型消息。
- 一个任务能够从任意一个消息队列接收和发送消息。
- 多个任务能够从同一个消息队列接收和发送消息。
- 当队列使用结束后,可以通过删除队列函数进行删除。
17.2 消息队列的运作机制
创建部分:
- FreeRTOS为消息分配空间
- 初始化消息队列
- 用于保存消息队列的一些信息如消息的存储位置:
- 头指针pcHead、
- 尾指针pcTail、
- 消息大小UxItemSize
- 队列长度uxLength等
- 每个消息队列都与消息空间在同一段连续的内存空间中
- 建成功的时候就已经分配好每个消息空间与消息队列的容量,无法更改,每个消息空间可以存放不大于消息大小 uxItemSize 的任意类型的数据。
- 所有消息队列中的消息空间总数即是消息队列的长度,这个长度可在消息队列创建时指定。
发送部分:
- 任务和中断都可以给消息队列发送消息
- 必须队里未满或者允许覆盖入队,并将消息放到队尾。否则会阻塞等待入队
- 当其它任务从其等待的队列中读取入了数据(队列未满),该任务将自动由阻塞态转移为就绪态。
- 当等待的时间超过了指定的阻塞时间,即使队列中还不允许入队,任务也会自动从阻塞态转移为就绪态,此时发送消息的任务或者中断程序会收到一个错误码 errQUEUE_FULL。
紧急消息:
- 紧急消息放置的位置时队首。可以进行及时的处理。
读取部分:
- 当某个任务试图读一个队列时,其可以指定一个阻塞超时时间
- 这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。
- 当其它任务或中断服务程序往其等待的队列中写入了数据,该任务将自动由阻塞态转移为就绪态
- 当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转移为就绪态
注意:当消息队列不再被使用时,应该删除它以释放系统资源,一旦操作完成,消息队列将
被永久性的删除
17.3 消息队列的阻塞机制
在FreeRTOS中,所有的API都已经写好了,只要使用即可。
接收数据:
每个对消息队列读写的函数,都有这种机制,我称之为阻塞机制。
每个任务对队列的读取可以设置3种状态:
- 查看是否有消息,没有就离开->不会阻塞
- 任务阻塞一段时间等待->到达时间后停止阻塞
- 一直等待这个消息,进入阻塞态,直到产生消息->一直阻塞
发送数据:
- 而在发送消息操作的时候,为了保护数据,当且仅当队列允许入队的时候,发送者才能成功发送消息。
- 队列中无可用消息空间时,说明消息队列已满,此时,系统会根据用户指定的阻塞超时时间将任务阻塞,在指定的超时时间内如果还不能完成入队操作,发送消息的任务或者中断服务程序会收到一个错误码 errQUEUE_FULL,然后解除阻塞状态。
- 只有在任务中发送消息才允许进行阻塞状态,而在中断中发送消息不允许带有阻塞机
制的,需要调用在中断中发送消息的 API 函数接口,因为发送消息的上下文环境是在中断
中,不允许有阻塞的情况。 - 假如有多个任务阻塞在一个消息队列中,那么这些阻塞的任务将按照任务优先级进行排序,优先级高的任务将优先获得队列的访问权。
17.4 消息队列的应用场景
发送不定长消息的场合:
- 任务与任务间的消息交换
注意:消息可以在任务与任务间、中断和任务间传送信息,发送到队列的消息是通过拷贝方式实现的。