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

FreeRTOS 队列(二)

文章目录

  • 一、向队列发送消息
    • 1. 函数原型
      • (1)函数 xQueueOverwrite()
      • (2)函数 xQueueGenericSend()
      • (3)函数 xQueueSendFromISR()、xQueueSendToBackFromISR()、xQueueSendToFrontFromISR()
      • (4)函数 xQueueOverwriteFromISR()
      • (5)函数 xQueueGenericSendFromISR()
    • 2. 任务级通用入队函数
    • 3. 中断级通用入队函数
  • 二、队列上锁和解锁
  • 三、从队列读取消息
    • 1. 函数 xQueueReceive()
    • 2. 函数 xQueuePeek()
    • 3. 函数 xQueueGenericReceive()
    • 4. 函数 xQueueReceiveFromISR()
    • 5. 函数 xQueuePeekFromISR()


一、向队列发送消息

1. 函数原型

创建好队列以后就可以向队列发送消息了,FreeRTOS 提供了 8 个向队列发送消息的 API 函数,如下表所示:
在这里插入图片描述
1、函数 xQueueSend()、xQueueSendToBack()和 xQueueSendToFront()这三个函数都是用于向队列中发送消息的,这三个函数本质都是宏,其中函数 xQueueSend()和 xQueueSendToBack()是一样的,都是后向入队,即将新的消息插入到队列的后面。函数xQueueSendToToFront()是前向入队,即将新消息插入到队列的前面。然而!这三个函数最后都是调用的同一个函数:xQueueGenericSend()。这三个函数只能用于任务函数中,不能用于中断服务函数,中断服务函数有专用的函数,它们以“FromISR”结尾,这三个函数的原型如下:

BaseType_t xQueueSend( QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait);
BaseType_t xQueueSendToBack(QueueHandle_t xQueue,const void* pvItemToQueue,TickType_t xTicksToWait);
BaseType_t xQueueSendToToFront(QueueHandle_t xQueue,
const void *pvItemToQueue,
TickType_t xTicksToWait);

参数:
xQueue: 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的
队列句柄。
**pvItemToQueue:**指向要发送的消息,发送时候会将这个消息拷贝到队列中。
xTicksToWait: 阻塞时间,此参数指示当队列满的时候任务进入阻塞态等待队列空闲的最大时间。如果为 0 的话当队列满的时候就立即返回;当为 portMAX_DELAY 的
话就会一直等待,直到队列有空闲的队列 项,也就是死等,但是宏INCLUDE_vTaskSuspend 必须为 1。

返回值:
pdPASS: 向队列发送消息成功!
errQUEUE_FULL: 队列已经满了,消息发送失败。

(1)函数 xQueueOverwrite()

此函数也是用于向队列发送数据的,当队列满了以后会覆写掉旧的数据,不管这个旧数据有没有被其他任务或中断取走。这个函数常用于向那些长度为 1 的队列发送消息,此函数也是一个宏,最终调用的也是函数 xQueueGenericSend(),函数原型如下:

BaseType_t xQueueOverwrite(QueueHandle_t xQueue,const void * pvItemToQueue);

参数:
xQueue: 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄。
**pvItemToQueue:**指向要发送的消息,发送的时候会将这个消息拷贝到队列中。

返回值:
pdPASS: 向队列发送消息成功,此函数也只会返回 pdPASS!因为此函数执行过程中不在乎队列满不满,满了的话我就覆写掉旧的数据,总之肯定能成功。

(2)函数 xQueueGenericSend()

此函数才是真正干活的,上面讲的所有的任务级入队函数最终都是调用的此函数,此函数也是我们后面重点要讲解的,先来看一下函数原型:

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

参数:
xQueue: 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄。
**pvItemToQueue:**指向要发送的消息,发送的过程中会将这个消息拷贝到队列中。
xTicksToWait: 阻塞时间。
xCopyPosition: 入队方式,有三种入队方式:
queueSEND_TO_BACK: 后向入队
queueSEND_TO_FRONT: 前向入队
queueOVERWRITE: 覆写入队。

上面讲解的入队 API 函数就是通过此参数来决定采用哪种入队方式的。

返回值:
pdTRUE: 向队列发送消息成功!
errQUEUE_FULL: 队列已经满了,消息发送失败

(3)函数 xQueueSendFromISR()、xQueueSendToBackFromISR()、xQueueSendToFrontFromISR()

这三个函数也是向队列中发送消息的,这三个函数用于中断服务函数中。这三个函数本质也宏,其中函数 xQueueSendFromISR ()和 xQueueSendToBackFromISR ()是一样的,都是后向入队,即将新的消息插入到队列的后面。函数xQueueSendToFrontFromISR ()是前向入队,即将新消息插入到队列的前面。这三个函数同样调用同一个函数 xQueueGenericSendFromISR ()。这三个函数的原型如下:

BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,const void * pvItemToQueue,BaseType_t * pxHigherPriorityTaskWoken);BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue,const void * pvItemToQueue,BaseType_t * pxHigherPriorityTaskWoken);BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,const void * pvItemToQueue,BaseType_t * pxHigherPriorityTaskWoken);

参数:
xQueue: 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄。
**pvItemToQueue:**指向要发送的消息,发送的时候会将这个消息拷贝到队列中。
pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。

返回值:
pdTRUE: 向队列中发送消息成功!
errQUEUE_FULL: 队列已经满了,消息发送失败。

(4)函数 xQueueOverwriteFromISR()

此函数是 xQueueOverwrite()的中断级版本,用在中断服务函数中,在队列满的时候自动覆写掉旧的数据,此函数也是一个宏,实际调用的也是函数xQueueGenericSendFromISR(),此函数原型如下:

BaseType_t xQueueOverwriteFromISR(QueueHandle_t xQueue,const void * pvItemToQueue,BaseType_t * pxHigherPriorityTaskWoken);

此函数的参数和返回值同上面三个函数相同。

(5)函数 xQueueGenericSendFromISR()

上面说了 4 个中断级入队函数最终都是调用的函数 xQueueGenericSendFromISR(),这是真正干活的主啊,也是我们下面会详细讲解的函数,先来看一下这个函数的原型,如下:

BaseType_t xQueueGenericSendFromISR(QueueHandle_t xQueue,const void* pvItemToQueue,BaseType_t* pxHigherPriorityTaskWoken,BaseType_t xCopyPosition);

参数:
xQueue: 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄。
**pvItemToQueue:**指向要发送的消息,发送的过程中会将这个消息拷贝到队列中。
pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。

xCopyPosition: 入队方式,有三种入队方式:
queueSEND_TO_BACK: 后向入队
queueSEND_TO_FRONT: 前向入队
queueOVERWRITE: 覆写入队。

返回值:
pdTRUE: 向队列发送消息成功!
errQUEUE_FULL: 队列已经满了,消息发送失败。

2. 任务级通用入队函数

不 管 是 后 向 入 队 、 前 向 入 队 还 是 覆 写 入 队 , 最 终 调 用 的 都 是 通 用 入 队 函 数xQueueGenericSend(),这个函数在文件 queue.c 文件中由定义,缩减后的函数代码如下:

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;for( ;; ){taskENTER_CRITICAL(); //进入临界区{//查询队列现在是否还有剩余存储空间,如果采用覆写方式入队的话那就不用在//乎队列是不是满的啦。if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) ||\ (1)( xCopyPosition == queueOVERWRITE ) ){traceQUEUE_SEND( pxQueue );xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue,\ (2)xCopyPosition );/**************************************************************************//**************************省略掉与队列集相关代码**************************//**************************************************************************/{//检查是否有任务由于等待消息而进入阻塞态if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) ==\(3)pdFALSE ){if( xTaskRemoveFromEventList( &( pxQueue->\ (4)xTasksWaitingToReceive ) ) != pdFALSE ){//解除阻塞态的任务优先级最高,因此要进行一次任务切换queueYIELD_IF_USING_PREEMPTION(); (5)}else{mtCOVERAGE_TEST_MARKER();}}else if( xYieldRequired != pdFALSE ){queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();return pdPASS; (6)}else{if( xTicksToWait == ( TickType_t ) 0 ) (7){//队列是满的,并且没有设置阻塞时间的话就直接返回taskEXIT_CRITICAL();traceQUEUE_SEND_FAILED( pxQueue );return errQUEUE_FULL; (8)}else if( xEntryTimeSet == pdFALSE ) (9){//队列是满的并且指定了任务阻塞时间的话就初始化时间结构体vTaskSetTimeOutState( &xTimeOut );xEntryTimeSet = pdTRUE;}else{//时间结构体已经初始化过了,mtCOVERAGE_TEST_MARKER();}}}taskEXIT_CRITICAL(); //退出临界区vTaskSuspendAll(); (10)prvLockQueue( pxQueue ); (11)//更新时间壮态,检查是否有超时产生if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) (12){if( prvIsQueueFull( pxQueue ) != pdFALSE ) (13){traceBLOCKING_ON_QUEUE_SEND( pxQueue );vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), \ (14)xTicksToWait );prvUnlockQueue( pxQueue ); (15)if( xTaskResumeAll() == pdFALSE ) (16){portYIELD_WITHIN_API();}}else{//重试一次prvUnlockQueue( pxQueue ); (17)( void ) xTaskResumeAll();}}else{//超时产生prvUnlockQueue( pxQueue ); (18)( void ) xTaskResumeAll();traceQUEUE_SEND_FAILED( pxQueue );return errQUEUE_FULL; (19)}}
}

