队列邮箱

队列邮箱

官方文档参考 4.7 Using a Queue to Create a Mailbox [[FreeRTOS_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf#page=170&selection=5,0,7

官方文档参考

4.7 Using a Queue to Create a Mailbox

[[FreeRTOS_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf#page=170&selection=5,0,7,33|FreeRTOS_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide, page 170]]


官方函数文档参考

3.12 xQueueOverwrite()

[[FreeRTOS_Reference_Manual_V10.0.0.pdf#page=178&selection=2,0,4,17|FreeRTOS_Reference_Manual_V10.0.0, page 178]]


邮箱对操作系统的概念

假设我们现在有一个邮箱,里面存放着一个数据,有一个任务会写这个输入,另外的几个任务会读这个数据,通过这个数据来改变任务代码的运行。对于 FreeRTOS 用 Queue 来创建 MailBox,首先 Queue 只包含一个数据,功能是有一些不同的,队列是把数据从一个任务传递到另外一个任务(或者到中断)。但是 邮箱 不会传递数据,而是会保持这个数据,保持这个数据被任何任务读取,他不会把这个数据传递出去,让其他任务去读取这个数据,根据这个数据去决定程序运行情况。
13. 队列邮箱-20240614194405410.webp

发送方会覆盖( Overwrite )这个值,接收方只能读这个数据,不能改变这个数据。

用队列创建邮箱,使 writeTask 和 readTask 进行通讯。

xQueueOverwrite()

#include "FreeRTOS.h"
#include "queue.h"

BaseType_t xQueueOverwrite( QueueHandle_t xQueue, const void *pvItemToQueue );

函数说明:

  • 即使在队列已经满了的情况,这个版本的xQueueSendToBack()函数会依然会将数据写入队列。xQueueOverwrite()的目的是:用只有一个长度的队列,意味着队列要么是满的,要么是空的。
  • 这个函数不能在中断过程中调用。

参数:

  • xQueue:队列,存放即将发送的数据。
  • pvItemToQueue:存放要放入队列的指针,队列的容量是在队列创建时设置的,数据会从指针拷贝到队列的地址空间。

返回值:

  • pdPASS:函数不论如何都会返回这个值,因为无论队列是空还是满,函数都会将值写入队列。

xQueuePeek()

#include "FreeRTOS.h"
#include "queue.h"

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

函数说明:

  • 从队列中读取数据,但是不会删除数据。

参数:

  • xQueue:数据将从这个队列读取。
  • pvBuffer:从队列读取到的数据将被拷贝到这个指针中。指针的长度必须大于等于队列项目的长度。
  • xTicksToWait:超时等待时间。

返回值:

  • pdPASS:读取数据成功返回。
  • errQUEUE_EMPTY:队列为空返回。

示例代码

#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "freertos/queue.h"

void write_task(void *pvPara)
{
    QueueHandle_t Mailbox = (QueueHandle_t)pvPara;
    int bufferToWrite = 0;

    while (true) {
		// 用邮箱,发送消息,此函数会覆盖之前 消息队列 中的值
        xQueueOverwrite(Mailbox, &bufferToWrite);
        bufferToWrite++;

        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void read_task(void *pvPara)
{
    QueueHandle_t Mailbox = (QueueHandle_t)pvPara;
    int bufferToRead = 0;

    while (true) {
		// 接收消息,此函数不会像 xQueueReceive() 函数一样直接将数据从队列拿出来,而是把数据拷贝到传入的指针中
        xQueuePeek(Mailbox, &bufferToRead, 0);
        printf("[READ]: From Mailbox: %d\n", bufferToRead);

        vTaskDelay(500 / portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
	// 用 消息队列 创建邮箱,长度为一
    QueueHandle_t Mailbox = xQueueCreate(1, sizeof(int));

    if (Mailbox) {
        xTaskCreate(write_task, "Write Task", 2048, (void *)Mailbox, 1, NULL);
        xTaskCreate(read_task, "Read Task", 2048, (void *)Mailbox, 1, NULL);
    } else {
        printf("[MAIN]: Not have enough memory to create a queue.\n");
    }
}

示例输出

13. 队列邮箱-20240615150352194.webp

因为代码中,读取频率比写入频率要高,可以看到会多次读取到一样的值,这也证明了xQueuePeek函数不会把数据从消息队列中删除。

Comment