FreeRTOS 消息队列

@(嵌入式)

Freertos
FreeRtos

简述

FreeRTOS 任务间通信方式有
* 消息通知 Notifications(V8.20版本开始支持)
* 消息队列 Queues
* 二进制信号量 Binary Semaphores
* 计数信号量 Counting Semaphores
* 互斥锁 Mutexes
* 递归互斥锁 Recursive Mutexes

上面这几中方式中, 除了消息通知, 其他几种实现都是基于消息队列。消息队列作为主要的通信方式, 支持在任务间, 任务和中断间传递消息内容。
这一章介绍 FreeRtos 消息队列的基本使用, 重点分析其实现的方式。

分析的源码版本是 v9.0.0


Queue 使用

FreeRTOS 官方提供了比较详细的接口使用文档 ( 戳我 ), 因此这里不花费太多的篇幅重复。
只是简单地介绍下, 主要使用到的接口以及示例。

创建一个消息队列

使用消息队列前, 需要先创建队列, 并拿到返回的句柄用于操作队列。

// 定义队列句柄变量
QueueHandle_t xQueue;
// 申请队列
// 参数 1 : 队列深度
// 参数 2 : 队列项内容大小
xQueue = xQueueCreate( 10, sizeof( unsigned long ) );

把队列比喻为一个邮箱, 那么队列项目是每封邮件的的大小, 深度是邮箱最大可以存储多少封邮件。

发送消息 & 接受消息

queue

队列的基本操作就是出队(接收消息)和入队(发送消息), 如上图所示, 有两个任务 A 和 B, A 发送消息给任务 B

void funOfTaskA()
{
    unsigned long pxMessage;
    // ...
    if( xQueue != 0 ) {
        // 发送消息
        // 参数 1 : 队列句柄
        // 参数 2 : 队列内容指针
        // 参数 3 : 允许阻塞时间
        xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
    }
}

void funOfTaskB()
{
    //... 
    // 接收消息
    // 参数 1 : 队列句柄
    // 参数 2 : 队列内容返回保存指针
    // 参数 3 : 允许阻塞时间
    if( xQueueReceive( xQueue, &( pxMessage ), ( TickType_t ) 10 ) )
        {
            // pcRxedMessage now points to the struct AMessage variable posted
            // by vATask.
        }
}

上面例子, 第一个函数发送消息到队列, 如果队列已经满了, 直接返回不阻塞。 第二过函数接收队列消息, 如果队列中没有消息, 会阻塞任务等待最长10个 Ticks。

FreeRTOS 的队列内容是内存拷贝, 我们将要发送的内容的地址传递给发送函数,该函数会将地址上的内容拷贝到自己的存储区域;而接收函数会将消息内容拷贝到我们传递给他的指针指向的内存区域。

如果消息内容太大, 队列需要提前占用的存储空间对应也会变大, 消息传递过程的内存拷贝也会导致效率下降。 对于这种情况, 可以通过传递指针而不是实际内容代替, 消息中是指向数据的指针, 接收任务接收消息后通过该指针读取到实际的内容。
如下例子所示

// 任务间传递的消息格式
 struct AMessage
 {
     char ucMessageID;
     char ucData[ 20 ];
 } xMessage;

 QueueHandle_t xQueue;

// 发送任务
 void vATask( void *pvParameters )
 {
    struct AMessage *pxMessage;
    // 队列的内容是 指向结构体的指针
    xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
    if( xQueue == 0 )
    {
        // Failed to create the queue.
    }

        // ...
    pxMessage = & xMessage;
    // 发送消息 传递的消息内容指针的指针
    xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );

    // ... Rest of task code.
 }

// 接受任务
void vADifferentTask( void *pvParameters )
{
    struct AMessage *pxRxedMessage;
    if( xQueue != 0 )
    {
        if( xQueueReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
            {
                // pcRxedMessage 指向 xMessage
            }
    }

    // ... Rest of task code.
}

简单的队列使用基本如上, 更加详细的操作, 请直接参考 官方文档

注意,在中断中使用 FreeRTOS 的接口, 需是结尾带有 FromISR的。


Queue 实现

按照上面举例的顺序, 从创建队列 -> 发送消息 -> 接收消息 依次展开分析 FreeRTOS 的队列源码实现。 这部分代码在源码目录下 queue.c 中。

数据结构

队列实现围绕其数据结构, 如下说明队列的数据结构, 其每个数据成员的作用。
姑且不管是否理解, 后续会一步一步介绍它的具体应用。

typedef struct QueueDefinition
{
    // 指向队列存储区域起始地址 -> 第一个队列项
    int8_t *pcHead;
    // 指向队列存储区域结束地址
    int8_t *pcTail;
    // 指向队列存储区域下一个空闲地址
    int8_t *pcWriteTo;

    // 不同情况下 一种有效
    union
    {
        // 作为队列时, 指向最后一个出队项
        int8_t *pcReadFrom;
        // 作为互斥变量, 记录 take 的次数, 递归计数
        UBaseType_t uxRecursiveCallCount;
    } u;

    // 管理因为等待入队而被阻塞的任务
    List_t xTasksWaitingToSend;
    // 管理因为等待消息而阻塞的任务
    List_t xTasksWaitingToReceive;

    // 当前队列消息数 
    volatile UBaseType_t uxMessagesWaiting;
    // 队列项最大数目
    UBase
  • 11
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值