FreeRTOS同步与互斥:(一)队列

注:本文摘自韦东山《FreeRTOS完全开发手册,如有侵权请告知》

同步与互斥的概念:

  • 同步: A等待B做完某件事

  • 互斥: 某一资源同一时间仅能有一个用户访问

  • RTOS同步与互斥的方式: 任务通知(task notification)、队列(queue)、事件组(event group)、信号量(semaphoe)、互斥量(mutex)等。

一、队列(FIFO)

1.1 传输数据的方式

  • 拷贝:把数据、把变量的值复制进队列里
  • 引用:把数据、把变量的地址复制进队列里

FreeRTOS使用拷贝值的方法:

  • 局部变量的值可以发送到队列中,后续即使函数退出、局部变量被回收,也不会影响队列中的数据

  • 无需分配buffer来保存数据,队列中有buffer

  • 队列的空间有FreeRTOS内核分配,无需任务操心

  • 传输大数据时可以使用结构体传输

  • 对于有内存保护功能的系统,如果队列使用引用方法,必须确保双方任务对这个地址都有访问权限。

1.2 队列阻塞访问

任务读写队列时,如果读写不成功,则阻塞;可以指定超时时间。如果队列有数据(有空间)了,则该阻塞的任务会变为就绪态。如果一直都没有数据(没有空间),则时间到之后它也会进入就绪态。

如果存在多个任务阻塞读取(写)队列数据,那么:

  • 优先级高的队列先进入就绪态
  • 如果优先级相同,等待时间最久的进入就绪态

1.3 队列函数

使用队列的流程:创建队列、写队列、读队列、删除队列

1.3.1 创建

队列的创建有两种方法:动态分配内存、静态分配内存

  • 动态分配内存:xQueueCreate,队列的内存在函数内部动态分配
/* 创建队列
 * uxQueueLength: 队列长度,最多能存放多少个数据(item)
 * uxItemSize: 每个数据(item)的大小:以字节为单位
 * 返回值: 非0:成功,返回句柄,以后使用句柄来操作队列
 * 		  NULL:失败,因为内存不足
 */
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
  • 静态分配内存:xQueueCreateStatic,队列的内存要事先分配好
/* 创建队列
 * uxQueueLength: 队列长度,最多能存放多少个数据(item)
 * uxItemSize: 每个数据(item)的大小:以字节为单位
 * pucQueueStorageBuffer:如果uxItemSize非0,pucQueueStorageBuffer必须指向一个uint8_t数组,
 * 						  此数组大小至少为"uxQueueLength * uxItemSize"
 * pxQueueBuffer:必须指向一个StaticQueue_t结构体,用来保存队列的数据结构
 * 返回值: 非0:成功,返回句柄,以后使用句柄来操作队列
 * 		  NULL:失败,因为pxQueueBuffer为NULL
 */
QueueHandle_t xQueueCreateStatic(UBaseType_t uxQueueLength,
								 UBaseType_t uxItemSize,
								 uint8_t *pucQueueStorageBuffer,
								 StaticQueue_t *pxQueueBuffer);

1.3.2 复位

队列刚被创建时,里面没有数据;使用过程中可以调用 xQueueReset() 把队列恢复为初始状态

/* pxQueue : 复位哪个队列
 * 返回值: pdPASS(必定成功)
 */
BaseType_t xQueueReset(QueueHandle_t pxQueue);	

1.3.3 删除

删除队列的函数为 vQueueDelete() ,只能删除使用动态方法创建的队列,它会释放内存

/* xQueue : 删除哪个队列 */
void vQueueDelete( QueueHandle_t xQueue );

1.3.4 写队列

可以把数据写到队列头部,也可以写到尾部,这些函数有两个版本:在任务中使用、在ISR中使用。

/* 写队列
 * xQueue: 队列句柄,要写哪个队列
 * pvItemToQueue: 数据指针,这个数据的值会被复制进队列
 * xTicksToWait:xTicksToWait表示阻塞的最大时间(Tick Count)
 * 返回值: pdPASS:数据成功写入了队列
 * 		  errQUEUE_FULL:写入失败,因为队列满了
 */

/* 等同于xQueueSendToBack,往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait */
BaseType_t xQueueSend(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);

1.3.5 读队列

使用 xQueueReceive() 函数读队列,读到一个数据后,队列中该数据会被移除。这个函数有两个版
本:在任务中使用、在ISR中使用。

/* 读队列
 * xQueue: 队列句柄,要读哪个队列
 * pvBuffer: bufer指针,队列的数据会被复制到这个buffer
 * xTicksToWait:xTicksToWait表示阻塞的最大时间(Tick Count)
 * 返回值: pdPASS:从队列成功读出数据
 * 		  errQUEUE_EMPTY:读取失败,因为队列空了
 */
BaseType_t xQueueReceive(QueueHandle_t xQueue,
						 void * const pvBuffer,
						 TickType_t xTicksToWait );

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

1.3.6 查询

查询队列中有多少个数据、有多少空余空间

/* 返回队列中可用数据的个数 */
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );

/* 返回队列中可用空间的个数 */
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );

1.3.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);

1.4 队列集

当某个任务需要监听来自多个队列的数据时,可以使用队列集

  • 创建队列集
/* 创建队列集
 * uxEventQueueLength:队列集大小, =队列个数*size
 * 返回值:非NULL:成功,返回句柄
 *        NULL:失败,因为没有足够内存
 */
QueueSetHandke_t xQueueCreatSet(const UBaseType_t uxEventQueueLength);
  • 将队列加入队列集
/* 加入队列集
 * xQueueOrSemaphore:要添加的队列
 * xQueueSet:加入哪个队列集
 * 返回值:pdPASS表示成功, pdFAIL表示失败
 */
BaseType_t xQueueAddToSet(QueueSetMemberHandle_t xQueueOrSemaphore,
                         QueueSetHandle_t xQueueSet);
  • 从队列集中读取队列句柄
/* 从队列集中读取队列句柄
 * xQueueSet:目标队列集
 * xTicksToWait:等待时间
 * 返回值:非NULL:包含数据的队列句柄
 *        NULL:无法从队列集中读取句柄
 */
QueueSetMemberHandle_t xQueueSelectFromSet(QueueSetHandle_t xQueueSet,
                                          const TickType_t xTicksToWait);
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值