尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
STM32 ADC-DMA多通道采样实战:原理、配置与避坑指南
1. 为什么我坚持用 ADC-DMA 协作采样而不是中断逐次读取做嵌入式电源控制或者数据采集这些年但凡涉及多路电压采样我最怕看到的就是在主循环里用 ADC 查询模式一个通道一个通道去读。不是说不能用而是你一旦把采样这件事和 CPU 的执行节奏绑死后面所有的时序分析、数据一致性、系统实时性都会跟着难受。ADC-DMA 协同工作的核心思路其实特别简单让 ADC 按照固定节拍去转换转换完的结果由 DMA 自动搬运到内存缓冲区CPU 完全不参与中间过程。ADC 只负责把模拟量变成数字量DMA 只负责把数字量搬走CPU 从头到尾只关心数据什么时候准备好了这一个问题。在这个协作模型里ADC 是数据生产者DMA 是搬运工内存缓冲区是仓库CPU 是仓库管理员管理员只需要在货到的时候清点入库不需要亲自去码头搬货。这套方案解决的核心痛点有三个第一CPU 零等待采样过程不阻塞主流程第二多通道数据能够严格按顺序归档不会出现通道间数据错位第三采样频率可以做得很稳定配合定时器触发尤其明显。不管你用的是 STM32、GD32 还是其它 Cortex-M 内核的 MCU这套思路基本通用。适合谁看呢做电机控制、电源管理、电池管理系统、工业采集模块的朋友只要你的系统里有需要连续监测多路模拟量的需求这篇文章都能给你一个可以直接落地的参考方案。2. 从转换时序到搬运机制的完整链路拆解2.1 ADC 采样周期和转换时间的对应关系到底是什么很多刚接触 ADC 的同学会把采样周期和转换时间混为一谈实际上这两个概念是两段不同的时间开销。ADC 的一次完整转换通常分为两个阶段采样阶段和转换阶段。采样阶段是采样保持电容去追踪输入电压的过程这段时间俗称采样周期用 ADC 时钟周期数来衡量比如 1.5 周期、7.5 周期、28.5 周期等。转换阶段是把采样得到的电荷逐次逼近成数字码的过程对 12 位 ADC 来说转换阶段固定是 12.5 个 ADC 时钟周期。总转换时间 采样周期 12.5 个 ADC 时钟周期。举个例子STM32F103 的 ADC 时钟最高 14 MHz如果你配置采样周期是 1.5那么一次转换的总时间就是 (1.5 12.5) / 14 MHz ≈ 1 μs也就是理论上能做到 1 Msps。这里需要特别提醒的是采样周期并不是越小越好。采样保持电容需要时间充电如果输入源阻抗很大而采样时间又很短电容还没充到目标电压就开始转换了那结果必然偏小。我一般建议在输入源阻抗不确定的情况下优先选择 7.5 或 13.5 周期的采样时间稳定性优先于速度。如果你用的是 GD32H7 系列它内置了硬件滤波器硬件过采样可以把多次转换结果做平均后输出从而换取更高的有效位数。这种情况下采样周期依旧影响单次转换速度一定要先算清楚。2.2 DMA 循环模式与 Continuous Requests 的关系DMA 在 ADC 采样场景里最常用的就是循环模式Circular Mode。循环模式的意思是DMA 把 ADC 转换结果按顺序搬运到指定的内存缓冲区当缓冲区写满后自动把地址指针重置回缓冲区起始位置继续搬运新一轮数据。这样一来只要 ADC 持续转换DMA 就会持续搬运CPU 随时可以从缓冲区里读取到最新的一轮数据。在这个基础上STM32 HAL 库的 ADC 配置里有一个选项叫 Continuous Requests。这个选项很多人没搞明白它和 Continuous Conversion 是不一样的。Continuous Conversion 控制 ADC 是否连续启动转换而 Continuous Requests 控制的是 DMA 是否在每次转换结束后持续发起新的传输请求。在实际使用中如果你用 DMA 循环模式搬运 ADC 数据建议把 Continuous Requests 开启否则可能出现数据只搬运了一轮就停住的现象。我用一个生活化的类比来解释整个链路ADC 是一个自动化的量杯每隔固定时间倒出一杯水DMA 是一个传送带把倒出来的每一杯水都运到仓库里。Continuous Requests 就好比传送带始终处于待命状态只要量杯一倒出来就立刻接住。如果你没用 DMA 而是用中断方式那就等于量杯每倒一杯水就要按一次门铃喊你去拿一次两次还行连续高频采集时 CPU 光跑门铃就得累死。3. 硬件电路与 PCB 布局3 个直接决定采样精度的细节3.1 电压采样电路的基本结构和参数计算先讲最常见的电阻分压采样电路。假设你要采集的电压范围是 0~24V而 MCU 的 ADC 输入范围是 0~3.3V那就必须通过电阻分压把电压缩放到 ADC 可接受的范围内。分压电阻的选择有两个关键点分压比和阻值大小。分压比容易理解比如 R1 100kΩR2 15kΩ那么分压比就是 15 / (100 15) ≈ 0.130424V 对应 3.13V留了一点余量给 ADC 参考电压的误差。阻值大小的选择没那么直观但要考虑两点一是功耗阻值越小分压回路的静态电流越大24V 下如果两个电阻都是 1kΩ光分压回路电流就是 12mA这在电池供电的系统里根本不可接受二是输入阻抗匹配阻值太大会导致 ADC 采样时采样电容充电时间不够动态误差变大。所以我一般建议总阻值选在 100kΩ~1MΩ 之间同时尽量在电阻分压节点后加一级电压跟随器。这样采样电路对 ADC 呈现的是低阻抗源采样时间可以选得比较短速度优势能发挥出来。当然如果你对成本敏感不想加运放那就需要把 ADC 采样周期调大比如 28.5 周期以上来弥补高阻抗源带来的充电时间不足问题。3.2 前端 RC 滤波的设计思路ADC 前端加 RC 低通滤波几乎是每个成熟项目都会做的事它既能滤除高频噪声又能起到抗混叠的作用。这里我直接给出一个常用的经验公式截止频率 f_c 1 / (2π × R × C)关键点是 R 的取值要从采样源阻抗里一并考虑。假设你用了电压跟随器输出阻抗很低那 R 取 100ΩC 取 1nF截止频率大约是 1.6 MHz基本不影响正常的信号带宽又能滤掉一部分高频毛刺。如果你没有跟随器采样源阻抗可能达到 10kΩ 以上那 R 就要谨慎选因为 R 和 ADC 内部采样电容还会构成另一个 RC 网络影响采样精度。∑-Δ ADC 的前端 RC 设计更讲究。这类 ADC 内部采样电容会周期性反冲导致前级电路出现纹波如果 RC 参数选得不对会出现明显的线性误差。我实测过一款 16 位 ∑-Δ ADC前端 RC 用 1kΩ 100nF 和用 100Ω 10nF结果是后者反而更差因为 RC 组合的阻抗特性影响了内部调制器的建立时间。这里我的建议是严格参考数据手册里给出的推荐 RC 区间不要自己盲目改大电容大电容不一定带来更好的滤波效果反而可能引入建立时间问题。3.3 规避时钟抖动与电源噪声的 PCB 布局要点PCB 布局是最容易被轻视但后患无穷的环节。ADC 的模拟精度再高前端参考电压有一丝噪声到最后都是白搭。第一个要点ADC 的参考电压VREF去耦电容要尽量靠近 MCU 的 REF 引脚走线要短而粗并且用独立的过孔回到地平面。参考电压是 ADC 转换的尺子尺子本身不稳测出来的东西自然不准。去耦电容我建议用 1μF 100nF 的组合形成宽频段的低阻抗路径。第二个要点模拟地与数字地要单点连接通常是建议在 MCU 下方的地平面处汇接。有些新手把模拟地和数字地彻底分开再用一根细线连接结果地阻抗加大噪声反而更大。我自己的习惯是在 PCB 布局时就规划好ADC 相关的模拟电路和数字电路分区放置地平面不做物理分割而是依靠布局分区来控制回流路径。第三个要点时钟抖动问题。ADC 转换的时钟如果抖动过大会直接表现为采样结果的噪声增加。这里说的时钟抖动包含外部晶振的抖动如果 ADC 时钟来自外部晶振和内部 PLL 引入的抖动。布局上要确保晶振尽量靠近 MCU晶振下方不要走任何数字信号线晶振地线独立打过孔到地平面。另外开关电源的布局也要远离模拟采样区域至少保持 1cm 以上的间距否则开关噪声会通过空间耦合进采样前端。4. 基于 STM32CubeMX 的多通道 ADC-DMA 采集实现4.1 从零开始的 CubeMX 配置流程现在的开发效率已经很高了我一般直接用 STM32CubeMX或者 GD32 的对应图形化工具初始化工程。针对 ADC 多通道 DMA 采集关键配置项我列一下我常用的方案ADC ModeIndependent Mode多通道开启 Scan Conversion ModeNumber Of Conversion根据实际通道数填写比如 4 个通道Conversion Trigger定时器触发推荐或软件触发简单场景Sampling Time7.5 Cycles 或根据前端阻抗调整DMA Continuous RequestsEnabledDMA ModeCircular这里要注意的是CubeMX 生成代码后DMA 配置里有一个 Data Width 的设置。如果你的 ADC 分辨率是 12 位转换结果存在 16 位寄存器里那 DMA 搬运的数据宽度应该选 Half Word。很多问题就出在这里比如 GD32E230 出现 ADC DMA 数据紊乱一排查往往就是数据宽度或者内存地址对齐没配置对。4.2 代码层面的核心逻辑初始化完成后真正的逻辑代码其实很少#define ADC_CHANNEL_NUM 4 uint16_t adc_buf[ADC_CHANNEL_NUM] {0}; HAL_ADC_Start_DMA(hadc1, (uint32_t *)adc_buf, ADC_CHANNEL_NUM);就这一行ADC 就会持续转换并搬运数据。HAL_ADC_Start_DMA 这个函数会完成 ADC 启动和 DMA 启动两步操作之后不需要你再干预。读取数据时你只需要访问 adc_buf 数组float voltage0 (float)adc_buf[0] * 3.3f / 4095.0f;每个通道对应的物理电压就是转换值乘以参考电压再除以满量程。有一点要重点提醒DMA 循环模式下CPU 读取 adc_buf 时DMA 可能正在更新同一个位置的数据。如果恰好你读了一半DMA 写入了新数据那这个通道的数据就是前后两帧的混合体。严谨的做法是使用 DMA 传输完成中断在中断回调里拷贝一份数据快照或者使用双缓冲区DMAMUX 支持的部分型号有 Ping-Pong 模式来实现无缝切换。4.3 定时器触发采样的进阶玩法如果你的采样频率需要和 PWM 同步比如电机控制里的电流采样那就强烈推荐你用定时器触发 ADC。具体做法是配置一个定时器的更新事件或者比较事件作为 ADC 的触发源通过 CubeMX 在 ADC Trigger 里选择 Timer X Trigger Out event。这种方式有一个极大的好处采样时刻和 PWM 开关时刻严格同步。比如你在 PWM 中心对齐模式下配置 ADC 采样点在计数周期的中点那采到的电流就是平均电流而不是 PWM 开或者关瞬间的尖峰电流。如果你用软件触发采样时刻会随着主循环的负载波动而抖动最后控制算法的性能就会受影响。5. 我踩过的 AFE 滤波、数据漂移与 DMA 数据紊乱的坑5.1 GD32E230 ADC DMA 数据紊乱问题实录有一段时间我在 GD32E230 上做多通道采样遇到一个非常诡异的现象ADC 数据偶尔出现通道间错位比如本该是通道 1 的数据跑到通道 3 里去了。排查过程我走了不少弯路最后锁定在三个因素第一个是 DMA 的 Memory Address Increment 没有正确配置。ADC 多通道转换后DMA 需要按顺序递增地址写入缓冲区如果地址增量没开那所有通道的数据都会写到同一个位置。第二个是 ADC 的转换顺序和 DMA 缓冲区通道位号的对应关系。规则组通道的转换顺序如果调整了而你的代码还是按固定顺序解析缓冲区数据自然会错位。这个问题最常见的场景是后期维护时在 CubeMX 里新增了一个通道编译烧录后数据全乱了因为缓冲区对应关系变了。第三个是 DMA 请求优先级问题。在多重 DMA 请求并存的系统里如果 ADC-DMA 请求优先级设置过低高优先级的外设可能会抢占导致某些批次的数据延迟更新。解决方案是给 ADC 对应的 DMA 通道分配较高优先级同时打开 DMA 的 FIFO 来吸收突发请求。5.2 C 语言实现 ADC 值滤波函数的实用套路硬件调试得差不多了软件滤波还是得做一层。我这里分享一个实测效果非常好的组合滤波方案中位值平均滤波。原理很简单连续采集 N 个值N 取奇数比如 7 或 11去掉最大值和最小值剩下的取平均。这样既抵抗了随机脉冲干扰又保留了平均值滤波的平滑效果。代码也很短uint16_t median_average_filter(uint16_t *buf, uint8_t len) { uint16_t max 0, min 0xFFFF; uint32_t sum 0; uint8_t i; for (i 0; i len; i) { sum buf[i]; if (buf[i] max) max buf[i]; if (buf[i] min) min buf[i]; } sum - max min; return (uint16_t)(sum / (len - 2)); }需要注意的是这个函数需要传入一个连续的缓冲区所以在 DMA 循环模式下你最好维护一个环形队列每次 DMA 中断来时把新值写进去然后调用滤波函数。5.3 ADC 数据漂移的处理思路数据漂移是模拟采样里最让人头大的问题之一。表现是同样的输入电压早上测是 1.498V下午变成 1.512V或者开机时和运行一段时间后的读数对不上。漂移的原因通常有三个方向。第一个是参考电压温漂这是拿到手先怀疑的对象。解决方法是换用低温漂的参考芯片或者对参考电压做软件校准。第二个是采样电路本身的热噪声电阻的温漂会改变分压比导致同样输入电压下采样值发生变化。这时候需要把分压电阻换成低温漂型号精度等级选 0.1% 或更高的。第三个是 MCU 内部的增益误差随温度变化这种情况可以通过标定曲线的方式修正在出厂前做两点标定或三点标定。这里有个很实用的排查技巧把 ADC 输入端直接短接到参考电压的地看读数是否稳定。如果接地读数都有漂移那问题一定在 MCU 内部控制逻辑或参考源而不是前端电路。6. HAL 库 DMA 发送连续性的处理细节除了 ADC 采集DMA 的另一个高频应用是串口或 SPI 的数据发送。如果你用过 STM32 HAL 库的 HAL_UART_Transmit_DMA一定会遇到一个问题连续两次调用 DMA 发送时第二次调用会失败或者数据发不出去。这个问题的根因是 HAL 库的 DMA 发送状态机没有在上一次传输完成后自动复位。解决思路有两种。第一种是在发送完成回调里手动清除发送状态void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if (huart-Instance USART1) { __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_TC); } }第二种是使用DMA 循环模式 内存缓冲区的方案把要发送的数据提前放入缓冲区DMA 自动循环发送CPU 只需要在需要更新数据时改写缓冲区内容。这种方式特别适合需要周期性发送固定长度数据的场景比如周期性上报采集结果。用这种思路做 ADC-DMA 协同采样时也是一样让采集、搬运、发送尽量各自独立运转CPU 只在关键节点做轻量级处理系统的实时性能会有明显的提升。7. 最后分享一个实用小技巧我自己做多通道电压采样时有一个习惯给每个通道分配一个带符号标记的名义满量程值在调试阶段通过串口周期打印各通道原始值。如果某个通道的数据始终是 0 或者满量程先不要怀疑算法去量一下对应的 MCU 引脚电压。很多时候是杜邦线接触不良或者 PCB 焊点虚焊软件调半天根本是在调空气。另外DMA 缓冲区如果定义成 uint16_t 类型要注意 C 语言里的对齐属性。部分编译器对 ARM 内核要求在访问 16 位数据时地址必须偶对齐如果你的缓冲区定义在结构体里且前面有 uint8_t 成员可能会出现对齐问题。稳妥的做法是直接用__ALIGNED(2)或者单独定义一个全局数组别放在结构体中间。ADC 和 DMA 这套协作机制刚开始接触时会觉得有点绕一旦用熟了你会爱上这种你干你的、我干我的的工作方式。每次看到数据稳定地依次写入缓冲区主循环还能腾出手来处理通信、显示、控制逻辑我就觉得当初花在原理理解上的功夫完全值回票价。
RELATED

