FreeRTOS - 队列

FreeRTOS系统中,应用程序是由多个独立任务组成的,各个任务之间需要进行通信和同步,而任务间的通信和同步机制都是由queue来实现的,包括信号量(Semaaphore)、互斥锁(Mutex)、事件(Event)、流缓冲器(Stream Buffer)、消息队列(Message Buffer)等都是以队列为基础实现的。例如,在FreeRTOS的源码中,创建一个Queue的函数原型定义如下:

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
	#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
#endif

而创建一个二值信号量的函数原型为:

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
	#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )
#endif

可以看到,两者都是调用的 xQueueGenericCreate() 这个基础函数,并且SemaphoreGive函数原型定义如下:

#define xSemaphoreGive( xSemaphore )		xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )

其实就是调用的Queue的写入操作函数。当然,除了很多系统功能中使用了queue,应用程序都可以创建自己的queue。

Queue其实是一个先进先出的FIFO数据结构,具体原理网上有很多资料可以参考。Queue最重要的两个参数分别是 maximum length 和 element size。Maximum length用于表示队列的能够存储的多少个元素。Element size表示队列中元素的大小。这两个参数都是在创建队列的时候确定的。

Queue和Task是独立存在的,Queue不属于某一个Task,多个任务可以向同一个Queue写入数据和读取数据,但是一般来说一个Queue有多个写入任务是比较常见的,但是一个Queue有多个任务读取是很少见的。

任务从一个空的队列中读取数据的时候会导致该任务进入Block状态,读取的时候可以指定一个超时等待时间,当超时时间到达或者队列中被填入数据之后,系统就会将该任务从Block状态切换到Ready状态。如果有多个任务在读取队列的时候进入Block状态,那么当有数据进入队列的时候会优先将优先级更高的任务从Block状态切换到Ready状态。如果两个任务的优先级相同,那么阻塞时间更长的任务将会优先恢复Ready状态。

任务向一个满的队列中写入数据的时候也会导致该任务进入Block状态,和读取队列相同,也可以指定一个超时等待时间,当超时时间达到或者队列中有空闲空间的时候,任务就会恢复到Ready状态。其余和队列的读取是相似的,不再赘述。

创建一个Queue可以使用 xQueueCreate() 函数,原型如下:

xQueueHandle xQueueCreate( 
    unsigned portBASE_TYPE uxQueueLength,//队列元素个数的最大值
    unsigned portBASE_TYPE uxItemSize//元素的大小
);

创建队列的时候,队列的数据结构和元素存储空间使用的内存是从系统的堆区进行动态分配的,函数返回NULL表示没有足够的内存空间,队列创建失败,返回的非NULL值则是指向队列数据结构的指针(句柄)。

向队列中写入可以使用xQueueSendToBack() 和 xQueueSendToFront()函数,xQueueSend()函数和xQueueSendToBack()函数式相同的,函数原型如下:

portBASE_TYPE xQueueSendToFront( 
    xQueueHandle xQueue,//队列指针
    const void * pvItemToQueue,//将要写入队列的数据指针
    portTickType xTicksToWait//超时时间
);
portBASE_TYPE xQueueSendToBack( 
    xQueueHandle xQueue,//队列指针
    const void * pvItemToQueue,//将要写入队列的数据指针
    portTickType xTicksToWait//超时时间
);

不可以在中断处理函数中调用xQueueSendToBack() 和 xQueueSendToFront()函数,取而代之的是中断版本的xQueueSendToFrontFromISR()  和 xQueueSendToBackFromISR()函数。具体的函数的参数说明和取值范围可以查看FreeRTOS的Reference Manual。

从队列中读取可以使用xQueueReceive() 和 xQueuePeek()函数,原型如下:

portBASE_TYPE xQueueReceive(
    xQueueHandle xQueue,
    const void * pvBuffer,
    portTickType xTicksToWait
);
portBASE_TYPE xQueuePeek(
    xQueueHandle xQueue,
    const void * pvBuffer,
    portTickType xTicksToWait
);

两个函数的不同地方在于 xQueueReceive() 函数在读取了一个元素之后会将该数据从队列中删除,而 xQueuePeek() 函数在读取了一个元素之后并不会将数据删除。同样不要在中断函数中使用这两个函数,替代的是 xQueueReceiveFromISR()  和 xQueuePeakFromISR() 函数。

函数 uxQueueMessagesWaiting() 用于或者当前队列中存在的有效元素的个数。

当使用队列时,如果队列中的元素比较大,那么建议使用指针的方式代替直接传输数据的方式,这样数据的传输效率会更高,而且避免了大量内存的消耗,但是需要注意的是要保证使用的指针是有效的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值