(1)、要向队列发送数据,肯定要先检查一下队列是不是满的,如果是满的话肯定不能发送的。当队列未满或者是覆写入队的话就可以将消息入队了。

(2)、调用函数 prvCopyDataToQueue()将消息拷贝到队列中。前面说了入队分为后向入队、前向入队和覆写入队,他们的具体实现就是在函数 prvCopyDataToQueue()中完成的。如果选择后向入队 queueSEND_TO_BACK 的话就将消息拷贝到队列结构体成员 pcWriteTo 所指向的队列项,拷贝成功以后 pcWriteTo 增加 uxItemSize 个字节,指向下一个队列项目。当选择前向入队 queueSEND_TO_FRONT 或者 queueOVERWRITE 的话就将消息拷贝到 u.pcReadFrom 所指向的队列项目,同样的需要调整 u.pcReadFrom 的位置。当向队列写入一个消息以后队列中统计当前消息数量的成员uxMessagesWaiting 就会加一,但是选择覆写入队 queueOVERWRITE 的话还会将 uxMessagesWaiting 减一,这样一减一加相当于队列当前消息数量没有变。

(3) 、 检查是否有任务由于请求队列 消 息 而 阻 塞 , 阻 塞 的 任 务 会 挂 在 队 列 的
xTasksWaitingToReceive 列表上。

(4)、有任务由于请求消息而阻塞,因为在(2)中已将向队列中发送了一条消息了,所以调用函数 xTaskRemoveFromEventList()将阻塞的任务从列表 xTasksWaitingToReceive 上移除,并且把这个任务添加到就绪列表中,如果调度器上锁的话这些任务就会挂到列表 xPendingReadyList 上。如果取消阻塞的任务优先级比当前正在运行的任务优先级高还要标记需要进行任务切换。当函数 xTaskRemoveFromEventList()返回值为 pdTRUE 的话就需要进行任务切换。

(5)、进行任务切换。

(6)、返回 pdPASS,标记入队成功。

(7)、(2)到(6)都是非常理想的效果,即消息队列未满,入队没有任何障碍。但是队列满了以后呢?首先判断设置的阻塞时间是否为 0,如果为 0 的话就说明没有阻塞时间。

(8)、由(7)得知阻塞时间为 0,那就直接返回 errQUEUE_FULL,标记队列已满就可以了。

(9)、如果阻塞时间不为 0 并且时间结构体还没有初始化的话就初始化一次超时结构体变量,调用函数 vTaskSetTimeOutState()完成超时结构体变量 xTimeOut 的初始化。其实就是记录当前的系统时钟节拍计数器的值 xTickCount 和溢出次数 xNumOfOverflows。

(10)、任务调度器上锁,代码执行到这里说明当前的状况是队列已满了,而且设置了不为 0的阻塞时间。那么接下来就要对任务采取相应的措施了,比如将任务加入到队列的
xTasksWaitingToSend 列表中。

(11)、调用函数 prvLockQueue()给队列上锁,其实就是将队列中的成员变量 cRxLock 和
cTxLock 设置为 queueLOCKED_UNMODIFIED。

(12)、调用函数 xTaskCheckForTimeOut()更新超时结构体变量 xTimeOut,并且检查阻塞时间是否到了。

(13)、阻塞时间还没到,那就检查队列是否还是满的。

(14)、经过(12)和(13)得出阻塞时间没到,而且队列依旧是满的,那就调用函数vTaskPlaceOnEventList()将任务添加到队列的 xTasksWaitingToSend 列表中和延时列表中,并且将 任 务 从 就 绪 列 表 中 移 除 。 注 意 ! 如 果 阻 塞 时 间 是 portMAX_DELAY 并 且 宏INCLUDE_vTaskSuspend 为 1 的话,函数vTaskPlaceOnEventList()会将任务添加到列表xSuspendedTaskList 上。

(15)、操作完成,调用函数 prvUnlockQueue()解锁队列。

(16)、调用函数 xTaskResumeAll()恢复任务调度器

(17)、阻塞时间还没到,但是队列现在有空闲的队列项,那么就在重试一次。

(18)、相比于第(12)步,阻塞时间到了!那么任务就不用添加到那些列表中了,那就解锁队列,恢复任务调度器。

(19)、返回 errQUEUE_FULL,表示队列满了。

3. 中断级通用入队函数

