FreeRTOS Queue消息队列-笔记
一、进程间通信(IPC)概述
1. 进程的定义
在使用RTOS时有多个任务,还可以有多个中断的ISR。任务和ISR可以统称为进程。
任务与任务之间或者任务与ISR之间有时候需要进行通信或者同步,这称为进程间通信。
总结就是在FreeRTOS中,任务(Tasks)和中断服务程序(ISRs)统称为进程。多任务系统中,进程之间常需要数据传递和状态同步
例如:
在实际的ADC连续采集中,一般使用双缓中区,一个缓冲区存满之后用于读取和处理,而另一个缓冲区继续用于保存ADC转换结果数据。两个缓冲区交替使用以保证采集和处理的连续性。
进程间通信就是ADC中断ISR与数据处理任务之间的通信,在ADC中断ISR向缓冲区写入数据后,如果发现缓冲区满了,就可以发出一个标志信号,通知数据处理任务一直在阻塞状态下,等待这个信号的数据处理任务就可以退出。阻塞状态被调度为运行状态后,就可以及时的读取缓冲区的数据并进行处理。
2. 进程间通信的实现方式
FreeRTOS提供以下机制:
补充一个是在事件组后面:任务通知task notification。使用任务通知不需要创建任何的中间对象,可以直接从任务向任务或者从ISR向任务发送通知,传递一个通知值。任务通知可以模拟二值信号量、计数信号量或者长度为一的消息队列。使用任务通知通常效率更高,消耗内存更少好。
二、队列的核心概念与作用
1. 队列的作用
- 数据缓冲:解决生产者-消费者问题(如ADC采样与数据处理的速率不匹配)。
- 任务同步:通过阻塞等待数据,避免忙等待浪费CPU资源。
- 优先级处理:支持FIFO或优先级插入。
2. 双缓冲区在ADC采样中的应用
- 双缓冲区工作原理:
- 一个缓冲区用于采集数据,另一个用于处理数据。
- 缓冲区满时通过队列通知任务处理,切换缓冲区继续采集。
- 队列的作用:在ADC中断与数据处理任务之间传递缓冲区切换信号。
三、队列的创建
在FreeRTOS创建对象,如任务队列、信号量等,都有静态分配内存和动态分配内存两种方式。
函数原型
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
#endif
xQueueCreate实际上是一个宏函数,它调用的函数xQueueGenericCreate,这个函数是创建队列、信号量、互斥量等对象的通用函数。
QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength,UBaseType_t uxItemSize);
四、队列的发送与接收操作
1. 向队列发送数据
可以把数据写到队列头部,也可以写到尾部,这些函数有两个版本:在任务中使用、在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
);
这些函数用到的参数是类似的,统一说明如下:
2. 从队列接收数据
使用 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);
参数说明如下:
五、队列的典型应用场景
1. ADC连续采样与双缓冲区
代码示例
// 定义双缓冲区
#define BUFFER_SIZE 100
uint16_t buffer1[BUFFER_SIZE];
uint16_t buffer2[BUFFER_SIZE];
// 队列用于缓冲区切换信号
QueueHandle_t xADCQueue = xQueueCreate(1, sizeof(uint16_t*));
// ADC中断服务程序
void ADC_IRQHandler(void) {
static uint16_t *currentBuffer = buffer1;
static uint16_t index = 0;
// 读取ADC数据
uint16_t data = ADC_GetValue();
// 写入当前缓冲区
currentBuffer[index++] = data;
// 判断是否填满缓冲区
if (index >= BUFFER_SIZE) {
index = 0;
// 切换缓冲区
if (currentBuffer == buffer1) {
currentBuffer = buffer2;
} else {
currentBuffer = buffer1;
}
// 通过队列通知任务处理数据
xQueueSendToBackFromISR(xADCQueue, ¤tBuffer, NULL);
}
}
// 数据处理任务
void vDataProcessingTask(void *pvParameters) {
uint16_t *pBuffer;
while (1) {
// 等待缓冲区切换信号
xQueueReceive(xADCQueue, &pBuffer, portMAX_DELAY);
// 处理缓冲区数据
ProcessData(pBuffer);
}
}
六、任务通知(Task Notification)的替代方案
1. 任务通知的优势
- 无需创建中间对象:直接从ISR或任务向任务发送通知。
- 内存占用低:每个任务自带一个通知值,无需额外内存。
- 效率更高:减少队列操作的开销。
2. 任务通知的使用
从ISR发送通知
BaseType_t xTaskNotifyFromISR(
TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction,
BaseType_t *pxHigherPriorityTaskWoken
);
从任务接收通知
BaseType_t xTaskNotifyWait(
uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue,
TickType_t xTicksToWait
);
示例:ADC中断通知任务
// 中断中发送通知
void ADC_IRQHandler(void) {
xTaskNotifyFromISR(xDataProcessingTask, 0, eSetValueWithOverwrite, NULL);
}
// 任务中等待通知
void vDataProcessingTask(void *pvParameters) {
while (1) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
// 处理ADC数据
}
}
功能 | 关键函数 | 适用场景 |
---|---|---|
队列创建 | xQueueCreate() / xQueueCreateStatic() |
数据缓冲、任务同步 |
队列发送 | xQueueSend() / xQueueSendToBackFromISR() |
任务与ISR间的数据传递 |
队列接收 | xQueueReceive() / xQueueReceiveFromISR() |
任务等待数据处理 |
任务通知 | xTaskNotifyFromISR() / xTaskNotifyWait() |
轻量级同步替代方案 |