相关推荐

Folo搜索功能:全文搜索与过滤算法

Folo搜索功能:全文搜索与过滤算法

Folo搜索功能:全文搜索与过滤算法 在信息爆炸的时代,如何快速精准地找到所需内容成为提升效率的关键。Folo作为新一代信息浏览器,其搜索功能融合了全文检索与智能过滤算法,帮助用户在海量订阅源中快速定位有价值的信息。本文将深…

📅 2026/9/17 7:00:57
Velero(v0.8.0 时代 Ark)插件管理命令 `ark plugin` 完全指南:add / remove / get 与插件体系原理

Velero(v0.8.0 时代 Ark)插件管理命令 `ark plugin` 完全指南:add / remove / get 与插件体系原理

Velero(v0.8.0 时代 Ark)插件管理命令 ark plugin 完全指南:add / remove / get 与插件体系原理 【免费下载链接】velero Backup and migrate Kubernetes applications and their persistent volumes 项目地址: https://gitcode.com/GitHub…

📅 2026/9/17 7:00:57
汽车电子工程师成长路径:CAN/CAN FD、AUTOSAR与功能安全实战指南

汽车电子工程师成长路径:CAN/CAN FD、AUTOSAR与功能安全实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/9/17 7:00:57
MORE NEWS

更多资讯

