FreeRTOS在中断服务例程(ISR)中使用队列
在 FreeRTOS 中,中断服务例程(ISR)中使用队列是一种常见的任务间通信方式。FreeRTOS 提供了专门的中断安全队列操作函数,如 xQueueSendToFrontFromISR()
和 xQueueSendToBackFromISR()
,以确保在中断上下文中安全地操作队列。
使用步骤
1. 创建队列
在任务中创建队列,通常在系统初始化时完成。例如:
QueueHandle_t xQueue;
const UBaseType_t uxQueueLength = 10; // 队列可以存储10个数据项
const size_t uxItemSize = sizeof(数据类型); // 数据项的大小
xQueue = xQueueCreate(uxQueueLength, uxItemSize);
if (xQueue == NULL) {// 处理队列创建失败的情况
}
2. 在中断服务例程中发送数据
在 ISR 中使用 xQueueSendToFrontFromISR()
或 xQueueSendToBackFromISR()
向队列发送数据。这些函数的原型如下:
BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken);
BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken);
xQueue
:队列的句柄。pvItemToQueue
:指向要发送的数据项的指针。pxHigherPriorityTaskWoken
:指向一个变量的指针,用于指示是否有更高优先级的任务被唤醒。
3. 示例代码
以下是一个使用 xQueueSendToBackFromISR()
的示例:
void vUARTInterruptHandler(void) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;char cReceivedChar;// 读取UART数据cReceivedChar = UART_DR;// 发送到队列if (xQueueSendToBackFromISR(xQueue, &cReceivedChar, &xHigherPriorityTaskWoken) != pdPASS) {// 队列已满,处理错误}// 如果需要,进行上下文切换portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
4. 在任务中接收数据
在任务中使用 xQueueReceive()
从队列中接收数据:
void vTaskFunction(void *pvParameters) {char cReceivedChar;while (1) {if (xQueueReceive(xQueue, &cReceivedChar, portMAX_DELAY) == pdTRUE) {// 处理接收到的数据printf("Received: %c\n", cReceivedChar);}}
}
完整代码
#include "FreeRTOS.h"
#include "queue.h"
#include "task.h"
#include "semphr.h"
#include "stm32f10x.h" // 假设使用 STM32F10x 系列微控制器// 队列句柄
QueueHandle_t xQueue;// UART 接收中断服务例程
void USART1_IRQHandler(void) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;char cReceivedChar;// 检查是否接收到数据if (USART1->SR & USART_SR_RXNE) {cReceivedChar = (char)(USART1->DR & (uint8_t)0x00FF); // 读取接收到的字符// 将接收到的字符发送到队列if (xQueueSendToBackFromISR(xQueue, &cReceivedChar, &xHigherPriorityTaskWoken) != pdPASS) {// 队列已满,处理错误}// 如果有更高优先级的任务就绪,请求上下文切换portYIELD_FROM_ISR(xHigherPriorityTaskWoken);}
}// 任务函数,用于处理队列中的数据
void vTaskFunction(void *pvParameters) {char cReceivedChar;while (1) {if (xQueueReceive(xQueue, &cReceivedChar, portMAX_DELAY) == pdTRUE) {// 处理接收到的数据printf("Received: %c\n", cReceivedChar);}}
}// 主函数
int main(void) {// 系统初始化SystemInit();// 初始化 UART1// 假设已经配置了 UART1 的波特率、数据位、停止位等参数USART1->CR1 |= USART_CR1_RE | USART_CR1_TE | USART_CR1_RXNEIE; // 启用接收、发送和接收中断USART1->CR2 = 0; // 默认配置USART1->CR3 = 0; // 默认配置USART1->BRR = 0x00001D4C; // 设置波特率为 9600USART1->CR1 |= USART_CR1_UE; // 启用 UART1// 创建队列xQueue = xQueueCreate(10, sizeof(char)); // 创建一个可以存储10个字符的队列if (xQueue == NULL) {// 队列创建失败,处理错误while (1);}// 创建任务xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);// 启动调度器vTaskStartScheduler();// 如果调度器启动成功,不会执行到这里while (1);
}
注意事项
- 中断优先级:确保中断优先级低于 FreeRTOS 内核中断优先级(如 PendSV 和 SysTick),以避免抢占内核任务。
- 队列已满:如果队列已满,发送操作会失败,返回
errQUEUE_FULL
。需要合理管理队列大小,避免数据丢失。 - 上下文切换:如果发送操作导致更高优先级的任务就绪,
pxHigherPriorityTaskWoken
会被设置为pdTRUE
,此时需要在 ISR 退出前调用portYIELD_FROM_ISR()
。
通过以上步骤和注意事项,可以在 FreeRTOS 中安全地在中断服务例程中使用队列进行任务间通信。