CommTask 异步调度

Babel36acl 嵌入式实战 无~ 6 次阅读 预计阅读时间: 6 分钟 发布于 1 天前 最后更新于 1 小时前 1412 字


九、UART 错误恢复流程

CommTask 的 app_main_consume_uart_recovery_flags() 消费来自 ISR 的位图(HAL_UART_ErrorCallback 在中断中设置 s_uart_recovery_needed)。三种错误类型:overrun error(溢出)、noise error(噪声)、framing error(帧错误)。

flowchart TB
    subgraph ISR[ISR 中断]
        I1[UART RX]
        I2[设置任务通知位图]
    end
    subgraph COMM[CommTask]
        C1[等待通知]
        C2[解析帧]
        C3[分发到 ProtoTask]
    end
    subgraph PROTO[ProtoTask]
        P1[协议处理]
        P2[响应发送]
    end
    ISR -->|通知| COMM --> PROTO
    style ISR fill:transparent,stroke:#8dc7ff,color:#eaf4ff
    style COMM fill:transparent,stroke:#8dc7ff,color:#eaf4ff
    style PROTO fill:transparent,stroke:#8dc7ff,color:#eaf4ff

恢复调用链:HAL_UART_ErrorCallback (ISR) → set bit in s_uart_recovery_neededCommTaskapp_main_consume_uart_recovery_flags()bsp_uart_port_rx_restart()

bsp_uart_port_rx_restart() 执行:tail=0, started=0, 清零 DMA 缓冲 → HAL_UART_Receive_DMA() 重新启动。注意:正在进行的 TX DMA 不受影响,只重启 RX。

错误码 类型 恢复动作
HAL_UART_ERROR_ORE Overrun (溢出) 重启 RX DMA,丢弃溢出字节
HAL_UART_ERROR_NE Noise (噪声) 重启 RX DMA
HAL_UART_ERROR_FE Framing (帧错误) 重启 RX DMA

十、TX DMA 续传机制

bsp_uart_start_tx_dma() 每端口检查:busy==0 && 队列非空 → 最多复制 64 字节到线性 dma_buf → HAL_UART_Transmit_DMA()

DMA 完成回调(ISR)只做两件事:busy=0, kick_pending=1。下一周期 bsp_uart_task_once() 在任务上下文中启动下一段 DMA。这避免了 HAL_UART_Transmit_DMA() 的重入问题(嵌套调用返回 HAL_BUSY)。

完整的发送生命周期 ASCII 图:

Task 写队列 → DMA 发送第一段 → ISR 清标志 → bsp_uart_task_once 启动下一段 → ... → 队列空
TX 队列 (RingBuf)
      │
      ▼  (bsp_uart_port_transmit 写入)
 ┌─────────────┐
 │  队列非空?   │ ← bsp_uart_start_tx_dma 检查
 └──────┬──────┘
        │ yes
        ▼
 ┌─────────────────────┐
 │ 复制 ≤64B → dma_buf │
 │ HAL_UART_Transmit_DMA│
 └──────────┬──────────┘
            │
            ▼  (DMA 传输完成)
 ┌─────────────────────┐
 │ ISR: busy=0         │
 │      kick_pending=1 │
 └──────────┬──────────┘
            │
            ▼  (下一周期 bsp_uart_task_once)
 ┌─────────────────────┐
 │ 队列空? → 结束       │
 │ 非空   → 启动下一段   │
 └─────────────────────┘

十一、整包入队原子性保证

bsp_uart_port_transmit() 在入队任何字节前等待直到整个数据包能放进 TX 队列。防止半帧交织。

// 等待直到队列有足够空间容纳整包
while (ringbuf_available(tx_queue) < len) {
    // 让出 CPU,等待 DMA 消费
    taskYIELD();
}
// 整包一次性入队
ringbuf_write(tx_queue, data, len);
// 触发发送
bsp_uart_start_tx_dma(port);
此作者没有提供个人介绍。
最后更新于 2026-05-30