FreeRTOS队列 | FreeRTOS九
创始人
2024-05-23 18:30:14
0

目录

说明:

一、队列简介

1.1、什么是队列

1.2、队列的优势

1.3、队列实现功能

1.4、队列使用了解

1.5、队列特点

1.6、队列阻塞处理

1.7、队列出队入队过程

二、队列结构体

2.1、结构体了解

2.2、共同体了解

2.3、队列结构体存储区

三、队列API函数

3.1、创建队列函数

3.2、入队函数

3.3、出队函数

四、队列API函数实现步骤

4.1、队列创建API函数

4.2、队列写入数据API函数

4.3、队列读取数据API函数


说明:

关于内容:

1)以下内容多为概念了解与步骤分析

2)暂无个人示例代码,使用的是FreeRTOS的官方示例代码

3)若想移植代码测试的,请移步其它地方寻找,下文内容暂无个人示例代码供测试

关于其它:

1)操作系统:win 10

2)平台:keil 5 mdk

3)语言:c语言

4)板子:STM32系列移植FreeRTOS
 

一、队列简介

1.1、什么是队列

        队列是任务到任务任务到中断中断到任务数据交互的一种机制(一种消息机制)。

 

1.2、队列的优势

        1)相比于裸机常用的全局变量,在FreeRTOS中队列保证了数据的安全性

        2)当出现多个任务同时操作一个变量时,该变量的读写数据不安全,如下图1、2

               

                             图1                                                                          图2

1.3、队列实现功能

        1)FreeRTOS基于队列,实现了多种功能,包括队列集、互斥信号量、计数型信号量、二值信号量、递归互斥信号量等

        2)读队列与写队列做了保护,防止多任务干扰,在使用时只需调用相关的API函数即可,如下图3、4

                                               

                    图3                                                                                                     图4

1.4、队列使用了解

        1)在队列中可以存储数量有限、大小固定的数据。队列中的每一个数据称为“队列项目”,队列能够存储“队列项目”的最大数量称为队列的长度

        2)在创建队列时,就要指定队列长度以及队列项目的大小(数值不固定),如下图5

 图5

1.5、队列特点

        1)数据出队方式,通常采用“先进先出”(FIFO)的数据存储缓冲机制,先入队的数据先被读取;当然也可以配置“后进后出”(LIFO)方式

        2)数据传递方式,采用实际值传递,直接将数值放到队列中传递;也可以使用传递指针,一般在传递较大数据是采用指针传递

        3)多任务访问,队列不属于某个任务,任何任务和中断都可以向队列发送(入队)/读取(出队)消息

        4)出队、入队阻塞,当任务向一个队列发送(入队)消息时,可以指定一个阻塞时间,当此队列已满无法入队时,有如下三种情况:

        1、阻塞时间为0,直接返回不会继续等待

        2、阻塞时间为0-port_Max_DELAY,等待设定的阻塞时间,在该时间内未能入队,返回

        3、阻塞时间为port_Max_DELAY,一直等到可以入队为止

        说明:出队与入队一样,不在重复

1.6、队列阻塞处理

        1)入队阻塞,当队列已满,而依然有任务X要入队时,此时无法入队,首先将任务X状态列表项挂载到pxDelayedTaskList,然后将任务X事件列表项挂载到xTaskWaitingToSend

        2)出队阻塞,当队列为空,而依然有任务Y要入队时,此时无法出队(因为没有数据),首先将任务Y状态列表项挂载到pxDelayedTaskList,然后将任务X事件列表项挂载到xTaskWaitingToReceive

当出现多个任务同时入队到一个“满队列”时,这些任务都会进入阻塞状态,也就是说多个任务在等待同一个队列的空间,当出现一个空间,那个任务先进入就绪态?

        1)在多个任务中优先级最高的任务

        2)如果多个任务中优先级相同,等待时间最久的任务会进入就绪态

1.7、队列出队入队过程

        1)创建队列,如下图6

图6

        2)入队(位置填充),如下图7、8

 图7

 图8

        3)出队(位置填充),如下图9、10

 图9

 图10

二、队列结构体

2.1、结构体了解

typedef struct QueueDefinition

{
        int8_ _t* pcHead                                                            /*存储区域的起始地址*/
        int8_ _t* pcWriteTo;                                                        /*下一个写入的位置*/
union                                                                                       /*共同体*/

{
        QueuePointers_ _t xQueue;
        SemaphoreData_ _t xSemaphore;
}u;
        List_ .t xTasksWaitingToSend;                                        /*等待发送列表*/
        List_ _t xTasksWaitingToReceive;                                  /*等待接收列表*/
        volatile UBaseType_ _t uxMessagesWaiting;                 /* 非空闲队列项目的数量*/
        UBaseType_ .t uxLength;                                                /*队列长度*/
        UBaseType_ .t uxltemSize;                                              /*队列项目的大小*/
        volatile int8_ _t cRxLock;                                                /*读取上锁计数器*/
        volatile int8_ _t cTxLock;                                                /*写入上锁计数器*/
/*其他的一些条件编译*/
}xQUEUE;

