为什么搭载DMA的UART仅能接收到发送数据的最后一个字节?

上图为UARTEx_RxEventCallback()函数代码,该函数预期实现持续接收串口字节的功能。从Hercules界面可以看到,发送测试数据123456789后仅能接收到最后一个字节。DMA配置为普通模式(非循环模式)、外设到内存方向、字节宽度,已开启NVIC中断,配置符合官方规范。
上图为对应的主任务代码。
问题成因
- DMA普通模式单次传输后自动失能,未及时重启接收
普通模式下DMA完成一次预设长度的传输、或接收超时触发IDLE中断后,会自动关闭对应通道,不会自动启动下一次传输。你仅在初始化阶段调用了一次DMA接收启动函数,未在UARTEx_RxEventCallback()中处理完当前数据后重新启动DMA接收,后续串口数据无法写入DMA缓冲区,只能暂存于UART数据寄存器中,新数据到来后直接覆盖旧值,最终仅能读到最后一个字节。 - DMA接收长度配置不合理
如果启动DMA接收时设置的缓冲区长度为1,且处理回调时未及时将数据转存到其他缓存,也会导致新接收的字节始终覆盖缓冲区的首个地址,最终仅保留最后一个接收值。 - 回调函数逻辑阻塞
如果UARTEx_RxEventCallback()内处理逻辑耗时过长,导致下一个字节到来时DMA还未完成重启,也会出现数据丢失的情况。
修复方案
- 在
UARTEx_RxEventCallback()的末尾重新调用UART DMA接收启动接口,比如HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, BUFFER_SIZE),保证每次数据处理完成后立即启动下一轮接收。 - 根据业务需求设置合理的DMA接收缓冲区长度,处理数据时按实际接收的字节数读取,避免数据覆盖。
- 精简回调函数内的处理逻辑,耗时操作转移到主循环或独立任务中执行,避免阻塞DMA接收流程。
内容的提问来源于stack exchange,提问作者daaarwiin
相关产品推荐
相关产品推荐

