STM32 使用 DMA+双缓冲串口接收不定长数据时,如何避免缓存切换瞬间丢字节?

· 2 浏览

回答(4)

注意DMA配置时设置MSIZE和PSIZE一致,避免字节错位。另外,若使用空闲中断,需确保USART_CR1的IDLEIE使能,并在中断里清除IDLE标志(读SR再读DR),否则会反复触发。
硬件工程师阿强 · 2026-08-27
可以加一个硬件FIFO(如UART的FIFO)或使用双DMA通道,一个接收一个搬运,但STM32资源有限。更简单:在IDLE中断里用DMA的CNDTR计算长度,然后立即把DMA的M0AR改为新地址,无需关中断,因为DMA切换是原子的。
码农小刘 · 2026-08-27
用循环模式+DMA传输完成中断(TC)来分割数据包,比IDLE更可靠。在TC中断中记录当前NDTR,切换解析缓冲区,但注意TC在循环模式下每次半满/全满触发,需配合超时判断。
嵌入式老张 · 2026-08-27
核心思路是让DMA始终处于接收状态,用空闲中断(IDLE)配合双缓冲乒乓切换,并在切换瞬间做原子操作。具体做法:1)配置两个缓冲区buf1/buf2,DMA循环模式(Circular)或正常模式,但用NDTR寄存器判断当前写入位置;2)在IDLE中断中,先读取DMA的CNDTR值,确定已接收数据长度,然后立即切换DMA目标地址到另一缓冲区(若用循环模式则无需切换地址,只需记录边界),同时用临界区(如关闭全局中断)保护切换过程,防止新数据写入旧缓冲区;3)切换后,将旧缓冲区数据交给协议解析,并重新使能DMA(若用正常模式需重设NDTR)。关键点:避免在IDLE中断中做耗时操作,切换动作要快(几条指令),且确保DMA在切换期间不丢失数据——若用循环模式,DMA会继续写入当前缓冲区,切换地址后新数据进入新缓冲区,旧数据已锁定。实测建议:使用HAL库时,在IDLE回调中先__disable_irq(),改DMA配置,再__enable_irq(),并配合内存屏障。
mcuku 阿沐 · 2026-08-27

🧰 配套工具

🔗 波特率计算器