低成本PWM转模拟电压方案:原理、设计与工程实践

📅 2026/7/1 11:30:42
低成本PWM转模拟电压方案:原理、设计与工程实践
1. 项目概述为什么我们需要低成本模拟信号输出在嵌入式开发、工业控制或者电子DIY项目中我们常常会遇到一个看似简单却有点“尴尬”的需求需要用单片机或数字控制器输出一个平滑、可调的模拟电压。比如你想用一块STM32去控制一个老式模拟调光LED的亮度或者驱动一个需要0-5V电压输入的电机调速器又或者为某个传感器提供一个可编程的基准电压。直接的想法可能是找一块带DAC数模转换器功能的单片机但现实往往是你手头的那块性价比极高的芯片比如经典的STM32F103C8T6或者为了成本考虑选型的MCU它偏偏没有集成DAC。这时候重新选型、增加外部DAC芯片如MCP4725都会带来额外的成本和PCB面积。于是一个经典且充满“工程师智慧”的方案就浮出水面了利用PWM脉冲宽度调制配合一个简单的低通滤波器来“模拟”出DAC的功能。这个方案的核心思想非常直观PWM输出的是占空比可变的数字方波其平均电压值与占空比成正比。如果我们能用一个滤波器把方波里的高频成分即开关的毛刺和基波滤除掉只留下缓慢变化的直流分量那么输出端就能得到一个相对平滑的模拟电压。这就像快速摇晃一个装有半杯水的水杯肉眼看到的水面是平稳的原理上就是高频运动被平均化了。我最近在一个小型温控设备上就用了这个方案。主控是一颗没有DAC的GD32需要输出一个0-3.3V的电压去控制一个加热模块的功率。外部DAC芯片要好几块钱而用一个GPIO口加上几个电阻电容成本可能不到一毛钱。这中间的性价比差异就是驱动我们深入理解这个方案的最佳理由。这个方案绝不仅仅是“凑合用”在精度和响应速度要求不极端比如12位分辨率、百Hz级带宽的场合它完全能胜任并且具有极高的灵活性和成本优势。接下来我就把这个方案的里里外外、设计要点、实操坑点都拆解清楚。2. 方案核心原理与设计权衡2.1 PWM信号的直流分量本质要理解这个方案首先得看透PWM的本质。一个理想的PWM波其电压值在高电平Vdd如3.3V和低电平0V之间跳变。占空比Duty Cycle定义为高电平时间在一个周期内所占的比例。当我们说这个PWM的“平均电压”时在数学上就是在一个周期内对电压值进行积分再平均。对于一个幅值为Vdd的PWM其平均电压 Vavg D * Vdd其中D是占空比0到1之间。例如3.3V系统50%占空比对应的平均电压就是1.65V。但是这个“平均电压”是我们的目标PWM信号本身并不是一个干净的直流电压它包含了丰富的谐波。除了我们想要的直流分量频率为0Hz它还包含一个基波频率等于PWM频率Fpwm及其无数个倍频谐波。我们的目标就是用低通滤波器像筛子一样把这些高频的“杂质”滤掉只留下我们想要的直流“细沙”。2.2 低通滤波器的角色与选型低通滤波器是这个方案中的关键“翻译官”负责将数字化的PWM语言“翻译”成平滑的模拟电压。它的性能直接决定了输出信号的质量——平滑度纹波大小和响应速度。一阶RC无源滤波器是最简单、最廉价的选择只需要一个电阻和一个电容。它的传递函数决定了其衰减特性截止频率 Fc 1 / (2πRC)。对于频率远高于Fc的信号其衰减速率是-20dB/十倍频程。这意味着如果PWM频率是1kHz滤波器截止频率设为10Hz那么1kHz的基波成分会被衰减大约40dB100倍这能有效平滑电压但响应速度会很慢。高阶滤波器如二阶、四阶可以提供更陡峭的滚降特性。例如一个二阶巴特沃斯低通滤波器在截止频率后的衰减速率是-40dB/十倍频程。这意味着它能用更高的截止频率从而获得更快的响应速度达到与一阶滤波器相同的对PWM基波的衰减效果。或者在相同截止频率下它能提供更好的高频抑制从而得到纹波更小的输出。有源滤波器使用运放还可以提供增益和缓冲避免负载影响滤波效果但成本也相应增加。设计时的核心权衡三角是PWM频率、滤波器截止频率、响应速度与纹波。这是一个需要仔细平衡的三角关系提高PWM频率可以让PWM基波和谐波离我们想要的直流信号更“远”更容易被滤波器滤除。这样我们可以适当提高滤波器的截止频率从而获得更快的响应速度同时保持低纹波。降低滤波器截止频率可以更彻底地滤除高频噪声得到更平滑的输出但代价是系统响应变慢。当你要改变的PWM占空比时输出电压需要多个RC时间常数才能稳定到新值。响应速度需求你的应用需要多快跟上设定值的变化对于温控这类慢过程几百毫秒的响应可以接受但对于电机控制或者音频生成可能需要毫秒甚至更快的响应。在我的温控项目中加热器本身的热惯性很大响应速度要求不高秒级但我希望电压尽量稳定以减少不必要的功率波动。因此我选择了较高的PWM频率20kHz超出人耳可闻范围也远离可能的干扰频段搭配一个截止频率约2Hz的二阶有源低通滤波器。这样20kHz的纹波被极大地抑制而温度设定值变化的响应也在可接受范围内。2.3 方案优势与局限性分析优势极低成本核心元件仅为电阻、电容可能再加一个廉价运放如LM358。几乎通用任何能输出PWM的数字器件单片机、FPGA、甚至数字逻辑电路均可使用。分辨率高PWM的分辨率取决于计数器的位数。一个16位定时器产生的PWM其占空比调节分辨率可达1/65535理论上比很多12位DAC1/4096还要高。灵活性强通过软件即可改变PWM频率和占空比适应不同滤波和响应需求。局限性输出阻抗与驱动能力无源滤波器的输出阻抗较高约等于滤波电阻R带负载能力弱。当连接后续电路如运放、ADC时负载效应会改变滤波特性导致输出电压不准。解决方案在滤波器后增加一个电压跟随器运放构成进行缓冲。纹波Ripple这是无法完全消除的。输出电压上总会叠加一个微小的、频率等于PWM频率的交流成分。纹波大小取决于PWM频率、滤波器阶数和截止频率。响应速度与低通滤波器的带宽直接相关。需要快速变化的场合此方案可能不适用。绝对精度输出电压精度受限于MCU电源电压Vdd的精度、PWM占空比精度以及运放的失调电压等。对于高精度基准源需求此方案不如专用DAC或基准源芯片。3. 硬件电路设计与参数计算实战3.1 无源一阶RC滤波器设计这是最简单的起点。电路就是一个电阻R和电容C串联从PWM引脚连接到电阻电容另一端接地从电阻和电容的连接点取输出Vout。设计步骤确定PWM频率Fpwm根据你的MCU定时器配置能力选择。通常建议选择较高的频率例如8kHz以上这样更容易滤波。假设我们选择Fpwm 10kHz。确定目标纹波电压Vripple这是你能接受的输出电压波动峰峰值。例如对于3.3V系统要求纹波小于10mV。计算所需的衰减系数PWM方波的基波幅值约为 (2/π)Vdd ≈ 0.636Vdd。对于3.3V系统基波幅值约2.1V。要将其衰减到10mV以下需要的衰减倍数 Att 2.1V / 0.01V 210倍即大约46dB。计算滤波器截止频率Fc对于一阶滤波器衰减公式为Att Fpwm / Fc 当Fpwm Fc时近似成立。因此Fc Fpwm / Att 10,000Hz / 210 ≈47.6Hz。选取R和C值根据公式 Fc 1 / (2πRC)。选取一个常见的电容值比如C 1μF。则 R 1 / (2π * Fc * C) 1 / (2 * 3.14 * 47.6 * 1e-6) ≈3.34kΩ。我们可以取一个标准值3.3kΩ。注意这个计算是近似值实际纹波会略大于计算值因为公式忽略了高次谐波。实际搭建后需要用示波器观察验证。另外输出阻抗等于R3.3kΩ这意味着如果后续电路输入阻抗小于33kΩ就会产生明显的负载效应导致输出电压下降。3.2 有源二阶低通滤波器设计以Sallen-Key拓扑为例为了获得更好的性能我们使用运放构建一个二阶滤波器。Sallen-Key拓扑结构简单易于设计。设计步骤以巴特沃斯响应为例确定设计参数假设 Vdd3.3V Fpwm20kHz 目标Fc2Hz 采用单位增益缓冲结构。查找巴特沃斯系数对于二阶巴特沃斯滤波器通常取 Q0.707 对应的元件系数有多种分配方式。一种常见简化是设 R1R2R C1C2C。计算RC值对于R1R2R C1C2C的Sallen-Key巴特沃斯滤波器截止频率公式为 Fc 1 / (2πRC)。因此RC 1 / (2πFc) 1 / (23.142) ≈ 0.0796。选取标准值先选取一个合适的电容值。由于Fc很低电容值会比较大。我们可以选C 10μF电解电容或钽电容注意极性。则 R 0.0796 / 10e-6 7960Ω 取标准值7.87kΩ或8.2kΩ。绘制电路图PWM信号通过一个电阻R1连接到运放同相输入端。在同相输入端和地之间接电容C1。在运放输出端和同相输入端之间接反馈电容C2。另一个电阻R2接在R1和C1的连接点与运放同相输入端之间这里需要更正标准Sallen-Key单位增益拓扑是输入通过R1连接到运放同相端同相端通过C1接地。运放输出通过C2连接到R1和同相端的连接点。同相端还通过R2接地不更标准的描述是信号经过R1然后节点连接R2到地同时该节点连接C1到运放输出端为了避免混淆建议直接使用标准设计工具或公式。 更可靠的方法是使用在线滤波器计算器如TI的FilterPro或根据标准公式计算。对于巴特沃斯响应若设R1R2R 则C12C, C2C/2 是一种常见配置其中C由Fc和R决定。例如设R10kΩ Fc2Hz 则 C 1 / (2πFcR) 1/(23.142*10000) ≈ 7.96μF。那么C115.92μF, C23.98μF。取标称值C122μF, C24.7μF 并微调R。实操心得对于极低频率的滤波器电容值会很大可能不得不使用电解电容。电解电容的容值误差大、漏电流大会影响滤波器精度和直流偏移。一个技巧是使用T型电阻网络配合较小容值的薄膜电容来模拟大电阻或者考虑使用开关电容滤波器等更专业的方案。在我的项目中我最终使用了两个10μF的钽电容和8.2kΩ的金属膜电阻实测-3dB点约为1.9Hz满足要求。3.3 运放选型与电源去耦运放的选择至关重要输入输出电压范围必须支持从0V到Vdd或你的供电电压的轨到轨Rail-to-Rail输入和输出。否则当PWM占空比接近0%或100%时输出无法接近0V或Vdd。推荐型号MCP6002双运放低成本轨到轨、TLV9002等。带宽运放的单位增益带宽至少应是滤波器截止频率的10倍以上。对于Fc2Hz任何通用运放都绰绰有余。失调电压Vos这会引入系统误差。对于精度要求高的场合选择低失调电压的运放。电源去耦必须在运放的电源引脚附近通常小于1cm放置一个0.1μF的陶瓷电容到地用于滤除高频噪声。对于较大电流或长导线供电还应并联一个10μF的电解或钽电容。这是保证运放稳定工作、避免振荡的必备措施千万不能省。4. 软件配置与PWM生成要点4.1 基于STM32 HAL库的PWM配置示例假设我们使用STM32F103C8T6的TIM1的通道1PA8输出PWM。// PWM初始化函数 void PWM_Init(void) { TIM_HandleTypeDef htim1; TIM_OC_InitTypeDef sConfigOC {0}; // 1. 定时器基础配置 htim1.Instance TIM1; htim1.Init.Prescaler 72 - 1; // 系统时钟72MHz 预分频后为1MHz htim1.Init.CounterMode TIM_COUNTERMODE_UP; htim1.Init.Period 1000 - 1; // 自动重装载值ARR 决定PWM频率 // PWM频率 Fpwm 72MHz / ((Prescaler1) * (Period1)) 72M / (72 * 1000) 1kHz // 这里仅为示例实际应设置更高频率如将Prescaler设为9-1 Period为100-1 得到Fpwm72M/(9*100)80kHz htim1.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter 0; htim1.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE; if (HAL_TIM_PWM_Init(htim1) ! HAL_OK) { Error_Handler(); } // 2. PWM通道配置 sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 0; // 初始占空比对应的比较值CCR 0代表0% sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; // 高电平有效 sConfigOC.OCFastMode TIM_OCFAST_DISABLE; sConfigOC.OCIdleState TIM_OCIDLESTATE_RESET; if (HAL_TIM_PWM_ConfigChannel(htim1, sConfigOC, TIM_CHANNEL_1) ! HAL_OK) { Error_Handler(); } // 3. 启动PWM输出 HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1); } // 设置占空比函数 (范围0-1000 对应Period1000) void Set_PWM_Duty(uint16_t duty) { if (duty 1000) duty 1000; // 限制范围 __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, duty); // 设置捕获比较寄存器CCR }关键参数解析Prescaler (PSC)定时器时钟预分频系数。定时器实际时钟 系统时钟 / (PSC1)。Period (ARR)自动重装载值。计数器从0计数到ARR然后归零形成一个周期。Pulse (CCR)捕获/比较值。当计数器计数到CCR值时根据PWM模式翻转输出电平。PWM频率计算公式Fpwm F_tim_clk / ((PSC1) * (ARR1))。其中F_tim_clk是定时器的输入时钟频率。占空比计算公式Duty (CCR1) / (ARR1)。在HAL库中通常设置CCR值来控制占空比。4.2 提高PWM有效分辨率与频率的策略有时我们需要高分辨率如16位的同时也需要高频率但这会冲突因为ARR值决定了分辨率ARR越大分辨率越高而频率Fpwm F_tim_clk / ( (PSC1)*(ARR1) )。在时钟F_tim_clk固定的情况下ARR和Fpwm成反比。策略一利用定时器时钟预分频如果系统时钟很高如STM32F1的72MHz我们可以设置较小的PSC从而在较高的ARR值下仍能获得较高的PWM频率。例如目标PWM频率80kHz16位分辨率ARR65535。计算所需定时器时钟F_tim_clk_needed Fpwm * (ARR1) 80k * 65536 ≈ 5.24GHz这显然不可能。因此在需要高频率时必须降低分辨率要求。例如80kHz频率下若F_tim_clk72MHz则(PSC1)*(ARR1) 72M/80k 900。我们可以设PSC0则ARR899分辨率约为1/900略低于10位。这通常足够用于模拟输出。策略二使用高级定时器互补输出与死区对于电机控制等应用TIM1/TIM8支持互补输出和死区插入。死区时间Dead Time是为了防止上下桥臂同时导通而设置的短暂关闭时间。在HAL_TIM_PWM_ConfigChannel之后可以通过HAL_TIMEx_ConfigBreakDeadTime()函数配置死区时间。这对于驱动H桥电路至关重要但在单纯的模拟电压生成场景中一般不需要。策略三动态调整PWM频率如果应用场景对响应速度和平滑度有不同阶段的要求可以在软件中动态改变ARR或PSC寄存器来切换PWM频率。但要注意改变这些寄存器可能会引起当前周期输出异常最好在计数器为0时同步更新。5. 系统测试、校准与性能优化5.1 测试方法与仪器使用静态精度测试工具数字万用表高阻抗模式。方法编写程序让PWM输出固定的占空比例如0% 25% 50% 75% 100%。用万用表测量滤波器后的输出电压。记录实测电压与理论电压Duty * Vdd的偏差。这个偏差反映了系统的整体精度包括MCU电源误差、PWM占空比误差、运放失调等。动态响应与纹波测试工具数字示波器。方法纹波观察将示波器探头设置为交流耦合AC Coupling垂直档位调到最小如10mV/div时基调到适合观察PWM周期的档位如50μs/div。观察输出电压波形上的毛刺或正弦波其峰峰值即为纹波电压。确保其在设计范围内。阶跃响应测试编写程序让PWM占空比从一个值阶跃到另一个值如从10%跳到90%。用示波器直流耦合观察输出电压的上升曲线。测量从阶跃开始到输出电压稳定在终值±5%范围内的时间这就是系统的响应时间。它应近似等于滤波器时间常数的数倍。负载调整率测试工具可调电子负载或不同阻值的功率电阻。方法在滤波器输出端接入一个可变负载。测量空载和带载如拉电流1mA 5mA时的输出电压变化。变化越小说明输出驱动能力越强通常得益于运放缓冲。对于无源RC滤波器这个变化会非常明显。5.2 软件校准与线性度补偿即使硬件完美由于电源电压微小的波动和运放的失调输入占空比和输出电压之间也可能存在非线性或偏移。我们可以通过软件进行两点校准。校准步骤让MCU输出Duty_min如0用高精度万用表测量实际输出电压Vout_min。让MCU输出Duty_max如对应ARR的值测量实际输出电压Vout_max。在软件中建立一个线性映射关系。假设我们期望的电压范围是V_target_min到V_target_max。计算实际斜率slope (Vout_max - Vout_min) / (Duty_max - Duty_min)计算实际偏移offset Vout_min那么为了得到目标电压V_target 需要设置的占空比Duty_set应为Duty_set Duty_min (V_target - V_target_min) * (Duty_max - Duty_min) / (V_target_max - V_target_min)但这是理想情况。更通用的方法是我们存储Vout_min和Vout_max的实测值。当需要输出任意电压V时使用公式Duty Duty_min (V - Vout_min) * (Duty_max - Duty_min) / (Vout_max - Vout_min)实操心得校准最好在系统上电稳定一段时间后进行。如果MCU的Vdd由LDO提供且非常稳定那么主要误差来源可能是运放的失调电压。对于精度要求不高的场合可以忽略校准。对于要求高的场合甚至可以进行多点校准并存储一个查找表LUT。5.3 降低纹波与噪声的进阶技巧增加PWM频率这是最有效的方法。将PWM频率提高到滤波器截止频率的100倍甚至1000倍以上可以极大地降低基波幅值从而在相同的滤波器阶数下获得更小的纹波。例如使用STM32的高级定时器在72MHz时钟下很容易产生100kHz以上的PWM。采用高阶滤波器如四阶巴特沃斯或贝塞尔滤波器。高阶滤波器在截止频率附近有更陡峭的滚降能更有效地抑制PWM基波。可以使用现成的滤波器设计软件如FilterPro Analog Filter Wizard来设计。后级增加LC滤波器在RC或有源滤波器之后再串联一个电感L和电容C组成二阶滤波器可以进一步滤除高频噪声。但电感体积大、成本高且可能引入谐振问题需谨慎设计。优化PCB布局PWM走线尽量短远离模拟信号线和滤波器部分。滤波器部分的元件尤其是电容应紧靠运放引脚放置。为模拟部分运放、滤波器提供干净、独立的电源并通过磁珠或0Ω电阻与数字电源隔离。大面积铺地为模拟信号提供良好的回流路径。6. 常见问题排查与实战案例6.1 问题速查表现象可能原因排查步骤与解决方案输出电压纹波过大1. PWM频率过低。2. 滤波器截止频率过高。3. 滤波器阶数不够。4. 电源噪声大。5. 负载过重或动态变化。1. 用示波器测量PWM引脚频率是否正确。2. 计算或测量滤波器截止频率确认是否远低于PWM频率建议100倍以上。3. 考虑增加滤波器阶数如一阶改二阶。4. 检查运放电源引脚的去耦电容0.1μF陶瓷电容是否紧靠引脚。5. 测量空载和带载时的纹波确认是否为负载引起考虑增加运放缓冲。输出电压无法达到Vdd或0V1. 运放不是轨到轨输出。2. PWM占空比未设置为0%或100%。3. 滤波器存在漏电流如电解电容。4. 负载电流过大超出运放输出能力。1. 确认运放型号是否支持轨到轨输出RRIO。2. 检查软件中CCR值是否设置为0和ARR。3. 更换为漏电流小的电容如薄膜电容、钽电容。4. 测量运放输出端电流选择输出电流能力更强的运放。输出电压响应太慢滤波器截止频率过低。1. 计算滤波器的时间常数τRC或类似。响应时间约为3τ-5τ。2. 根据应用需求权衡纹波和响应速度适当提高截止频率。可能需要同时提高PWM频率。输出电压有台阶或不平滑1. PWM分辨率不足占空比变化步进太大。2. 软件中占空比更新方式导致毛刺。1. 增加PWM的ARR值以提高分辨率但会降低频率需权衡。2. 在更新PWM占空比寄存器CCR时确保在计数器为0或使用定时器的预装载功能如HAL库中__HAL_TIM_SET_COMPARE是安全的。系统上电后输出电压漂移1. 运放输入失调电压随温度变化。2. 电容特别是电解电容的漏电流或介质吸收效应。3. 电源电压不稳定。1. 选用低失调电压、低温漂的运放。2. 避免在关键路径使用大容量电解电容改用多个陶瓷电容并联或钽电容。3. 为模拟部分使用线性稳压器LDO供电并确保其带载能力和散热。6.2 实战案例基于STM32G474的高精度可调电压源在这个案例中我需要一个0-3V可调、分辨率优于1mV、纹波小于1mV的电压源。STM32G474具有高精度定时器可以产生非常高分辨率的PWM。方案设计PWM生成使用STM32G474的高分辨率定时器HRTIM其计数器时钟可达4.6GHz通过PLL。我配置产生一个200kHz的PWM信号。选择200kHz是为了平衡滤波难度和开关损耗对后级MOSFET。HRTIM可以轻松实现16位分辨率ARR65535此时占空比最小步进对应电压变化约为 3V / 65536 ≈ 46μV 远优于1mV的要求。滤波电路为了在200kHz下实现极低纹波我设计了一个四阶巴特沃斯有源低通滤波器截止频率设定为50Hz。使用FilterPro工具选择单位增益Sallen-Key拓扑计算得到两组RC值。运放选用双路、低噪声、轨到轨的OPA2182。缓冲与输出滤波器后级再跟随一个由OPA2182另一路构成的电压跟随器提供低输出阻抗1Ω确保带载能力。校准系统内置一个16位ADC如STM32G474内部的ADC用于采样一个外部高精度基准电压源如REF5025和本方案输出的电压。上电后MCU控制输出几个已知占空比的电压用ADC读取实际值通过最小二乘法拟合出占空比-电压曲线并存储在Flash中。后续输出时通过这个曲线进行逆运算补偿消除运放失调和非线性。实测结果输出电压纹波在示波器上AC耦合 1mV/div几乎为一条直线峰峰值小于500μV。在0-3V范围内用6位半数字万用表测量线性度误差在±0.5mV以内。整个方案的成本远低于同等性能的16位DAC模块。这个案例表明通过精心设计PWM参数、采用高阶滤波和软件校准基于PWM和滤波器的模拟输出方案完全可以达到甚至超越中低档专用DAC的性能尤其在需要高分辨率、低成本且对绝对精度要求可通过校准解决的场合其优势非常明显。