1.概念
- 只要知道队列的句柄,谁都可以读、写该队列。任务、 ISR 都可读、写队列。可以多个任务读写队列。
- 任务读写队列时,简单地说:
如果读写不成功,则阻塞
;可以指定超时时间。如果能读写了就马上进入就绪态,否则就阻塞直到超时。 - 读取队列的任务个数没有限制,那么当多个任务读取空队列时,这些任务都会进入阻塞状态。写队列的任务个数没有限制,那么当多个任务写"满队列"时,这些任务都会进入阻塞状态。
- 有多个任务在等待同一个队列的数据。当队列中有数据时,哪个任务会进入
就绪态?- 优先级最高的任务
- 如果大家的优先级相同,那等待时间最久的任务会进入就绪态
- 有多个任务在等待同一个队列的空间。当队列中有空间时,哪个任务会进入就绪态?
- 优先级最高的任务
- 如果大家的优先级相同,那等待时间最久的任务会进入就绪态
2.阻塞程序
创建一个队列,然后创建 2 个发送任务、 1 个接收任务。
1. 创建的队列,用来发送结构体:数据大小是结构体的大小
2. 发送任务优先级为 2,接收任务优先级为 1。
3. 读队列、打印信息
main函数
/* 定义2种数据来源(ID) */
typedef enum
{
eMotorSpeed,
eSpeedSetPoint
} ID_t;
/* 定义在队列中传输的数据的格式 */
typedef struct {
ID_t eDataID;
int32_t lDataValue;
}Data_t;
/* 定义2个结构体 */
static const Data_t xStructsToSend[ 2 ] =
{
{ eMotorSpeed, 10 }, /* CAN任务发送的数据 */
{ eSpeedSetPoint, 5 } /* HMI任务发送的数据 */
};
static void vSenderTask( void *pvParameters );
static void vReceiverTask( void *pvParameters );
/* 队列句柄, 创建队列时会设置这个变量 */
QueueHandle_t xQueue;
int main( void )
{
prvSetupHardware();
/* 创建队列: 长度为5,数据大小为4字节(存放一个整数) */
xQueue = xQueueCreate( 5, sizeof( Data_t ) );
if( xQueue != NULL )
{
xTaskCreate( vSenderTask, "CAN Task", 1000, ( void * ) &( xStructsToSend[ 0 ] ), 2, NULL );
xTaskCreate( vSenderTask, "HMI Task", 1000, ( void * ) &( xStructsToSend[ 1 ] ), 2, NULL );
xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 1, NULL );
vTaskStartScheduler();
}
else
{
/* 无法创建队列 */
}
return 0;
}
发送函数:
static void vSenderTask( void *pvParameters )
{
BaseType_t xStatus;
const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
/* 无限循环 */
for( ;; )
{
/* 写队列
xStatus = xQueueSendToBack( xQueue, pvParameters, xTicksToWait );
if( xStatus != pdPASS )
{
printf( "Could not send to the queue.\r\n" );
}
}
}
接收函数:
static void vReceiverTask( void *pvParameters )
{
/* 读取队列时, 用这个变量来存放数据 */
Data_t xReceivedStructure;
BaseType_t xStatus;
/* 无限循环 */
for( ;; )
{
xStatus = xQueueReceive( xQueue, &xReceivedStructure, 0 );
if( xStatus == pdPASS )
{
/* 读到了数据 */
if( xReceivedStructure.eDataID == eMotorSpeed )
{
printf( "From CAN, MotorSpeed = %d\r\n", xReceivedStructure.lDataValue );
}
else if( xReceivedStructure.eDataID == eSpeedSetPoint )
{
printf( "From HMI, SpeedSetPoint = %d\r\n", xReceivedStructure.lDataValue );
}
}
else
{
/* 没读到数据 */
printf( "Could not receive from the queue.\r\n" );
}
}
}
3.运行结果以及分析
- HMI 是最后创建的最高优先级任务,它先执行,一下子向队列写入 5
个数据,把队列都写满了。 - 队列已经满了, HMI 任务再发起第 6 次写操作时,进入阻塞状态。这
时 CAN 任务是最高优先级的就绪态任务,它开始执行。 - CAN 任务发现队列已经满了,进入阻塞状态;接收任务变为最高优先
级的就绪态任务,它开始运行。 - 现在, HMI 任务、 CAN 任务的优先级都比接收任务高,它们都在等待
队列有空闲的空间;一旦接收任务读出 1 个数据,会马上被抢占。被谁抢占?谁等待最久? HMI 任务!所以在 t4 时刻,切换到 HMI 任务。 - HMI 任务向队列写入第 6 个数据,然后再次阻塞,这是 CAN 任务已经
阻塞很久了。接收任务变为最高优先级的就绪态任务,开始执行。 - 现在, HMI 任务、 CAN 任务的优先级都比接收任务高,它们都在等待
队列有空闲的空间;一旦接收任务读出 1 个数据,会马上被抢占。被谁抢占?谁等待最久? CAN 任务!所以在 t6 时刻,切换到 CAN 任务。 - CAN 任务向队列写入数据,因为仅仅有一个空间供写入,所以它马上
再次进入阻塞状态。这时 HMI 任务、 CAN 任务都在等待空闲空间,只有接收任
务可以继续执行。
总结
谁等的最久,谁先执行。
跟着韦东山老师学习的。