当前位置: 首页 > news >正文

如何用front怕个做网站青海省住房和城乡建设厅 网站首页

如何用front怕个做网站,青海省住房和城乡建设厅 网站首页,网站被采集了 一个栏目不收录,中山精品网站建设精英前言 本篇文章主要对 FreeRTOS 中消息队列的概念和相关函数进行了详解消息队列【下篇】详细剖析了消息队列中发送、接收时队列消息控制块中各种指针的行为#xff0c;以及几个发送消息和接收消息的函数的运作流程笔者有关于 【FreeRTOS】【应用篇】消息队列【上篇】——队列基…前言 本篇文章主要对 FreeRTOS 中消息队列的概念和相关函数进行了详解消息队列【下篇】详细剖析了消息队列中发送、接收时队列消息控制块中各种指针的行为以及几个发送消息和接收消息的函数的运作流程笔者有关于 【FreeRTOS】【应用篇】消息队列【上篇】——队列基本概念、创建和删除讲解了消息队列的基本概念作用、存储结构、出入队列逻辑、阻塞机制以及相关函数队列创建和删除一部分代码和图片参考野火 FreeRTOS 教程 文章目录 前言一、发送、接收时队列消息控制块中各种指针的行为1. 初始化后2. 发送消息到队列时① 发送到队尾普通消息② 发送到队头紧急消息 3. 从队列中读取消息时 二、消息发送 API1. 概要① 适用于任务中的消息发送函数② 适用于中断中的消息发送函数 2. xQueueSend()与 xQueueSendToBack()① xQueueSend() 代码② xQueueSend() 使用示例③ xQueueSendToBack() 代码 3. xQueueSendFromISR()与 xQueueSendToBackFromISR()① 代码② 使用示例 4. xQueueSendToFront()① 代码 5. xQueueSendToFrontFromISR()① 代码 三、通用的任务中发送消息函数 xQueueGenericSend()1. xQueueGenericSend() 函数定义2. xQueueGenericSend() 函数流程图3. prvCopyDataToQueue() 函数说明① 如何被调用② 参数说明③ prvCopyDataToQueue() 函数完整代码 4. 超时状态结构的配置 vTaskSetTimeOutState()① 超时状态结构如何配置② vTaskSetTimeOutState() 函数定义 5. 挂起调度器和锁上队列的意义① 如何操作② 意义③ 队列上锁代码 6. 检查延时是否到期函数 xTaskCheckForTimeOut()① 使用② 主要逻辑③ 源代码 7. 将任务插入任务等待发送列表 vTaskPlaceOnEventList()① 使用② 函数定义 8. 队列解锁函数 prvUnlockQueue()① 函数原型② 函数结构分析③ 计数锁的原理 四、用于中断中的 通用的消息发送函数 xQueueGenericSendFromISR()1. 函数概要2. 函数代码 五、消息读取 API1. 简介2. xQueueReceive()① 原型及作用② 宏展开③ 应用 3. xQueuePeek()① 作用② 宏展开 4. xQueueReceiveFromISR() 与 xQueuePeekFromISR()① xQueueReceiveFromISR() 使用示例 六、通用读取消息函数 xQueueGenericReceive()1. 函数结构分析2. 函数定义 四、性能提示后记 一、发送、接收时队列消息控制块中各种指针的行为 1. 初始化后 消息空间从第一个单元到最后一个单元是从低地址增长到高地址pcHead 指向消息空间第一个消息单元此后固定不动是为了方便快速定位消息空间头部而设定的指针pcTail 指向消息空间最后一个消息单元这个消息单元不作存储消息使用前一个单元才是被用作存储的最后一个单元此后固定不动是为了方便快速定位消息空间尾部而设定的指针pcWriteTo 指向 pcHead指向下一个插入到队尾的消息的存储单元pcReadFrom 指向 pcTail 的前一个消息单元指向队列中下一个要被读取的消息的前一个单元 2. 发送消息到队列时 ① 发送到队尾普通消息 xQueueSend() 和 xQueueSendToBack()消息被拷贝到 pcWriteTo 指向的消息单元然后 pcWriteTo 自增一个消息单元 ( void ) memcpy( ( void * ) pxQueue-pcWriteTo, pvItemToQueue, ( size_t ) pxQueue-uxItemSize ); pxQueue-pcWriteTo pxQueue-uxItemSize;当 pcWriteTo 自增到 pcTail 位置时返回 pcHead if( pxQueue-pcWriteTo pxQueue-pcTail ) {pxQueue-pcWriteTo pxQueue-pcHead;}② 发送到队头紧急消息 xQueueSendToFront()消息被拷贝到 pcReadFrom 指向的消息单元然后 pcReadFrom 自减一个消息单元 ( void ) memcpy( ( void * ) pxQueue-u.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue-uxItemSize ); pxQueue-u.pcReadFrom - pxQueue-uxItemSize;pcReadFrom 指向 pcHead 的上一个消息单元时此时越界就重置 pcReadFrom 指向 pcTail 的前一个消息单元 if( pxQueue-u.pcReadFrom pxQueue-pcHead ){pxQueue-u.pcReadFrom ( pxQueue-pcTail - pxQueue-uxItemSize );}3. 从队列中读取消息时 首先自增 pcReadFrom使其指向要读取的消息单元pcReadFrom 初始化后保持为指向要读取的消息单元的前一个单元 pxQueue-u.pcReadFrom pxQueue-uxItemSize;然后判断自增后的 pcReadFrom 是否越界如果指向的位置等于或者大于 pcTail 指向的位置那么使 pcReadFrom 重新指向 pcHead if( pxQueue-u.pcReadFrom pxQueue-pcTail ) /*lint !e946 MISRA exception justified as use of the relational operator is the cleanest solutions. */{pxQueue-u.pcReadFrom pxQueue-pcHead;}最后从队列中将 pcReadFrom 指向的要读取的消息拷贝出来 ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue-u.pcReadFrom, ( size_t ) pxQueue-uxItemSize ); 二、消息发送 API 1. 概要 ① 适用于任务中的消息发送函数 适用于任务中的消息发送函数有三个版本 xQueueSend() 等同于 xQueueSendToBack()作用都是将消息插入队列尾部 从尾部插入的消息是普通消息会按照插入顺序的先后被先后读取注意不是指 pcTail 所指的位置pcTail 只是一个为了方便快速定位消息空间尾部设定的指针在队列创建后就固定不动了这里所说的插入到队尾是指插入到 pcWriteTo 所指的位置插入完成后pcWriteTo 自增 xQueueSendToFront() 作用是将消息插入队列头部 从头部插入的消息是紧急消息紧急消息会比普通消息先被读取这里所说的插入头部并不是指插入到 pcHead 所指的位置和 pcTail 一样pcHead 只是一个为了方便快速定位消息空间头部设定的指针在队列创建后就固定不动了这里所说的插入到队头是指插入到 pcReadFrom 所指的位置插入完成后pcReadFrom 自减保持指向下一个要读取的消息的前一个消息 这三个适用于任务中的消息发送函数实际上都调用了 xQueueGenericSend()只是传入的发送位置的参数不同 ② 适用于中断中的消息发送函数 适用于中断中的消息发送函数也有三个版本只能用于中断中执行是不带阻塞机制的 xQueueSendToBackFromISR() 等同于 xQueueSendFromISR() 作用是将消息插入队列尾部与上文的 xQueueSend() 说明一致 xQueueSendToFrontFromISR() 将消息插入队列头部与上文的 xQueueSendToFront() 说明一致 2. xQueueSend()与 xQueueSendToBack() ① xQueueSend() 代码 原型 BaseType_t xQueueSend(QueueHandle_t xQueue,const void * pvItemToQueue,TickType_t xTicksToWait);参数说明 xQueue队列句柄pvItemToQueue指针指向要发送到队列尾部的队列消息xTicksToWait队列满时等待队列空闲的最大超时时间。如果队列满并且 xTicksToWait 被设置成 0函数立刻返回。超时时间的单位为系统节拍周期常量 portTICK_PERIOD_MS 用于辅助计算真实的时间单位为 ms。如果 INCLUDE_vTaskSuspend 设置成 1并且指定延时为 portMAX_DELAY 将导致任务挂起没有超时 宏定义 #define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )② xQueueSend() 使用示例 该宏是为了向后兼容没有包含 xQueueSendToFront() 和 xQueueSendToBack() 这两个宏的 FreeRTOS 版本。 static void Send_Task(void* parameter) {BaseType_t xReturn pdPASS; /* 定义一个创建信息返回值默认为 pdPASS */uint32_t send_data1 1;uint32_t send_data2 2;while (1) {if (Key_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN) KEY_ON) {/* K1 被按下 */printf(发送消息 send_data1\n);xReturn xQueueSend(Test_Queue, /* 消息队列的句柄 */send_data1, /* 发送的消息内容 */0); /* 等待时间 0 */if (pdPASS xReturn)printf(消息 send_data1 发送成功!\n\n);}if (Key_Scan(KEY2_GPIO_PORT, KEY2_GPIO_PIN) KEY_ON) {/* K2 被按下 */printf(发送消息 send_data2\n);xReturn xQueueSend(Test_Queue, /* 消息队列的句柄 */send_data2, /* 发送的消息内容 */0); /* 等待时间 0 */if (pdPASS xReturn)printf(消息 send_data2 发送成功!\n\n);}vTaskDelay(20); /* 延时 20 个 tick */} }③ xQueueSendToBack() 代码 原型 与 xQueueSend() 一样宏定义 #define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )3. xQueueSendFromISR()与 xQueueSendToBackFromISR() xQueueSendFromISR() 与 xQueueSendToBackFromISR() 是等同的。 ① 代码 原型 BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);参数说明 xQueue队列句柄pvItemToQueue指针指向要发送到队列尾部的消息pxHigherPriorityTaskWoken如果入队导致一个任务解锁并且解锁的任务优先级高于当前被中断的任务则将 *pxHigherPriorityTaskWoken设置成 pdTRUE然后在中断退出前需要进行一次上下文切换 去执行被唤醒的优先级更高的任务 。 宏定义 #define xQueueSendToFrontFromISR(xQueue, pvItemToQueue, pxHigherPriorityTaskWoken) \xQueueGenericSendFromISR((xQueue), (pvItemToQueue), (pxHigherPriorityTaskWoken), queueSEND_TO_FRONT)#define xQueueSendToBackFromISR(xQueue, pvItemToQueue, pxHigherPriorityTaskWoken) \xQueueGenericSendFromISR((xQueue), (pvItemToQueue), (pxHigherPriorityTaskWoken), queueSEND_TO_BACK)② 使用示例 使用 pxHigherPriorityTaskWoken 来确定是否需要上下文切换 void vBufferISR(void) {char cIn;BaseType_t xHigherPriorityTaskWoken;/* 在 ISR 开始的时候我们并没有唤醒任务 */xHigherPriorityTaskWoken pdFALSE;/* 直到缓冲区为空 */do {/* 从缓冲区获取一个字节的数据 */cIn portINPUT_BYTE(RX_REGISTER_ADDRESS);/* 发送这个数据 */xQueueSendFromISR(xRxQueue, cIn, xHigherPriorityTaskWoken);} while (portINPUT_BYTE(BUFFER_COUNT));/* 这时候 buffer 已经为空如果需要则进行上下文切换 */if (xHigherPriorityTaskWoken) {/* 上下文切换这是一个宏不同的处理器具体的方法不一样 */taskYIELD_FROM_ISR();} }4. xQueueSendToFront() 用于向队列队首发送一个消息该消息会被优先读取。 ① 代码 原型 BaseType_t xQueueSendToFront( QueueHandle_t xQueue,const void * pvItemToQueue,TickType_t xTicksToWait );参数说明 xQueue 队列句柄pvItemToQueue 指针指向要发送到队首的消息xTicksToWait 队列满时等待队列空闲的最大超时时间。如果队列满并且 xTicksToWait 被设置成 0函数立刻返回。超时时间的单位为系统节拍周期常量 portTICK_PERIOD_MS 用于辅助计算真实的时间单位为 ms。如果 INCLUDE_vTaskSuspend 设置成 1并且指定延时为 portMAX_DELAY 将导致任务无限阻塞没有超时 宏定义 #define xQueueSendToFront(xQueue, pvItemToQueue, xTicksToWait) \xQueueGenericSend((xQueue), (pvItemToQueue), \(xTicksToWait), queueSEND_TO_FRONT)5. xQueueSendToFrontFromISR() 用于在中断中向队首发送信息。 ① 代码 原型 BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);参数说明 xQueue 队列句柄pvItemToQueue 指针指向要发送到队首的消息pxHigherPriorityTaskWoken 如果入队导致一个任务解锁并且解锁的任务优先级高于当前被中断的任务则将 *pxHigherPriorityTaskWoken 设置成 pdTRUE然后在中断退出前需要进行一次上下文切换去执行被唤醒的优先级更高的任务 。 宏定义 #define xQueueSendToFrontFromISR(xQueue, pvItemToQueue, pxHigherPriorityTaskWoken) \xQueueGenericSendFromISR((xQueue), (pvItemToQueue), \(pxHigherPriorityTaskWoken), queueSEND_TO_FRONT)三、通用的任务中发送消息函数 xQueueGenericSend() 上述的任务中的消息发送函数 API 经过宏定义展开后实际上就是传入不同参数的 xQueueGenericSend()下面将对这个函数进行详细的介绍。 1. xQueueGenericSend() 函数定义 BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) { BaseType_t xEntryTimeSet pdFALSE, xYieldRequired; TimeOut_t xTimeOut; Queue_t * const pxQueue ( Queue_t * ) xQueue;configASSERT( pxQueue );configASSERT( !( ( pvItemToQueue NULL ) ( pxQueue-uxItemSize ! ( UBaseType_t ) 0U ) ) );configASSERT( !( ( xCopyPosition queueOVERWRITE ) ( pxQueue-uxLength ! 1 ) ) );#if ( ( INCLUDE_xTaskGetSchedulerState 1 ) || ( configUSE_TIMERS 1 ) ){configASSERT( !( ( xTaskGetSchedulerState() taskSCHEDULER_SUSPENDED ) ( xTicksToWait ! 0 ) ) );}#endif/* This function relaxes the coding standard somewhat to allow returnstatements within the function itself. This is done in the interestof execution time efficiency. */for( ;; ){taskENTER_CRITICAL();{/* Is there room on the queue now? The running task must be thehighest priority task wanting to access the queue. If the head itemin the queue is to be overwritten then it does not matter if thequeue is full. *///消息空间未满或允许覆盖写入if( ( pxQueue-uxMessagesWaiting pxQueue-uxLength ) || ( xCopyPosition queueOVERWRITE ) ){traceQUEUE_SEND( pxQueue );xYieldRequired prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );//此处省略队列集相关代码taskEXIT_CRITICAL();return pdPASS;}else//消息空间已满且不允许覆盖写入{if( xTicksToWait ( TickType_t ) 0 ){/* The queue was full and no block time is specified (orthe block time has expired) so leave now. *///消息空间已满且不允许覆盖写入且阻塞时间设置为0 或 阻塞时间已过taskEXIT_CRITICAL();/* Return to the original privilege level before exitingthe function. */traceQUEUE_SEND_FAILED( pxQueue );return errQUEUE_FULL;}else if( xEntryTimeSet pdFALSE ){/* The queue was full and a block time was specified soconfigure the timeout structure. *///消息空间已满且不允许覆盖写入并且阻塞时间大于0所以需要配置超时结构vTaskSetTimeOutState( xTimeOut );xEntryTimeSet pdTRUE;}else{/* Entry time was already set. *///已经设置了进入时间mtCOVERAGE_TEST_MARKER();}}}taskEXIT_CRITICAL();/* Interrupts and other tasks can send to and receive from the queuenow the critical section has been exited. *///挂起调度器 并 锁定队列的等待发送任务列表和等待接收任务列表//这两个操作都是为了防止其它任务或者中断操作 xTasksWaitingToReceive 列表和 xTasksWaitingToSend 列表而引起优先级翻转//问为什么可能会优先级翻转//答设想当前要进入阻塞的任务B 的优先级比已经在阻塞中的任务A 的优先级高。//正常情况下当任务B 也进入阻塞后此时两个任务都在阻塞中比如都在阻塞等待入队//那么当可以入队时应该是优先级高的B 线解除阻塞并入队//但是可能会发生这样一种情况在设置任务B 进入阻塞的过程中//其它任务或者中断操作了 xTasksWaitingToReceive 列表和 xTasksWaitingToSend 列表导致可以入队了//正确的逻辑应该是解除优先级较高的任务B 的阻塞而任务A 继续阻塞等待入队//但是此时由于任务B 还在设置进入阻塞的过程中所以阻塞列表中只有任务A导致只能解除//任务A 的阻塞。挂起调度器来禁止其它任务对两个列表的操作 和 锁定中断对两个列表的操作可以确保//在任务B 成功进入阻塞后在通过优先级高低来解除任务的阻塞防止优先级较低的任务A 先于任务B 解除阻塞。vTaskSuspendAll(); //暂停任务切换防止在运行其它任务时该任务修改队列的等待接收和等待发送列表prvLockQueue( pxQueue ); //锁定队列防止此时有中断触发修改队列的等待接收和等待发送列表/* Update the timeout state to see if it has expired yet. */if( xTaskCheckForTimeOut( xTimeOut, xTicksToWait ) pdFALSE )//延时未到期且还未插进去{if( prvIsQueueFull( pxQueue ) ! pdFALSE )//如果队列还是满就把未到期且未成功插入等待插入的任务放入 xTasksWaitingToSend 列表中{traceBLOCKING_ON_QUEUE_SEND( pxQueue );vTaskPlaceOnEventList( ( pxQueue-xTasksWaitingToSend ), xTicksToWait );//等待发送的任务放进等待发送列表中/* Unlocking the queue means queue events can effect theevent list. It is possible that interrupts occurring nowremove this task from the event list again - but as thescheduler is suspended the task will go onto the pendingready last instead of the actual ready list. */prvUnlockQueue( pxQueue );//任务插入完成解锁中断对两个列表的操作但此时调度器还是挂起的状态/* Resuming the scheduler will move tasks from the pendingready list into the ready list - so it is feasible that thistask is already in a ready list before it yields - in whichcase the yield will not cause a context switch unless thereis also a higher priority task in the pending ready list. *///恢复任务调度器根据返回值确定是否要进行任务切换//如果挂起的就绪列表中有优先级较高的任务恢复后调度器后才需要进行任务切换if( xTaskResumeAll() pdFALSE ){portYIELD_WITHIN_API();}}else//延时未到期但是队列未满时解除对队列列表的锁定从而可以再次尝试插入队列{/* Try again. */prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();}}else//延时到期了还没有插进去返回由于队列满插入失败{/* The timeout has expired. */prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();traceQUEUE_SEND_FAILED( pxQueue );return errQUEUE_FULL;}} } /*-----------------------------------------------------------*/2. xQueueGenericSend() 函数流程图 下面将对 xQueueGenericSend() 中调用到的函数进行详细的说明 prvCopyDataToQueue() 消息拷贝到队列函数vTaskSetTimeOutState() 超时结构配置函数挂起调度器函数 vTaskSuspendAll() 和 队列上锁函数 prvLockQueue() 的作用检查延时是否到期函数 xTaskCheckForTimeOut()将任务插入任务等待发送列表 vTaskPlaceOnEventList()队列解锁函数 prvUnlockQueue() 3. prvCopyDataToQueue() 函数说明 ① 如何被调用 在 xQueueGenericSend() 中被调用队列未满或者允许覆盖写入时使用 prvCopyDataToQueue() 将消息拷贝到队列中 if( ( pxQueue-uxMessagesWaiting pxQueue-uxLength ) || ( xCopyPosition queueOVERWRITE ) ){traceQUEUE_SEND( pxQueue );xYieldRequired prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );}② 参数说明 prvCopyDataToQueue() 需要三个参数 指向操作的队列指针指向要拷贝到队列中的消息指针说明拷贝位置的参数队头、队尾、覆盖 插入队尾时xPosition queueSEND_TO_BACK为普通消息的发送将消息拷贝到 pcWriteTo 所指向的消息单元后 pcWriteTo 自增pcWriteTo 自增越界后返回 pcHead else if( xPosition queueSEND_TO_BACK ){( void ) memcpy( ( void * ) pxQueue-pcWriteTo, pvItemToQueue, ( size_t ) pxQueue-uxItemSize );pxQueue-pcWriteTo pxQueue-uxItemSize;if( pxQueue-pcWriteTo pxQueue-pcTail ){pxQueue-pcWriteTo pxQueue-pcHead;}}插入队头时为紧急消息的发送将消息拷贝到 pcReadFrom 指向的消息单元然后 pcReadFrom 自减一个单元。当 pcReadFrom 自减越界时重置为指向 pcTail 的前一个单元 ( void ) memcpy( ( void * ) pxQueue-u.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue-uxItemSize ); pxQueue-u.pcReadFrom - pxQueue-uxItemSize;if( pxQueue-u.pcReadFrom pxQueue-pcHead ) {pxQueue-u.pcReadFrom ( pxQueue-pcTail - pxQueue-uxItemSize );}覆盖写入时( xPosition queueOVERWRITE )即使队列已满也可以写入队列这个功能旨在用于长度为 1 的队列来自 FreeRTOS 官方插入的位置和插入队头的位置是一样的 ③ prvCopyDataToQueue() 函数完整代码 static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition ) { BaseType_t xReturn pdFALSE; UBaseType_t uxMessagesWaiting;/* This function is called from a critical section. */uxMessagesWaiting pxQueue-uxMessagesWaiting;if( pxQueue-uxItemSize ( UBaseType_t ) 0 ){#if ( configUSE_MUTEXES 1 ){if( pxQueue-uxQueueType queueQUEUE_IS_MUTEX ){/* The mutex is no longer being held. */xReturn xTaskPriorityDisinherit( ( void * ) pxQueue-pxMutexHolder );pxQueue-pxMutexHolder NULL;}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_MUTEXES */}else if( xPosition queueSEND_TO_BACK ){( void ) memcpy( ( void * ) pxQueue-pcWriteTo, pvItemToQueue, ( size_t ) pxQueue-uxItemSize ); /*lint !e961 !e418 MISRA exception as the casts are only redundant for some ports, plus previous logic ensures a null pointer can only be passed to memcpy() if the copy size is 0. */pxQueue-pcWriteTo pxQueue-uxItemSize;if( pxQueue-pcWriteTo pxQueue-pcTail ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */{pxQueue-pcWriteTo pxQueue-pcHead;}else{mtCOVERAGE_TEST_MARKER();}}else{( void ) memcpy( ( void * ) pxQueue-u.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue-uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */pxQueue-u.pcReadFrom - pxQueue-uxItemSize;if( pxQueue-u.pcReadFrom pxQueue-pcHead ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */{pxQueue-u.pcReadFrom ( pxQueue-pcTail - pxQueue-uxItemSize );}else{mtCOVERAGE_TEST_MARKER();}if( xPosition queueOVERWRITE ){if( uxMessagesWaiting ( UBaseType_t ) 0 ){/* An item is not being added but overwritten, so subtractone from the recorded number of items in the queue so whenone is added again below the number of recorded items remainscorrect. */--uxMessagesWaiting;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}pxQueue-uxMessagesWaiting uxMessagesWaiting 1;return xReturn; } /*-----------------------------------------------------------*/4. 超时状态结构的配置 vTaskSetTimeOutState() ① 超时状态结构如何配置 在 xQueueGenericSend() 中被调用消息空间已满且不允许覆盖写入并且阻塞时间大于 0 且未配置超时结构时需要配置超时状态结构 vTaskSetTimeOutState( xTimeOut ); xEntryTimeSet pdTRUE;超时结构的配置主要是 溢出次数、进入阻塞时间的配置 ② vTaskSetTimeOutState() 函数定义 void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) {configASSERT( pxTimeOut );pxTimeOut-xOverflowCount xNumOfOverflows; //溢出次数pxTimeOut-xTimeOnEntering xTickCount; //进入时间 当前时间 }5. 挂起调度器和锁上队列的意义 ① 如何操作 //挂起调度器 并 锁定队列的等待发送任务列表和等待接收任务列表 //这两个操作都是为了防止其它任务或者中断操作 xTasksWaitingToReceive 列表 xTasksWaitingToSend 列表而引起优先级翻转 vTaskSuspendAll(); //暂停任务切换防止在运行其它任务时该任务修改队列的等待接收和等待发送列表prvLockQueue( pxQueue ); //锁定队列防止此时有中断触发修改队列的等待接收和等待发送列表② 意义 对于挂起调度器和锁上队列的意义笔者思考了很久如下 挂起调度器 并 锁定队列的等待发送任务列表和等待接收任务列表这两个操作都是为了防止其它任务或者中断操作 xTasksWaitingToReceive 列表和 xTasksWaitingToSend 列表而引起优先级翻转。 当要使任务进入阻塞时假设此任务任务B的优先级比已经在阻塞中的任务任务A优先级高。 正常情况下两个任务都应该在阻塞列表中等待操作当可以进行队列操作时应该优先解除优先级高的任务B的阻塞并进行操作。 但是在某种情况下可能发生在设置任务B进入阻塞的过程中有其他任务或者中断对等待接收和等待发送列表进行了操作使得可以进行队列操作。 正确的行为应该是先解除优先级高的任务B的阻塞任务A继续保持阻塞状态等待入队。但是由于任务B还在设置进入阻塞的过程中阻塞列表中只有任务A导致只能解除任务A的阻塞。这就导致了优先级较低的任务A先于任务B解除阻塞出现了优先级翻转。 为了防止这种情况发生代码中使用了两个控制操作 vTaskSuspendAll() 函数暂停任务切换即暂停调度器防止在运行其他任务时当前任务修改队列的等待接收和等待发送列表。prvLockQueue(pxQueue) 函数锁定队列防止此时发生中断并触发了对等待接收和等待发送列表的修改。 通过挂起调度器和锁定队列可以确保在任务B成功进入阻塞状态后根据优先级高低来解除任务阻塞防止优先级较低的任务A先于任务B解除阻塞从而避免了优先级翻转的问题。 这样的保护措施可以确保在任务切换及队列操作过程中保持任务的正确优先级顺序和避免优先级翻转对系统的影响。 ③ 队列上锁代码 挂起调度器的代码在前面的文章中我们已经介绍过这里不再赘述。 【学习日记】【FreeRTOS】调度器函数实现详解 此处介绍队列上锁代码 进入临界段标记队列控制块中的发送锁和接收锁 /** Macro to mark a queue as locked. Locking a queue prevents an ISR from* accessing the queue event lists.*/ #define prvLockQueue( pxQueue ) \taskENTER_CRITICAL(); \{ \if( ( pxQueue )-cRxLock queueUNLOCKED ) \{ \( pxQueue )-cRxLock queueLOCKED_UNMODIFIED; \} \if( ( pxQueue )-cTxLock queueUNLOCKED ) \{ \( pxQueue )-cTxLock queueLOCKED_UNMODIFIED; \} \} \taskEXIT_CRITICAL() /*-----------------------------------------------------------*/6. 检查延时是否到期函数 xTaskCheckForTimeOut() ① 使用 传入超时状态结构和设置的超时时长进行检查 if( xTaskCheckForTimeOut( xTimeOut, xTicksToWait ) pdFALSE ){//...}② 主要逻辑 当使能了任务挂起且设定延时时间为 portMAX_DELAY 时任务将永远被阻塞不会超时如果现在的时间比进入延时的时间大 且 现在的时间计时溢出过这意味着时基计数已经跑过一轮了那么延时肯定到期了或者是当前时间减进入时间 需要等待的时间意味着延时还未到期 现在需要等待的时间 原来需要等待的时间 - 已经等待的时间 ③ 源代码 BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ) { BaseType_t xReturn;configASSERT( pxTimeOut );configASSERT( pxTicksToWait );taskENTER_CRITICAL();{/* Minor optimisation. The tick count cannot change in this block. */const TickType_t xConstTickCount xTickCount;#if( INCLUDE_xTaskAbortDelay 1 )if( pxCurrentTCB-ucDelayAborted ! pdFALSE ){/* The delay was aborted, which is not the same as a time out,but has the same result. */pxCurrentTCB-ucDelayAborted pdFALSE;xReturn pdTRUE;}else#endif#if ( INCLUDE_vTaskSuspend 1 )if( *pxTicksToWait portMAX_DELAY ){/* If INCLUDE_vTaskSuspend is set to 1 and the block timespecified is the maximum block time then the task should blockindefinitely, and therefore never time out. */xReturn pdFALSE;}else#endifif( ( xNumOfOverflows ! pxTimeOut-xOverflowCount ) ( xConstTickCount pxTimeOut-xTimeOnEntering ) ) /*lint !e525 Indentation preferred as is to make code within pre-processor directives clearer. */{/* The tick count is greater than the time at whichvTaskSetTimeout() was called, but has also overflowed sincevTaskSetTimeOut() was called. It must have wrapped all the wayaround and gone past again. This passed since vTaskSetTimeout()was called. *///现在的时间比进入延时的时间大 且 现在的时间计时溢出过//意味着时基计数已经跑过一轮了延时肯定到期了xReturn pdTRUE; //延时肯定到期了}else if( ( ( TickType_t ) ( xConstTickCount - pxTimeOut-xTimeOnEntering ) ) *pxTicksToWait ) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */{//当前时间减进入时间 需要等待的时间意味着延时还未到期/* Not a genuine timeout. Adjust parameters for time remaining. *///现在需要等待的时间 原来需要等待的时间 - 已经等待的时间 *pxTicksToWait - ( xConstTickCount - pxTimeOut-xTimeOnEntering );vTaskSetTimeOutState( pxTimeOut );xReturn pdFALSE; //延时没有到期}else{xReturn pdTRUE;}}taskEXIT_CRITICAL();return xReturn; }7. 将任务插入任务等待发送列表 vTaskPlaceOnEventList() ① 使用 用于将延时未到期且暂时无法入队的任务插入任务等待列表 传入指向任务等待发送列表的指针传入要延时的时长 vTaskPlaceOnEventList( ( pxQueue-xTasksWaitingToSend ), xTicksToWait );//等待发送的任务放进等待发送列表中② 函数定义 将当前 TCB 的事件项目插入 xTasksWaitingToSend 标识该任务正在等待发送消息将当前 TCB 插入延时列表中进行阻塞延时 void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait ) {configASSERT( pxEventList );/* THIS FUNCTION MUST BE CALLED WITH EITHER INTERRUPTS DISABLED OR THESCHEDULER SUSPENDED AND THE QUEUE BEING ACCESSED LOCKED. *//* Place the event list item of the TCB in the appropriate event list.This is placed in the list in priority order so the highest priority taskis the first to be woken by the event. The queue that contains the eventlist is locked, preventing simultaneous access from interrupts. */vListInsert( pxEventList, ( pxCurrentTCB-xEventListItem ) );prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); } /*-----------------------------------------------------------*/8. 队列解锁函数 prvUnlockQueue() ① 函数原型 /* Constants used with the cRxLock and cTxLock structure members. */ #define queueUNLOCKED ( ( int8_t ) -1 ) #define queueLOCKED_UNMODIFIED ( ( int8_t ) 0 )static void prvUnlockQueue( Queue_t * const pxQueue ) {/* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. *//* The lock counts contains the number of extra data items placed orremoved from the queue while the queue was locked. When a queue islocked items can be added or removed, but the event lists cannot beupdated. */taskENTER_CRITICAL();{int8_t cTxLock pxQueue-cTxLock;/* See if data was added to the queue while it was locked. */while( cTxLock queueLOCKED_UNMODIFIED ){/* Data was posted while the queue was locked. Are any tasksblocked waiting for data to become available? */#if ( configUSE_QUEUE_SETS 1 ){if( pxQueue-pxQueueSetContainer ! NULL ){if( prvNotifyQueueSetContainer( pxQueue, queueSEND_TO_BACK ) ! pdFALSE ){/* The queue is a member of a queue set, and posting tothe queue set caused a higher priority task to unblock.A context switch is required. */vTaskMissedYield();}else{mtCOVERAGE_TEST_MARKER();}}else{/* Tasks that are removed from the event list will getadded to the pending ready list as the scheduler is stillsuspended. */if( listLIST_IS_EMPTY( ( pxQueue-xTasksWaitingToReceive ) ) pdFALSE ){if( xTaskRemoveFromEventList( ( pxQueue-xTasksWaitingToReceive ) ) ! pdFALSE ){/* The task waiting has a higher priority so record that acontext switch is required. */vTaskMissedYield();}else{mtCOVERAGE_TEST_MARKER();}}else{break;}}}#else /* configUSE_QUEUE_SETS */{/* Tasks that are removed from the event list will get added tothe pending ready list as the scheduler is still suspended. */if( listLIST_IS_EMPTY( ( pxQueue-xTasksWaitingToReceive ) ) pdFALSE ){if( xTaskRemoveFromEventList( ( pxQueue-xTasksWaitingToReceive ) ) ! pdFALSE ){/* The task waiting has a higher priority so record thata context switch is required. */vTaskMissedYield();}else{mtCOVERAGE_TEST_MARKER();}}else{break;}}#endif /* configUSE_QUEUE_SETS */--cTxLock;}pxQueue-cTxLock queueUNLOCKED;}taskEXIT_CRITICAL();/* Do the same for the Rx lock. */taskENTER_CRITICAL();{int8_t cRxLock pxQueue-cRxLock;while( cRxLock queueLOCKED_UNMODIFIED ){if( listLIST_IS_EMPTY( ( pxQueue-xTasksWaitingToSend ) ) pdFALSE ){if( xTaskRemoveFromEventList( ( pxQueue-xTasksWaitingToSend ) ) ! pdFALSE ){vTaskMissedYield();}else{mtCOVERAGE_TEST_MARKER();}--cRxLock;}else{break;}}pxQueue-cRxLock queueUNLOCKED;}taskEXIT_CRITICAL(); } /*-----------------------------------------------------------*/② 函数结构分析 这段代码是 FreeRTOS 中的 prvUnlockQueue 函数的实现。以下是函数执行的主要步骤 进入临界区。检查发送锁的计数器 cTxLock如果大于 queueLOCKED_UNMODIFIED表示有锁被添加或删除则执行以下步骤 a. 如果队列是队列集的成员且添加数据导致更高优先级的任务解除阻塞则记录需要进行任务切换。 b. 否则从等待接收任务列表中移除任务并记录需要进行任务切换。 c. 递减 cTxLock 计数器。将发送锁设置为解锁状态。再次进入临界区。检查接收锁的计数器 cRxLock如果大于 queueLOCKED_UNMODIFIED表示有锁被添加或删除则执行以下步骤 a. 从等待发送任务列表中移除任务并记录需要进行任务切换。 b. 递减 cRxLock 计数器。将接收锁设置为解锁状态。离开临界区。 可以看到忽略锁的计数器操作锁的计数器主要用于对队列的并发访问3 和 6 说明只要调用这个函数队列的等待发送任务列表和等待接收任务列表都会解锁。 ③ 计数锁的原理 在FreeRTOS中队列的cRxLock大于0的情况是在调用接收数据的API比如xQueueReceive()函数时。这是由于队列需要在多任务环境中保持线程安全性。当一个任务正在从队列接收数据时会通过增加cRxLock的计数来锁定队列防止其他任务同时进行接收操作。 具体来说以下情况会使得cRxLock大于0 任务 A 调用了接收数据的 API队列的 cRxLock 值增加到 1。在任务 A 还未完成接收操作之前另一个任务 B 也调用了接收数据的 API此时 cRxLock 值增加到 2。当任务 A 完成接收操作后cRxLock 值减少到 1。当任务 B 完成接收操作后cRxLock 值减少到 0。 这个锁定机制确保了队列在多任务环境中的正确操作避免了竞态条件的发生。 四、用于中断中的 通用的消息发送函数 xQueueGenericSendFromISR() 上述的任务中的消息发送函数 API 经过宏定义展开后实际上就是传入不同参数的 xQueueGenericSendFromISR()下面将对这个函数进行详细的介绍。 1. 函数概要 这个函数用于在中断中向队列发送消息由于是在中断中使用的所以不带阻塞机制队列未满时或者可以覆盖写入时直接进行消息拷贝否则直接返回队列已满错误 因为是在中断中执行需要对队列发送锁进行判断和操作 如果队列发送锁是解锁状态表示没有任务正在进行队列的发送操作因此其他任务可以尝试从阻塞状态中恢复并依据优先级高低进行上下文切换如果队列发送锁是上锁状态每调用一次 xQueueGenericSendFromISR()队列发送锁就自增 1任务需要等待锁释放才能进行队列的发送操作队列发送锁记录队列上锁时有多少数据尝试入队 2. 函数代码 BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition ) { BaseType_t xReturn; UBaseType_t uxSavedInterruptStatus; Queue_t * const pxQueue ( Queue_t * ) xQueue;configASSERT( pxQueue );configASSERT( !( ( pvItemToQueue NULL ) ( pxQueue-uxItemSize ! ( UBaseType_t ) 0U ) ) );configASSERT( !( ( xCopyPosition queueOVERWRITE ) ( pxQueue-uxLength ! 1 ) ) );/* RTOS ports that support interrupt nesting have the concept of a maximumsystem call (or maximum API call) interrupt priority. Interrupts that areabove the maximum system call priority are kept permanently enabled, evenwhen the RTOS kernel is in a critical section, but cannot make any calls toFreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.hthen portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertionfailure if a FreeRTOS API function is called from an interrupt that has beenassigned a priority above the configured maximum system call priority.Only FreeRTOS functions that end in FromISR can be called from interruptsthat have been assigned a priority at or (logically) below the maximumsystem call interrupt priority. FreeRTOS maintains a separate interruptsafe API to ensure interrupt entry is as fast and as simple as possible.More information (albeit Cortex-M specific) is provided on the followinglink: http://www.freertos.org/RTOS-Cortex-M3-M4.html */portASSERT_IF_INTERRUPT_PRIORITY_INVALID();/* Similar to xQueueGenericSend, except without blocking if there is no roomin the queue. Also dont directly wake a task that was blocked on a queueread, instead return a flag to say whether a context switch is required ornot (i.e. has a task with a higher priority than us been woken by thispost). */uxSavedInterruptStatus portSET_INTERRUPT_MASK_FROM_ISR();{if( ( pxQueue-uxMessagesWaiting pxQueue-uxLength ) || ( xCopyPosition queueOVERWRITE ) ){const int8_t cTxLock pxQueue-cTxLock;traceQUEUE_SEND_FROM_ISR( pxQueue );/* Semaphores use xQueueGiveFromISR(), so pxQueue will not be asemaphore or mutex. That means prvCopyDataToQueue() cannot resultin a task disinheriting a priority and prvCopyDataToQueue() can becalled here even though the disinherit function does not check ifthe scheduler is suspended before accessing the ready lists. */( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );/* The event list is not altered if the queue is locked. This willbe done when the queue is unlocked later. */if( cTxLock queueUNLOCKED ){#if ( configUSE_QUEUE_SETS 1 ){if( pxQueue-pxQueueSetContainer ! NULL ){if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) ! pdFALSE ){/* The queue is a member of a queue set, and postingto the queue set caused a higher priority task tounblock. A context switch is required. */if( pxHigherPriorityTaskWoken ! NULL ){*pxHigherPriorityTaskWoken pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{if( listLIST_IS_EMPTY( ( pxQueue-xTasksWaitingToReceive ) ) pdFALSE ){if( xTaskRemoveFromEventList( ( pxQueue-xTasksWaitingToReceive ) ) ! pdFALSE ){/* The task waiting has a higher priority sorecord that a context switch is required. */if( pxHigherPriorityTaskWoken ! NULL ){*pxHigherPriorityTaskWoken pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}}#else /* configUSE_QUEUE_SETS */{if( listLIST_IS_EMPTY( ( pxQueue-xTasksWaitingToReceive ) ) pdFALSE ){if( xTaskRemoveFromEventList( ( pxQueue-xTasksWaitingToReceive ) ) ! pdFALSE ){/* The task waiting has a higher priority so record that acontext switch is required. */if( pxHigherPriorityTaskWoken ! NULL ){*pxHigherPriorityTaskWoken pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_QUEUE_SETS */}else{/* Increment the lock count so the task that unlocks the queueknows that data was posted while it was locked. */pxQueue-cTxLock ( int8_t ) ( cTxLock 1 );}xReturn pdPASS;}else{traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue );xReturn errQUEUE_FULL;}}portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );return xReturn; } /*-----------------------------------------------------------*/五、消息读取 API 1. 简介 任务中读取消息时也可以指定阻塞超时时间在队列中没有消息可供读取时对任务进行阻塞。队列中存入数据后自动转为就绪态进行数据读取或者等到超时后变回就绪态而中断中读取消息时就不能指定阻塞机制但同样可以输入一个提示是否进行上下文切换的参数来进行上下文的切换判断读取消息的函数分为读完将消息从队列中删除和读完后将消息放回队列中原位的两种函数 2. xQueueReceive() ① 原型及作用 用于从一个队列中接收消息并把消息从队列中删除接收的消息是以拷贝的形式进行的 BaseType_t xQueueReceive(QueueHandle_t xQueue,void *pvBuffer,TickType_t xTicksToWait);xQueue 队列句柄pvBuffer 指针指向接收到要保存的数据xTicksToWait 队列空时阻塞超时的最大时间。如果该参数设置为 0函数立刻返回。超时时间的单位为系统节拍周期常量 portTICK_PERIOD_MS 用于辅助计算真实的时间单位为 ms。如果 INCLUDE_vTaskSuspend 设置成 1并且指定延时为 portMAX_DELAY 将导致任务无限阻塞永远不会超时 ② 宏展开 #define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) \xQueueGenericReceive( ( xQueue ), ( pvBuffer ), \( xTicksToWait ), pdFALSE )③ 应用 static void Receive_Task(void* parameter) {BaseType_t xReturn pdTRUE; /* 定义一个创建信息返回值默认为pdPASS */uint32_t r_queue; /* 定义一个接收消息的变量 */while (1) {xReturn xQueueReceive(Test_Queue, /* 消息队列的句柄 */r_queue, /* 接收的消息内容 */portMAX_DELAY); /* 等待时间 一直等 */if (pdTRUE xReturn)printf(本次接收到的数据是%d\n\n, r_queue);elseprintf(数据接收出错,错误代码: 0x%lx\n, xReturn);} } 3. xQueuePeek() ① 作用 xQueuePeek()函数接收消息完毕不会删除消息队列中的消息。 ② 宏展开 #define xQueuePeek( xQueue, pvBuffer, xTicksToWait ) \xQueueGenericReceive( ( xQueue ), ( pvBuffer ), \( xTicksToWait ), pdTRUE )4. xQueueReceiveFromISR() 与 xQueuePeekFromISR() 类同于上文 API但是只能在中断中使用队列项接收成功返回 pdTRUE否则返回 pdFALSE ① xQueueReceiveFromISR() 使用示例 QueueHandle_t xQueue;/* 创建一个队列并往队列里面发送一些数据 */ void vAFunction(void *pvParameters) {char cValueToPost;const TickType_t xTicksToWait (TickType_t)0xff;/* 创建一个可以容纳10个字符的队列 */xQueue xQueueCreate(10, sizeof(char));if (xQueue 0) {/* 队列创建失败 */}/* ... 任务其他代码 *//* 往队列里面发送两个字符如果队列满了则等待xTicksToWait个系统节拍周期*/cValueToPost a;xQueueSend(xQueue, (void *)cValueToPost, xTicksToWait);cValueToPost b;xQueueSend(xQueue, (void *)cValueToPost, xTicksToWait);/* 继续往队列里面发送字符当队列满的时候该任务将被阻塞 */cValueToPost c;xQueueSend(xQueue, (void *)cValueToPost, xTicksToWait); }/* 中断服务程序输出所有从队列中接收到的字符 */ void vISR_Routine(void) {BaseType_t xTaskWokenByReceive pdFALSE;char cRxedChar;while (xQueueReceiveFromISR(xQueue,(void *)cRxedChar,xTaskWokenByReceive)) {/* 接收到一个字符然后输出这个字符 */vOutputCharacter(cRxedChar);/* 如果从队列移除一个字符串后唤醒了向此队列投递字符的任务那么参数xTaskWokenByReceive将会设置成pdTRUE这个循环无论重复多少次仅会有一个任务被唤醒 */}if (xTaskWokenByReceive ! pdFALSE) {/* 我们应该进行一次上下文切换当ISR返回的时候则执行另外一个任务 *//* 这是一个上下文切换的宏不同的处理器具体处理的方式不一样 */taskYIELD();} }六、通用读取消息函数 xQueueGenericReceive() 1. 函数结构分析 这段代码是FreeRTOS中的xQueueGenericReceive函数的实现。以下是函数执行的主要步骤 检查输入参数的有效性。进入临界区。检查队列中是否有数据。 如果有数据则 如果只是查看数据xJustPeeking为pdTRUE则将数据复制到pvBuffer并恢复读指针然后从等待接收任务列表中移除任务。如果要删除数据xJustPeeking为pdFALSE则将数据复制到pvBuffer并更新队列中的消息计数、处理互斥如果队列类型是互斥并从等待发送任务列表中移除任务。退出临界区返回pdPASS表示成功。 如果队列为空且等待时间为0则退出临界区返回errQUEUE_EMPTY表示队列为空。如果队列为空且需要等待一段时间则设置超时结构体xTimeOut并继续循环。 退出临界区。挂起调度器并锁定队列。更新超时状态并检查超时时间是否已过期。 如果超时时间未过期且队列仍为空则 如果队列是互斥队列类型则提升互斥的持有任务的优先级。将任务添加到等待接收任务列表中并解锁队列。恢复调度器状态如果返回pdFALSE则进行任务切换。 如果超时时间已过期则返回errQUEUE_EMPTY表示队列为空。如果队列非空则继续循环。 循环直到成功或超时。 总之这个函数的主要功能是从队列中接收数据并在数据不可用时进行阻塞等待直到有数据可用或超时。它还处理了互斥队列和任务优先级的协调。 2. 函数定义 BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeeking ) { BaseType_t xEntryTimeSet pdFALSE; TimeOut_t xTimeOut; int8_t *pcOriginalReadPosition; Queue_t * const pxQueue ( Queue_t * ) xQueue;configASSERT( pxQueue );configASSERT( !( ( pvBuffer NULL ) ( pxQueue-uxItemSize ! ( UBaseType_t ) 0U ) ) );#if ( ( INCLUDE_xTaskGetSchedulerState 1 ) || ( configUSE_TIMERS 1 ) ){configASSERT( !( ( xTaskGetSchedulerState() taskSCHEDULER_SUSPENDED ) ( xTicksToWait ! 0 ) ) );}#endif/* This function relaxes the coding standard somewhat to allow returnstatements within the function itself. This is done in the interestof execution time efficiency. */for( ;; ){taskENTER_CRITICAL();{const UBaseType_t uxMessagesWaiting pxQueue-uxMessagesWaiting;/* Is there data in the queue now? To be running the calling taskmust be the highest priority task wanting to access the queue. */if( uxMessagesWaiting ( UBaseType_t ) 0 ){/* Remember the read position in case the queue is only beingpeeked. */pcOriginalReadPosition pxQueue-u.pcReadFrom;prvCopyDataFromQueue( pxQueue, pvBuffer );if( xJustPeeking pdFALSE ){traceQUEUE_RECEIVE( pxQueue );/* Actually removing data, not just peeking. */pxQueue-uxMessagesWaiting uxMessagesWaiting - 1;#if ( configUSE_MUTEXES 1 ){if( pxQueue-uxQueueType queueQUEUE_IS_MUTEX ){/* Record the information required to implementpriority inheritance should it become necessary. */pxQueue-pxMutexHolder ( int8_t * ) pvTaskIncrementMutexHeldCount(); /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_MUTEXES */if( listLIST_IS_EMPTY( ( pxQueue-xTasksWaitingToSend ) ) pdFALSE ){if( xTaskRemoveFromEventList( ( pxQueue-xTasksWaitingToSend ) ) ! pdFALSE ){queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{traceQUEUE_PEEK( pxQueue );/* The data is not being removed, so reset the readpointer. */pxQueue-u.pcReadFrom pcOriginalReadPosition;/* The data is being left in the queue, so see if there areany other tasks waiting for the data. */if( listLIST_IS_EMPTY( ( pxQueue-xTasksWaitingToReceive ) ) pdFALSE ){if( xTaskRemoveFromEventList( ( pxQueue-xTasksWaitingToReceive ) ) ! pdFALSE ){/* The task waiting has a higher priority than this task. */queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();return pdPASS;}else{if( xTicksToWait ( TickType_t ) 0 ){/* The queue was empty and no block time is specified (orthe block time has expired) so leave now. */taskEXIT_CRITICAL();traceQUEUE_RECEIVE_FAILED( pxQueue );return errQUEUE_EMPTY;}else if( xEntryTimeSet pdFALSE ){/* The queue was empty and a block time was specified soconfigure the timeout structure. */vTaskSetTimeOutState( xTimeOut );xEntryTimeSet pdTRUE;}else{/* Entry time was already set. */mtCOVERAGE_TEST_MARKER();}}}taskEXIT_CRITICAL();/* Interrupts and other tasks can send to and receive from the queuenow the critical section has been exited. */vTaskSuspendAll();prvLockQueue( pxQueue );/* Update the timeout state to see if it has expired yet. */if( xTaskCheckForTimeOut( xTimeOut, xTicksToWait ) pdFALSE ){if( prvIsQueueEmpty( pxQueue ) ! pdFALSE ){traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );#if ( configUSE_MUTEXES 1 ){if( pxQueue-uxQueueType queueQUEUE_IS_MUTEX ){taskENTER_CRITICAL();{vTaskPriorityInherit( ( void * ) pxQueue-pxMutexHolder );}taskEXIT_CRITICAL();}else{mtCOVERAGE_TEST_MARKER();}}#endifvTaskPlaceOnEventList( ( pxQueue-xTasksWaitingToReceive ), xTicksToWait );prvUnlockQueue( pxQueue );if( xTaskResumeAll() pdFALSE ){portYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();}}else{/* Try again. */prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();}}else{prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();if( prvIsQueueEmpty( pxQueue ) ! pdFALSE ){traceQUEUE_RECEIVE_FAILED( pxQueue );return errQUEUE_EMPTY;}else{mtCOVERAGE_TEST_MARKER();}}} } /*-----------------------------------------------------------*/四、性能提示 无论是发送或者是接收消息都是以拷贝的方式进行如果消息过于庞大可以将消息的地址作为消息进行发送、接收。 后记 如果您觉得本文写得不错可以点个赞激励一下作者 如果您发现本文的问题欢迎在评论区或者私信共同探讨 共勉
http://www.yayakq.cn/news/3790/

