前言
本系列基于stm32
系列单片机来使用freerots
FreeRTOS是一个广泛使用的开源实时操作系统(RTOS),它提供了丰富的功能和特性,使嵌入式系统的开发更加简单和高效。队列是FreeRTOS中常用的一种通信机制,它用于在任务之间传递数据。
一、队列是什么?
队列是一种先进先出(FIFO)的数据结构,允许任务将数据项放入队列的末尾,并从队列的前端取出数据项。队列提供了一种线程安全的方式来实现任务之间的数据传输,尤其适用于生产者-消费者模式的应用。
二、队列的操作
1. xQueueCreate():
函数原型: QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength, UBaseType_t uxItemSize);
作用: 创建一个队列并返回队列的句柄。
参数:
uxQueueLength: 队列中的最大项数,即队列的深度。
uxItemSize: 单个队列项的大小(以字节为单位)。
2. xQueueCreate():
函数原型: BaseType_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);
作用: 将一个数据项发送到队列中。
参数:
xQueue: 队列的句柄。
pvItemToQueue: 要发送的数据项的指针。
xTicksToWait: 阻塞等待的时间,如果队列已满,将会等待指定的时间,直到队列有空间可用。
3. xQueueReceive():
函数原型: BaseType_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait);
作用: 从队列中接收一个数据项。
参数:
xQueue: 队列的句柄。
pvBuffer: 接收数据项的缓冲区指针。
xTicksToWait: 阻塞等待的时间,如果队列为空,将会等待指定的时间,直到队列中有数据可用。
4. uxQueueMessagesWaiting():
函数原型: UBaseType_t uxQueueMessagesWaiting(const QueueHandle_t xQueue);
作用: 获取队列中当前等待处理的数据项数量。
参数:
xQueue: 队列的句柄。
5. vQueueDelete():
vQueueDelete():
函数原型: void vQueueDelete(QueueHandle_t xQueue);
作用: 删除一个已创建的队列及其相关资源。
参数:
xQueue: 队列的句柄。
6.xQueueCreateStatic()
作用:静态分配queue内存
函数原型:QueueHandle_t xQueueCreateStatic( UBaseType_t uxQueueLength, UBaseType_t uxItemSize, uint8_t *pucQueueStorageBuffer, StaticQueue_t *pxQueueBuffer );
参数:uxQueueLength 队列长度,最多能存放多少个数据(item)
uxItemSize 每个数据(item)的大小:以字节为单位
pucQueueStorageBuffer
如果uxItemSize非0,pucQueueStorageBuffer必须指向一个
uint8_t数组,
此数组大小至少为"uxQueueLength * uxItemSize"
pxQueueBuffer 必须执行一个StaticQueue_t结构体,用来保存队列的数据结构
返回值 非0:成功,返回句柄,以后使用句柄来操作队列
NULL:失败,因为pxQueueBuffer为NULL
7.xQueueReset();
作用:队列刚被创建时,里面没有数据;使用过程中可以调用 xQueueReset() 把队列恢复为初始状态
函数原型:BaseType_t xQueueReset( QueueHandle_t pxQueue);
参数为要复位的队列。
8.xQueueSendToBack()
函数原型:BaseType_t xQueueSendToBack( QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait );
作用:往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait。
参数为要写的队列,数据,等待的时间
8.xQueueSendToBackFromISR()
函数原型:BaseType_t xQueueSendToBackFromISR( QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken );
作用:往队列尾部写入数据,此函数可以在中断函数中使用,不可阻塞
9.xQueueSendToFront()
作用:往队列头部写入数据,如果没有空间,阻塞时间为xTicksToWait
函数原型:BaseType_t xQueueSendToFront( QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait );
10.xQueueSendToFrontFromISR()
作用:往队列头部写入数据,此函数可以在中断函数中使用,不可阻塞
函数原型:BaseType_t xQueueSendToFrontFromISR( QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken );
11.xQueueReceiveFromISR()
作用:读函数,此函数可以在中断内使用,不可阻塞
函数原型:BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxTaskWoken );
参数为要读的队列,存储的位置,一个指向 BaseType_t 变量的指针,在正常情况下被置为 pdFALSE。它用于通知内核是否有任务的优先级因为该操作而发生了改变。
12.uxQueueSpacesAvailable()
作用: 返回队列中可用空间的个数
函数原型:UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );
参数为要查询的队列
13.xQueueOverwrite()与xQueueOverwriteFromISR()
作用:当队列满时,这些函数会覆盖里面的数据,这也意为着这些函数不会被阻塞。
函数原型:
/* 覆盖队列 * 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 );
14.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, );
二、示例代码
下面是一个简单的示例代码,演示了如何使用FreeRTOS队列实现一个生产者任务和一个消费者任务之间的数据传输:
// 创建一个队列句柄 QueueHandle_t xQueue; void vProducerTask(void *pvParameters) { int data = 0; while (1) { // 生产数据 data = generateData(); // 发送数据到队列 if (xQueueSend(xQueue, &data, portMAX_DELAY) != pdPASS) { // 处理发送失败的情况 } // 延时,模拟生产速度 vTaskDelay(pdMS_TO_TICKS(1000)); } } void vConsumerTask(void *pvParameters) { int receivedData; while (1) { // 从队列接收数据 if (xQueueReceive(xQueue, &receivedData, portMAX_DELAY) == pdPASS) { // 处理接收到的数据 processData(receivedData); } } } int main() { // 创建队列,队列长度为5,每个数据项的大小为sizeof(int) xQueue = xQueueCreate(5, sizeof(int)); // 创建生产者任务 xTaskCreate(vProducerTask, "Producer", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL); // 创建消费者任务 xTaskCreate(vConsumerTask, "Consumer", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL); // 启动调度器 vTaskStartScheduler(); return 0; }
总结
队列是FreeRTOS中一种有用的通信机制,可用于在任务之间传递数据。通过使用队列,我们可以实现任务之间的数据共享和同步。在使用队列时,需要注意队列的深度和每个数据项的大小,并选择适当的阻塞等待时间以避免资源争用和任务饥饿等问题。
在后面我们还将介绍到信号量,互斥锁,任务通知等任务的通信机制,他们都是为了解决任务的通信研究出来的技术和算法。