讲完任务级入队函数再来看一下中断级入队函数 xQueueGenericSendFromISR(),其他的中断级入队函数都是靠此函数来实现的。中断级入队函数和任务级入队函数大同小异,函数代码如下:

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;portASSERT_IF_INTERRUPT_PRIORITY_INVALID();uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();{if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) ||\ (1)( xCopyPosition == queueOVERWRITE ) ){const int8_t cTxLock = pxQueue->cTxLock; (2)traceQUEUE_SEND_FROM_ISR( pxQueue );( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); (3)			{//队列上锁的时候就不能操作事件列表,队列解锁的时候会补上这些操作的。if( cTxLock == queueUNLOCKED ) (4){/**************************************************************************//**************************省略掉与队列集相关代码**************************//**************************************************************************/if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == \ (5)pdFALSE ){if( xTaskRemoveFromEventList( &( pxQueue->\ (6)xTasksWaitingToReceive ) ) != pdFALSE ){//刚刚从事件列表中移除的任务对应的任务优先级更高,所以标记要进行任务切换if( pxHigherPriorityTaskWoken != NULL ){*pxHigherPriorityTaskWoken = pdTRUE; (7)}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}}else{//cTxLock 加一,这样就知道在队列上锁期间向队列中发送了数据pxQueue->cTxLock = ( int8_t ) ( cTxLock + 1 ); (8)}xReturn = pdPASS; (9)}else{traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue );xReturn = errQUEUE_FULL; (10)}}portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );return xReturn;
}

(1)、队列未满或者采用的覆写的入队方式,这是最理想的壮态。

(2)、读取队列的成员变量 xTxLock,用于判断队列是否上锁。

(3)、将数据拷贝到队列中。

(4)、队列上锁了,比如任务级入队函数在操作队列中的列表的时候就会对队列上锁。

(5)、判断队列列表 xTasksWaitingToReceive 是否为空,如果不为空的话说明有任务在请求消息的时候被阻塞了。

(6)、将相应的任务从列表 xTasksWaitingToReceive 上移除。跟任务级入队函数处理过程一样。

(7)、如果刚刚从列表 xTasksWaitingToReceive 中移除的任务优先级比当前任务的优先级高,那么标记 pxHigherPriorityTaskWoken 为 pdTRUE,表示要进行任务切换。如果要进行任务切换的话就需要在退出此函数以后,退出中断服务函数之前进行一次任务切换。

(8)、如果队列上锁的话那就将队列成员变量 cTxLock 加一,表示进行了一次入队操作,在队列解锁(prvUnlockQueue())的时候会对其做相应的处理。

(9)、返回 pdPASS,表示入队完成。

(10)、如果队列满的话就直接返回 errQUEUE_FULL,表示队列满。

二、队列上锁和解锁

在上面讲解任务级通用入队函数和中断级通用入队函数的时候都提到了队列的上锁和解锁,队列的上锁和解锁是两个 API 函数:prvLockQueue()和 prvUnlockQueue()。首先来看一下队列上锁函数 prvLockQueue(),此函数本质上就是一个宏,定义如下:

#define prvLockQueue( pxQueue ) \
taskENTER_CRITICAL(); \
{ \if( ( pxQueue )->cRxLock == queueUNLOCKED ) \{ \( pxQueue )->cRxLock = queueLOCKED_UNMODIFIED;\} \if( ( pxQueue )->cTxLock == queueUNLOCKED ) \{ \( pxQueue )->cTxLock = queueLOCKED_UNMODIFIED;\} \
} \
taskEXIT_CRITICAL()

prvLockQueue()函数很简单,就是将队列中的成员变量 cRxLock 和 cTxLock 设置为
queueLOCKED_UNMODIFIED 就行了。

在来看一下队列的解锁函数 prvUnlockQueue(),函数如下:

static void prvUnlockQueue( Queue_t * const pxQueue )
{//上锁计数器(cTxLock 和 cRxLock)记录了在队列上锁期间,入队或出队的数量,当队列//上锁以后队列项是可以加入或者移除队列的,但是相应的列表不会更新。taskENTER_CRITICAL();{//处理 cTxLock。int8_t cTxLock = pxQueue->cTxLock;while( cTxLock > queueLOCKED_UNMODIFIED ) (1){/**************************************************************************//**************************省略掉与队列集相关代码**************************//**************************************************************************/{//将任务从事件列表中移除if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == \ (2)pdFALSE ){if( xTaskRemoveFromEventList( &( pxQueue->\ (3)xTasksWaitingToReceive ) ) != pdFALSE ){//从列表中移除的任务优先级比当前任务的优先级高,因此要//进行任务切换。vTaskMissedYield(); (4)}else{	mtCOVERAGE_TEST_MARKER();}}else{break;}}--cTxLock; (5)}pxQueue->cTxLock = queueUNLOCKED; (6)}taskEXIT_CRITICAL();//处理 cRxLock。taskENTER_CRITICAL();{int8_t cRxLock = pxQueue->cRxLock;while( cRxLock > queueLOCKED_UNMODIFIED ) (7){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();
}

(1)、判断是否有中断向队列发送了消息,在前小节讲解中断级通用入队函数的时候说了,如果当队列上锁的话那么向队列发送消息成功以后会将入队计数器 cTxLock 加一。

(2)、判断列表 xTasksWaitingToReceive 是否为空,如果不为空的话就要将相应的任务从列表中移除。

(3)、将任务从列表 xTasksWaitingToReceive 中移除。

(4)、如果刚刚从列表 xTasksWaitingToReceive 中移除的任务优先级比当前任务的优先级高,那么就要标记需要进行任务切换。这里调用函数 vTaskMissedYield()来完成此任务,函数vTaskMissedYield()只是简单的将全局变量 xYieldPending 设置为 pdTRUE。那么真正的任务切换是在哪里完成的呢?在时钟节拍处理函数 xTaskIncrementTick()中,此函数会判断 xYieldPending的值,从而决定是否进行任务切换

(5)、每处理完一条就将 cTxLock 减一,直到处理完所有的。

(6)、当处理完以后标记 cTxLock 为 queueUNLOCKED,也就说 cTxLock 是没有上锁的了。

(7)、处理完 cTxLock 以后接下来就要处理 xRxLock 了,处理过程和 xTxLock 很类似

三、从队列读取消息

有入队就有出队,出队就是从队列中获取队列项(消息),FreeRTOS 中出队函数如下表所示:
在这里插入图片描述

1. 函数 xQueueReceive()

此函数用于在任务中从队列中读取一条(请求)消息,读取成功以后就会将队列中的这条数
据删除,此函数的本质是一个宏,真正执行的函数是 xQueueGenericReceive()。此函数在读取消息的时候是采用拷贝方式的,所以用户需要提供一个数组或缓冲区来保存读取到的数据,所读取的数据长度是创建队列的时候所设定的每个队列项目的长度,函数原型如下:

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

参数:
xQueue: 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的队列句柄。
pvBuffer: 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中。
xTicksToWait: 阻塞时间,此参数指示当队列空的时候任务进入阻塞态等待队列有数据的最大时间。如果为 0 的话当队列空的时候就立即返回;当为 portMAX_DELAY
的 话 就 会 一 直 等 待 , 直 到 队 列 有 数 据 , 也 就 是 死 等 , 但 是 宏
INCLUDE_vTaskSuspend 必须为 1。

返回值:
pdTRUE: 从队列中读取数据成功。
pdFALSE: 从队列中读取数据失败。

2. 函数 xQueuePeek()

此函数用于从队列读取一条(请求)消息,只能用在任务中!此函数在读取成功以后不会将
消息删除,此函数是一个宏,真正执行的函数是 xQueueGenericReceive()。此函数在读取消息的时候是采用拷贝方式的,所以用户需要提供一个数组或缓冲区来保存读取到的数据,所读取的数据长度是创建队列的时候所设定的每个队列项目的长度,函数原型如下:

BaseType_t xQueuePeek(QueueHandle_t xQueue,void * pvBuffer,TickType_t xTicksToWait);

参数:
xQueue: 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的队列句柄。
pvBuffer: 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中。
xTicksToWait: 阻塞时间,此参数指示当队列空的时候任务进入阻塞态等待队列有数据的最大时间。如果为 0 的话当队列空的时候就立即返回;当为 portMAX_DELAY
的 话 就 会 一 直 等 待 , 直 到 队 列 有 数 据 , 也 就 是 死 等 , 但 是 宏INCLUDE_vTaskSuspend 必须为 1。

返回值:
pdTRUE: 从队列中读取数据成功。
pdFALSE: 从队列中读取数据失败。

3. 函数 xQueueGenericReceive()

不 管 是 函 数 xQueueReceive() 还 是 xQueuePeek() ,最终都是调用的函数xQueueGenericReceive(),此函数是真正干事的,函数原型如下:

BaseType_t xQueueGenericReceive(QueueHandle_t xQueue,void* pvBuffer,TickType_t xTicksToWaitBaseType_t xJustPeek)

参数:
xQueue: 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的队列句柄。
pvBuffer: 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中。
xTicksToWait: 阻塞时间,此参数指示当队列空的时候任务进入阻塞态等待队列有数据的最大时间。如果为 0 的话当队列空的时候就立即返回;当为 portMAX_DELAY
的 话 就 会 一 直 等 待 , 直 到 队 列 有 数 据 , 也 就 是 死 等 , 但 是 宏
INCLUDE_vTaskSuspend 必须为 1。
xJustPeek: 标记当读取成功以后是否删除掉队列项,当为 pdTRUE 的时候就不用删除,也就是说你后面再调用函数 xQueueReceive()获取到的队列项是一样的。当为
pdFALSE 的时候就会删除掉这个队列项。

返回值:
pdTRUE: 从队列中读取数据成功。
pdFALSE: 从队列中读取数据失败。

4. 函数 xQueueReceiveFromISR()

此函数是 xQueueReceive()的中断版本,用于在中断服务函数中从队列中读取(请求)一条消息,读取成功以后就会将队列中的这条数据删除。此函数在读取消息的时候是采用拷贝方式的,所以需要用户提供一个数组或缓冲区来保存读取到的数据,所读取的数据长度是创建队列的时候所设定的每个队列项目的长度,函数原型如下:

BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,void* pvBuffer,BaseType_t * pxTaskWoken);

