电容滤波全解析:从RC低通到STM32 ADC软件滤波实践
大家好很久没有更新“电容”这个系列了。前面几篇聊过电容的基本特性、容值读取和常见分类今天专门把电容的滤波作用讲透。这里先说明一下本文不是单纯讲几个公式就结束而是从电容为什么能滤波的物理原理出发一路拆到 RC 低通滤波、电源滤波、去耦电容最后再给出 STM32 ADC 软件滤波的完整代码实现。无论你是硬件工程师还是嵌入式开发只要手上项目出现过“电源纹波大”“ADC 采样值乱跳”“加了电容还是滤不掉杂波”这类问题这篇文章都值得收藏备用。## 1. 电容滤波的核心概念1.1 滤波的本质是什么滤波的本质是“让有用信号通过让无用信号衰减”。在电路里我们常见的噪声和干扰可以分为两类一类是电源线上的纹波通常是 100Hz 或更高频的脉动分量另一类是信号线上的高频毛刺比如数字电路开关瞬间产生的尖峰。电容的作用就是利用它对不同频率信号呈现不同阻抗的特性把这些不需要的频率成分“旁路”到地上去。电容的容抗公式是Xc 1 / (2πfC)其中 f 是信号频率C 是电容容量。频率越高容抗越小容量越大容抗也越小。换句话说高频信号更容易从电容上流过。于是我们经常能看到这样的电路结构信号线或电源线上串联一个电阻然后对地并联一个电容高频干扰从电容跑掉正常信号继续往前走。1.2 电容充放电与滤波的关系要理解滤波先要理解电容的充放电特性。电容两端电压不能突变。当电容通过电阻充电时电压按指数规律上升Vc(t) V0 (VCC - V0) * (1 - e^(-t/RC))去掉电源后电容又会对负载放电电压按指数规律下降Vc(t) V0 * e^(-t/RC)这里面的RC 就是时间常数 τ决定了电容充放电的快慢。τ 越大电压变化越平缓。这个特性和滤波有什么关系想一想整流电路的输出它是一串带有波谷的脉动直流。如果直接给负载供电电压会忽高忽低。当我们在输出端并联一个大电容后整流输出的波峰给电容充电当波形跌落到波谷时电容开始向负载放电维持输出电压不会掉得太低。这样一来原本锯齿状的波形就变得平滑了。所以可以这样理解电容滤波的两种工作状态工作状态发生时机作用充电输入电压高于电容电压存储能量吸收多余波动放电输入电压低于电容电压释放能量填补输出电压低谷1.3 三个容易被混淆的概念讨论电容滤波时经常出现三个词滤波电容、旁路电容、去耦电容。虽然它们本质上都是利用电容的频率特性工作但侧重点不同。滤波电容通常指电源整流后的储能电容容量大多用电解电容主要目的是平滑低频纹波。旁路电容强调的是把高频干扰信号旁路到地给干扰提供一个低阻抗回路。去耦电容则更强调为芯片提供瞬态电流同时抑制芯片开关噪声向电源传播。实际使用时它们经常是同一颗电容比如芯片电源引脚附近的 0.1μF 陶瓷电容你说它是旁路电容也可以说是去耦电容也对。理解侧重点的差异比死记名词更有用。## 2. 硬件滤波电路从 RC 低通到电源滤波2.1 RC 低通滤波电路与截止频率最基础的一阶 RC 低通滤波电路长这样信号从电阻 R 的一端输入输出从电容 C 两端取出电容的另一端接地。高频成分会通过电容旁路到地低频成分则经过电阻到达输出端所以叫低通滤波。判断滤波效果的指标是截止频率 fcfc 1 / (2πRC)当信号频率远低于 fc 时信号几乎无衰减地通过当信号频率远高于 fc 时信号以每十倍频程 -20dB 的斜率衰减在 fc 处信号幅度衰减到原来的 0.707 倍也就是 -3dB。举例如果取 R 10kΩC 100nF那么fc 1 / (2 × 3.14 × 10000 × 100e-9) ≈ 159Hz这意味着频率超过 159Hz 的信号开始被明显衰减。如果你想滤除的是几百 kHz 的噪声这个 RC 参数显然不够需要把截止频率降低。这里有个常见误区有人会把 RC 低通滤波的电阻选得很大结果信号本身也被衰减得很厉害。因为输出端如果接的是后级电路的输入阻抗这个阻抗会和 C 形成并联影响实际截止频率。设计时必须考虑负载阻抗让负载阻抗远大于 R否则滤波特性会发生偏移。2.2 电源滤波电路的典型拓扑电源滤波和信号滤波还不完全一样。信号滤波关心波形保真度电源滤波更关心纹波抑制能力。常见的电源滤波结构有三种第一种是整流后并联大电解电容。桥式整流后得到的是 100Hz 的脉动直流并联大电容后利用电容的充放电把输出拉平。这种结构最简单但纹波抑制能力有限大电流时对电容容量要求很高。第二种是RC π 型滤波。结构是 C1、R、C2 组成一个 π 字形状。相比单电容多了一个电阻和一个电容高频衰减能力更强代价是电阻上有压降不适合大电流负载。第三种是LC 滤波。把 π 型滤波中的电阻换成电感因为电感对直流几乎零阻抗对交流呈现高阻抗所以压降小、滤波效果好。但电感体积大、成本高通常用在功率较大的电源中。实际产品中开关电源输入端还会看到 EMI 滤波电路里面使用 X 电容和 Y 电容分别跨接在电源线之间和电源线对地之间用于抑制传导干扰这类滤波电路在安规要求严格的设备上非常常见。2.3 理想电容 vs 实际电容教科书里的电容是理想的但实际电容是一个复合模型除了理想电容 C 本身还串联着等效电阻 ESR 和等效电感 ESL整体等价于一个 RLC 串联电路。这个模型解释了为什么“大电容滤不掉高频噪声”。大容量电解电容的 ESL 和 ESR 通常较大当干扰频率超过电容的自谐振频率后电容开始表现出感性阻抗反而随频率升高而增大滤波能力急剧下降。而小容量的陶瓷电容因为 ESL 小自谐振频率很高更适合滤除几十 MHz 到 GHz 级别的干扰。这就是为什么工程上常用“大电容 小电容”并联组合。大电容负责低频纹波小电容负责高频噪声两者各司其职。2.4 晶振负载电容的计算方法顺带提一个容易被忽略的电容滤波应用晶振电路里的两个负载电容。它们虽然不是用来滤除外部噪声但对振荡波形的稳定起着关键作用。晶振的负载电容计算方式为CL (C1 × C2) / (C1 C2) Cstray其中 Cstray 是 PCB 走线和芯片引脚带来的寄生电容一般估算 3~5pF。如果晶振要求 CL 20pF两边的 C1、C2 通常取 22pF 左右。电容偏小可能导致晶振振荡幅度不足甚至不起振偏大则可能让振荡波形变差、功耗增加。## 3. 实战案例一电源滤波电路参数设计3.1 设计需求与参数估算假设我们要做一个线性电源AC 220V 经过变压器降压为 12V 交流再经桥式整流和电容滤波给一个最大电流 0.2A 的负载供电允许输出纹波 100mV。滤波电容的容量可以用放电公式估算C I × Δt / ΔV全波整流后脉动频率为 100Hz放电时间 Δt 可以近似按 10ms 计算代入数值C 0.2A × 0.01s / 0.1V 0.02F 20000μF这个容量相当大说明仅靠电容滤波去压纹波在低纹波要求下成本很高。实际工程中不会硬用 20000μF 电容而是先用电容把纹波粗滤到几百毫伏再用三端稳压器抑制残余纹波。比如采用 7812 稳压器方案输入侧滤波电容取 2200μF~4700μF 即可满足绝大多数场景稳压器输出侧再加 100μF 和 100nF 电容进一步稳定输出。3.2 耐压和纹波电流的选择选滤波电容不是只看容量。12V 交流电的峰值电压约为 12 × 1.414 ≈ 17V考虑到电网有 ±10% 波动峰值可能达到 19V 左右。如果电容耐压选 16V余量不足存在击穿风险。工程上通常选择耐压为工作峰值电压的 1.5~2 倍这里选 35V 比较稳妥。另一个容易忽略的参数是纹波电流。电容在反复充放电过程中会流过较大的交流分量这部分电流流过电容内部的 ESR 会产生热量。如果纹波电流超过电容的允许值电容温度会升高电解电容的寿命会明显缩短严重时还会鼓包甚至爆裂。所以大功率电源中要查阅电容规格书中的“额定纹波电流”参数并留出足够降额。3.3 原理图设计要点完整的线性电源滤波部分可以这样设计整流桥输出正极接 C14700μF / 35V 电解电容滤除低频纹波C1 旁边并联 C2100nF / 50V 陶瓷电容滤除高频噪声经过 7812 稳压后输出端接 C310μF 钽电容或电解电容改善瞬态响应输出端再接 C4100nF 陶瓷电容抑制高频振荡。大电解旁边并小陶瓷的原因前面已经讲过电解电容高频特性差小陶瓷负责滤除 IC 和电路中的高频成分。## 4. 软件滤波ADC 采样值的常用算法4.1 为什么硬件滤波还不够很多嵌入式项目会遇到这种场景硬件上已经加了滤波电容但 ADC 采集到的数据仍然在不停跳动。原因很多可能是传感器本身噪声大可能是参考电压不稳定也可能是采样信号频率和开关电源噪声发生了叠加。这时候在硬件上继续加大电容不一定有效还可能导致信号响应变慢。软件滤波是硬件滤波的有效补充。它的优势在于灵活不增加 BOM 成本改一个参数就能调整滤波强度缺点是会引入延迟并且无法处理带宽极高或频率超出采样率的干扰。下面介绍几种在嵌入式开发中最常用的软件滤波算法全部使用 C 语言实现。4.2 限幅滤波限幅滤波的思路很简单比较本次采样值和上一次有效值如果差值超过设定的阈值就认为本次采样是干扰丢弃或使用上次值。#define LIMIT_THRESHOLD 20 uint32_t last_valid_value 0; uint8_t is_first_sample 1; uint32_t LimitFilter(uint32_t new_value) { if (is_first_sample) { is_first_sample 0; last_valid_value new_value; return new_value; } int32_t diff (int32_t)new_value - (int32_t)last_valid_value; if (diff 0) { diff -diff; } if (diff LIMIT_THRESHOLD) { last_valid_value new_value; } return last_valid_value; }限幅滤波适合剔除明显跳变的毛刺比如按键抖动、偶发脉冲干扰。缺陷在于它无法滤除小幅高频噪声而且阈值的设置需要根据实际信号变化速度来调整设置不当会把正常信号也误判成干扰。4.3 中值滤波中值滤波的思路是连续采集 N 次数据排序后取中间值。因为脉冲干扰通常分布在序列两端取中间值可以把极端值排除。#define MEDIAN_SAMPLES 5 static uint32_t sort_buf[MEDIAN_SAMPLES]; uint32_t MedianFilter(void) { uint32_t temp; for (int i 0; i MEDIAN_SAMPLES; i) { sort_buf[i] ADC_Get_Value(); } for (int i 0; i MEDIAN_SAMPLES - 1; i) { for (int j 0; j MEDIAN_SAMPLES - 1 - i; j) { if (sort_buf[j] sort_buf[j 1]) { temp sort_buf[j]; sort_buf[j] sort_buf[j 1]; sort_buf[j 1] temp; } } } return sort_buf[MEDIAN_SAMPLES / 2]; }中值滤波对脉冲性干扰抑制效果很好但缺点也很明显需要连续采样多次实时性差且对随机高斯噪声的平滑效果不如均值滤波。一般 N 取 3、5、7取太大响应会很慢。4.4 滑动窗口平均滤波滑动平均滤波维护一个固定长度的缓冲区每次采样到新值就把最旧的值替换掉然后求整个窗口的平均值。相比每次重新采集 N 次再平均滑动窗口只需要一次新采样就能得到一个滤波结果实时性更好。#define SLIDING_WINDOW_SIZE 8 static uint32_t window_buf[SLIDING_WINDOW_SIZE] {0}; static uint8_t window_index 0; static uint32_t window_sum 0; static uint8_t window_full 0; uint32_t SlidingAverageFilter(uint32_t new_value) { window_sum - window_buf[window_index]; window_buf[window_index] new_value; window_sum new_value; window_index (window_index 1) % SLIDING_WINDOW_SIZE; if (window_index 0) { window_full 1; } if (window_full) { return window_sum / SLIDING_WINDOW_SIZE; } return new_value; }滑动平均滤波对高频随机噪声平滑效果明显窗口越大越平滑但延迟也越大。如果信号本身变化很快窗口太大会造成严重的滞后控制类项目要特别注意。4.5 软件一阶低通滤波软件一阶低通滤波的思想和硬件 RC 低通滤波完全一致公式如下y[n] α × x[n] (1 - α) × y[n-1]其中 x[n] 是本次采样值y[n-1] 是上次滤波输出α 是滤波系数范围 0~1。α 越大新采样值的权重越高响应越快滤波效果越弱α 越小滤波越平滑响应越慢。static float lowpass_value 0.0f; float LowPassFilter(float new_value, float alpha) { lowpass_value alpha * new_value (1.0f - alpha) * lowpass_value; return lowpass_value; }这个算法的好处是代码极简、占用资源少适合实时性要求高的场合。α 的取值一般从 0.1 到 0.5 之间调试。如果希望更接近硬件 RC 的截止频率可以根据采样周期 T 和目标截止频率 fc 计算α (2π × fc × T) / (1 2π × fc × T)## 5. 实战案例二STM32 ADC 软件滤波实现5.1 环境准备与 CubeMX 配置下面用一个实际工程演示在 STM32F103C8T6 上采集一路 ADC 电压分别用滑动平均滤波、一阶低通滤波和中值滤波对原始数据进行处理并通过串口打印对比结果。硬件准备STM32F103C8T6 核心板电位器或信号源接到 PA0ADC1 通道 0USB 转 TTL 串口模块连接 PA9USART1 TX和 PA10RX使用 STM32CubeMX 生成工程时主要配置如下外设配置项建议值RCCHSECrystal/Ceramic ResonatorSYSDebugSerial WireADC1IN0 使能单次转换模式ADC1Sampling Time55.5 Cycles 或更大USART1波特率115200采样时间尽量设置大一些因为 STM32 的 ADC 输入阻抗不是无限大如果信号源内阻高采样时间太短会采样不准读出来的数据反而显得噪声很大。5.2 编写 ADC 读取函数采用单次转换模式时每次读取需要依次调用启动、等待转换完成、读取结果、停止转换这几个步骤。// 文件路径adc_filter_demo.c核心函数 #include main.h extern ADC_HandleTypeDef hadc1; uint32_t ADC_Get_Value(void) { HAL_ADC_Start(hadc1); if (HAL_ADC_PollForConversion(hadc1, 100) HAL_OK) { uint32_t value HAL_ADC_GetValue(hadc1); HAL_ADC_Stop(hadc1); return value; } HAL_ADC_Stop(hadc1); return 0; }这句话值得强调在单次转换模式下如果每次读完不调用HAL_ADC_Stop下次HAL_ADC_Start可能不会重新启动转换这是新手经常踩的坑。5.3 组合滤波测试代码将前面几种滤波算法整合到主循环中。为了让效果更直观我们直接把原始值和三种滤波后的值通过串口打印出来。// 文件路径main.cmain函数片段 #include string.h #include stdio.h #define ALPHA 0.2f int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); MX_USART1_UART_Init(); uint32_t adc_raw; uint32_t avg_result; float lp_result; uint32_t median_result; char buf[128]; while (1) { adc_raw ADC_Get_Value(); avg_result SlidingAverageFilter(adc_raw); lp_result LowPassFilter((float)adc_raw, ALPHA); median_result MedianFilter(); snprintf(buf, sizeof(buf), raw:%lu avg:%lu lp:%.1f med:%lu\r\n, (unsigned long)adc_raw, (unsigned long)avg_result, (double)lp_result, (unsigned long)median_result); HAL_UART_Transmit(huart1, (uint8_t *)buf, strlen(buf), 100); HAL_Delay(20); } }需要提醒的是在资源紧张的 MCU 上浮点运算和snprintf会占用不少 Flash 和 RAM。如果项目对资源敏感建议把电压换算成“整数电压值 原始值 × 参考电压 / 4096”这一步放到上位机去做或者使用定点数处理避免浮点运算。5.4 运行效果说明把程序下载到开发板给 PA0 输入一个相对稳定的电压用串口助手可以看到raw 值可能在 1230~1260 之间波动这是 ADC 采样本身存在的量化噪声和电源波动avg 值被平滑到 1243 附近波形明显稳定lp 值响应最平滑但跟踪信号突变时会有明显滞后med 值对偶发跳变抑制效果最好但刷新频率比前面两种慢。如果出现“原始值稳定但滤波后反而波动”的情况要先怀疑是不是滤波算法对短时窗口内快速变化的信号过度响应或者 α 取值过大。调试时先从串口数据观察再调参数。## 6. 电容滤波常见问题与排查6.1 增大滤波电容依然无法滤除杂波这是硬件开发里出现频率最高的疑问之一。很多人觉得杂波滤不掉就无限加大电容结果发现效果不明显。出现这个情况通常有三个原因第一干扰频率很高大电解电容的 ESL 导致它在高频下呈现感性此时应该并联 100nF 甚至 1nF 的小陶瓷电容第二干扰是空间辐射耦合进来的加再多的滤波电容也没用需要从 PCB 布局、屏蔽和接地入手第三示波器测量方法有问题探头地线夹过长会引入新的干扰导致测到的“杂波”其实是测量系统带来的。排查顺序建议是先用短地线弹簧探头重新测量确认噪声真实存在再分析噪声频率和来源最后针对性地选择滤波元件。6.2 电源纹波测量不准确测量电源纹波时示波器探头地线夹子形成的环路会像一个天线拾取很多高频干扰测出来的纹波偏大。正确做法是使用探头自带的接地弹簧让地线尽可能短地接触到被测点同时把示波器带宽限制在 20MHz避免测到无关的高频分量。另外不要在纹波测量点额外并联大电容。那会让测量结果看起来很好看但掩盖了真实问题。6.3 软件滤波滞后严重软件滤波参数设置过大是滞后严重的直接原因。滑动平均窗口取到 16、32 甚至更大或者一阶低通滤波的 α 只有 0.01信号的真实变化就会被“抹平”。解决思路是调整参数或者在滤波算法中加入“数据突变判断”逻辑。当检测到连续多次采样值的差值超过阈值时说明信号确实在快速变化此时可以临时增大 α 或减小滤波窗口让响应跟上信号。问题现象常见原因解决思路滤波后数据仍抖动干扰频率过高大电容失效并联小陶瓷电容检查测量方法波形滞后严重滤波系数或窗口过大调整 α、减小窗口增加突变判断电容发烫或鼓包纹波电流过大、耐压不足、极性接反查看规格书纹波电流参数提高耐压检查极性ADC 采样值规律性跳动采样时间过短内部电容未充满增大采样周期降低信号源阻抗晶振无法起振负载电容取值不当按 CL 公式计算兼顾寄生电容用示波器测纹波偏大地线夹子太长形成天线使用接地弹簧限带宽 20MHz6.4 晶振电容选择不当很多同学在画 STM32 最小系统板时会参考网上的电路晶振两边直接放两个 22pF 电容。这通常是没问题的但如果板子布线寄生电容很大或者晶振要求的负载电容不是常规值就可能出现起振困难、频率偏差等问题。设计时先查晶振数据手册中要求的 CL 值再按公式CL (C1×C2)/(C1C2) Cstray反推 C1、C2 的取值。Cstray 按 3~5pF 估算最后通过示波器观察振荡波形确认。## 7. 最佳实践与工程建议7.1 电容选型原则滤波电容选型不能只看容值要同时关注耐压、ESR、ESL、温度特性和纹波电流能力。电解电容适合低频储能但 ESR 大、高频特性差。陶瓷电容适合高频去耦但大容量陶瓷电容在直流偏压下容值会衰减尤其是小封装。钽电容稳定性好但对浪涌电流敏感耐压要留足余量。温度特性优先选 X7R、X5R避免使用 Z5U、Y5V 这类容值随温度变化剧烈的介质。7.2 布局布线规范滤波电容的放置位置比容值更重要。去耦电容必须紧挨芯片电源引脚否则走线电感会让电容失效。电源入口处的大电解电容放在电源插座附近先过大电容再过小电容。信号滤波电路尽量靠近信号源或 ADC 采样引脚。地回路设计同样关键。滤波电容的接地端要和负载的接地端形成低阻抗回路避免地线噪声串入。多层板中滤波电容下方尽量保证完整的地平面。7.3 软硬件滤波的配合策略建议遵循“先硬件、后软件”的原则。硬件上先保证电源纹波在一个可接受范围内软件滤波再处理剩余噪声。如果一个系统的 ADC 采样值靠软件滤波才能勉强稳定说明硬件设计本身是有问题的这种项目进入量产会埋下隐患。软件滤波具体选哪种取决于噪声类型脉冲干扰用限幅或中值滤波随机噪声用滑动平均或一阶低通两者都有就组合使用。7.4 采样与滤波的工程经验采样时间不要追求极致速度很多传感器和模拟信号不需要毫秒级刷新。采样前确认 ADC 参考电压是否稳定参考电压上有纹波滤波算法再强也救不回来。调试时把原始值和滤波后的值一起打印出来对比着看能快速定位问题。滤波参数不要拍脑袋定要根据实际信号的频率和噪声水平计算或实测调整。## 8. 总结电容滤波这个知识点看起来简单真正落地时涉及的细节却很多。硬件上要懂充放电原理、容抗公式、实际电容模型会算 RC 截止频率和电源滤波电容容量软件上要会滑动平均、中值、一阶低通等常用算法并且知道参数变化带来的影响。之前有读者问为什么硬件上已经加了不小的滤波电容AD 采样值还在跳。后来发现是示波器探头地线太长测得的结果本身就是错的。这种问题换再大的电容也解决不了得先把测试方法搞对。做滤波设计时建议先测清楚噪声真实存在的频率和幅度再决定是加强硬件滤波还是补一层软件滤波。两种手段配合使用才能把信号处理得干净又可控。