2.2、共同体了解

用于队列时:

typedef struct QueuePointers

{
        int8_ _t* pcTail;                             /*存储区的结束地址*/
        int8_ _t * pcReadFrom;                /*最后一个读取队列的地址*/
} QueuePointers_ _t;

用于互斥信号量和递归互斥信号量时:

typedef struct SemaphoreData

{
        TaskHandle_ t xMutexHolder;                         /* 互斥信号量持有者*/
        UBaseType_ t uxRecursiveCallCount;           /* 递归互斥信号量的获取计数器*/
} SemaphoreData_ t;

2.3、队列结构体存储区

如下图11:

 图11

三、队列API函数

使用队列的主要流程:创建流程-->写队列-->读队列

3.1、创建队列函数

1)函数名,xQueueCreate(),作用:动态方式创建队列

2)函数名,xQueueCreateStatic(),作用:静态方式创建队列

3)二者区别:动态方式创建队列内存由FreeRTOS所管理的内存动态分配,而静态创建需要用户自己分配内存

代码部分:

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

参数解释:

uxQueueLength,含义:队列长度

uxItemSize ,含义:队列项目大小

queueQUEUE_TYPE_BASE ,含义:实现什么功能的队列

可选参数如下图12:

 图12

返回值解释:

返回:NULL,含义:队列创建失败

返回:其他值,含义:队列创建成功

3.2、入队函数

如下图13:

图13

代码部分:

如下图14:

 图14

入队位置,如下图15:

 图15

入队入口函数:

BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
                              const void * const pvItemToQueue,
                              TickType_t xTicksToWait,
                              const BaseType_t xCopyPosition );

参数解释:

xQueue,含义:待写入的队列

pvItemToQueue,含义:待写入消息

xTicksToWait,含义:阻塞超时时间

xCopyPosition ,含义:消息写入位置

返回值解释:

返回:pdTRUE,含义:队列写入成功

返回:errQUEUE_FULL,含义:队列写入失败

3.3、出队函数

如下图16:

 图16

代码部分:

BaseType_t xQueueReceive( QueueHandle_t xQueue,
                          void * const pvBuffer,
                          TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;

参数解释:

xQueue,含义:待读出队列

pvBuffer,含义:消息读取缓存区

xTicksToWait ,含义:阻塞超时时间

返回值解释:

返回:pdTRUE,含义:队列写入成功

返回:pdFALSE,含义:队列写入失败

说明:

此函数在成功读取消息后,会讲已读取的消息移除,而函数xQueuePeek函数不会移除已读消息

四、队列API函数实现步骤

4.1、队列创建API函数

名称:xQueueCreate

实现过程:

1)实际执行的是xQueueGenericCreate( )
2)xQueueGenericCreate( ( uxQueueLength ). ( uxltemSize ), (
queueQUEUE_ TYPE. BASE))
3)计算队列需要多大内存xQueueSizeInBytes = ( size. t)( uxQueueLength *
uxltemSize )
4)为队列申请内存,申请大小: sizeof( Queue, t) + xQueueSizeInBytes .前面部分存
放结构体成员,后面存放队列项
5)判断内存是否申请成功,成功即计算出队列项存储区的首地址
6)调用prvlnitialiseNewQueue ()初始化新队列pxNewQueue

1、初始化队列结构体成员变量
2、调用xQueueGenericReset ()复位队列

4.2、队列写入数据API函数

名称:xQueueSend

实现过程:

1)实际执行的是:xQueueGenericSend( QueueHandle_t xQueue,
                              const void * const pvItemToQueue,
                              TickType_t xTicksToWait,
                              const BaseType_t xCopyPosition );

2)进入临界区(关中断)

3)判断队列是否已满

4)队列有空闲位置,则

1、只有在队列有空闲位置或为覆写的情况才能写入消息

2、当有空闲位置或覆写时,将代写入消息按指定写入方式复制到队列中

3、判断是否有因为读不到消息而阻塞的任务,有则解除阻塞态,通过:xTaskRemoveFromEventList()函数实现-->判断调度器是否被挂起

        1、没有被挂起:将移除相应的事件列表项和状态列表项,并且将任务添加到就绪列表中

        2、已挂起:将移除事件列表项,将事件列表项添加到等待就绪列表项:xPendingReadyList,当调用恢复任务调度器xTaskResumeALL(),xPendingReadyList中任务就会被处理

4、退出临界区

5)队列已满,则

1、此时不能写入消息,因此要将任务阻塞

2、如果阻塞时间为0,代表不阻塞,直接返回队列已满的错误