参数:
xQueue: 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的队列句柄。
pvBuffer: 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中。
pxTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值是由函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值
为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。

返回值:
pdTRUE: 从队列中读取数据成功。
pdFALSE: 从队列中读取数据失败。

5. 函数 xQueuePeekFromISR()

此函数是 xQueuePeek()的中断版本,此函数在读取成功以后不会将消息删除,此函数原型如下:

BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue,void * pvBuffer)

参数:
xQueue: 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的队列句柄。
pvBuffer: 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区中。

返回值:
pdTRUE: 从队列中读取数据成功。
pdFALSE: 从队列中读取数据失败。

相关文章:

FreeRTOS 队列(二)

文章目录 一、向队列发送消息1. 函数原型&#xff08;1&#xff09;函数 xQueueOverwrite()&#xff08;2&#xff09;函数 xQueueGenericSend()&#xff08;3&#xff09;函数 xQueueSendFromISR()、xQueueSendToBackFromISR()、xQueueSendToFrontFromISR()&#xff08;4&…...

用python获取当前目录下的创建时间超过3天的所有python文件

直接上代码&#xff1a; import os import datetime print(os.getcwd()) # 获取当前目录下所有的html文件 html_files [] for filename in os.listdir(): if filename.endswith(.py): html_files.append(os.path.join(., filename)) now date…...

