【FreeRTOS】-04消息队列

1、消息队列的基本概念

在FreeRTOS中,消息队列(Message Queue)是任务间通讯(IPC)、中断和任务间通讯的核心机制,用于实现数据传递和同步。
FreeRTOS中使用队列数据结构实现任务异步通信方式,具有如下特性:

  • 消息支持先进先出、先进后出方式排队,支持异步读写工作方式;
  • 读写队列均支持超时机制;
  • 可以允许不同长度的任意类型消息;
  • 一个任务能够从任意一个消息队列接收和发送消息;
  • 多个任务能从同一个消息队列接收和发送消息;
  • 当队列使用结束后,可以通过删除队列函数进行删除。

2、消息队列的运作机制

在这里插入图片描述
1、消息队列的创建与删除
创建消息队列时,FreeRTOS会先给消息队列分配一块内存空间,大小等于消息队列控制块大小加上(单个消息空间大小与消息队列长度的乘积)。只有当删除该消息队列的时候,这块内存才会被释放掉。
2、消息的发送
任务或者中断服务程序都可以给消息队列发送消息,如果队列未满或允许覆盖入队,FreeRTOS会将消息拷贝到消息队列的队尾,否则,会根据用户指定的阻塞超时时间进行阻塞。当消息队列中可以继续发送信息后,任务将自动有阻塞态转换为就绪态。当等待时间超过指定的阻塞时间,即使队列中还不允许入队,任务也会自动从阻塞态转移为就绪态,此时发送消息的任务或中断程序会收到一个错误码errQUEUE_FULL
3、消息的接收
当某个任务试图读取一个消息队列时,同样可以指定阻塞超时时间。超时时间到或可以读出消息后的任务状态都与消息的发送一致。

3、消息队列的阻塞机制

每个对消息队列读写的函数都有阻塞机制。
当我们进行读操作时,根据队列中是否有可读取的消息,任务可以有三种选择:

  • 1、不等待消息,继续执行,任务保持运行状态;
  • 2、等待指定tick时间,在等待中,任务进入阻塞态。若在等待中,读取到消息,任务进入就绪态;若超时,函数返回错误代码,继续执行其余任务;
  • 3、任务死等,一直处于阻塞态,直到读取到消息。

当我们进行写操作时,根据队列中是否有空余消息空间,任务同样有上述三种选择。

4、消息队列的常用函数

使用队列模块的典型操作如下:

  • 创建消息队列;
  • 写队列操作;
  • 读队列操作;
  • 删除队列。

中断服务函数有专用的队列操作函数,要注意,在中断服务函数中也不允许有任务延时。

xQueueCreate() ; 			/* 消息队列动态创建函数 */
xQueueCreateStatic(); 		/* 消息队列静态创建函数 */
vQueueDelete() ;			/* 消息队列删除 */
xQueueSend();				/* 向消息队列尾部发送一个队列消息 */
xQueueSendToBack() ;		/* 向消息队列尾部发送一个队列消息 */
xQueueSendFromISR();		/* 中断中用 */
xQueueSendToBackFromISR() ;	/* 中断中用 */
xQueueSendToFront() ;		/* 将队列消息发送至队列队首 */
xQueueGenericSend();		/* 通用发送队列消息函数 */
xQueueGenericSendFromISR();	/* 中断专用通用发送队列消息函数 */
xQueueReceive();			/* 从队列中读取消息,并删除 */
xQueuePeek() ;				/* 从队列中读取消息,不删除 */
xQueueReceiveFromISR();
xQueuePeekFromISR();
xQueueGenericReceive() ;

5、消息队列的应用场景

5.1、数据生产者 - 消费者模型

任务 A(生产者)收集传感器数据,通过队列传递给任务 B(消费者)进行处理。
示例:温度传感器数据采集→数据滤波→LCD 显示。

5.2、异步事件处理

中断服务函数(ISR)将事件信息(如按键触发)放入队列,由任务后续处理,避免 ISR 执行过长。
示例:外部中断检测到按键→队列传递按键值→任务响应 UI 事件。

5.3、任务间同步

任务 A 完成特定操作后,通过队列发送信号通知任务 B 继续执行。
示例:初始化任务完成硬件配置→队列发送 “READY”→控制任务启动。

5.4、资源共享

通过队列传递资源指针,确保同一时间只有一个任务访问共享资源。
示例:多个任务通过队列请求访问 SD 卡,避免竞争。(核心思想:通过消息队列访问共享资源的访问令牌,而非资源本身)

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

/* 定义资源令牌(简单整数即可) */
typedef uint8_t ResourceToken_t;
#define SD_CARD_TOKEN 1

/* 创建资源令牌队列(长度1,表示只能有一个任务持有令牌) */
QueueHandle_t xSDCardTokenQueue;

/* 初始化SD卡(示例函数,需根据硬件实现) */
static void prvInitSDCard(void) {
    /* 初始化SD卡硬件 */
    // ...
}

/* 获取SD卡访问权限(阻塞直到获取成功) */
static BaseType_t xAcquireSDCardAccess(void) {
    ResourceToken_t xToken;
    return xQueueReceive(xSDCardTokenQueue, &xToken, portMAX_DELAY);
}

/* 释放SD卡访问权限 */
static void vReleaseSDCardAccess(void) {
    ResourceToken_t xToken = SD_CARD_TOKEN;
    xQueueSend(xSDCardTokenQueue, &xToken, 0);
}

/* 任务1:日志记录(低优先级) */
void vLoggingTask(void *pvParameters) {
    for (;;) {
        /* 模拟产生日志数据 */
        char *pcLogData = pcGenerateLogData();
        
        /* 请求SD卡访问权限 */
        if (xAcquireSDCardAccess() == pdTRUE) {
            /* 访问SD卡,写入日志 */
            vWriteLogToSDCard(pcLogData);
            
            /* 释放SD卡访问权限 */
            vReleaseSDCardAccess();
        }
        
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

/* 任务2:数据存储(高优先级) */
void vDataStorageTask(void *pvParameters) {
    for (;;) {
        /* 等待数据更新(示例:信号量) */
        xSemaphoreTake(xDataReadySemaphore, portMAX_DELAY);
        
        /* 模拟产生需要存储的数据 */
        float *pfData = pfGetSensorData();
        
        /* 请求SD卡访问权限 */
        if (xAcquireSDCardAccess() == pdTRUE) {
            /* 访问SD卡,存储数据 */
            vSaveDataToSDCard(pfData);
            
            /* 释放SD卡访问权限 */
            vReleaseSDCardAccess();
        }
    }
}

/* 主函数 */
int main(void) {
    /* 初始化硬件 */
    prvSetupHardware();
    prvInitSDCard();
    
    /* 创建SD卡令牌队列(长度1) */
    xSDCardTokenQueue = xQueueCreate(1, sizeof(ResourceToken_t));
    
    /* 初始化令牌(放入队列,表示资源可用) */
    ResourceToken_t xToken = SD_CARD_TOKEN;
    xQueueSend(xSDCardTokenQueue, &xToken, 0);
    
    /* 创建任务 */
    xTaskCreate(vLoggingTask, "Logging", 256, NULL, 1, NULL);
    xTaskCreate(vDataStorageTask, "Storage", 256, NULL, 2, NULL);
    
    /* 启动调度器 */
    vTaskStartScheduler();
    
    /* 如果程序执行到此,说明发生了错误 */
    for (;;);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值