3、如果阻塞时间不为0,任务需要阻塞,记录下此时系统节拍计数器的值和溢出次数,用于下面对阻塞时间进行补偿

4.3、队列读取数据API函数

名称:xQueueReceive

实现过程:

1)进入临界区

2)判断队列是否为空

3)有数据,则

1、使用函数prvCopyDataFromQueue()拷贝数据

2、队列项目个数减一

3、因为前面已经减了一个队列项,所以队列存在空位,如果xTaskWaitingToSend等待发送列表中,有任务,解除阻塞态,通过xTaskRemoveFromEventList()函数判断调度器是否被挂起:

        1、没有被挂起:将移除相应的事件列表项和状态列表项,并且将任务添加到就绪列表中

        2、已挂起:将移除事件列表项,将事件列表项添加到等待就绪列表项:xPendingReadyList,当调用恢复任务调度器xTaskResumeALL(),xPendingReadyList中任务就会被处理

        

4、退出临界区(开中断)

4)没有数据

1、此时读取不到消息,将任务阻塞

2、如果阻塞时间为0,代表不阻塞,直接返回队列为空的错误

3、如果阻塞时间不为0,任务需要阻塞,记录下此时系统节拍计数器的值和溢出次数,用于下面对阻塞时间进行补偿

4、判断阻塞时间补偿后,是否还需要继续阻塞,则

        1、需要:将任务的事件列表添加到等待接收列表中,将任务状态列表项添加到阻塞列表进行阻塞,队列解锁,恢复调度器

        2、不需要:队列解锁,恢复调度器、返回队列为空错误

相关内容

热门资讯

婚礼主持词 婚礼主持词(精选20篇)  主持词要注意活动对象,针对活动对象写相应的主持词。在人们越来越多的参与各...
半年总结会主持词 半年总结会主持词  以下是由应届毕业生网PQ小编为大家整理出来的半年总结会主持词,仅供参考,半年总结...
最短的对口相声台词 最短的对口相声台词范文  相声是一种中国曲艺表演艺术,源于华北,流行于京津冀,普及于全国及海内外,始...
司仪主持词 精选司仪主持词(精选14篇)  主持词需要富有情感,充满热情,才能有效地吸引到观众。在各种集会、活动...
电影节颁奖典礼主持词 电影节颁奖典礼主持词  颁奖典礼上最重要的就是主持人手中的台词啦!下面来看看小编带来的电影节颁奖典礼...
安全生产会议的致辞 安全生产会议的致辞(精选5篇)  在日常的学习、工作、生活中,要用到致辞的地方还是很多的,致辞具有“...
最新半台词分享 最新三句半台词分享  俺们几个话挺多,大家不要嫌罗嗦,希望能够捧捧场,鼓掌!  北京先把地方占,天津...
《教父》经典台词中英文对照 《教父》经典台词中英文对照  1、To be close to your friend, but c...
播音主持稿 播音主持稿(精选21篇)  在现在的社会生活中,我们很多时候都不得不用到主持稿,主持稿是主持人为把整...
年会主持词 精选年会主持词四篇  主持词要注意活动对象,针对活动对象写相应的主持词。在现今人们越来越重视活动氛围...
金秋国庆主持词开场白 金秋国庆主持词开场白  国庆节是我们祖国母亲的生日,下面unjs小编整理了金秋国庆主持词开场白,欢迎...
金榜题名升学宴主持词 金榜题名升学宴主持词  金榜题名升学宴主持词开场白(一)  大家中午好!  在这鸟语花香、绿意盎然的...
春节晚会主持词串词 春节晚会主持词串词(精选10篇)  在人们积极参与各种活动的今天,我们越来越需要串词,有的时候,涉及...
高考动员大会主持词 高考动员大会主持词  同学们:  大家早上好!  xxxx年高考就要拉开帷幕,可谓弓已经在弦,剑已经...
职工代表大会会议主持词 职工代表大会会议主持词  职工代表大会是职工群众当家作主,参加企业经营决策、管理、监督干部、行使民主...
运动会闭幕式的主持词 运动会闭幕式的主持词(精选5篇)  主持词是主持人在节目进行过程中用于串联节目的串联词。在当今社会中...
德芙橱窗篇广告 德芙橱窗篇广告德芙橱窗篇广告评论整个广告的广告词只有简简单单的“德芙,此刻尽丝滑”,却给人很深刻的印...
《老友记》中超中英文对照经典... 《老友记》中超实用的中英文对照经典台词  《老友记》可以说是最风靡中国的`一部学习美语的剧集了。这几...
员工生日主持词 员工生日主持词3篇  篇一:公司员工集体生日晚会主持词主持人开场白:(背景音乐:激动人心)  各位领...
三八主持词开场白 三八主持词开场白  在现实社会中,很多时候,我们都不可避免的需要用到开场白,独具匠心的开场白,才能给...