第五章 Linux实际操作——用户管理

第五章 Linux实际操作——用户管理 5.1 基本介绍5.2 添加用户5.3 指定、修改密码5.4 删除用户5.5 查询用户信息指令5.6 切换用户5.7 查看当前用户、登录用户5.8 用户组5.9 用户和组相关文件8.9.1/etc/passwd 文件8.9.2/etc/shadow文件8.9.3/etc/group文件 5.1 基本介绍 Linux系…...

悲观锁和乐观锁详细

悲观锁和乐观锁详细 悲观锁 ​ 悲观锁就是悲观的思想&#xff0c;他认为数据每一次被访问的时候都会被上锁&#xff0c;所以每次获得锁的时候都会上锁&#xff0c;这样其他线程想要获取这个锁的时候就会被堵塞&#xff0c;要等待上一个线程锁的释放。也就是说这个线程只一次只…...

三谈ChatGPT(ChatGPT可以解决问题的90%)

这是我第三次谈ChatGPT&#xff0c;前两篇主要谈了ChatGPT的概念&#xff0c;之所以火的原因和对人们的影响&#xff0c;以及ChatGPT可能存在的安全风险和将面临的监管问题。这一篇主要讲讲ChatGPT的场景和处理问题的逻辑。 这一次我特意使用了ChatGPT中文网页版体验了一番。并…...

Qt QSet 详解:从底层原理到高级用法

目录标题 引言&#xff1a;QSet的重要性与简介QSet 的常用接口迭代器&#xff1a;遍历Qset 中的元素&#xff08;Iterators: Traversing Elements in Qset &#xff09;高级用法&#xff1a;QSet 中的算法与功能&#xff08;Advanced Usage: Algorithms and Functions in QList…...

Mac Doxygen的使用

Doxygen的使用 安装着Doxygen和Graphviz这两个东西 在源码目录先使用doxygen -g生成一个叫 ‘Doxyfile’ 的Doxygen的配置文件修改配置文件&#xff0c;里面都有介绍各个选项的功能&#xff0c;这里主要修改一下几个: HAVE_DOT YES EXTRACT_ALL YES EXTRACT_PRIVATE YES E…...

FPGA基础代码复用

