网站建设开标书,品牌seo公司,做网站遇到的困难总结,网站设置的建设过程和准备阶段文章目录 一、队列的特性1.1 队列常规操作1.2 传输数据的两种方法1.3 队列的阻塞访问 二 队列函数2.1创建2.2 复位2.3 删除2.4 写队列2.5 读队列2.6 查询2.7 覆盖/偷看 三、示例3.1示例 队列的基本使用3.2 示例: 分辨数据源3.3 示例: 传输大块数据3.4 : 邮箱(Mailbox) 四、队列… 文章目录 一、队列的特性1.1 队列常规操作1.2 传输数据的两种方法1.3 队列的阻塞访问 二 队列函数2.1创建2.2 复位2.3 删除2.4 写队列2.5 读队列2.6 查询2.7 覆盖/偷看 三、示例3.1示例 队列的基本使用3.2 示例: 分辨数据源3.3 示例: 传输大块数据3.4 : 邮箱(Mailbox) 四、队列集 一、队列的特性 
1.1 队列常规操作 
队列的简化操如入下图所示从此图可知 
队列可以包含若干个数据队列中有若干项这被称为长度(length)每个数据大小固定创建队列时就要指定长度、数据大小数据的操作采用先进先出的方法(FIFOFirst In First Out)写数据时放到尾部读数据时从头部 读也可以强制写队列头部覆盖头部数据 更详细的操作入下图所示 1.2 传输数据的两种方法 
使用队列传输数据时有两种方法 
拷贝把数据、把变量的值复制进队列里引用把数据、把变量的地址复制进队列里 
reeRTOS使用拷贝值的方法这更简单 
局部变量的值可以发送到队列中后续即使函数退出、局部变量被回收也不会影响队列中的数据无需分配buffer来保存数据队列中有buffer局部变量可以马上再次使用发送任务、接收任务解耦接收任务不需要知道这数据是谁的、也不需要发送任务来释放数据如果数据实在太大你还是可以使用队列传输它的地址队列的空间有FreeRTOS内核分配无需任务操心对于有内存保护功能的系统如果队列使用引用方法也就是使用地址必须确保双方任务对这个 地址都有访问权限。使用拷贝方法时则无此限制内核有足够的权限把数据复制进队列、再把 数据复制出队列。 
1.3 队列的阻塞访问 
只要知道队列的句柄谁都可以读、写该队列。任务、ISR都可读、写队列。可以多个任务读写队列。 任务读写队列时简单地说如果读写不成功则阻塞可以指定超时时间。口语化地说就是可以定 个闹钟如果能读写了就马上进入就绪态否则就阻塞直到超时。 
某个任务读队列时如果队列没有数据则该任务可以进入阻塞状态还可以指定阻塞的时间。如果队 列有数据了则该阻塞的任务会变为就绪态。如果一直都没有数据则时间到之后它也会进入就绪态。 
既然读取队列的任务个数没有限制那么当多个任务读取空队列时这些任务都会进入阻塞状态有多 个任务在等待同一个队列的数据。当队列中有数据时哪个任务会进入就绪态 
优先级最高的任务如果大家的优先级相同那等待时间最久的任务会进入就绪态 
跟读队列类似一个任务要写队列时如果队列满了该任务也可以进入阻塞状态还可以指定阻塞的 时间。如果队列有空间了则该阻塞的任务会变为就绪态。如果一直都没有空间则时间到之后它也会 进入就绪态。 
既然写队列的任务个数没有限制那么当多个任务写满队列时这些任务都会进入阻塞状态有多个 任务在等待同一个队列的空间。当队列中有空间时哪个任务会进入就绪态 
优先级最高的任务如果大家的优先级相同那等待时间最久的任务会进入就绪态 
二 队列函数 
使用队列的流程创建队列、写队列、读队列、删除队列。 
2.1创建 
队列的创建有两种方法动态分配内存、静态分配内存 
前提 configSUPPORT_DYNAMIC_ALLOCATION 置于1 
动态分配内存xQueueCreate队列的内存在函数内部动态分配 
函数原型如下 
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );参数说明uxQueueLength队列长度最多能存放多少个数据(item)uxItemSize每个数据(item)的大小以字节为单位返回值非0成功返回句柄以后使用句柄来操作队列NULL失败因为内存不足 
静态分配内存xQueueCreateStatic队列的内存要事先分配好 
前提configSUPPORT_STATIC_ALLOCATION 置于1 
函数原型如下 
QueueHandle_t xQueueCreateStatic(UBaseType_t uxQueueLength,UBaseType_t uxItemSize,uint8_t *pucQueueStorageBuffer,StaticQueue_t *pxQueueBuffer
);参数说明uxQueueLength队列长度最多能存放多少个数据(item)uxItemSize每个数据(item)的大小以字节为单位pucQueueStorageBuffer如果uxItemSize非0pucQueueStorageBuffer必须指向一个uint8_t数组此数组大小至少为uxQueueLength * uxItemSizepxQueueBuffer必须执行一个StaticQueue_t结构体用来保存队列的数据结构返回值非0成功返回句柄以后使用句柄来操作队列NULL失败因为pxQueueBuffer为NULL 
示例代码 
// 示例代码
#define QUEUE_LENGTH 10
#define ITEM_SIZE sizeof( uint32_t )// xQueueBuffer用来保存队列结构体
StaticQueue_t xQueueBuffer;// ucQueueStorage 用来保存队列的数据
// 大小为队列长度 * 数据大小uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ];void vATask( void *pvParameters )
{
QueueHandle_t xQueue1;
// 创建队列: 可以容纳QUEUE_LENGTH个数据每个数据大小是ITEM_SIZE
xQueue1  xQueueCreateStatic( QUEUE_LENGTH,ITEM_SIZE,ucQueueStorage,xQueueBuffer );
}2.2 复位 
队列刚被创建时里面没有数据使用过程中可以调用xQueueReset() 把队列恢复为初始状态此函 数原型为 
/* pxQueue : 复位哪个队列;
* 返回值: pdPASS(必定成功)
*/
BaseType_t xQueueReset( QueueHandle_t pxQueue);2.3 删除 
删除队列的函数为vQueueDelete() 只能删除使用动态方法创建的队列它会释放内存。原型如下 
void vQueueDelete( QueueHandle_t xQueue );2.4 写队列 
可以把数据写到队列头部也可以写到尾部这些函数有两个版本在任务中使用、在ISR中使用。函 数原型如下 
/* 等同于xQueueSendToBack
* 往队列尾部写入数据如果没有空间阻塞时间为xTicksToWait
*/
BaseType_t xQueueSend(QueueHandle_t xQueue,const void *pvItemToQueue,TickType_t xTicksToWait
);
/*
* 往队列尾部写入数据如果没有空间阻塞时间为xTicksToWait
*/
BaseType_t xQueueSendToBack(QueueHandle_t xQueue,const void *pvItemToQueue,TickType_t xTicksToWait
);
/*
* 往队列尾部写入数据此函数可以在中断函数中使用不可阻塞
*/
BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken
);
/*
* 往队列头部写入数据如果没有空间阻塞时间为xTicksToWait
*/
BaseType_t xQueueSendToFront(QueueHandle_t xQueue,const void *pvItemToQueue,
TickType_t xTicksToWait
);
/*
* 往队列头部写入数据此函数可以在中断函数中使用不可阻塞
*/
BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken
);这些函数用到的参数是类似的统一说明如下 
参数说明xQueue队列句柄要写哪个队列pvItemToQueue数据指针这个数据的值会被复制进队列复制多大的数据在创建队列时已经指定了数据大小xTicksToWait如果队列满则无法写入新数据可以让任务进入阻塞状态xTicksToWait表示阻塞的最大时间(Tick Count)。如果被设为0无法写入数据时函数会立刻返回如果被设为portMAX_DELAY则会一直阻塞直到有空间可写返回值pdPASS数据成功写入了队列errQUEUE_FULL写入失败因为队列满了。 
2.5 读队列 
使用xQueueReceive() 函数读队列读到一个数据后队列中该数据会被移除。这个函数有两个版 本在任务中使用、在ISR中使用。函数原型如下 
BaseType_t xQueueReceive( QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait );
BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,void *pvBuffer,BaseType_t *pxTaskWoken
);参数说明如下 
参数说明xQueue队列句柄要读哪个队列pvBufferbufer指针队列的数据会被复制到这个buffer复制多大的数据在创建队列时已经指定了数据大小xTicksToWait果队列空则无法读出数据可以让任务进入阻塞状态xTicksToWait表示阻塞的最大时间(Tick Count)。如果被设为0无法读出数据时函数会立刻返回如果被设为portMAX_DELAY则会一直阻塞直到有数据可写返回值pdPASS从队列读出数据入errQUEUE_EMPTY读取失败因为队列空了。 
2.6 查询 
可以查询队列中有多少个数据、有多少空余空间。函数原型如下 
/*
* 返回队列中可用数据的个数
*/
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
/*
* 返回队列中可用空间的个数
*/
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );2.7 覆盖/偷看 
当队列长度为1时可以使用xQueueOverwrite() 或xQueueOverwriteFromISR() 来覆盖数据。 注意队列长度必须为1。当队列满时这些函数会覆盖里面的数据这也以为着这些函数不会被阻 塞。 函数原型如下 
/* 覆盖队列
* xQueue: 写哪个队列
* pvItemToQueue: 数据地址
* 返回值: pdTRUE表示成功, pdFALSE表示失败
*/
BaseType_t xQueueOverwrite(QueueHandle_t xQueue,const void * pvItemToQueue
);
BaseType_t xQueueOverwriteFromISR(QueueHandle_t xQueue,const void * pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken
);如果想让队列中的数据供多方读取也就是说读取时不要移除数据要留给后来人。那么可以使用窥 视也就是xQueuePeek() 或xQueuePeekFromISR() 。这些函数会从队列中复制出数据但是不移除 数据。这也意味着如果队列中没有数据那么偷看时会导致阻塞一旦队列中有数据以后每次偷 看都会成功。 函数原型如下 
/* 偷看队列
* xQueue: 偷看哪个队列
* pvItemToQueue: 数据地址, 用来保存复制出来的数据
* xTicksToWait: 没有数据的话阻塞一会
* 返回值: pdTRUE表示成功, pdFALSE表示失败
*/
BaseType_t xQueuePeek(QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait
);
BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue,void *pvBuffer,
);三、示例 
3.1示例 队列的基本使用 
本程序会创建一个队列然后创建2个发送任务、1个接收任务 发送任务优先级为1分别往队列中写入100、200 接收任务优先级为2读队列、打印数值 
main函数中创建的队列、创建了发送任务、接收任务代码如下 
/* 队列句柄, 创建队列时会设置这个变量 */
QueueHandle_t xQueue;
int main( void )
{prvSetupHardware();/* 创建队列: 长度为5数据大小为4字节(存放一个整数) */xQueue  xQueueCreate( 5, sizeof( int32_t ) );if( xQueue ! NULL ){/* 创建2个任务用于写队列, 传入的参数分别是100、200* 任务函数会连续执行向队列发送数值100、200* 优先级为1*/xTaskCreate( vSenderTask, Sender1, 1000, ( void * ) 100, 1, NULL );xTaskCreate( vSenderTask, Sender2, 1000, ( void * ) 200, 1, NULL );/* 创建1个任务用于读队列* 优先级为2, 高于上面的两个任务* 这意味着队列一有数据就会被读走*/xTaskCreate( vReceiverTask, Receiver, 1000, NULL, 2, NULL );/* 启动调度器 */vTaskStartScheduler();}else{/* 无法创建队列 */}/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */return 0;
}发送任务的函数中不断往队列中写入数值代码如下 
static void vSenderTask( void *pvParameters )
{
int32_t lValueToSend;
BaseType_t xStatus;
/* 我们会使用这个函数创建2个任务
* 这些任务的pvParameters不一样
*/
lValueToSend  ( int32_t ) pvParameters;
/* 无限循环 */for( ;; ){/* 写队列* xQueue: 写哪个队列* lValueToSend: 写什么数据? 传入数据的地址, 会从这个地址把数据复制进队列* 0: 不阻塞, 如果队列满的话, 写入失败, 立刻返回*/xStatus  xQueueSendToBack( xQueue, lValueToSend, 0 );if( xStatus ! pdPASS ){printf( Could not send to the queue.\r\n );}}
}接收任务的函数中读取队列、判断返回值、打印代码如下 
static void vReceiverTask( void *pvParameters )
{
/* 读取队列时, 用这个变量来存放数据 */
int32_t lReceivedValue;
BaseType_t xStatus;
const TickType_t xTicksToWait  pdMS_TO_TICKS( 100UL );
/* 无限循环 */for( ;; ){/* 读队列* xQueue: 读哪个队列* lReceivedValue: 读到的数据复制到这个地址* xTicksToWait: 如果队列为空, 阻塞一会*/xStatus  xQueueReceive( xQueue, lReceivedValue, xTicksToWait );if( xStatus  pdPASS ){/* 读到了数据 */printf( Received  %d\r\n, lReceivedValue );}else{/* 没读到数据 */printf( Could not receive from the queue.\r\n );}}
}程序运行结果如下 任务调度情况如下图所示 3.2 示例: 分辨数据源 
当有多个发送任务通过同一个队列发出数据接收任务如何分辨数据来源数据本身带有来源信 息比如写入队列的数据是一个结构体结构体中的lDataSouceID用来表示数据来源 
typedef struct {
ID_t eDataID;
int32_t lDataValue;
}Data_t;不同的发送任务先构造好结构体填入自己的eDataID 再写队列接收任务读出数据后根据 eDataID 就可以知道数据来源了如下图所示 
CAN任务发送的数据eDataIDeMotorSpeedHMI任务发送的数据eDataIDeSpeedSetPoint 程序会创建一个队列然后创建2个发送任务、1个接收任务 
创建的队列用来发送结构体数据大小是结构体的大小发送任务优先级为2分别往队列中写入自己的结构体结构体中会标明数据来源接收任务优先级为1读队列、根据数据来源打印信息 
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任务发送的数据 */
};/* vSenderTask被用来创建2个任务用于写队列* vReceiverTask被用来创建1个任务用于读队列*/
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 ){/* 创建2个任务用于写队列, 传入的参数是不同的结构体地址* 任务函数会连续执行向队列发送结构体* 优先级为2*/xTaskCreate( vSenderTask, CAN Task, 1000, ( void * ) ( xStructsToSend[ 0 ] ), 2, NULL );xTaskCreate( vSenderTask, HMI Task, 1000, ( void * ) ( xStructsToSend[ 1 ] ), 2, NULL );/* 创建1个任务用于读队列* 优先级为1, 低于上面的两个任务* 这意味着发送任务优先写队列队列常常是满的状态*/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( ;; ){/* 写队列* xQueue: 写哪个队列* pvParameters: 写什么数据? 传入数据的地址, 会从这个地址把数据复制进队列* xTicksToWait: 如果队列满的话, 阻塞一会*/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( ;; ){/* 读队列* xQueue: 读哪个队列* xReceivedStructure: 读到的数据复制到这个地址* 0: 没有数据就即刻返回不阻塞*/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 );}}
}运行结果如下 任务调度情况如下图所示 
t1HMI是最后创建的最高优先级任务它先执行一下子向队列写入5个数据把队列都写满了t2队列已经满了HMI任务再发起第6次写操作时进入阻塞状态。这时CAN任务是最高优先级 的就绪态任务它开始执行t3CAN任务发现队列已经满了进入阻塞状态接收任务变为最高优先级的就绪态任务它开始 运行t4现在HMI任务、CAN任务的优先级都比接收任务高它们都在等待队列有空闲的空间一旦 接收任务读出1个数据会马上被抢占。被谁抢占谁等待最久HMI任务所以在t4时刻切换 到HMI任务。t5HMI任务向队列写入第6个数据然后再次阻塞这是CAN任务已经阻塞很久了。接收任务变 为最高优先级的就绪态任务开始执行。t6现在HMI任务、CAN任务的优先级都比接收任务高它们都在等待队列有空闲的空间一旦 接收任务读出1个数据会马上被抢占。被谁抢占谁等待最久CAN任务所以在t6时刻切换 到CAN任务。t7CAN任务向队列写入数据因为仅仅有一个空间供写入所以它马上再次进入阻塞状态。这时 HMI任务、CAN任务都在等待空闲空间只有接收任务可以继续执行。  
3.3 示例: 传输大块数据 
FreeRTOS的队列使用拷贝传输也就是要传输uint32_t时把4字节的数据拷贝进队列要传输一个8 字节的结构体时把8字节的数据拷贝进队列。 
如果要传输1000字节的结构体呢写队列时拷贝1000字节读队列时再拷贝1000字节不建议这么 做影响效率 
这时候我们要传输的是这个巨大结构体的地址把它的地址写入队列对方从队列得到这个地址使 用地址去访问那1000字节的数据。 
使用地址来间接传输数据时这些数据放在RAM里对于这块RAM要保证这几点 RAM的所有者、操作者必须清晰明了 这块内存就被称为共享内存。要确保不能同时修改RAM。比如在写队列之前只有由发送者修 改这块RAM在读队列之后只能由接收者访问这块RAM。  RAM要保持可用 这块RAM应该是全局变量或者是动态分配的内存。对于动然分配的内存要确保它不能提前释 放要等到接收者用完后再释放。另外不能是局部变量。  
程序会创建一个队列然后创建1个发送任务、1个接收任务 
创建的队列长度为1用来传输char *指针发送任务优先级为1在字符数组中写好数据后把它的地址写入队列接收任务优先级为2读队列得到char *值把它打印出来 
这个程序故意设置接收任务的优先级更高在它访问数组的过程中接收任务无法执行、无法写这个数 组。 
main 函数中创建了队列、创建了发送任务、接收任务代码如下 
/* 定义一个字符数组 */
static char pcBuffer[100];/* vSenderTask被用来创建2个任务用于写队列* vReceiverTask被用来创建1个任务用于读队列*/
static void vSenderTask( void *pvParameters );
static void vReceiverTask( void *pvParameters );/*-----------------------------------------------------------*//* 队列句柄, 创建队列时会设置这个变量 */
QueueHandle_t xQueue;int main( void )
{prvSetupHardware();/* 创建队列: 长度为1数据大小为4字节(存放一个char指针) */xQueue  xQueueCreate( 1, sizeof(char *) );if( xQueue ! NULL ){/* 创建1个任务用于写队列* 任务函数会连续执行构造buffer数据把buffer地址写入队列* 优先级为1*/xTaskCreate( vSenderTask, Sender, 1000, NULL, 1, NULL );/* 创建1个任务用于读队列* 优先级为2, 高于上面的两个任务* 这意味着读队列得到buffer地址后本任务使用buffer时不会被打断*/xTaskCreate( vReceiverTask, Receiver, 1000, NULL, 2, NULL );/* 启动调度器 */vTaskStartScheduler();}else{/* 无法创建队列 */}/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */return 0;
}发送任务的函数中现在全局大数组pcBuffer中构造数据然后把它的地址写入队列代码如下 
static void vSenderTask( void *pvParameters )
{BaseType_t xStatus;static int cnt  0;char *buffer;/* 无限循环 */for( ;; ){sprintf(pcBuffer, www.100ask.net Msg %d\r\n, cnt);buffer  pcBuffer; // buffer变量等于数组的地址, 下面要把这个地址写入队列/* 写队列* xQueue: 写哪个队列* pvParameters: 写什么数据? 传入数据的地址, 会从这个地址把数据复制进队列* 0: 如果队列满的话, 即刻返回*/xStatus  xQueueSendToBack( xQueue, buffer, 0 ); /* 只需要写入4字节, 无需写入整个buffer */if( xStatus ! pdPASS ){printf( Could not send to the queue.\r\n );}}
}接收任务的函数中读取队列、得到buffer的地址、打印代码如下 
static void vReceiverTask( void *pvParameters )
{/* 读取队列时, 用这个变量来存放数据 */char *buffer;const TickType_t xTicksToWait  pdMS_TO_TICKS( 100UL );	BaseType_t xStatus;/* 无限循环 */for( ;; ){/* 读队列* xQueue: 读哪个队列* xReceivedStructure: 读到的数据复制到这个地址* xTicksToWait: 没有数据就阻塞一会*/xStatus  xQueueReceive( xQueue, buffer, xTicksToWait);if( xStatus  pdPASS ){/* 读到了数据 */printf(Get: %s, buffer);}else{/* 没读到数据 */printf( Could not receive from the queue.\r\n );}}
}运行结果如下图所示 3.4 : 邮箱(Mailbox) 
FreeRTOS的邮箱概念跟别的RTOS不一样这里的邮箱称为橱窗也许更恰当 
它是一个队列队列长度只有1写邮箱新数据覆盖旧数据在任务中使用xQueueOverwrite() 在中断中使用  xQueueOverwriteFromISR()。 既然是覆盖那么无论邮箱中是否有数据这些函数总能成功写入数据。读邮箱读数据时数据不会被移除在任务中使用xQueuePeek() 在中断中使用 xQueuePeekFromISR()。 这意味着第一次调用时会因为无数据而阻塞一旦曾经写入数据以后读邮箱时总能成功。 
main函数中创建了队列(队列长度为1)、创建了发送任务、接收任务 
发送任务的优先级为2它先执行接收任务的优先级为1 
代码如下 
/* 队列句柄, 创建队列时会设置这个变量 */
QueueHandle_t xQueue;int main( void )
{prvSetupHardware();/* 创建队列: 长度为1数据大小为4字节(存放一个char指针) */xQueue  xQueueCreate( 1, sizeof(uint32_t) );if( xQueue ! NULL ){/* 创建1个任务用于写队列* 任务函数会连续执行构造buffer数据把buffer地址写入队列* 优先级为2*/xTaskCreate( vSenderTask, Sender, 1000, NULL, 2, NULL );/* 创建1个任务用于读队列* 优先级为1*/xTaskCreate( vReceiverTask, Receiver, 1000, NULL, 1, NULL );/* 启动调度器 */vTaskStartScheduler();}else{/* 无法创建队列 */}/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */return 0;
}发送任务、接收任务的代码和执行流程如下 
A发送任务先执行马上阻塞BC接收任务执行这是邮箱无数据打印Could not …。在发送任务阻塞过程中接收任务多 次执行、多次打印。D发送任务从阻塞状态退出立刻执行、写队列E发送任务再次阻塞FG、HI、……接收任务不断偷看邮箱得到同一个数据打印出多个Get: 0J发送任务从阻塞状态退出立刻执行、覆盖队列写入1K发送任务再次阻塞LM、……接收任务不断偷看邮箱得到同一个数据打印出多个Get: 1 运行结果如下图所示 四、队列集 
队列知道是什么那么队列集呢没错就是套娃队列集里面是队列。 
队列集使用的前提 configUSE_QUEUE_SETS 置1  
int main( void )
{TaskHandle_t xHandleTask1;#ifdef DEBUGdebug();
#endifprvSetupHardware();printf(Hello, world!\r\n);/* 1. 创建2个queue */xQueueHandle1  xQueueCreate(2, sizeof(int));if (xQueueHandle1  NULL){printf(can not create queue\r\n);}xQueueHandle2  xQueueCreate(2, sizeof(int));if (xQueueHandle2  NULL){printf(can not create queue\r\n);}/* 2. 创建queue set */xQueueSet  xQueueCreateSet(3);/* 3. 把2个queue添加进queue set */xQueueAddToSet(xQueueHandle1, xQueueSet);xQueueAddToSet(xQueueHandle2, xQueueSet);/* 4. 创建3个任务 */xTaskCreate(Task1Function, Task1, 100, NULL, 1, xHandleTask1);xTaskCreate(Task2Function, Task2, 100, NULL, 1, NULL);xTaskCreate(Task3Function, Task3, 100, NULL, 1, NULL);/* Start the scheduler. */vTaskStartScheduler();/* Will only get here if there was not enough heap space to create theidle task. */return 0;
}void Task1Function(void * param)
{int i  0;while (1){xQueueSend(xQueueHandle1, i, portMAX_DELAY);i;vTaskDelay(10);}
}void Task2Function(void * param)
{int i  -1;while (1){xQueueSend(xQueueHandle2, i, portMAX_DELAY);i--;vTaskDelay(20);}
}void Task3Function(void * param)
{QueueSetMemberHandle_t handle;int i;while (1){/* 1. read queue set: which queue has data */handle  xQueueSelectFromSet(xQueueSet, portMAX_DELAY);/* 2. read queue */xQueueReceive(handle, i, 0);/* 3. print */printf(get data : %d\r\n, i);}
}我们把队列1 队列2 添加到queue set里面 
队列1 和队列2 分别再任务1和任务2里面发送数据 
队列集再任务三里面接收数据 
效果 文章是自己总结而记录有些知识点没说明白的请各位看官多多提意见多多交流欢迎大家留言 如果技术交流可以加以下群方便沟通 QQ群370278903 点击链接加入群聊【蜡笔小芯的嵌入式交流群】