4. ADC采样故障:参考电压异常、采样时序错误、滤波算法失效

ADC采样,说白了就是给单片机装上一双“眼睛”,让它能看到模拟世界的电压变化。但这双“眼睛”特别容易出问题。我这些年修过的剃须刀主板里,ADC相关的故障至少占了三分之一。今天咱们就聊聊最常见的三个坑:参考电压、采样时序、滤波算法。

4.1 参考电压异常——ADC的“尺子”歪了

ADC测量电压,本质上是拿输入电压和参考电压做比较。参考电压就是那把“尺子”。尺子不准,量什么都白搭。

核心问题:参考电压一旦漂移,ADC转换结果会整体偏移。比如你测1.2V,实际读出来可能是1.35V或1.05V。

4.1.1 常见原因

  • 外部参考电压芯片损坏:比如TL431输出不稳定,或者直接挂了
  • VREF引脚滤波电容失效:电解电容干涸、MLCC开裂,导致纹波过大
  • PCB漏电:潮湿环境下,VREF走线之间出现微弱漏电流
  • MCU内部参考源精度不够:有些低成本芯片内部1.2V参考源温漂能达到±5%

我记得有一次,客户反馈一批剃须刀电量显示不准。查了两天,最后发现是VREF引脚的对地电容虚焊了。示波器一测,参考电压上叠加了200mV的纹波。嗯,这种问题最坑人,因为不是完全坏掉,而是“半死不活”的状态。

4.1.2 诊断方法

步骤操作预期结果
1万用表测VREF引脚对地电压稳定在标称值±1%以内
2示波器AC耦合看VREF纹波纹波峰峰值<10mV
3用精密电源输入已知电压ADC读数误差<1%
4加热/制冷测试温漂全温范围内变化<2%

我的习惯:设计时在VREF引脚并联两个电容——10μF电解+100nF MLCC。电解负责低频滤波,MLCC负责高频噪声。这个组合我用了十年,极少出问题。

4.2 采样时序错误——你采的根本不是那个时刻的值

采样时序问题,说白了就是“你想要的时机”和“实际采到的时机”对不上。这在剃须刀这种电机频繁启停的场景里特别常见。

4.2.1 典型场景

剃须刀电机启动瞬间,电流会从0A飙升到3A,然后回落到1A左右。如果你在电流峰值时刻采样,读到的电压会偏低(因为电源被拉低了)。如果你在电机停转瞬间采样,读到的电压又会偏高(因为反电动势)。

为什么会这样?因为ADC采样需要时间——从开始采样到转换完成,通常需要几个微秒到几十个微秒。这期间如果电压在变化,你采到的就是“平均值”或者“某个中间值”,而不是你想要的那个点。

4.2.2 我踩过的坑

我曾经做过一个项目,电池电压采样总是跳变。软件加了各种滤波都没用。最后用示波器一抓,发现ADC采样时刻正好落在电机PWM开关的边沿上。每次采样,电压都在剧烈抖动。解决方案很简单:把采样触发点移到PWM周期的中间位置,避开开关边沿。

注意:不要以为MCU的ADC速度够快就忽略时序。剃须刀电机产生的EMI干扰频率可能高达几十MHz,而ADC的采样保持电容只有几皮法,根本扛不住这种高频干扰。

4.2.3 时序优化策略

  1. 硬件同步:用定时器触发ADC,确保采样时刻与PWM周期对齐
  2. 软件延迟:在电机启动后延迟10-50ms再开始采样
  3. 多次采样取中值:连续采5次,去掉最大最小值,取平均
  4. 使用DMA:让ADC自动连续采样,CPU只管读结果
// 示例:定时器触发ADC采样,避开PWM边沿
// 假设PWM周期为100μs,我们在50μs处触发采样
void ADC_Init_With_TimerTrigger(void) {
    // 配置定时器2,50μs产生一次触发事件
    TIM2->PSC = 72 - 1;  // 72MHz时钟,预分频后1MHz
    TIM2->ARR = 50 - 1;  // 50μs周期
    TIM2->CR2 |= TIM_CR2_MMS_1; // 更新事件作为触发输出
    
    // 配置ADC为定时器触发模式
    ADC1->CR2 |= ADC_CR2_EXTSEL_SRC_TIM2_TRGO;
    ADC1->CR2 |= ADC_CR2_EXTEN_RISING_EDGE;
}

4.3 滤波算法失效——数字世界的“近视眼”

滤波算法,就是给ADC数据配一副“眼镜”。眼镜度数不对,要么看不清(响应慢),要么看花眼(噪声大)。

4.3.1 三种常见滤波算法对比

算法优点缺点适用场景
滑动平均滤波实现简单,平滑效果好响应滞后,需要RAM缓存缓慢变化的信号(如温度)
中值滤波抗脉冲干扰能力强计算量大,不适合高频采样有偶发尖峰的场景(如电机换向)
一阶低通滤波内存占用小,实时性好相位滞后,对高频噪声衰减有限大多数通用场景

4.3.2 滤波参数调优经验

我个人习惯用一阶低通滤波,因为它简单、省内存。但参数调不好,效果还不如不滤波。

// 一阶低通滤波实现
// alpha = 0.1 ~ 0.3 适合慢变信号
// alpha = 0.5 ~ 0.8 适合快变信号
uint16_t LowPassFilter(uint16_t raw_value, uint16_t last_filtered, float alpha) {
    return (uint16_t)((float)raw_value * alpha + (float)last_filtered * (1.0f - alpha));
}

// 实际使用示例
uint16_t filtered_battery_voltage = 0;
void BatteryVoltageTask(void) {
    uint16_t raw = ReadADC();
    filtered_battery_voltage = LowPassFilter(raw, filtered_battery_voltage, 0.2f);
}

避坑指南:我曾经把alpha设成0.01,结果电池电压从4.2V掉到3.0V,滤波输出花了5秒才跟上。用户反馈说“剃须刀没电了还不停机”。后来我把alpha改成0.3,同时加了一个阈值判断——如果连续3次采样变化超过0.5V,直接跳过滤波,用原始值。这样既平滑了噪声,又保证了快速响应。

4.3.3 滤波算法失效的典型表现

  • 数据跳变:滤波后仍有明显毛刺 → 检查alpha是否太小,或者采样频率是否太低
  • 响应迟钝:实际电压变了,滤波值半天不动 → 增大alpha,或者改用自适应滤波
  • 相位滞后:滤波后的波形比原始波形明显延迟 → 这是低通滤波的固有特性,无法完全消除

4.4 综合诊断流程

当你遇到ADC采样不准的问题时,按这个顺序排查:

  1. 先查硬件:用万用表测参考电压,用示波器看纹波
  2. 再查时序:确认采样时刻是否在噪声最小的窗口内
  3. 最后查软件:检查滤波参数是否合理,有没有溢出或精度丢失

记住一个原则:硬件能解决的问题,不要用软件去扛。你想想看,如果参考电压本身就不稳,你写再牛的滤波算法也是白搭。反过来,如果硬件已经做到极致了,软件滤波就是锦上添花。

总结一下:ADC采样故障,80%是参考电压的问题,15%是时序问题,只有5%是滤波算法的问题。但大多数人一上来就调滤波参数,这是典型的“软件思维”。我建议你下次遇到ADC不准,先拿起万用表,而不是打开IDE。