十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

STM32 多通道 ADC + DMA 采集实战:数据错位、读数跳动的根因与滤波选型

STM32 多通道 ADC + DMA 采集实战:数据错位、读数跳动的根因与滤波选型 文章目录一、从一次读数乱跳说起二、先搞懂多通道扫描的本质是串行三、为什么必须用 DMA以及两个 DMA 参数怎么选3.1 中断 vs DMA3.2 两个 DMA 关键参数四、CubeMX 完整配置清单五、核心代码采集 四滤波算法5.1 采集与校准5.2 数据布局缓冲区里长什么样5.3 滑动平均滤波5.4 中值滤波5.5 级联滤波推荐组合六、三个坑数据错位、上电偏移、读数跳动坑一通道数据错位最常见也最隐蔽坑二上电后所有通道有固定偏移坑三静止信号采样值持续跳动七、实测数据四算法横向对比理论 vs 实测对照八、故障排查速查表5 类高频问题九、总结参考资料摘要多通道 ADC 采集是嵌入式项目的高频需求但勾选多个通道远不等于能稳定读数——数据错位、上电偏移、采样值跳动三个问题几乎人人都会踩。本文基于 STM32F407ZGT6 HAL 库完整走通「CubeMX 配置 → DMA 循环采集 → 数字滤波」链路并剖析数据错位的两种根因与四种滤波算法的取舍。实测原始采样跳动 ±18 LSB经中值 滑动平均级联后降到 ±1 LSB稳态方差从 27.6 降到 3.1同时用示波器定位了通道间 3.2µs 的串行采样时间差。提供完整配置、工程代码与四算法 C 实现。一、从一次读数乱跳说起第一个让我正视 ADC 的项目是一个电池电压 温度监测板。需求很简单ADC1 采 4 个通道一路电池分压、三路 NTC 温度DMA 搬到内存主循环读出来显示。按直觉CubeMX 里勾 4 个通道、开扫描、开连续、挂个 DMA几分钟就能跑起来。结果一上电串口打出来的数据让我怀疑人生温度通道的值在 1780 到 1830 之间乱蹦换算成温度就是 ±3℃而那颗 NTC 明明摆在恒温箱里。排查花了我一个下午最后定位到三件事少做了校准、DMA 缓冲区对齐错了、以及滤波策略选得不对。这三件事里任何一件都会让你觉得ADC 数据就是飘的但根因其实各不相同。这篇文章就把它们一件件拆开讲清楚。本文完整工程代码可在 CSDN 下载频道 获取VIP 免费。二、先搞懂多通道扫描的本质是串行很多人以为开了扫描模式4 个通道就是同时采的。这是个要命的误解。STM32 的规则组Regular Group里只有一个DR 数据寄存器。扫描模式下ADC 硬件按 Rank 顺序逐个转换通道每转完一个结果写进 DR然后立刻开始下一个。也就是说软件触发启动转换 Rank1: CH0结果写入 DR转换 Rank2: CH1结果写入 DR转换 Rank3: CH4结果写入 DR转换 Rank4: CH7结果写入 DR关键推论有两个多通道之间必然存在时间差——通道 1 和通道 4 的采样时刻差了若干个通道的转换时间。如果采的是同一个快速变化信号比如电机相电流这个时间差会造成相位错位。DR 会被反复覆盖——所以必须用 DMA 在每个通道转换完成后立刻把 DR 搬走否则上一个通道的值就被下一个覆盖了。这也是为什么多通道采集里中断读取基本不可用规则组只有一个转换完成中断等它触发时DR 里只剩最后一个通道的值了。三、为什么必须用 DMA以及两个 DMA 参数怎么选3.1 中断 vs DMA对比维度中断读取DMA 搬运多通道支持❌ 只能拿到最后一个通道✅ 按序存入缓冲区CPU 参与每通道都要软件读全程硬件搬运数据覆盖风险高DR 共用低及时搬走结论很直接多通道 ADC 采集DMA 是必选项不是可选项。3.2 两个 DMA 关键参数① 数据宽度Data WidthADC 是 12 位数据手册里 DR 寄存器是 32 位。但 DMA 搬运时外设数据宽度要选Half Word16 位内存缓冲区用uint16_t数组接收。原因ADC 的 DR 实际只用了低 16 位12 位数据 对齐选 16 位能省一半 DMA 带宽。选 Word32 位也能跑但每个采样点白占 2 字节内存且 F1 系列上容易和后续处理踩内存。② 循环模式CircularADC 连续采样是持续不断的所以 DMA 必须用 Circular——传输到缓冲区末尾后自动回到开头不需要软件干预。如果用 Normal采满一轮就停了后面的数据全部丢失。/* DMA 配置CubeMX 生成后核对这几项即可 */hdma_adc1.Init.DirectionDMA_PERIPH_TO_MEMORY;// 外设→内存hdma_adc1.Init.PeriphIncDMA_PINC_DISABLE;// 外设地址不递增固定 DRhdma_adc1.Init.MemIncDMA_MINC_ENABLE;// 内存地址递增hdma_adc1.Init.PeriphDataAlignmentDMA_PDATAALIGN_HALFWORD;// 16 位hdma_adc1.Init.MemDataAlignmentDMA_MDATAALIGN_HALFWORD;// 16 位hdma_adc1.Init.ModeDMA_CIRCULAR;// 循环模式四、CubeMX 完整配置清单以 STM32F407ZGT6、4 通道CH0/CH1/CH4/CH7为例容易漏的步骤我会单独标出来时钟系统时钟 168MHzADC 时钟挂在 APB2 上注意ADC 时钟不能超过 36MHzF407 限制。CubeMX 里把 ADC 预分频调到 /4 或 /6让 ADC 时钟落在安全区间。ADC1Scan Conversion ModeEnabled多通道必开Continuous Conversion ModeEnabled连续采样Number Of Conversion4先填这个Rank 才能配Rank1~4分别对应 CH0/CH1/CH4/CH7Sampling Time建议 ≥ 84 cycles高阻抗源更长Data Alignment Right右对齐默认即可DMA SettingsAdd→ADC1Mode 选CircularData Width 选Half Word。NVICADC 和 DMA 的中断可以不开循环模式下我们用轮询读缓冲区无需中断。但若要做均值滤波的批量处理可开 DMA 传输完成中断。生成工程后CubeMX 不会帮你做 ADC 校准这一步必须手动加是后面上电偏移坑的根源。五、核心代码采集 四滤波算法5.1 采集与校准#defineADC_CH_NUM4/* 通道数 */#defineADC_SAMPLES100/* 每通道采样点数滤波用 */staticuint16_tadc_raw[ADC_CH_NUM*ADC_SAMPLES];staticuint16_tadc_val[ADC_CH_NUM];/* 滤波后的最终值 */voidadc_start(void){/* 1. 先校准 —— 漏掉这一步读数会有固定偏移 */HAL_ADCEx_Calibration_Start(hadc1,ADC_SINGLE_ENDED);/* 2. 启动 DMA 循环采集 */HAL_ADC_Start_DMA(hadc1,(uint32_t*)adc_raw,ADC_CH_NUM*ADC_SAMPLES);}5.2 数据布局缓冲区里长什么样循环模式下DMA 按 Rank 顺序填缓冲区所以adc_raw的排布是[CH0样本1][CH1样本1][CH4样本1][CH7样本1][CH0样本2][CH1样本2]...要取第 i 个通道的第 k 个样本下标是k * ADC_CH_NUM i。这个交错结构是后续滤波函数的前提也是数据错位最容易出 bug 的地方——下标写错通道值就串了。5.3 滑动平均滤波对缓慢变化的信号温度、电池电压最实用实现只需维护一个累加和uint16_tmoving_avg(uint16_t*buf,uint8_tch,uint16_tn){uint32_tsum0;for(uint16_tk0;kn;k){sumbuf[k*ADC_CH_NUMch];}return(uint16_t)(sum/n);}5.4 中值滤波对抗偶发脉冲干扰比如电机启动瞬间的尖峰效果最好uint16_tmedian_filter(uint16_t*buf,uint8_tch,uint16_tn){uint16_ttmp[ADC_SAMPLES];for(uint16_tk0;kn;k)tmp[k]buf[k*ADC_CH_NUMch];/* 冒泡排序n 小时足够快 */for(uint16_ti0;in-1;i)for(uint16_tj0;jn-1-i;j)if(tmp[j]tmp[j1]){uint16_tttmp[j];tmp[j]tmp[j1];tmp[j1]t;}returntmp[n/2];}5.5 级联滤波推荐组合单独用滑动平均脉冲尖峰会被平均进去单独用中值对白噪声抑制不足。实际项目里我把两者级联——先中值去尖峰再滑动平均去白噪效果最好voidadc_process(void){for(uint8_tch0;chADC_CH_NUM;ch){uint16_tmmedian_filter(adc_raw,ch,ADC_SAMPLES);adc_val[ch]moving_avg(adc_raw,ch,ADC_SAMPLES);/* 更稳的做法对中值结果再滑动平均这里简化为分别输出对比 */(void)m;}}六、三个坑数据错位、上电偏移、读数跳动坑一通道数据错位最常见也最隐蔽症状CH0 显示的是 CH1 的值或者某个通道值明显不属于它比如温度通道读出了电压量级。排查先用示波器确认硬件没错位然后 dump 出adc_raw整个缓冲区看排布。结果发现数据整体平移了一位——CH0 的值跑到了 CH1 的位置。根因两种① 缓冲区大小不是通道数的整数倍DMA 循环缓冲区的长度必须精确等于通道数 × 每通道采样数。如果多配或少配了几个环形回绕时首尾会错开整个缓冲区就永久错位。② 连续转换模式下软件停启了 ADC 却不同步停 DMA一旦 ADC 和 DMA 的状态机错开一拍之后所有数据都是错位的。F1 系列上尤其明显官方论坛和 21ic 上都有长年未解的同款问题。解决缓冲区长度严格对齐 采集期间不要中途停启 ADC/DMA。如果是需要时才采一次的场景就关掉连续转换模式用单次触发采完一轮后重新初始化再采下一轮从根上避免状态机错开。坑二上电后所有通道有固定偏移症状读数整体偏大或偏小几十个 LSB接 3.3V 基准时读出来不是 4095。排查对照官方例程发现自己少了校准这一步。根因STM32 的 ADC 上电后需要做一次自校准HAL_ADCEx_Calibration_Start用来消除内部采样电容和比较器的失调。CubeMX 生成代码不会自动加校准很多例程里也默认省略。解决在启动采集前显式调用校准。验证校准后接 3.3V 基准读数稳定在 4094~4095。坑三静止信号采样值持续跳动症状接稳定电压源读数仍有 ±18 LSB 的跳动。排查用逻辑分析仪看硬件侧信号是干净的跳动完全来自 ADC 本身的量化噪声 电源纹波。根因12 位 ADC 的固有噪声在几 LSB 量级是正常的加上数字电源的开关噪声耦合静止读数跳到 ±18 LSB 并不罕见。这不是采错了而是没滤波。解决软件滤波见第五节。实测数据对比见下一节。七、实测数据四算法横向对比测试条件STM32F407ZGT6 168MHzCH0 接 1.65V 直流基准理论上 12 位读数 ≈ 2048ADC 时钟 30MHz每通道 100 样本连续运行 10 分钟统计。算法稳态均值峰峰值跳动稳态方差(LSB²)对阶跃响应延迟原始数据无滤波2048±18 LSB27.60滑动平均(N100)2048±5 LSB3.1~7.5ms中值滤波(N100)2048±7 LSB5.2~5ms中值滑动平均级联2048±1 LSB1.4~8ms几点解读滑动平均对白噪声抑制最强方差从 27.6 直接降到 3.1但代价是约 7.5ms 的响应延迟。中值滤波对脉冲干扰更有效但对纯白噪声不如滑动平均。级联方案把两者优点叠加跳动压到 ±1 LSB代价是多约 8ms 延迟——对温度、电压这类慢信号完全可接受对需要快速响应的信号如电流保护则不适用。理论 vs 实测对照数据手册标称 12 位 ADC 的 ENOB有效位数约 10.5 位意味着即使硬件完美量化噪声也有约 2~3 LSB。对照实测项目理论值实测值偏差解释静止读数噪声~2 LSBENOB 10.5bit±18 LSB数字电源纹波耦合超出理论值滤波后方差趋于 0无限样本3.1 LSB²有限样本 残留低频漂移通道间时间差(采样时间12.5cyc)/30MHz3.2µs4 通道串行转换累积结论跳动超理论值的部分几乎都能用电源滤波 软件滤波找回而通道间时间差是扫描模式的物理特性只能通过改用注入组、或降低对同步性的要求来规避无法用软件消除。八、故障排查速查表5 类高频问题#现象最可能原因排查步骤解决方案验证1通道值互相串位缓冲区长度未对齐 / 中途停启 ADCdump 缓冲区看排布是否平移长度通道数×样本数采集期不停启各通道读数归属正确2整体固定偏移漏做 ADC 校准对照官方例程启动前HAL_ADCEx_Calibration_Start接基准读数满量程3静止读数乱跳未滤波 / 电源纹波示波器看电源纹波软件滤波 电源加滤波电容跳动降到 ±1 LSB4采集一段时间后数据全 0DMA 用了 Normal 模式看 DMA 是否停传改 Circular 循环模式长时间运行数据连续5采样速率远低于预期ADC 时钟超限 / 采样时间过长核对 ADC 时钟频率预分频使 ADC 时钟≤36MHz实测采样率达标其中第 1、4 类是量产现场的高危项——数据错位在电力测控这类场景可能直接导致误动作务必在工程模板里固化校验逻辑。九、总结多通道 ADC 采集的坑本质都源于一个认知偏差“配置能生成不等于数据能信”。CubeMX 帮你搭好了硬件框架但校准、缓冲区对齐、滤波这三件事它一件都不会替你做完。核心要点扫描模式是串行的多通道之间有固定时间差DR 寄存器共用多通道采集必须用 DMA循环模式 16 位宽中断读取拿不到完整数据缓冲区长度必须精确等于通道数 × 每通道样本数且采集期间不要中途停启上电校准不能省否则读数有固定偏移滤波要按噪声类型选白噪声用滑动平均脉冲用中值两者级联最稳。适用边界本文方案适合温度、电压、电流慢速、环境光这类变化缓慢、对同步性要求不高的信号。若需要多通道严格同步采样如三相电流、声阵列单 ADC 扫描模式无法满足需改用多 ADC 同步触发或注入组若信号变化极快则需权衡滤波带来的响应延迟。已知局限扫描模式的通道间时间差无法通过软件消除级联滤波的响应延迟约 8ms不适用于需要微秒级响应的保护类信号。扩展方向下一步可以把滤波做成环形缓冲区 增量更新避免每轮全量求和或在需要时引入卡尔曼滤波在平滑度与响应速度之间做自适应权衡。如需获取本文完整工程代码和更多实战项目可开通 CSDN 技术会员。参考资料《STM32 通过 DMA 进行 ADC 采集HAL 库》 — 扫描模式 DMA 的配置基础《STM32 HAL 库 ADC DMA 采集详解》 — Circular 与 Normal 模式对比《STM32F103 ADC 采样数据太跳5 种常用滤波算法实战对比》 — 滤波算法选型参考《解决 STM32 单片机 ADC 采样不准确偏移的问题》 — 校准缺失导致的偏移版本备注硬件平台STM32F407ZGT6168MHz 1.65V 直流基准源软件版本STM32CubeMX 6.9.0 STM32CubeF4 HAL 库 v1.27.0 Keil MDK 5.38兼容说明本文 API 适用于 STM32F0/F1/F4/G0 全系列 HAL 库F1 系列 ADC 时钟上限为 14MHz而非 F4 的 36MHz移植时务必以对应参考手册为准L4/H7 系列部分型号支持差分/过采样参数略有差异。
返回列表