英飞凌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
相关产品推荐
相关产品推荐

