FreeRTOS Queue消息队列-笔记
FreeRTOS Queue消息队列-笔记
- **一、进程间通信(IPC)概述**
- 1. **进程的定义**
- 2. **进程间通信的实现方式**
- **二、队列的核心概念与作用**
- 1. **队列的作用**
- 2. **双缓冲区在ADC采样中的应用**
- **三、队列的创建**
- **四、队列的发送与接收操作**
- 1. **向队列发送数据**
- 2. **从队列接收数据**
- **五、队列的典型应用场景**
- 1. **ADC连续采样与双缓冲区**
- **代码示例**
- **六、任务通知(Task Notification)的替代方案**
- 1. **任务通知的优势**
- 2. **任务通知的使用**
- **从ISR发送通知**
- **从任务接收通知**
- **示例:ADC中断通知任务**
一、进程间通信(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() | 轻量级同步替代方案 |