企业网站强制备案,福建省住房城乡建设部网站,建设银行网站每天几点更新,企业网站制作报价队列简介 队列是任务到任务、任务到中断、中断到任务数据交流的一种机制#xff08;消息传递#xff09; 全局变量的弊端#xff1a;数据无保护#xff0c;导致数据不安全#xff0c;当多个任务同时对该变量操作时#xff0c;数据易受损 使用队列的情况如下#xff1a;…队列简介 队列是任务到任务、任务到中断、中断到任务数据交流的一种机制消息传递 全局变量的弊端数据无保护导致数据不安全当多个任务同时对该变量操作时数据易受损 使用队列的情况如下 读写队列做好了保护防止多任务同时访问冲突我们只需要直接调用API函数即可简单易用 FreeRTOS基于队列 实现了多种功能其中包括队列集、互斥信号量、计数型信号量、
二值信号量、 递归互斥信号量因此很有必要深入了解 FreeRTOS 的队列 。 在队列中可以存储数量有限、大小固定的数据。队列中的每一个数据叫做“队列项目”队列能够存储“队列项目”的最大数量称为队列的长度。 在创建队列时就要指定队列长度以及队列项目的大小 FreeRTOS队列特点
1、数据入队出队方式 队列通常采用“先进先出”(FIFO)的数据存储缓冲机制即先入队的数据会先从队列中被读取FreeRTOS中也可以配置为“后进先出”LIFO方式
2、数据传递方式 FreeRTOS中队列采用实际值传递即将数据拷贝到队列中进行传递 FreeRTOS采用拷贝数据传递也可以传递指针所以在传递较大的数据的时候采用指针传递
3、多任务访问 队列不属于某个任务任何任务和中断都可以向队列发送/读取消息
4、出队、入队阻塞 当任务向一个队列发送消息时可以指定一个阻塞时间假设此时当队列已满无法入队
① 若阻塞时间为0 直接返回不会等待 ② 若阻塞时间为0~port_MAX_DELAY 等待设定的阻塞时间若在该时间内还无法入队超时后直接返回不再等待 ③ 若阻塞时间为port_MAX_DELAY 死等一直等到可以入队为止。出队阻塞与入队阻塞类似 入队阻塞 队列满了此时写不进去数据 ①将该任务的状态列表项挂载在pxDelayedTaskList ②将该任务的事件列表项挂载在xTasksWaitingToSend 出队阻塞 队列为空此时读取不了数据 ①将该任务的状态列表项挂载在pxDelayedTaskList ②将该任务的事件列表项挂载在xTasksWaitingToReceive 问题当多个任务写入消息给一个“满队列”时这些任务都会进入阻塞状态也就是说有多个任务 在等待同一 个队列的空间。那当队列中有空间时哪个任务会进入就绪态 答 1、优先级最高的任务 2、如果大家的优先级相同那等待时间最久的任务会进入就绪态 队列结构体介绍 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 uxItemSize; /* 队列项目的大小 */volatile int8_t cRxLock; /* 读取上锁计数器 */volatile int8_t cTxLock /* 写入上锁计数器 *//* 其他的一些条件编译 */
} xQUEUE;当我们锁住队列的时候你是可以正常读写队列的只不过操作不了等待发送\接收列表。 当用于队列使用时 typedef struct QueuePointers
{int8_t * pcTail; /* 存储区的结束地址 */int8_t * pcReadFrom; /* 最后一个读取队列的地址 */
} QueuePointers_t;当用于互斥信号量和递归互斥信号量时 typedef struct SemaphoreData
{TaskHandle_t xMutexHolder; /* 互斥信号量持有者 */UBaseType_t uxRecursiveCallCount; /* 递归互斥信号量的获取计数器 */
} SemaphoreData_t;队列结构体整体示意图 队列相关API函数介绍 使用队列的主要流程创建队列 --- 写队列 --- 读队列。 创建队列相关API函数介绍 函数 描述 xQueueCreate() 动态方式创建队列 xQueueCreateStatic() 静态方式创建队列 动态和静态创建队列之间的区别队列所需的内存空间由 FreeRTOS 从 FreeRTOS 管理的堆中分配而静态创建需要用户自行分配内存。
创建队列函数入口参数解析
#define xQueueCreate ( uxQueueLength, uxItemSize ) \ xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), (queueQUEUE_TYPE_BASE )) 此函数用于使用动态方式创建队列队列所需的内存空间由 FreeRTOS 从 FreeRTOS 管理的堆中分配 前面说 FreeRTOS 基于队列实现了多种功能每一种功能对应一种队列类型队列类型的 queue.h 文件中有定义
#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U ) /* 队列 */
#define queueQUEUE_TYPE_SET ( ( uint8_t ) 0U ) /* 队列集 */
#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U ) /* 互斥信号量 */
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( ( uint8_t ) 2U ) /* 计数型信号量 */
#define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) /* 二值信号量 */
#define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U ) /* 递归互斥信号量 */往队列写入消息API函数 函数 描述 xQueueSend() 往队列的尾部写入消息 xQueueSendToBack() 同 xQueueSend() xQueueSendToFront() 往队列的头部写入消息 xQueueOverwrite() 覆写队列消息只用于队列长度为 1 的情况 xQueueSendFromISR() 在中断中往队列的尾部写入消息 xQueueSendToBackFromISR() 同 xQueueSendFromISR() xQueueSendToFrontFromISR() 在中断中往队列的头部写入消息 xQueueOverwriteFromISR() 在中断中覆写队列消息只用于队列长度为 1 的情况 队列写入消息:
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) \ xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) \ xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) \ xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT )#define xQueueOverwrite( xQueue, pvItemToQueue ) \ xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE )可以看到这几个写入函数调用的是同一个函数xQueueGenericSend( )只是指定了不同的写入位置 队列一共有 3 种写入位置
#define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) /* 写入队列尾部 */
#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) /* 写入队列头部 */
#define queueOVERWRITE ( ( BaseType_t ) 2 ) /* 覆写队列*/注意覆写方式写入队列只有在队列的队列长度为 1 时才能够使用
往队列写入消息函数入口参数解析
BaseType_t xQueueGenericSend( QueueHandle_t xQueue,const void * const pvItemToQueue,TickType_t xTicksToWait,const BaseType_t xCopyPosition );从队列读取消息API函数 函数 描述 xQueueReceive() 从队列头部读取消息并删除消息 xQueuePeek() 从队列头部读取消息 xQueueReceiveFromISR() 在中断中从队列头部读取消息并删除消息 xQueuePeekFromISR() 在中断中从队列头部读取消息
从队列读取消息函数入口参数解析
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )此函数用于在任务中从队列中读取消息并且消息读取成功后会将消息从队列中移除。 BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) 此函数用于在任务中从队列中读取消息 但与函数 xQueueReceive()不同此函数在成功读取消息后并不会移除已读取的消息 队列相关API函数工作流程具体解析
创建队列xQueueCreate( ) 实际执行的是xQueueGenericCreate( )
xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) ) 1、计算队列需要多大内存 xQueueSizeInBytes ( size_t ) ( uxQueueLength * uxItemSize ) 2、为队列申请内存申请大小sizeof( Queue_t ) xQueueSizeInBytes 前面部分存放结构体成员后面就是队列项大小 3、判断内存是否申请成功成功即计算出队列项存储区的首地址 4、调用prvInitialiseNewQueue初始化新队列pxNewQueue (1)、初始化队列结构体成员变量 (2)、调用xQueueGenericReset复位队列 1)、初始化其他队列结构体成员变量 2)、判断要复位的队列是否为新创建的队列 不是新创建队列那就复位它将列表xTasksWaitingToSend移除是新创建的队列那就初始化这两个列表xTasksWaitingToSend和xTasksWaitingToReceive 往队列写入数据入队xQueueSend( ) 实际执行的是xQueueGenericSend(
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) 1、进入临界区关中断 2、判断队列是否已满 3、队列有空闲位置 (1)、只有在队列有空闲位置或为覆写的情况才能写入消息 (2)、当有空闲位置或覆写时将待写入消息按指定写入方式复制到队列中 (3)、判断是否有因为读不到消息而阻塞的任务有的话将解除阻塞态通过这个函数 判断调度器是否被挂起没有挂起将移除相应的事件列表项和状态列表项并且将任务添加到就绪列表中。挂起将移除事件列表项将事件列表项添加到等待就绪列表xPendingReadyList当调用恢复调度器时xTaskResumeAll( )xPendingReadyList中多任务就会被处理 (4)、退出临界区开中断 3、队列已满 (1)、此时不能写入消息因此要将任务阻塞 (2)、如果阻塞时间为0 代表不阻塞直接返回队列满错误 (3)、如果阻塞时间不为0任务需要阻塞记录下此时系统节拍计数器的值和溢出次数用于下面对阻塞时间进行补偿补偿就是计算剩余的阻塞时间还有多久 (4)、判断阻塞时间补偿后是否还需要阻塞 需要将任务的事件列表项添加到等待发送列表中将任务状态列表项添加到阻塞列表中进行阻塞队列解锁恢复调度器。不需要队列解锁恢复调度器返回队列满错误。 不需要队列解锁恢复调度器返回队列满错误 从队列读取数据出队xQueueReceive( ) 1、进入临界区关中断 2、判断队列是否为空 3、有数据 (1)、使用函数prvCopyDataFromQueue( )拷贝数据 (2)、队列项目个数减一 (3)、因为前面已经减了一个队列项所以队列已经有空位了如果xTasksWaitingToSend等待发送列表中有任务则解除阻塞态通过这个函数xTaskRemoveFromEventList( ) 判断调度器是否被挂起没有挂起将移除相应的事件列表项和状态列表项并且将任务添加到就绪列表中。挂起将移除事件列表项将事件列表项添加到等待就绪列表xPendingReadyList当调用恢复调度器时xTaskResumeAll( )xPendingReadyList中多任务就会被处理 (4)、退出临界区开中断 4、为空 (1)、此时读取不到消息因此要将任务阻塞 (2)、如果阻塞时间为0 代表不阻塞直接返回队列空错误 (3)、如果阻塞时间不为0任务需要阻塞记录下此时系统节拍计数器的值和溢出次数用于下面对阻塞时间进行补偿 (4)、判断阻塞时间补偿后是否还需要阻塞 需要将任务的事件列表项添加到等待接收列表中将任务状态列表项添加到阻塞列表中进行阻塞队列解锁恢复调度器。 不需要队列解锁恢复调度器返回队列空错误