一、verilog中有关代码复用的语法 1、连接符“{}” {4{1b1}} 或者 {5d6, 5d8} 2、参数(Parameter)型常量定义 parameter 参数名&#xff1d;表达式&#xff1b; 或者 localparam 参数名&#xff1d;表达式&#xff1b; parameter DATA_WIDTH 20; 3、function函数定义 …...

Hbase简介

HBase简介 一、HBase简介 1. HBase简介 (1) apache的顶级项目&#xff0c;hadoop的数据库&#xff0c;分布式、大规模的大数据存储。 HBase是Google的BigTable的开源java版本&#xff0c;建立在hdfs之上的&#xff0c;分布式、列存储、非关系&#xff08;nosql、key-value&a…...

科海思除COD树脂,大孔树脂,除COD专用树脂

一、产品介绍 Tulsimer A-722 MP具有控制孔径的大孔强碱性Ⅰ型阴离子交换树脂 Tulsimer A-722 MP 是一款具有便于颜色和有机物去除的控制孔径的&#xff0c;专门开发的大孔强碱性Ⅰ型阴离子交换树脂。 Tulsimer A-722 MP&#xff08;氯型&#xff09;专门应用于去除COD…...

Qt 多线程 QThread、QThreadPool使用场景

QThread 和 QRunnable 都是 Qt 框架中用于多线程编程的类&#xff0c;它们之间有以下不同点&#xff1a; 继承关系不同 QThread 继承自 QObject 类&#xff0c;而 QRunnable 没有父类。 实现方式不同 QThread 是一个完整的线程实现&#xff0c;包含了线程的创建、启动、停止、…...

如何一招搞定PCB阻焊过孔问题?

PCB阻焊油墨根据固化方式&#xff0c;阻焊油墨有感光显影型的油墨&#xff0c;有热固化的热固油墨&#xff0c;还有UV光固化的UV油墨。而根据板材分类&#xff0c;又有PCB硬板阻焊油墨&#xff0c;FPC软板阻焊油墨&#xff0c;还有铝基板阻焊油墨&#xff0c;铝基板油墨也可以用…...

【代码随想录】刷题Day2

