You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

英飞凌XMC47系列:基于DMA驱动UART接收未知长度数据

基于轮询空闲标志的DMA驱动UART可变Payload接收方案

针对你没有UART/DMA空闲线路检测中断、只能轮询PCR.RXIdle的场景,以下是几种可落地的解决方案:

方案1:DMA循环缓冲+定时轮询空闲标志

这是最通用的方案,核心是用DMA循环接收数据,配合定时器定时检测空闲标志来判断帧结束:

  • 配置DMA为循环模式,开辟足够大的接收缓冲区(需大于最大帧长度)
  • 开启DMA的半传输(HT)和传输完成(TC)中断,每次中断时记录当前DMA剩余传输字节数,计算已接收数据长度并暂存
  • 启动一个定时器,周期设置为小于协议帧间空闲时间、大于单字节传输时间(比如波特率115200时,单字节约86us,定时器周期可设为1ms),定时读取PCR.RXIdle
  • 检测到空闲时,立即停止DMA,处理剩余未解析的数据,之后重启DMA等待下一帧

伪代码示例

#define RX_BUF_MAX 256  // 缓冲区需大于最大帧长度
uint8_t rx_buf[RX_BUF_MAX];
volatile uint16_t last_processed_len = 0;

// DMA+UART初始化
void dma_uart_init(void) {
    // 配置UART启用DMA接收
    UART->CR |= UART_CR_DMAEN;
    // 配置DMA:循环模式、内存增量、外设固定、8位数据宽度
    DMA->CCR |= DMA_CCR_CIRC | DMA_CCR_MINC;
    DMA->CCR &= ~DMA_CCR_PINC;
    DMA->CNDTR = RX_BUF_MAX;
    DMA->CMAR = (uint32_t)rx_buf;
    DMA->CPAR = (uint32_t)&UART->DR;
    // 开启半传输、传输完成中断
    DMA->CCR |= DMA_CCR_HTIE | DMA_CCR_TCIE;
    DMA->CCR |= DMA_CCR_EN;

    // 配置定时器(以1ms周期为例)
    TIM->PSC = 7199;  // 假设系统时钟72MHz,分频后10kHz
    TIM->ARR = 9;     // 10kHz*10=1ms
    TIM->DIER |= TIM_DIER_UIE;
    TIM->CR1 |= TIM_CR1_CEN;
}

// DMA中断服务函数:处理半传输/传输完成的数据
void DMA_IRQHandler(void) {
    uint16_t remain_cnt = DMA->CNDTR;
    uint16_t total_received = RX_BUF_MAX - remain_cnt;

    if (DMA->ISR & DMA_ISR_HTIF) {
        DMA->IFCR |= DMA_IFCR_CHTIF;
        // 处理前半段缓冲区数据
        process_frame(&rx_buf[last_processed_len], (RX_BUF_MAX/2) - last_processed_len);
        last_processed_len = RX_BUF_MAX/2;
    }

    if (DMA->ISR & DMA_ISR_TCIF) {
        DMA->IFCR |= DMA_IFCR_CTCIF;
        // 处理后半段缓冲区数据
        process_frame(&rx_buf[last_processed_len], total_received - last_processed_len);
        last_processed_len = 0;
    }

    // 中断中顺便检查空闲标志,提前触发帧处理
    if (PCR->RXIdle) {
        trigger_idle_process();
    }
}

// 定时器中断服务函数:定时检测空闲标志
void TIM_IRQHandler(void) {
    if (TIM->SR & TIM_SR_UIF) {
        TIM->SR &= ~TIM_SR_UIF;
        if (PCR->RXIdle) {
            trigger_idle_process();
        }
    }
}

// 空闲检测触发的帧处理逻辑
void trigger_idle_process(void) {
    DMA->CCR &= ~DMA_CCR_EN;  // 停止DMA
    uint16_t remain_cnt = DMA->CNDTR;
    uint16_t total_received = RX_BUF_MAX - remain_cnt;
    // 处理剩余未解析的数据
    process_frame(&rx_buf[last_processed_len], total_received - last_processed_len);
    last_processed_len = 0;
    // 重启DMA
    DMA->CNDTR = RX_BUF_MAX;
    DMA->CCR |= DMA_CCR_EN;
}

// 协议解析函数:处理可变长度Payload
void process_frame(uint8_t* data, uint16_t len) {
    // 此处实现你的协议解析逻辑,比如从data中提取可变长度Payload
}

方案2:结合协议帧头/帧尾辅助判断

如果你的通信协议有固定帧头(如0xAA)或帧尾(如0x55),可以在DMA中断中实时解析缓冲区数据:

  • 无需依赖空闲标志,在DMA半传输/传输完成中断中遍历缓冲区,查找帧尾标志
  • 检测到帧尾时,立即停止DMA,提取从帧头到帧尾的完整数据进行处理
  • 处理完成后重置DMA和缓冲区,等待下一帧

优化技巧

  • 不要死循环轮询PCR.RXIdle,尽量在DMA中断、定时器中断或系统空闲任务(RTOS环境)中执行检测,减少CPU占用
  • 缓冲区大小要留有余量,避免连续帧传输时数据溢出
  • 若使用RTOS,可将空闲检测逻辑放在空闲钩子函数中,用信号量同步帧处理线程

内容的提问来源于stack exchange,提问作者IHK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 13:13:11