相关文章:

  • 游戏网站怎么赚钱推荐几个高端大气上档次网站
  • 如何建一个自己的网站wordpress 入侵视频教程
  • 东营免费网站制作金华公司做网站
  • 网站建设安全规划网站不接入备案
  • 网站建设的目的与意义是什么意思左右左布局网站建设
  • 如何夸奖客户网站做的好房地产网站建设案例
  • 网站运营内容太原的网站建设公司哪家好
  • 外贸网站用什么空间好使用net域名的大网站
  • 烟台网站建设专业臻动传媒医院有关页面设计模板
  • 深圳分销网站制作wordpress 手机商城
  • 用路由器做网站推广普通话宣传语
  • 辛集市建设局网站海淀区网站搭建
  • 福州城乡建设发展总公司官方网站免费 成品模板网站
  • 商城版手机网站制作网站建设宣传文案
  • 做网站实时数据用接口win7网站服务器制作软件
  • 沈阳高端网站商城网站数据库表关系设计
  • 学雷锋 做美德少年网站金昌百度seo
  • 网站建设 300元有哪些网络营销公司
  • 建设银行网站图片大全北京网站优化指导
  • 网站建设课本wordpress 中文乱码
  • 可以做彩页的网站快递网站推广怎么做
  • wordpress 自适应主题搜seo
  • 网站建设开发价格怎么算网上做平面设计的网站
  • 网站设计个人定制网络教研系统
  • 网站开发公司运营流程网站开发都有什么端
  • 青岛建个网站多少钱公众平台网站价格
  • 什么软件做网站链接做网站工作室名字
  • 长春建站公众号免费网站建设apk
  • 网站域名申请流程天猫分销平台
  • 怎么看一个网站什么程序做的沧州建设工程信息网