创建好队列以后就可以向队列发送消息了,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_txQueueSendToBack(QueueHandle_txQueue,
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:发送消息成功。其他:出错 |
2、函数xQueueOverwrite()
此函数也是用于向队列发送数据的,当队列满了以后会覆写掉旧的数据,不管这个旧数据有没有被其他任务或中断取走。这个函数常用于向那些长度为1的队列发送消息,此函数也是一个宏,最终调用的也是函数xQueueGenericSend(),函数原型如下:
BaseType_t xQueueOverwrite(QueueHandle_t xQueue,
const void* pvItemToQueue);
参数 | 描述 |
---|---|
xQueue | 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄。 |
pvItemToQueue | 指向要发送的消息,发送的时候会将这个消息拷贝到队列中。 |
返回值 | pdPASS:发送消息成功。 |
3、函数xQueueGenericSend()
此函数才是真正干活的,上面讲的所有的任务级入队函数最终都是调用的此函数,此函数也是重点要讲解的,先来看一下函数原型:
BaseType_txQueueGenericSend(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:覆写入队。 |
返回值 | pdPASS:发送消息成功。errQUEUE_FULL:队列已经满了,消息发送失败。 |
4、函数xQueueSendFromISR()、
xQueueSendToBackFromISR()、
xQueueSendToFrontFromISR()
这三个函数也是向队列中发送消息的,这三个函数用于中断服务函数中。这三个函数本质也是宏,其中函数xQueueSendFromISR()和xQueueSendToBackFromISR()是一样的,都是后向入队,即将新的消息插入到队列的后面。函数xQueueSendToFrontFromISR()是前向入队,即将新消息插入到队列的前面。这三个函数同样调用同一个函数xQueueGenericSendFromISR0。这三个函数的原型如下:
Base Type_t xQueueSendFromISR(
QueueHandle_t xQueue,
const void pvItemToQueue,
BaseType_t* pxHigherPriorityTaskWoken);
BaseType_txQueueSendToBackFromISR(
QueueHandle_t xQueue,
const void pvItemToQueue
BaseType_t* pxHigherPriorityTaskWoken);
BaseType_txQueueSendToFrontFromISR(
QueueHandle_t xQueue,
const void*pvItemToQueue,
BaseType_t*pxHigherPriorityTaskWoken );
参数 | 描述 |
---|---|
xQueue | 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄。 |
pvltemToQueue | 指向要发送的消息,发送的时候会将这个消息拷贝到队列中。 |
pxHigherPriorityTaskWoken | 标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为pdTRUE的时候在退出中断服务函数之前一定要进行一次任务切换。 |
返回值 | pdTRUE:向队列中发送消息成功!errQUEUE_FULL:队列已经满了,消息发送失败。 |
注意观察,可以看出这些函数都没有设置阻塞时间值。原因很简单,这些函数都是在中断服务函数中调用的,并不是在任务中,所以也就没有阻塞这一说了!
5、函数xQueueOverwriteFromISR()
此函数是xQueueOverwrite()的中断级版本,用在中断服务函数中,在队列满的时候自动覆写掉旧的数据,此函数也是一个宏,实际调用的也是函数xQueueGenericSendFromISR(),此函数原型如下:
BaseType_t xQueueOverwriteFromISR(
QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t*pxHigherPriorityTaskWoken);
参数和上面表格一样。
6、函数xQueueGenericSendFromISR()
上面说了4个中断级入队函数最终都是调用的函数xQueueGenericSendFromISR(),这是真正干活的主,也是我们下面会详细讲解的函数,先来看一下这个函数的原型,如下:
BaseType_txQueueGenericSendFromISR(
QueueHandle_t xQueue,
const void*pvItemToQueue,
BaseType_t*pxHigherPriorityTaskWoken,
BaseType_tXCopyPosition);
参数 | 描述 |
---|---|
xQueue | 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄。 |
pvItemToQueue | 指向要发送的消息,发送的时候会将这个消息拷贝到队列中。 |
pxHigherPriorityTaskWoken | 标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为pdTRUE的时候在退出中断服务函数之前一定要进行一次任务切换。 |
xCopyPosition | 入队方式,有三种入队方式:queueSEND_TO_BACK:后向入队queueSEND_TO_FRONT:前向入队queueOVERWRITE:覆写入队。 |
返回值 | pdPASS:发送消息成功。errQUEUE_FULL:队列已经满了,消息发送失败。 |
不管是后向入队、前向入队还是覆写入队,最终调用的都是通用入队函数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 ) || ( xCopyPosition == queueOVERWRITE ) ) (1)
{
traceQUEUE_SEND( pxQueue );
xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );(2)
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )(3)
{
//检测是否有任务由于等待消息而进入阻塞态
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )(4)
{
//解除阻塞态的任务优先级最高,因此要进行一次任务切换 queueYIELD_IF_USING_PREEMPTION();(5)
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else if( xYieldRequired != pdFALSE )
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_QUEUE_SETS */
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 ), xTicksToWait );(14)
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)
}
}
}
讲完任务级入队函数再来看一下中断级入队函数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 ) || ( xCopyPosition == queueOVERWRITE ) )(1)
{
const int8_t cTxLock = pxQueue->cTxLock;(2)
traceQUEUE_SEND_FROM_ISR( pxQueue );
( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );(3)
//队列上锁的时候不能操作事件队列,队列解锁的时候会补上这些操作
/*队列集相关代码开始*/
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 posting
to the queue set caused a higher priority task to
unblock. 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 )(5)
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )(6)
{
//刚刚从事件列表中移除的任务对应的任务优先级更高,所以需要标记进行任务切换
if( pxHigherPriorityTaskWoken != NULL )
{
*pxHigherPriorityTaskWoken = pdTRUE;(7)
}
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 )
{
if( pxHigherPriorityTaskWoken != NULL )
{
*pxHigherPriorityTaskWoken = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_QUEUE_SETS */
}
else
{
//cTxLock +1,z这样就知道队列在上锁期间向队列中发送了数据
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;
}