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

资讯详情

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

ADC从原理到实战:模拟信号采样与STM32滤波调优

ADC从原理到实战:模拟信号采样与STM32滤波调优 1. 从物理世界到数字世界的桥梁为什么嵌入式开发绕不开 ADC我接触嵌入式开发这么多年发现很多初学者在点亮LED、跑通串口之后拿到的第一个有点难的外设基本都是 ADC。这东西通俗讲就是让单片机睁开眼睛去感知真实的物理世界。电压、温度、光照、压力、声音这些自然界里连续变化的物理量本质上都是模拟信号而单片机是一个数字世界里的计算器只能处理 0 和 1 的组合。如果没有 ADCAnalog-to-Digital Converter模数转换器单片机就是瞎子只能按预设逻辑机械地执行命令无法根据环境变化做任何自适应调整。可能有人会问为什么不能直接用单片机测量电压原因是单片机内部的核心逻辑单元只能识别高低电平也就是有没有超过阈值电压。它能判断输入引脚是高于某个阈值比如1.8V或3.3V的一半还是低于这个阈值但无法告诉你具体是 2.3V 还是 2.4V。ADC 的核心价值恰恰在这里它能把一个连续变化的电压值映射成一个具体的数字比如把 0V~3.3V 的范围映射成 0~4095 的整数。这样单片机就能知道现在的温度是 26.5 摄氏度或者当前光线的强度是 38%这类精确信息而不是只知道天气好热或者天色暗了。这篇文章适合谁看正在学习单片机但还没真正跑通过 ADC 的初学者、准备嵌入式领域面试的求职者、或者已经有单片机基础但被 ADC 采样精度和稳定性折磨过的开发者。我会从 ADC 外设的工作原理、关键参数、与 MCU 内部结构的配合、到实际的代码写法和调试技巧把这条链路完整地捋一遍。看到最后的读者应该能摆脱对着例程改引脚然后靠玄学调参的状态真正做到知道自己在干什么。2. 从零理解信号模拟信号与数字信号到底差在哪2.1 模拟信号的本质连续的世界模拟信号的特点就是连续两个字。当一个声音通过麦克风被转换成电信号时这个信号的电压会随着声波的变化而平滑地起伏波动在一瞬间可以取到无数个值。同样地一个 NTC 热敏电阻两端的电压也会随着温度变化而连续升降。这种信号有两大特征时间上连续、取值上也连续。这意味着什么呢意味着如果你有一个 3.3V 的模拟信号它在 0V 到 3.3V 之间有无限多个可能的取值。比如 1.0V、1.0001V、1.0000001V理论上都可以出现。但单片机不可能处理无限多的取值所以必须通过 ADC 把无限多压缩成有限多。理解这个无限和有限的差别非常重要因为很多 ADC 采样的坑——比如量化噪声、分辨率不足导致温度跳变——根源都在这里。用生活化的例子来说模拟信号就是一根平滑的斜坡而数字信号是被切成了一格一格的台阶。斜坡走上去非常顺滑但台阶只能一级一级地跳。2.2 数字信号的本质离散的世界数字信号的典型特征是非 0 即 1。它在时间上是离散的在取值上也是离散的。单片机内部总线、GPIO 引脚的电平状态、存储器里的数据本质上都是数字信号。一个引脚要么是高电平通常是接近 VDD 的电压比如 3.3V要么是低电平通常接近 GND 的 0V。虽然物理上电平也有微小的波动范围但逻辑上只承认两个状态。从模拟世界跨到数字世界这个翻译工作就是 ADC 做的。但 ADC 做完翻译之后数字信号的含义并不是天生的。同样是数字 2048在 12 位分辨率、3.3V 参考电压下代表 1.65V但如果参考电压改成 2.5V同一个数字 2048 就代表 1.25V。所以理解 ADC 的量化过程必须先搞清楚分辨率和参考电压这两个概念。2.3 为什么不能把模拟信号直接连到单片机引脚新手最容易犯的一个错误是直接把传感器的模拟输出引脚接到单片机的普通 GPIO 口上结果发现读回来始终是 0 或者始终是 1。原因前面已经说了GPIO 只能做阈值判断。要让单片机读到数值型的电压信息必须把传感器输出接到 ADC 专用输入通道上。每个型号的单片机 ADC 通道数量和位置都不一样。以常见的 STM32F103 系列为例它通常有 18 个 ADC 输入通道分布在 PA0~PA7、PB0~PB1 等引脚上。使用前不仅要看引脚是不是 ADC 功能还要确认这个引脚是否与其他功能复用、是否被其他外设占用。这是很多实操翻车的重灾区我在第 5 部分会专门讲排查方法。注意模拟信号和数字信号的分界点不是简单靠电压大小判断的。模拟信号关心的是精确的电压值数字信号关心的是逻辑状态。搞混这两者后面所有 ADC 相关的讨论都会失去意义。3. ADC 外设工作原理拆解采样、量化、编码三步走3.1 采样保持抓住瞬间的电压值ADC 的第一步是采样也就是在某个时间点把模拟电压值冻结下来。这个冻结为啥必要因为 ADC 转换需要时间而模拟信号一直在变化如果不先保持住转换过程中电压在变结果就没法准确了。想象一下你拿着一个量杯接水水龙头还在哗哗流你就没法量出此时此刻杯子里有多少水。你得先关上水龙头让水位稳定下来再去看刻度。采样保持电路干的就是关水龙头这件事。ADC 内部通常有一个采样开关和一个保持电容。采样开关闭合时电容充电电压跟着输入变化开关断开后电容上的电压就保持住了ADC 再对这个稳定的电压做转换。这里有个关键参数叫采样时间Sampling Time也就是采样开关闭合、给保持电容充电的时间。充电时间不够电容上的电压还没充到和输入信号一致转换结果就会偏小。STM32 的 ADC 采样时间通常可以配置为 1.5 个 ADC 时钟周期到 239.5 个时钟周期具体选多少取决于输入源的内阻。我给个经验值常规传感器的输出阻抗不高选择 15 个周期左右的采样时间比较稳妥如果信号源内阻很大比如几十 kΩ就要把采样时间拉到 55 周期以上否则读数会明显偏低。3.2 量化把连续的电压切成有限份采样保持之后接下来就是量化。这个过程决定了 ADC 把连续的电压范围切分成多少份。这个份数取决于分辨率位数。8 位 ADC把参考电压范围切成 2^8256 份10 位 ADC切成 2^101024 份12 位 ADC切成 2^124096 份16 位 ADC切成 2^1665536 份24 位 ADC切成 2^2416777216 份量化不可避免地会产生误差这叫量化误差。比如参考电压是 3.3V用 12 位 ADC每份的电压宽度就是 3.3V / 4096 ≈ 0.8mV。也就是说哪怕输入电压非常精确地落在两个量化等级之间ADC 也只能选其中一个最接近的等级来表示最多会有约 0.8mV 的偏差。用 8 位 ADC 的话每份宽度是 3.3V / 256 ≈ 12.9mV这个精度对于很多应用就不够了。从工程角度讲分辨率的选择不是越高越好因为高分辨率 ADC 通常转换速度更慢、功耗更高、价格也更贵。比如 24 位 ADC 虽然能感知极其微小的电压变化但因为内部结构复杂转换速度很难做高常用于称重传感器、高精度仪器仪表这类场景而 12 位 ADC 在绝大多数 MCU 控制场景下性能已足够所以成了主流选择。3.3 编码把量化等级变成数字量化的结果是一个对应的等级编号编码阶段就是把这个编号输出为具体的数字值也就是 ADC 数据寄存器里读到的那个数。如果你用的是 12 位 ADC参考电压是 3.3V那么输入 0V编码输出 0输入 1.65V编码输出 2048输入 3.3V编码输出 4095需要注意ADC 编码的参考某个电压值实际是相对于参考电压的比例关系。这一点至关重要。如果参考电压发生波动比如外部稳压源从 3.3V 漂到了 3.5V那么同样的输入电压 1.65V 读出来的编码就会变小因为 1.65V 在 0~3.5V 范围内的占比变小了。ADC 参考电压的来源有两种常见方式一是用单片机自己的供电电压 VDD 作为参考电压二是用独立的 VREF 引脚接外部基准源。低成本产品里直接使用 VDD 做参考比较普遍但如果 VDD 纹波大或者会随负载波动采样精度就会受影响。精度要求高的设计建议使用独立基准芯片而且基准芯片的输出噪声和温漂性能要过硬。3.4 通俗理解三种常见 ADC 架构MCU 内部集成度高的 ADC 架构主要有逐次逼近型SAR、积分型Delta-Sigma 的简化版、流水线型主要见于高速 ADC 芯片不常见于通用 MCU 内部。搞懂 SAR 的原理就够了因为它是 STM32、GD32、NXP 等绝大多数 MCU 内置 ADC 的标准架构。SAR 全称 Successive Approximation Register逐次逼近寄存器。它的工作方式非常像猜数字游戏我得猜一个 0~4095 之间的数先猜中间值 2048如果偏大就往小了猜偏小就往大了猜每次大概砍掉一半的范围经过 12 次接近后就能逼近真实值。每一次比较需要 1 个 ADC 时钟周期所以 12 位 SAR ADC 至少需要 12 个时钟周期完成转换再加上采样保持和其他开销总转换时间会略长。SAR ADC 的优点是比较容易做到中等速度和中等精度的平衡功耗也不太高。缺点是速度受分辨率限制并且对输入信号的噪声比较敏感所以在采样前通常需要加 RC 低通滤波器把高频噪声干掉。4. 关键参数与工程选型分辨率、采样率、参考电压怎么理解4.1 分辨率和量程你能分辨多小的电压变化分辨率已经提过了位数越高能分辨的电压变化越小。但分辨率和精度不是一回事。分辨率是刻度有多细精度是刻度准不准。一把尺子刻度能读到 0.1mm分辨率高但如果尺子本身在制作时就歪了 0.5mm精度低那么读出的数依然不可信。ADC 同理高分辨率不代表高精度内部噪声、参考电压漂移、PCB 布局干扰都会拉低实际精度。量程是指 ADC 能测量的最大输入电压范围。对多数 MCU 来说量程通常是 0V 到参考电压。这就是为什么模拟输入引脚边的电压绝对不能超过参考电压否则轻则读数饱和到最大值并失真重则烧毁 ADC 输入结构。输入负电压同样是禁区因为大部分 MCU 的 ADC 输入不支持负压测量只能接 0V 以上的单极性信号。4.2 采样率和奈奎斯特定理多快采一次才靠谱采样率Sample Rate指 ADC 每秒完成多少次转换单位是 SPSSamples Per Second或 Hz。采样率不能随便定有理论约束。奈奎斯特定理说为了能无失真地重建一个信号采样频率至少要是信号最高频率分量的 2 倍。注意这里说的是至少工程上一般会取 5~10 倍以上才够用。举个例子如果被测信号是 50Hz 的市电工频干扰叠加在一个缓慢变化的直流信号上你想准确还原信号本身的变化那么采样率至少要高于 100Hz才能勉强区分出这个 50Hz 分量。如果采样率不足还会出现混叠现象——高频信号被折叠成低频信号看起来像真实数据实际上是无中生有的假信号而且这种情况一旦发生就很难在事后处理中消除。对大多数传感器应用温度、湿度、光照、压力信号变化本来就很慢几十到几百 Hz 的采样率足够。真正需要高采样率的场景是音频采集至少 8k~44.1kHz、振动分析至少 10kHz 以上这一类动态信号测量。所以选 ADC 采样率之前先想清楚你的信号频率上限是多少记住一个原则够用就行过高的采样率会增加功耗和数据处理负担。4.3 转换时间、通道切换与多路复用MCU 内部通常不可能为每个引脚都配一个独立的 ADC 内核那样太费硅片面积了。实际做法是一个 ADC 内核 一个多路选择器多个输入通道共用同一个转换器。这意味着同一时刻只能有一个通道被采样要采多路信号就得轮流切换。切换通道本身也需要时间而且每次切换后最好让采样保持电路重新稳定所以多通道采样时的有效采样率会下降。如果系统里有多个通道需要高速、同步采样比如三相电流同时采集单 ADC 多路复用的方案就不合适了。要么选择多个 ADC 内核的 MCU比如 STM32F3 系列有两个 ADC 内核要么外扩多片 ADC 同时触发采样。这是方案选型时容易被忽略的点务必提前评估需求别等画完板子才发现一个 ADC 忙不过来。4.4 增量型与逐次逼近型的典型应用差异逐次逼近型SARADC 在中低速、中高精度场合非常吃香典型的 MCU 内置 ADC 基本都是它。Delta-Sigma 型 ADC 靠过采样和噪声整形技术换取高分辨率常见于高精度称重、温度测量和音频 ADC。称重传感器输出的是直流或者极低频的差分信号Delta-Sigma 的高分辨率优势非常明显常见的 HX71124 位、ADS123224 位、MCP356124 位都是这种类型的分立 ADC 芯片。这里有一个热词值得展开为什么 HX711 这类 24 位 ADC 在嵌入式称重项目里几乎无处不在原因在于称重传感器出来的差分电压只有几个 mV 级别如果直接用 MCU 内置的 12 位 ADC0.8mV 的量化宽度和几个 mV 的信号量级完全没法比根本测不出有效变化。而 24 位 ADC 配合内部可编程增益放大器PGA可以把信号放大后量化做到克级的重量变化都能反映出来。所以做高精度信号采集选型时不要迷信MCU 内置 ADC 很方便得看量程和精度需求到底够不够。5. 实操手记用 STM32 HAL 库配置 ADC 的完整过程5.1 硬件准备与接线原则本文的实操示例我以 STM32F103 系列为例因为 STM32 在嵌入式入门和项目开发中覆盖面最广资料也最多。核心板选常见的蓝色药丸板或者最小系统板都行。接线前先明确三件事信号源的地线和单片机的地线要共地模拟输入引脚的电压区间必须在 0~3.3V 之间传感器距离单片机较远时要注意加滤波电容。拿一个常见的电位器做实验它的三个引脚分别接 VDD、GND、ADC 输入引脚。这样旋转电位器输入电压就能在 0~3.3V 之间连续变化是最直观的 ADC 测试信号源。如果你想测真实传感器比如 NTC 热敏电阻就需要一个分压电路把它和固定电阻串联再把分压点接到 ADC 输入。接线过程中有个容易踩的坑用杜邦线飞线时信号线太靠近电源线或 PWM 输出线就容易引入耦合噪声ADC 读数会明显跳动。条件允许的情况下模拟信号线尽量包地或者远离数字噪声源。5.2 STM32CubeMX 图形化配置的操作要点打开 STM32CubeMX选择芯片型号后在 Pinout 视图里找到 ADC 对应的引脚。单击引脚在弹出菜单中选择 ADC 功能然后进入 Analog 子页面配置参数。几个关键参数我逐个解释Resolution分辨率选 12 bits 默认即可这也是 STM32F103 的最高分辨率。Scan Conversion Mode扫描转换模式如果只用一个通道Disable多通道采集需要 Enable并且要在 Configuration 中打开通道扫描顺序。Continuous Conversion Mode连续转换模式开启后 ADC 会不断自动转换不需要软件重复触发适合持续监控如果只想在需要的时候转换一次就 Disable 然后用软件触发。Discontinuous Conversion Mode非连续转换模式多通道时配合扫描模式使用一般初学者可以选择默认不开启。DMA Continuous RequestsDMA 连续请求如果用 DMA 把多通道结果搬到内存这里一般要 Enable这样每次转换完都会自动触发 DMA 传输。采样周期的选择前面说过常规源阻抗选 15 周期或 28.5 周期即可。如果外界信号源输出阻抗很高比如高于 10kΩ建议选 55.5 周期以上。STM32F103 的 ADC 时钟最高是 14MHzCubeMX 默认配置多半是 12MHz这个不用担心时钟树的 APB2 分频配好了就行。5.3 使用 HAL 库实现单通道 ADC 采集用 CubeMX 生成工程后ADC 相关的初始化代码已经自动生成了我们只需要在 main 函数里添加采集逻辑。最简单的方式是使用阻塞式读取uint32_t adc_value 0; float voltage 0.0f; HAL_ADC_Start(hadc1); if (HAL_ADC_PollForConversion(hadc1, 100) HAL_OK) { adc_value HAL_ADC_GetValue(hadc1); } HAL_ADC_Stop(hadc1); voltage (float)adc_value * 3.3f / 4095.0f;这段代码的思路是启动 ADC等待转换完成超时 100ms读取转换值然后转换成实际电压。计算电压用的是比例关系因为 12 位 ADC 满量程是 4095对应的电压是参考电压 3.3V。注意这里参考电压我默认用的是 VDD如果你的板子用了外部基准要用基准电压值代替 3.3。阻塞式读取的好处是逻辑简单、不占额外资源缺点是在等待转换期间 CPU 一直空转。对多数传感器应用来说几十微秒的阻塞无伤大雅但对有实时性要求的系统建议用中断或 DMA 方式。5.4 用 DMA 实现多通道不阻塞采集多通道采集的常规优化方案是 DMA 连续转换模式。DMA 可以把多个通道的转换结果自动搬运到内存数组里全程不需要 CPU 干预等全部数据搬完再触发一次中断通知 CPU 处理。CubeMX 里配置方式如下开启扫描模式、连续转换模式在 ADC 配置中添加需要采集的多个通道同时使能 DMA 请求。然后生成代码在 user code 区声明一个全局数组用来存放数据#define ADC_CHANNEL_COUNT 3 uint32_t adc_values[ADC_CHANNEL_COUNT] {0}; HAL_ADC_Start_DMA(hadc1, adc_values, ADC_CHANNEL_COUNT);在 DMA 传输完成中断回调里做数据处理void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { if (hadc-Instance ADC1) { float voltage_ch0 adc_values[0] * 3.3f / 4095.0f; float voltage_ch1 adc_values[1] * 3.3f / 4095.0f; float voltage_ch2 adc_values[2] * 3.3f / 4095.0f; // 处理三路电压数据 } }DMA 模式的核心价值是让 CPU 专注于业务逻辑而不是耗在等待转换上。但在使用 DMA 时有几个细节必须注意ADC 的 DMA 请求要配置成循环模式否则搬完一次就不再搬了数组的长度必须和配置的通道数一致DMA 的数据宽度要和 ADC 数据寄存器宽度匹配STM32 的 ADC 数据寄存器是 16 位宽但有效数据只有低 12 位。5.5 多通道切换的一个隐藏坑通道串扰多通道采集时如果发现 A 通道的值会受到 B 通道影响多半是通道间串扰。根源在于采样保持电容在前一个通道转换完成后还残留着上一个通道的电荷切换到下一个通道后需要额外时间放电和重新充电。解决方法是每次通道切换后加一个稳定时间或者适当延长采样时间。如果通道数量多、通道切换频率高另一个办法是采用逐通道独立采集采集完一个再切下一个的方式即不开启扫描模式通过软件切换通道并逐次触发转换。虽然慢一点但数据稳定性会明显更好。这个取舍取决于你的应用到底更看重速度还是更看重准确度。6. ADC 数据处理进阶从裸读数到稳定可靠的真实值6.1 软件滤波ADC 数值跳动的经典解法所有接触过 ADC 的工程师都会遇到同一个问题读到的数值不稳定即使信号源是一个稳定的直流电压最后一位或几位也总是在跳。跳变的来源包括电源噪声、参考电压纹波、信号源本身噪声、电磁干扰等。工业上最常使用的软件滤波手段有几种各自适用场景不同限幅滤波把连续两次采样的差值限制在一个范围内超过就认为发生了跳变采用上一次值。适合抑制偶发脉冲干扰但对持续的随机噪声无能为力。中值滤波连续采 3 个或 5 个值排序后取中间值。对尖峰脉冲干扰非常有效但对快速变化的信号响应变慢。滑动平均滤波维护一个固定长度的队列每次取队列中所有值的平均值。平滑效果好但会引入延迟对缓慢变化的信号很适合。加权平均滤波越靠近当前时刻的采样值权重越高兼顾平滑性和响应速度但实现稍复杂。实际项目里我经常的组合是中值滤波 滑动平均滤波两段式处理先取 5 个原始采样值做中值去掉尖峰脉冲再把中值结果送入一个 10 点滑动平均缓冲进一步平滑随机噪声。这套组合在绝大多数慢变信号场景下表现都非常好。6.2 C 语言实现一个可复用的 ADC 滤波函数以 STM32 平台为例写一个可复用的去极值滑动平均滤波函数代码风格贴近嵌入式实战#define FILTER_BUFFER_SIZE 10 static uint32_t filter_buffer[FILTER_BUFFER_SIZE]; static uint8_t filter_index 0; uint32_t ADC_Filter(uint32_t new_value) { uint32_t sum 0; uint8_t i; filter_buffer[filter_index] new_value; filter_index (filter_index 1) % FILTER_BUFFER_SIZE; for (i 0; i FILTER_BUFFER_SIZE; i) { sum filter_buffer[i]; } return sum / FILTER_BUFFER_SIZE; }这个函数的思路就是把每次采集到的新值轮流放到环形缓冲区里每次取缓冲区全部元素的平均值。缓冲区大小选 10 或 16 都行数值越大越平滑但响应越慢。如果你需要更强的抗脉冲能力可以在存入缓冲之前加一级中值滤波。实际项目中还有一个老工程师常说的经验滤波算法的参数不能闭着眼睛拍脑袋。滤波深度越大输出对真实信号的跟随越迟钝。如果你的产品需要对快速变化的信号进行监控比如过流保护滤波参数要是设得不合理可能真实故障信号已经被滤得看不见了。所以做滤波前一定要清楚信号本身的带宽和需求响应时间不要为了读数好看牺牲系统的实时反应能力。6.3 校准和映射将 ADC 读数值还原为物理量ADC 采集到的原始数值本身没有物理意义必须结合传感器特性和电路设计换算成实际物理量。以 NTC 热敏电阻测温为例温度从 0 度变到 100 度电阻值会大幅度变化分压点电压也跟着变化。要得到具体的温度值光用电压比例公式是不够的必须知道 NTC 的阻值-温度对应关系。计算 NTC 阻值的公式是R_ntc R_fixed * (VDD - V_adc) / V_adc其中 R_fixed 是分压电路里固定电阻的阻值V_adc 是 ADC 读到的实际电压。得到 NTC 阻值后再用 Steinhart-Hart 方程或者查表法换算成温度。实际项目里查表法更常用因为计算量小、结果稳定把 -20 度到 80 度之间每个整数的 ADC 值提前算好放在数组里运行的时候查最近的值就行。校准是另一个关键环节。ADC 采集链路中任一处误差——分压电阻的精度只有 5%、参考电压实际是 3.28V 而不是标称 3.3V——都会导致最终物理量计算偏差。高精度应用必须做两点校准:用标准源施加两个已知物理量比如用恒温槽给 0 度和 50 度记录对应的 ADC 读数建立起线性映射关系之后所有的读数都通过这条校准曲线换算。7. 超声环境下的 ADC 稳定性优化硬件与软件协同作战7.1 PCB 布局和接地策略对 ADC 精度的巨大影响很多开发者调试 ADC 时只盯着软件反复改滤波参数其实问题根源可能出在硬件布局上。ADC 是单片机引脚上最脆弱的模拟功能之一因为它要感知微小电压差异对外部干扰极其敏感。PCB 布局时有几个必须遵守的原则。首先模拟信号线要尽可能短并且远离数字信号线和高频切换节点。单片机 IO 口做 PWM 输出时引脚电平以几百 kHz 甚至几 MHz 的频率翻转这些翻转信号会通过空间辐射和电源耦合的方式干扰附近的模拟信号。其次模拟地和数字地要单点连接通常是在 ADC 附近的某个位置汇合避免数字地回流噪声污染模拟地。最后模拟输入引脚旁边必须放一个 0.1uF 的滤波电容电容要尽量靠近引脚直接从引脚旁的地过孔接入形成最短回流路径。这个电容值不是随便选的它和信号源内阻形成 RC 低通滤波器截止频率是 1/(2πRC)。如果信号源内阻是 10kΩ0.1uF 电容的截止频率大约 159Hz这对慢变信号没问题但如果你的信号本身含有更快的变化分量滤波电容太大反而会把有用信号滤掉。所以选择滤波电容时要结合信号带宽来算而不是越大越稳。7.2 参考电压稳定容易被忽视但极其关键前面多次提到了参考电压对 ADC 准确度的影响这里专门展开说。在 STM32 中如果没有外部 VREF 引脚默认参考就是 MCU 的 VDD。VDD 上如果有较大的纹波ADC 的满量程参考值就会来回摆动读数的绝对值也跟着跳动。很多电源是用 DC-DC 降压出来的开关纹波动辄几十 mV对于 3.3V 满量程、12 位 ADC每个 LSB 约 0.8mV来说这个纹波相当于几十个 LSB 的干扰相当恐怖。改善措施有三种一是尽量用 LDO 给 MCU 供电LDO 的输出纹波比 DC-DC 小一个量级二是在 PCB 上对 MCU 电源做 Π 型滤波磁珠 两个电容三是对模拟精度要求高的设计单独用基准芯片给 VREF 供电把参考电压和主供电彻底隔离。第三种方案最贵但效果最明显常用于仪器仪表类产品。7.3 数字信号的软件避让关键的采样时机软件层面有一个成本极低的抗干扰策略控制 ADC 采样时刻主动避开数字噪声剧烈的窗口。比如和 2.4GHz 无线通信模块共用一块板子时无线发射瞬间电流会突然拉高、电源电压跌落此时 ADC 采样结果大概率异常。通过把 ADC 采样时序和射频发射时隙错开可以在不增加任何硬件成本的情况下大幅提升采样稳定性。这个方法在实践中有个变体叫同步采样就是让 ADC 转换触发信号和 PWM 载波同步。电机的 PWM 驱动在开关瞬间会产生强烈的电磁干扰如果 ADC 转换正好发生在 PWM 开关时刻采样结果必然被污染。让 PWM 在计数周期的中点远离开关沿触发 ADC 转换就能躲过干扰最剧烈的瞬间。STM32 的高级定时器和 ADC 触发联动支持这种用法自己实现起来也不复杂。7.4 多路模拟信号通过一条线远距离传输的可行做法搜索热词里有一条多路模拟信号怎么用一根线传输这是个很实际的工程问题。模拟信号直接通过一根长线传输会有两方面问题一是线缆电阻导致信号衰减二是长线相当于天线容易耦合进一大堆电磁干扰。成熟的工程方案是把模拟信号先做数字化编码再用串行总线传输。具体做法是如果传感器端离单片机远就在离传感器近的地方放一个 ADC 芯片把模拟信号变成数字量然后用 UART、SPI、I2C 或者 RS485、CAN 这种串行总线传到主控端。这样做的本质是把通信任务从对精度敏感的模拟信号传输变成对噪声容忍度高的数字信号传输可靠性会提升好几个量级。如果是板内传输很多路采样信号到另一个处理器更常见的做法是用 SPI 接口连接多片 ADC 芯片每片 ADC 有不同的片选信号处理器逐个选通读取。如果传感器数量特别多也可以考虑用 1-Wire 或自定义低速率总线方案但这类方案通用性差只能在特定场景下用不如老老实实选标准接口。8. 常见问题与排查技巧实录8.1 ADC 读数始终为 0 或最大值先查这几处这是 ADC 调试中最常见的问题大概占了我遇到问题的六成。按照排查优先级排列引脚功能是否配置正确。有些引脚的 ADC 功能默认是关闭的必须确认 CubeMX 中 Pinout 配置正确、代码里 GPIO 初始化为模拟模式Analog。信号源是否真的接上了杜邦线有没有松。这个问题很傻但经常发生。参考电压是否正确。如果 VREF 引脚悬空或者供电不正常ADC 转换结果会异常。采样时间是否够长。高阻信号源配短采样时间会导致读数偏低。配置的通道号与实际引脚是否对应。STM32 的 ADC 通道编号和引脚不是简单的一一对应关系查数据手册的 mapping 表格。8.2 读数跳得厉害加滤波就好先别急读数跳动不一定都要靠软件滤波解决盲目滤波可能掩盖硬件问题。正确的排查顺序是先用万用表直接量引脚上的电压如果万用表读数稳定而 ADC 读数跳动说明问题出在 ADC 配置或参考电压如果万用表读数本身就跳动说明信号源或供电有问题。判断之后再决定是加滤波电容、换电源方案还是用软件滤波。有些情况确实是成本考虑只能靠软件兜底那就需要明确滤波会牺牲多少响应速度做到心中有数。8.3 采样结果整体偏小或偏大问题出在哪整体偏差通常是两个原因参考电压不准确或者分压电阻精度太低。参考电压假设 3.3V 但实际上只有 3.1V那么电压换算公式里的分母就不对所有计算结果都会系统性偏小。解决方法是用万用表量出实际的电源电压把换算公式里的参考值改成实测值或者后续加入校准算法。分压电阻的精度问题也很常见。标准 5% 精度的贴片电阻实际值可能是标称值的 95% 到 105%这会让分压比的精度直接差 5 个百分点。对温度测量这种场景5% 的误差对应好几度的偏差无法接受。解决方法是选 1% 精度的电阻或者用校准方法把每台设备的真实分压比测出来写进 Flash。8.4 多通道采集时某一路数据异常多通道采集时某一路数据异常可能是通道配置顺序问题、DMA 缓冲区长度/顺序不匹配或者硬件上该引脚附近有干扰源。我遇到过一个奇葩案例ADC 的通道 4 和通道 5 接了信号源但采集结果总是通道 4 的值出现在通道 5 的位置。查了很久才发现是 CubeMX 配置时通道添加顺序和代码里数组读写的顺序不一致。这种问题用调试器查看原始数组就不难定位但初期排查费了不少时间。8.5 采样周期选择速查表针对不同应用场景我整理了一份采样周期选择速查表方便对照参考低阻抗信号源电压源、运放输出内阻 1kΩ采样周期 1.5~15 周期足够中等阻抗信号源NTC 分压约 10kΩ采样周期 55.5~239.5 周期稳妥高阻抗信号源直接接一个 100kΩ 电位器采样周期必须用最大档或者加一级运放缓冲多通道扫描模式采样周期取单通道时的 2 倍以上更保险如果要用 ADC 采集高阻抗信号源最推荐的方案不是强行拉长采样时间而是在信号源和 ADC 输入之间加一级电压跟随器。运放输入阻抗极高百 MΩ 以上输出阻抗极低几 Ω能极大减轻 ADC 采样保持电路的充电压力这是模拟电路常见的基本功。9. 关于 ADC 的几个面试高频考点准备嵌入式面试的读者建议把下面这些知识点吃透面试官对 ADC 的考察往往从这几个角度切入ADC 的工作原理能画出来 SAR ADC 的逐次逼近流程口述采样、量化、编码三步。分辨率和精度的区别能举例说明12 位分辨率不等于 12 位精度。奈奎斯特定理的含义以及混叠是怎么发生的。为什么高分辨率 ADC 通常速度更慢因为 SAR 每次转换需要 N 个时钟周期N 就是分辨率位数。模拟地和数字地为什么要分开如果不分开会有什么后果。怎么用 C 语言实现对 ADC 原始数据进行软件滤波至少能写出中值滤波或滑动平均的代码。面到 ADC 相关项目问题时建议结合具体案例讲比如我做过一个用 NTC 测温的项目用的是 12 位 ADC 加 5 点中值滤波加 10 点滑动平均最终精度做到了上下 0.5 度以内这样的表述比单纯背概念有力得多。我个人还有一个小建议学 ADC 最好的方式不是只看教程而是拿着单片机开发板接一个电位器或者光敏电阻亲手把原始读数、滤波后的读数和实测电压一起打印到串口上对着数据调参。把整个链路跑通一次之后你对模拟信号到数字信号到底经历了什么会有远超看文档的理解深度。这种基本功到了做高精度仪器、传感器采集、电源管理这类项目的时候会给你带来实实在在的回报。
返回列表