FreeRTOS消息队列
消息队列是FreeRTOS中重要的通信机制,需要熟练掌握。
1 消息队列的概念及其作用
消息队列就是通过 RTOS 内核提供的服务,任务或中断服务子程序可以将一个消息(注意,FreeRTOS 消息队列传递的是实际数据,并不是数据地址,RTX,uCOS-II 和 uCOS-III 是传递的地址)放入到队列。 同样,一个或者多个任务可以通过 RTOS 内核服务从队列中得到消息。通常,先进入消息队列的消息先传 给任务,也就是说,任务先得到的是最先进入到消息队列的消息,即先进先出的原则(FIFO),FreeRTOS 的消息队列支持 FIFO 和 LIFO 两种数据存取方式。
也许有不理解的初学者会问采用消息队列多麻烦,搞个全局数组不是更简单,其实不然。在裸机编程时,使用全局数组的确比较方便,但是在加上 RTOS 后就是另一种情况了。相比消息队列,使用全局数组主要有如下四个问题:
◆ 使用消息队列可以让 RTOS 内核有效地管理任务,而全局数组是无法做到的,任务的超时等机制需要用户自己去实现。
◆ 使用了全局数组就要防止多任务的访问冲突,而使用消息队列则处理好了这个问题,用户无需担心。
◆ 使用消息队列可以有效地解决中断服务程序与任务之间消息传递的问题。
◆ FIFO 机制更有利于数据的处理。
2 FreeRTOS任务间消息队列的实现
任务间消息队列的实现是指各个任务之间使用消息队列实现任务间的通信。下面我们通过如下的框图来说明一下 FreeRTOS 消息队列的实现:

运行条件:
◆ 创建消息队列,可以存放 10 个消息。
◆ 创建 2 个任务 Task1 和 Task2,任务 Task1 向消息队列放数据,任务 Task2 从消息队列取数据。
◆ FreeRTOS 的消息存取采用 FIFO 方式。
运行过程主要有以下两种情况:
◆ 任务 Task1 向消息队列放数据,任务Task2 从消息队列取数据,如果放数据的速度快于取数据的速度,那么会出现消息队列存放满的情况,
FreeRTOS 的消息存放函数 xQueueSend 支持超时等待,用户可以设置超时等待,直到有空间可以存放消息或者设置的超时时间溢出。
◆ 任务 Task1 向消息队列放数据,任务 Task2 从消息队列取数据,如果放数据的速度慢于取数据的速度,那么会出现消息队列为空的情况,FreeRTOS 的消息获取函数 xQueueReceive 支持超时等待, 用户可以设置超时等待,直到消息队列中有消息或者设置的超时时间溢出。
FIFO 方式数据存取过程的动态演示看官方地址:
http://www.freertos.org/Embedded-RTOS-Queues.html 里面的 GIF 图片。
3 FreeRTOS中断方式消息队列的实现
FreeRTOS 中断方式消息队列的实现是指中断函数和 FreeRTOS 任务之间使用消息队列。
下面我们通过如下的框图来说明一下 FreeRTOS 消息队列的实现:

