0
收藏
微博
微信
复制链接

用DMA传输串口数据,第一次正常,第二次就传错了

2026-07-01 17:34
51

串口通信用DMA来搬数据,第一次发送完全正常,但紧接着发第二次时,接收端收到的数据全乱了——要么少字节,要么多出来一些莫名其妙的内容。单步调试确认数据在发送前是对的,DMA配置也没问题,但就是第二次会出错。问题不在DMA配置本身,而在DMA和CPU对同一个缓冲区的协同访问上。

第一次对、第二次错,典型是缓冲区管理问题

DMA在后台搬运数据,CPU在前台准备下一批数据。两者如果操作同一块内存且没有同步机制,就会出现数据被覆盖或者写入位置错位的问题。第一次传输刚好没问题,是因为缓冲区是空的,数据全部在CPU控制下写入,DMA才开始搬运。但第二次调用时,如果DMA上一轮还没完全结束,或者缓冲区数据还没更新完,DMA就按旧的配置开始搬数据,导致收端收到的是半新半旧的内容。

核心矛盾是:CPU写缓冲区和DMA读缓冲区之间没有互斥机制。

0740a23339b008253a7394816e2f36.jpeg

最常见的三个根因

第一个:传输完成标志没清除就重新启动DMA。DMA传输完成后会产生完成中断,但在中断里只处理了数据,没有把DMA通道的传输完成标志清除干净,或者清除的顺序不对。第二次调用时DMA判断"上一轮还没结束",拒绝启动新传输,或者用了旧的源地址和目标地址寄存器值,导致数据被搬到错误位置。

查一下DMA的TC状态标志和EN使能位的时序,确保在启动下一次传输之前,所有相关标志位都已经清零,并且DMA已经处于关闭状态。

第二个:缓冲区地址在传输过程中被改写了。代码里把缓冲区的指针传给DMA,传输完成后修改了指针指向的内容,但DMA的地址寄存器存的是指针的副本,不是数据本身。如果在DMA读取某几个字节后、传输完成前,指针指向的数据被改写,就会出现"前几个字节正确,后几个字节错误"的现象。

第三个:循环模式和非循环模式切换出错。如果DMA配置成了循环模式,传输完成后自动从缓冲区起点重新开始,第一次调用正常,第二次时DMA还在按上一次循环的进度继续搬数据,而CPU已经写入了新数据,两者完全错位。如果不需要循环模式,检查一下DMA配置里的循环模式位有没有被意外打开。

【注意】调试DMA问题,加GPIO翻转配合示波器看时序是最有效的方法。在DMA传输开始、更新缓冲区和传输完成三个节点各翻转一次GPIO,用示波器看这三个翻转的先后顺序和时间间隔,能快速判断DMA和CPU的执行顺序是否正确。

怎么修

针对缓冲区同步问题,最稳妥的方案是双缓冲ping-pong机制:准备两个缓冲区,CPU往A写数据时DMA从B搬,DMA搬B时CPU往A写。这一轮完成后交换角色,周而复始。CPU和DMA永远操作不同的缓冲区,不存在竞争关系。

如果单缓冲就够了,那就严格执行先查状态再启动的流程:等DMA传输完成 -> 清除所有标志位 -> 更新缓冲区内容 -> 重新配置DMA源/目的地址 -> 启动DMA。每一步都不能省。

实战经验:一个STM32的UART DMA发送项目,第一次发送正常,第二次数据全乱。最后追踪发现DMA传输完成后,中断回调里只设置了`huart->gState = HAL_UART_STATE_READY`,但没有关闭DMA通道本身(`HAL_UART_AbortTransmit`没被调用),DMA的EN位还处于使能状态。下一次调用`HAL_UART_Transmit_DMA`时,DMA认为上一轮还在进行,拒绝启动新的传输。解决办法是在回调里先调用`HAL_DMA_Abort`再设置状态。DMA通道的使能位是一个常被忽略的关键点。

DMA传输第一次对第二次错,不一定是配置错误,更可能是缓冲区管理和传输状态同步的问题。用ping-pong双缓冲或严格的"查状态再启动"流程,能从根本上解决这类竞争问题。

登录后查看更多
0
评论 0
收藏
侵权举报
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表凡亿课堂立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。

热门评论0

相关文章

开班信息