欢迎光临
我们一直在努力

UART+ DMA +FIFO+ 空闲中断 (IDLE) + Queue接收数据

摘要:针对STM32串口高波特率下CPU占用率高的问题,提出DMA+空闲中断+消息队列组合方案。DMA负责数据搬运降低CPU负载,空闲中断检测数据包结束,通过消息队列通知任务处理数据。方案实现了硬件自动搬运、实时响应和任务解耦,特别适合FIFO深度较浅的芯片。工作流程包括DMA初始化、自动搬运、空闲中断触发、队列通知和任务处理五个阶段,通过中断服务程序和主任务协同完成高效数据接收与处理。

目录

一、为什么使用这套组合?

二、这个方案的完整工作流

1.准备阶段:

2.数据到来:

3.传输结束:

4.中断触发:

5.任务处理:

三、代码逻辑

1.中断处理函数

2.主任务


一、为什么使用这套组合?

方案 痛点
仅用中断 (RXNE) 每收 1 字节进出一次中断,波特率高了 CPU 占用率暴涨(都在干重复活)。
仅用 DMA DMA 像个机器人,只管搬运。它不知道对方发完了没有,无法实现实时处理。
DMA + 空闲中断 (IDLE) 完美。 数据由 DMA 搬运(省 CPU),对方发完一串后触发空闲中断(保实时性)。
加入消息队列 (RTOS) 解耦。 中断只负责发信号,复杂的数据解析交给低优先级任务,防止阻塞系统。

在硬件 FIFO 深度较浅的芯片(如 STM32F407 ),FIFO 通常是指通过软件实现的环形缓冲区(Ring Buffer)

二、这个方案的完整工作流

在 STM32F407 上,一个典型的接收流程如下:

1.准备阶段:

开辟一个数组作为 DMA 接收区,开启串口的 DMA 传输和 IDLE 中断。

2.数据到来:

数据直接由 DMA 从串口引脚搬运到内存,CPU 此时可以去处理显示屏、电机控制或其他任务,完全不参与搬运。

3.传输结束:

对方停止发送,串口总线空闲超过特定时间,硬件会自动把 SR 寄存器里的 IDLE 位置 1。

4.中断触发:

  • 触发 USARTx_IRQHandler。
  • 在中断里读取 DMA 寄存器,算出这波到底收了多少字节。
  • 发送队列信号:将“数据长度”或“数据就绪标志”塞进队列。
  • 5.任务处理:

    主任务收到队列消息,从 DMA 缓冲区(或你拷贝出来的 Ring Buffer)里读取并处理。

    当外部数据传入时,每个接收到的字节都会通过 DMA 自动传输到 rx_buffer 缓冲区。DMA 内部配置的 CNDTR 寄存器在每次数据传输完成后自动递减 1。DMA_BufferSize初始值可以自己设置

    三、代码逻辑

    1.中断处理函数

    void USART1_IRQHandler(void) {
    if (USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) {
    // 1. 必须软件清除 IDLE 标志位(F4 的规则是读 SR 再读 DR)
    volatile uint32_t temp;
    temp = USART1->SR;
    temp = USART1->DR;

    // 2. 停止 DMA 搬运,计算收到了多少字节
    // 剩余量 = DMA_GetCurrentMemoryTarget(DMA2_Stream2); // 或是读 CNDTR
    // 接收长度 = 总大小 – 剩余大小
    uint16_t rx_len = RX_BUF_SIZE – DMA_GetCurrDataCounter(DMA2_Stream2);

    // 3. 发送消息队列通知任务
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    // 注意:这里我们可以把长度 rx_len 直接发过去
    xQueueSendFromISR(uart_event_queue, &rx_len, &xHigherPriorityTaskWoken);

    // 4. 重置 DMA 计数器,准备迎接下一波
    DMA_Cmd(DMA2_Stream2, DISABLE);
    DMA_SetCurrDataCounter(DMA2_Stream2, RX_BUF_SIZE);
    DMA_Cmd(DMA2_Stream2, ENABLE);

    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
    }

    2.主任务

    将缓冲区数据存储到比较安全的位置

    void vUartTask(void *pvParameters) {
    uint16_t packet_len;
    while(1) {
    // 阻塞等待“门铃”响,拿到的是数据长度
    if (xQueueReceive(uart_event_queue, &packet_len, portMAX_DELAY)) {
    // 直接处理 rx_buffer 里的前 packet_len 个字节
    // 或者将其拷贝到你的 Ring Buffer 进行解析
    Parse_Protocol(rx_buffer, packet_len);
    }
    }
    }

    赞(0)
    未经允许不得转载:171主机测评 » UART+ DMA +FIFO+ 空闲中断 (IDLE) + Queue接收数据
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址