ADC开了DMA,采样序列为何还会错位?
DMA不会识别“这是第几路传感器”,它只把ADC_DR每次出现的数据按请求顺序写进内存
多通道ADC加DMA后,CPU负担降下来了,数组里的通道顺序却可能偶尔错位。问题通常不是DMA乱搬,而是ADC Rank、转换数量、缓冲长度和循环重启没有保持同一个节拍。
调试时最迷惑的现象是:三个通道的数值都在变化,但数组下标有时像互换了。重新上电恢复,跑一段时间又出现;只看DMA使能位,所有配置似乎都正常。
工程判断是:ADC决定转换顺序,DMA只执行顺序搬运。软件若没有把通道数、Rank、数据宽度和缓冲周期绑定起来,下一轮数据就可能从错误的位置继续。
从ADC_DR到内存,先画清一条数据链
规则组按照Rank 1、Rank 2、Rank 3依次完成转换,每完成一次,结果进入ADC_DR并产生DMA请求。DMA从固定外设地址读取,再把目标内存地址递增到下一个元素。
图1 ADC规则组把结果写入数据寄存器,再由DMA请求搬到内存
这条链路里没有通道标签。数组第0项之所以代表通道A,只因为Rank 1恰好是A,并且DMA从缓冲区起点开始写。任何一次少搬、多搬或中途重启,后面的下标含义都会整体偏移。
ADC架构图还能看到规则通道、数据寄存器和DMA请求之间的关系。理解这条硬件路径,比在应用层给数组做猜测性重排更可靠。
六个数量必须是同一个答案ADC的NbrOfChannel、实际配置的Rank数量、DMA缓冲区元素个数、DMA传输计数、数据宽度和应用层一帧长度必须一致。三通道扫描却把DMA长度设成四,会让每帧边界不断滑动。
图2 DMA外设地址、内存地址、数据宽度和循环模式共同定义搬运节拍
外设地址应固定指向ADC_DR,内存地址按元素递增;ADC结果若按半字读取,DMA外设与内存数据宽度也应对应。宽度不一致时,数组可能出现高低半字拼接、覆盖或看似随机的异常。
循环模式并不会自动理解帧。它只在计数归零后重装并回到内存起点。应用层若在任意时刻停止、重新配置或再次使能DMA,应明确清除标志、重装计数并与ADC扫描起点同步。
Rank才是通道在数组中的真实身份配置规则通道时,ADC_Channel选择物理通道,Rank指定它在扫描序列中的位置,SampleTime决定该通道的采样周期。函数调用顺序不一定等于最终Rank顺序,必须直接核对参数。
图3 ADC_DR位域和数据对齐决定DMA读取后的元素格式
两个通道误用了同一个Rank,或Rank从2开始留下空位,库函数可能覆盖前一次配置或产生与预期不同的序列。把通道、引脚、Rank和数组下标写成一张表,代码评审会更容易发现冲突。
不同源阻抗的通道还可能需要不同采样时间。采样时间不够常表现为幅值串扰或跟随前一通道,并非下标错位;可通过拉长高阻通道采样时间、降低源阻抗来区分这两类问题。
数组下标没有硬件身份,Rank和帧边界才赋予它身份。
用半满和全满边界验证一整帧双缓冲或半满/全满中断能让处理与采样并行,但处理块大小应是通道数的整数倍。三通道扫描若在四个元素边界处理一次,每个块开头对应的通道会轮换,看起来就像序列漂移。
调试可给各通道输入易识别的固定电平,并在DMA回调里记录缓冲区索引、剩余计数和ADC状态。触发启动后连续抓取多帧,确认每一帧都从Rank 1落到数组起点。
图4 规则通道配置中的Rank直接决定多通道扫描顺序
若运行中需要换通道表,应暂停触发源,等待当前转换与DMA完成,再重新配置Rank、数量和缓冲区。直接在循环中改规则组,会让旧序列与新序列在帧中间交叉。
缓存一致性也要结合目标内核判断。带数据缓存的MCU或MPU上,DMA写内存后CPU可能仍读到旧缓存行;这类现象看似“数组没更新”,机制与Rank错位不同,需要按平台做缓存失效或使用非缓存区。
若回调处理时间超过一个缓冲周期,DMA会继续覆盖尚未处理的数据。可在半满与全满回调中只做快照或投递事件,把耗时滤波和协议打包移到后台,并用溢出计数验证实时性余量。
1. 1列出通道、Rank与数组下标对照。
2. 2核对ADC数量、DMA计数和缓冲块长度。
3. 3在完整帧边界启动、停止和重装。
结论ADC多通道DMA的稳定性,不靠搬运速度保证,而靠序列和帧边界一致。只要Rank、数量、宽度或重装时机有一处不同步,DMA仍会忠实地把错误节拍写进数组。
日志中可增加帧序号、回调类型和溢出计数,避免只打印传感器数值。数值相近时通道交换很难看出,帧级状态却能直接暴露丢中断、处理超时或重新启动造成的边界变化。
把六个数量写成编译期常量和断言,再用固定输入跑多帧验证,能在接入真实传感器前关掉大部分错位风险。

扫码关注















