📰

海上无线信道建模:从文档到可执行仿真模型的工程实践

简介:本资源是一份面向通信工程专业本科生及海上无线系统设计初学者的理论建模文档,聚焦海上复杂信道环境下的传播损耗机理分析与简化仿真建模。文档系统梳理了自由空间传播损耗、海面反射传播损耗及大气吸收损耗三大核心影响因素,给出关键公…

📰

2026年AI大模型API聚合平台选型指南:词元之河(TokenRiver.ai)等八大服务商能力对比与企业部署参考

AI 应用正从原型验证批量进入生产环境,API 聚合平台随之成为企业与开发者基础设施的重要组成部分。对需要同时调用 Claude、GPT、Gemini、DeepSeek、GLM、Kimi 的团队来说,统一接口下的多模型切换、并发调度、成本管理与稳定运行是架构核心问题。市场也已…

📰

智慧靶场选型指南:军警公安室内外一体化总包与报靶技术路线

智慧靶场这四个字,这两年被问到的频率明显高了。上个月还有个做弱电工程的朋友打电话过来,说手上接了个县级单位的场地改造,甲方原话是"要能自动报靶、要能联网考核、室内室外都得用得上",预算却只给了个零头。他问我&a…

📰

ONS15454配置手册实战:从光口保护到交叉连接与验证

简介:面向光网络工程技术人员与运维人员的Cisco ONS15454 SDH配置手册教学课件,聚焦客户端端口定义、SFP模块激活、高阶/VCAT电路创建及线路时钟配置等核心操作,适合已具备基础SDH概念、希望快速上手设备配置的初学者。共1个PPT文件&#xff…

📰

进销存软件哪个简单好用?2026年选型指南:从记账工具到分销系统,一套够用不踩坑

导读:当一家企业的SKU从几十个增长到几百个,销售渠道从单一线下拓展到多平台、多分销商时,最初那套"够用就行"的进销存工具往往第一个掉链子。2026年,进销存软件哪个简单好用?分销系统软件哪个好用&#xff…

📰

DB-NDS格式详解:用SQLite工具打开导航地图数据库

做车载导航相关工作久了,只要碰过地图升级、导航数据库移植、或者自己折腾车机地图卡,迟早会遇到一个词——DB-NDS。第一次见这东西的人通常一脸懵:DB不是数据库吗?NDS又是什么?为什么一个地图包会以数据库的形式存在&…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