运行条件:
◆ 创建消息队列,可以存放 10 个消息。
◆ 创建 1 个任务 Task1 和一个串口接收中断。
◆ FreeRTOS 的消息存取采用 FIFO 方式。
运行过程主要有以下两种情况:
◆ 中断服务程序向消息队列放数据,任务 Task1 从消息队列取数据,如果放数据的速度快于取数据的速度,那么会出现消息队列存放满的情况。由于中断服务程序里面的消息队列发送函数 xQueueSendFromISR 不支持超时设置,所以发送前要通过函数 xQueueIsQueueFullFromISR 检测消息队列是否满。
◆ 中断服务程序向消息队列放数据,任务 Task1 从消息队列取数据,如果放数据的速度慢于取数据的速度,那么会出现消息队列存为空的情况。在 FreeRTOS 的任务中可以通过函数 xQueueReceive 获取消息,因为此函数可以设置超时等待,直到消息队列中有消息存放或者设置的超时时间溢出。
4 消息队列 API 函数
◆ xQueueCreate ()
◆ xQueueSend ()
◆ xQueueSendFromISR ()
◆ xQueueReceive ()
函数 xQueueCreate 用于创建消息队列。
◆ 第 1 个参数是消息队列支持的消息个数。
◆ 第 2 个参数是每个消息的大小,单位字节。
◆ 返回值,如果创建成功会返回消息队列的句柄,如果由于 FreeRTOSConfig.h 文件中 heap 大小不足, 无法为此消息队列提供所需的空间会返回 NULL。
使用这个函数要注意以下问题:FreeRTOS 的消息传递是数据的复制,而不是传递的数据地址,这点要特别注意。每一次传递都是 uxItemSize 个字节。举例:
函数 xQueueSend 用于任务中消息发送。
◆ 第 1 个参数是消息队列句柄。
◆ 第 2 个参数要传递数据地址,每次发送都是将消息队列创建函数 xQueueCreate 所指定的单个消息大小复制到消息队列空间中。
◆ 第 3 个参数是当消息队列已经满时,等待消息队列有空间时的最大等待时间,单位系统时钟节拍。
◆ 返回值,如果消息成功发送返回 pdTRUE,否则返回 errQUEUE_FULL。 使用这个函数要注意以下问题:
使用这个函数要注意以下问题:
1. FreeRTOS 的消息传递是数据的复制,而不是传递的数据地址。
2. 此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是 xQueueSendFromISR。
3. 如果消息队列已经满且第三个参数为 0,那么此函数会立即返回。
4. 如果用户将 FreeRTOSConfig.h 文件中的宏定义 INCLUDE_vTaskSuspend 配置为 1 且第三个参数配置为 portMAX_DELAY,那么此发送函数会永久等待直到消息队列有空间可以使用。
5. 消息队列还有两个函数 xQueueSendToBack 和 xQueueSendToFront,函数 xQueueSendToBack 实现的是 FIFO 方式的存取,函数 xQueueSendToFront 实现的是 LIFO 方式的读写。我们这里说的函 数 xQueueSend 等效于 xQueueSendToBack,即实现的是 FIFO 方式的存取。
举例:
函数 xQueueSendFromISR 用于中断服务程序中消息发送。
◆ 第 1 个参数是消息队列句柄。
◆ 第 2 个参数要传递数据地址,每次发送都是将消息队列创建函数 xQueueCreate 所指定的单个消息大 小复制到消息队列空间中。
◆ 第3 个参数用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是 pdTRUE, 说明有高优先级任务要执行,否则没有。
◆ 返回值,如果消息成功发送返回 pdTRUE,否则返回 errQUEUE_FULL。
举例:
函数 xQueueReceive 用于接收消息队列中的数据。
◆ 第 1 个参数是消息队列句柄。
◆ 第 2 个参数是从消息队列中复制出数据后所储存的缓冲地址,缓冲区空间要大于等于消息队列创建函 数 xQueueCreate 所指定的单个消息大小,否则取出的数据无法全部存储到缓冲区,从而造成内存溢出。
◆ 第 3 个参数是消息队列为空时,等待消息队列有数据的最大等待时间,单位系统时钟节拍。 ◆ 返回值,如果接到到消息返回 pdTRUE,否则返回 pdFALSE。
使用这个函数要注意以下问题:
1. 此函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序使用的是 xQueueReceiveFromISR。
2. 如果消息队列为空且第三个参数为 0,那么此函数会立即返回。
3. 如果用户将 FreeRTOSConfig.h 文件中的宏定义 INCLUDE_vTaskSuspend 配置为 1 且第三个参数配 置为 portMAX_DELAY,那么此函数会永久等待直到消息队列有数据。
举例: