消息队列的相关知识
1、基本概念
队列:即消息队列,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任务间传递信息,实现了任务接收来自其他任务或中断的不固定长度的消息。
通过消息队列服务,任务或者中断服务例程可以将一条或多条消息放入队列中;同样一个或多个任务可以从消息队列中获得消息。
支持先进先出原则,也支持后进先出原则。
2、运行机制
在创建消息队列的时候FreeRTOS会先给消息队列分配一块内存,这块内存的大小等于消息队列控制块大小加上单个消息空间大小与消息队列长度的乘积;接着会初始化消息队列。
任务或中断服务程序都可以给消息队列发送消息:(从消息队列中读消息时也一样的道理)
1)若队列未满则会拷贝到消息队列队尾。
2)若队列已满则会根据用户指定的超时时间进行阻塞,此时要么未超过时间队列有空,则直接入队;要么超过时间,任务自动从阻塞态转就绪态,同时发送消息者会收到一个错误码errQUEUE_FULL。
发送紧急消息时同理,只不过此时是拷贝开队头。
3、阻塞机制
只有在任务中发送消息才允许进行阻塞状态,而在中断中发送消息不允许带有阻塞机制的,需要调用在中断中发送消息的API函数接口。
当有多个任务阻塞在一个消息队列中时,优先级高的任务将优先获得队列的访问权。
4、消息队列的控制块
FreeRTOS 的消息队列控制块由多个元素组成,当消息队列被创建时,系统会为控制块分配对应的内存空间,用于保存消息队列的一些信息如消息的存储位置,头指针 pcHead、尾指针 pcTail、消息大小 uxItemSize 以及队列长度 uxLength,以及当前队列消息个数uxMessagesWaiting 等。
5、消息队列的常用函数
使用队列模块的典型流程:创建消息队列、写队列操作、读队列操作、删除队列操作。
(1)消息队列创建函数 xQueueCreate()
用于创建一个新的队列并返回可用于访问这个队列的队列句柄。
要想使用该函数必须在FreeRTOSConfig.h 中把 configSUPPORT_DYNAMIC_ALLOCATION 定义为 1。
在FreeRTOS中,凡是创建任务,队列,信号量和互斥量等内核对象都需要使用动态内存分配,所以上述的宏在FreeRTOS.h中默认使能了。
(2)消息队列静态创建函数 xQueueCreateStatic()
与前者的区别是其在创建队列的时候使用的是静态内存分配,即要想使用该函数必须在FreeRTOSConfig.h 中把 configSUPPORT_STATIC_ALLOCATION 定义为 1 来使能。
(3)消息队列删除函数 vQueueDelete()
根据消息队列的句柄可直接将消息队列删除。
(4)向消息队列发送消息函数
向消息队列发送消息的具体流程如开头所描述一样。
1)xQueueSend()与 xQueueSendToBack()
xQueueSend()用于向队列尾部发送一个队列消息。消息以拷贝的形式入队,而不是以引用的形式。
2)xQueueSendFromISR()与 xQueueSendToBackFromISR()
是 xQueueSend()的中断保护版本,用于在中断服务程序中向队列尾部发送一个队列消息,等价于 xQueueSendToBackFromISR()。
3)xQueueSendToFront()
用于向队列队首发送一个消息。消息以拷贝的形式入队,而不是以引 用的形式。
4)xQueueSendToFrontFromISR()
是 xQueueSendToFront()的中断保护版本,用于在中断服务程序中向消息队列队首发送一个消息。
5)通用消息队列发送函数 xQueueGenericSend()(任务)
为任务中发送消息的函数。
6)消息队列发送函数 xQueueGenericSendFromISR()(中断)
只能用于中断中执行,是不带阻塞机制的。
最前面的四个发送函数均为一个宏,都是以最后两个函数为底层调用。
(5)从消息队列读取消息函数
1)xQueueReceive()与 xQueuePeek()
用于从一个队列中接收消息并把消息从队列中删除。其为一个宏,展开是调用函数 xQueueGenericReceive() 。
不能在中断服务程序中被调用。
若想要接收完信息但不想删除,则调用 xQueuePeek()函数。
2)xQueueReceiveFromISR()与 xQueuePeekFromISR()
用于在中断服务程序中接收一个队列消息并把消息从队列中删除。
xQueuePeekFromISR()是 xQueuePeek()的中断版本,用于在中断中从一个队列中接收消息,但并不会把消息从队列中移除。
3)从队列读取消息函数 xQueueGenericReceive()
6、消息队列的使用注意事项
1)使用 xQueueSend()、xQueueSendFromISR()、xQueueReceive()等这些函数之前应先创建需消息队列,并且是根据队列句柄进行操作。
2)队列可先进先出,也可后进先出。
3)在获取队列中的消息时,必须先提前定义一个合适的存储读取数据的地方。
4)无论发送还是接收消息都是以拷贝的方式进行。
5)队列具备自己独立权限的内核对象,并不属于任何任务。