1.左右指针比大小 977. 有序数组的平方 class Solution { public:vector<int> sortedSquares(vector<int>& nums) {vector<int> ret nums;int left 0;int right nums.size()-1;int end nums.size();while(left<right){if(abs(nums[left])>abs…...

Python机器学习、深度学习技术提升气象、海洋、水文领域实践应用

Python是功能强大、免费、开源&#xff0c;实现面向对象的编程语言&#xff0c;在数据处理、科学计算、数学建模、数据挖掘和数据可视化方面具备优异的性能&#xff0c;这些优势使得Python在气象、海洋、地理、气候、水文和生态等地学领域的科研和工程项目中得到广泛应用。可以…...

计及调度经济性的光热电站储热容量配置方法【IEEE30节点】(Matlab代码实现)

&#x1f4a5; &#x1f4a5; &#x1f49e; &#x1f49e; 欢迎来到本博客 ❤️ ❤️ &#x1f4a5; &#x1f4a5; &#x1f3c6; 博主优势&#xff1a; &#x1f31e; &#x1f31e; &#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 …...

“不要放过这个春天”解锁品牌春日宣传新玩法

在万物复苏的春天&#xff0c;人们换新装、踏青等需求蓄势待发&#xff0c;出现了全民消费热情高涨的趋势&#xff0c;让品牌「贩卖春天」的宣传此起彼伏。 品牌洞察到用户的消费需求&#xff0c;打造具有品牌特色的浪漫宣传&#xff0c;如采用春日限定元素、创新春天宣传场景…...

利用GPT2 预测 福彩3d预测

使用GPT2预测福彩3D项目 个人总结彩票数据是随机的,可以预测到1-2个数字,但是有一两位总是随机的 该项目紧做模型学习用,通过该项目熟练模型训练调用生成过程. 福彩3D数据下载 https://www.17500.cn/getData/3d.TXT data数据格式 处理后数据格式 每行 2023 03 08 9 7 3 训…...

类加载过程

基本说明 反射机制是Java实现动态语言的关键&#xff0c;也就是通过反射实现类动态加载。 静态加载&#xff1a;编译时加载相关的类&#xff0c;如果没有则报错&#xff0c;依赖性太强动态加载&#xff1a;运行时加载需要的类&#xff0c;如果运行时不用该类&#xff0c;即使…...

【C/C++】C++11 无序关联容器的诞生背景

文章目录 背景无序关联容器适用场景有序关联容器适用场景 背景 C11 引入了无序关联容器&#xff08;unordered_map、unordered_set、unordered_multimap 和 unordered_multiset&#xff09;是为了提供一种高效的元素存储和查找方式。相比于有序关联容器&#xff08;map、set、…...

h264编码原理

在介绍编码器原理之前首先了解三个制定编码标准的组织&#xff1a; 1.国际电信联盟(ITU-T)&#xff0c;这是一个音视频领域非常强的组织&#xff0c;规定了很多标准如h261&#xff0c;h262&#xff0c;h263&#xff0c;h263。h263也就是h264的前身。 2.国际标准化组织(ISO)&…...

网络工程师经常搞混的路由策略和策略路由,两者到底有啥区别?

当涉及到网络路由时&#xff0c;两个术语经常被混淆&#xff1a;策略路由和路由策略。虽然这些术语听起来很相似&#xff0c;但它们实际上有着不同的含义和用途。在本文中&#xff0c;我们将详细介绍这两个术语的区别和应用。 一、路由策略 路由策略是指一组规则&#xff0c;用…...

高精度气象模拟软件WRF实践技术

【原文链接】&#xff1a;高精度气象模拟软件WRF(Weather Research Forecasting)实践技术及案例应用https://mp.weixin.qq.com/s?__bizMzU5NTkyMzcxNw&mid2247538149&idx3&sn3890c3b29f34bcb07678a9dd4b9947b2&chksmfe68938fc91f1a99bbced2113b09cad822711e7f…...

总结827

学习目标&#xff1a; 4月&#xff08;复习完高数18讲内容&#xff0c;背诵21篇短文&#xff0c;熟词僻义300词基础词&#xff09; 学习内容&#xff1a; 高等数学&#xff1a;刷1800&#xff0c;做了26道计算题&#xff0c;记录两道错题&#xff0c;搞懂了&#xff0c;但并不…...

还在发愁项目去哪找?软件测试企业级Web自动化测试实战项目

今天给大家分享一个简单易操作的实战项目&#xff08;已开源&#xff09; 项目名称 ET开源商场系统 项目描述 ETshop是一个电子商务B2C电商平台系统&#xff0c;功能强大&#xff0c;安全便捷。适合企业及个人快速构建个性化网上商城。 包含PCIOS客户端Adroid客户端微商城…...

总结下Spring boot异步执行逻辑的几种方式

文章目录 概念实现方式Thread说明 Async注解说明 线程池CompletableFuture&#xff08;Future及FutureTask&#xff09;创建CompletableFuture异步执行 消息队列 概念 异步执行模式&#xff1a;是指语句在异步执行模式下&#xff0c;各语句执行结束的顺序与语句执行开始的顺序…...

【开发日志】2023.04 ZENO----Composite----CompNormalMap

NormalMap-Online (cpetry.github.io)https://cpetry.github.io/NormalMap-Online/ CompNormalMap 将灰度图像转换为法线贴图 将灰度图像转换为法线贴图是一种常见的技术&#xff0c;用于在实时图形渲染中增加表面细节。下面是一个简单的方法来将灰度图像转换为法线贴图&…...

春秋云境:CVE-2022-28525 (文件上传漏洞)

目录 一、题目 1.登录 2.burp抓包改包 3.蚁剑获取flag 一、题目 ED01CMSv20180505存在任意文件上传漏洞 英语不够 翻译来凑&#xff1a; 点击其他页面会Not Found 找不到&#xff1a; 先登录看看吧&#xff1a; 试试万能密码&#xff1a;admin&#xff1a;123 发现错误…...

【软件测试二】开发模型和测试模型,BUG概念篇

目录 1.软件的生命周期 2.瀑布模型 3.螺旋模型 4.增量&#xff0c;迭代 5.敏捷---scrum 1. 敏捷宣言 2.角色 6. 软件测试v模型 7.软件测试w模型 8.软件测试的生命周期 9.如何描述一个BUG 10.如何定义BUG的级别 11.BUG的生命周期 12.产生争执怎么办 1.软件的生命周期…...

短视频app开发:如何实现视频直播功能

短视频源码的实现 在短视频app开发中&#xff0c;实现视频直播功能需要借助短视频源码。短视频源码可以提供一个完整的视频直播功能模块&#xff0c;包括视频采集、编码、推流等。因此&#xff0c;我们可以选择一些开源的短视频源码&#xff0c;例如LFLiveKit、ijkplayer等&am…...

[架构之路-174]-《软考-系统分析师》-5-数据库系统-7-数据仓库技术与数据挖掘技术

5 . 7 数据仓库技术 数据仓库是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合&#xff0c;用于支持管理决策。近年来&#xff0c;人们对数据仓库技术的关注程度越来越尚&#xff0c;其原因是过去的几十年中&#xff0c;建设了无数的应用系统&#xff0c;积累了…...