核心思路是让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