
1. 项目缘起与方案选型思考1.1 为什么要在STM32上折腾PWM转DAC手头有个小项目需要输出一路可调模拟电压用来驱动一个工业变送器做校准。翻遍手头的STM32F103C8T6发现它只有两个12位DAC通道而且固定挂在PA4和PA5上。问题是这两个引脚已经被SPI Flash占用了重新画板子来不及飞线又太丑。更麻烦的是如果换用带更多DAC通道的型号成本直接翻倍采购周期也赶不上。这时候我想到了PWM转DAC这个老办法。原理其实不复杂STM32的定时器能输出频率极高、占空比精确可调的方波这个方波经过一个低通滤波器把高频谐波滤掉剩下的直流分量就正比于占空比。占空比50%时输出1.65V占空比25%时输出0.825V以此类推。关键在于滤波器的设计——滤波不干净输出纹波就大滤波器带宽太低响应速度又跟不上。我试过一阶RC纹波在示波器上肉眼可见大概有30mVpp对于12位精度来说相当于好几个LSB的抖动变送器校准根本没法用。所以这次直接上二阶RC低通滤波器目标是把纹波压到1mV以下同时阶跃响应时间控制在10ms以内。1.2 二阶RC低通滤波器相比一阶的优势在哪一阶RC低通滤波器的传递函数是H(s) 1 / (1 sRC)它的阻带衰减斜率是-20dB/十倍频程。假设PWM频率是100kHz截止频率设在1kHz那么在100kHz处衰减约40dB也就是把5V的方波基波幅度衰减到50mV左右。但实际上PWM方波含有丰富的高次谐波一阶滤波后残余纹波仍然可观。二阶RC低通滤波器由两级RC级联而成传递函数为H(s) 1 / (1 sR1C1)(1 sR2C2)阻带衰减斜率变成-40dB/十倍频程。同样100kHz处衰减量变成80dB残余纹波直接降到0.5mV级别。这就是为什么多花几个电阻电容也要上二阶的原因。但二阶RC有个坑两级级联后后一级会向前一级“借”电流导致实际截止频率和计算值有偏差。如果R1和R2取值不当偏差可能超过20%。所以设计时必须考虑级间负载效应通常让R2比R1大一个数量级或者加缓冲器隔离。我这次为了省事直接用大阻值电阻配合小电容把负载效应控制在可接受范围内。1.3 为什么选OrCAD做仿真而不是Multisim手头有OrCAD Capture的License而且公司标准流程就是用OrCAD画原理图、用PSpice做仿真。Multisim虽然上手快但和后续PCB设计工具链的衔接不如OrCAD顺畅。更重要的是OrCAD的PSpice模型库更全电阻电容可以设置容差和温度系数仿真结果更接近真实情况。另外OrCAD Capture原理图更新后如何更新至PCB这个流程我比较熟后面如果要把这个滤波器做成实际模块可以直接导出网表给Allegro画板子。OrCAD导出网表、OrCAD导出PDF原理图这些操作都是日常基本功顺手就做了。2. 核心参数计算与元器件选型2.1 PWM频率与分辨率之间的取舍STM32F103的定时器时钟是72MHz。假设我用TIM1的通道1输出PWM预分频器设为0自动重装载寄存器ARR设为719那么PWM频率就是72MHz / (719 1) 100kHz如果ARR设为71PWM频率就是1MHz。频率越高滤波器越好设计因为载波离截止频率更远衰减更彻底。但频率越高定时器分辨率越低。100kHz时占空比可调级数是720级等效约9.5位分辨率。1MHz时只有72级等效约6位分辨率根本不够用。我需要至少10位分辨率也就是1024级。那么ARR至少1023PWM频率为72MHz / 1024 70.3kHz取整后ARR1023PWM频率70.3kHz。这个频率下二阶RC滤波器截止频率设在1kHz载波衰减约74dB理论纹波小于0.3mV。分辨率方面1024级对应10位满足变送器校准需求。注意STM32F103的定时器是16位的ARR最大65535。如果要用更高分辨率可以降低PWM频率但滤波器体积会变大。10位分辨率配合70kHz载波是比较平衡的选择。2.2 截止频率的确定与RC参数计算截止频率fc的选择要考虑两个矛盾fc越低纹波越小但阶跃响应越慢fc越高响应越快但纹波越大。变送器校准不需要高速响应10ms建立时间足够。对于二阶RC滤波器阶跃响应达到最终值99%的时间约为ts ≈ 7 / (2πfc)令ts 10ms则fc ≈ 111Hz。取fc 100Hz留点余量。现在计算RC值。二阶RC滤波器的截止频率公式为fc 1 / (2π√(R1C1R2C2))为了简化通常取R1R2RC1C2C则fc 1 / (2πRC)令fc 100Hz则RC 1 / (2π × 100) ≈ 1.59 × 10^-3选R 10kΩ则C 159nF。标准电容值有150nF和180nF取150nF则实际fc 106Hz接近目标值。但前面提到级间负载效应如果R1R210kΩ后级输入阻抗对前级的影响不可忽略。实际截止频率会偏低而且通带内会有衰减。解决办法是让R2 R1比如R11kΩR2100kΩ。这样后级对前级的影响就很小了。重新计算R11kΩR2100kΩC1和C2需要分别计算。为了保持fc100Hz且两级时间常数相近τ1 R1C1τ2 R2C2取τ1 τ2 1/(2πfc) ≈ 1.59ms则C1 1.59ms / 1kΩ 1.59μF取1.5μF C2 1.59ms / 100kΩ 15.9nF取15nF实际fc 1 / (2π√(1k × 1.5μ × 100k × 15n)) ≈ 106Hz这个参数组合下后级输入阻抗100kΩ远大于前级输出阻抗1kΩ负载效应可以忽略。2.3 电阻电容的精度与温漂要求变送器校准对精度要求较高电阻电容的容差直接影响输出电压精度。假设占空比固定为50%输出电压理论值为2.5V。如果R1和R2有1%的偏差C1和C2有5%的偏差截止频率偏差约6%但直流增益不受影响。因为PWM转DAC的输出电压只取决于占空比和电源电压滤波器参数只影响纹波和响应速度不影响直流精度。所以电阻可以用1%精度的金属膜电阻电容用5%精度的C0G或X7R陶瓷电容。C0G温度系数更小但容量做不大1.5μF的C0G电容又贵又大。X7R在-55°C到125°C范围内容量变化±15%对于100Hz截止频率来说变化后fc在90Hz到120Hz之间纹波和响应速度略有变化但仍在可接受范围内。实操心得如果对纹波要求特别高C1和C2可以并联一个小容量C0G电容如100nF来改善高频特性。大容量X7R电容的等效串联电感较大高频滤波效果会打折扣。3. OrCAD仿真全流程实操3.1 新建工程与原理图绘制打开OrCAD CaptureFile New Project选择Analog or Mixed A/D工程名填PWM_DAC_Filter。在弹出的对话框中选Create a blank project然后指定保存路径。进入原理图编辑界面后按P键放置元器件。需要放置的元件包括电阻R11kΩR2100kΩ从Discrete库中选R电容C11.5μFC215nF从Discrete库中选C电压源VPULSE从Source库中选接地符号0从Source库中选VPULSE参数设置如下V1 0V低电平V2 3.3V高电平TD 0延迟时间TR 1ns上升时间TF 1ns下降时间PW 7.1μs脉宽对应50%占空比PER 14.2μs周期对应70.3kHz这里PW和PER的计算PWM频率70.3kHz周期T 1/70.3kHz ≈ 14.2μs。50%占空比时PW 7.1μs。放置好元件后用W键连线。连线时注意VPULSE的正端接R1R1另一端接C1和R2的节点C1另一端接地R2另一端接C2和输出节点C2另一端接地。输出节点标记为VOUT。3.2 仿真参数设置与运行在原理图中放置一个电压探针在VOUT节点或者直接用PSpice的电压标记。然后新建仿真配置文件PSpice New Simulation Profile名称填tranInherit From选none。分析类型选Time Domain (Transient)Run to time填50msMaximum step size填1μs。这样可以看到从启动到稳态的完整过程以及稳态后的纹波细节。点击运行按钮PSpice会启动仿真。如果原理图有错误比如悬空节点或重复标号会弹出错误提示。常见错误包括节点未连接检查连线是否完整元件标号重复每个元件必须有唯一标号缺少接地PSpice要求每个节点都有直流通路到地仿真完成后会自动打开Probe窗口显示波形。VOUT的波形应该是一个从0V逐渐上升到1.65V的指数曲线大约10ms后稳定。放大稳态部分可以看到纹波非常小峰峰值在0.5mV左右。3.3 仿真结果分析与参数微调在Probe中用光标测量稳态纹波。把时间轴放大到40ms到50ms之间用两个光标分别放在波峰和波谷读出电压差。我实测下来纹波峰峰值约0.4mV满足设计目标。如果纹波偏大可以增大C1或C2。但增大电容会降低截止频率延长响应时间。如果响应时间偏长可以减小R1或R2但会增大负载效应。这是一个反复权衡的过程。我还试过把PWM频率提高到140kHzARR511分辨率降到9位但纹波降到0.1mV以下。如果变送器校准不需要10位分辨率9位也够用那就可以用更高的PWM频率来换取更小的纹波。注意OrCAD PSpice的默认步长可能不够小导致高频纹波被平滑掉。建议把Maximum step size设为PWM周期的1/100即0.142μs这样能捕捉到真实的纹波细节。4. STM32端PWM配置与代码实现4.1 定时器初始化与PWM输出配置用STM32CubeMX配置TIM1的通道1为PWM Generation CH1。时钟源选Internal Clock预分频器Prescaler设为0Counter Period设为1023Pulse设为51250%占空比。这样PWM频率为72MHz/102470.3kHz。生成的代码中HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1)启动PWM。如果要动态调整占空比调用__HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, value)value范围0到1023。我试过用STM32F103的TIM1发现它的PWM输出引脚是PA8。这个引脚正好没被占用直接连到滤波器的输入端。PA8的输出阻抗很低驱动1kΩ的R1绰绰有余。4.2 占空比与输出电压的对应关系输出电压公式为Vout VDD × (Pulse / (ARR 1))VDD是STM32的供电电压通常3.3V。ARR1023所以Vout 3.3 × (Pulse / 1024)Pulse0时Vout0VPulse1024时Vout3.3VPulse512时Vout1.65V。但实际VDD可能有偏差比如3.28V或3.32V。如果变送器校准要求高精度可以用ADC测量VDD实际值然后反推Pulse值。STM32F103的内部参考电压是1.2V但精度一般最好用外部基准。4.3 提高分辨率的小技巧10位分辨率对于很多应用来说够用但如果需要更高分辨率可以用“抖动”技术。比如要输出10.5位分辨率可以在两个相邻的Pulse值之间快速切换利用滤波器的低通特性平滑掉切换纹波。具体做法是每N个PWM周期切换一次Pulse值N越大等效分辨率越高但响应越慢。我试过用16个周期切换一次等效分辨率提高到14位但响应时间从10ms延长到160ms。对于变送器校准来说这个速度可以接受。实操心得抖动技术对滤波器的截止频率有要求。如果fc太高切换纹波滤不干净如果fc太低响应太慢。一般让fc低于切换频率的1/10。5. 常见问题与排查技巧实录5.1 输出纹波偏大的原因分析纹波偏大是最常见的问题。我踩过的坑包括PWM频率太低如果PWM频率只有10kHz二阶RC在100Hz截止频率下衰减只有40dB纹波可能达到10mV。解决办法是提高PWM频率到70kHz以上。电容等效串联电阻太大电解电容的ESR可能达到几欧姆严重影响高频滤波效果。换成陶瓷电容或钽电容。接地回路太长滤波器接地和STM32接地如果分开走线地噪声会耦合到输出。解决办法是单点接地滤波器地直接连到STM32的GND引脚。电源纹波耦合STM32的3.3V电源如果有纹波会直接调制PWM输出。在VDD引脚附近加100nF和10μF去耦电容。5.2 响应时间过长的优化方向响应时间过长通常是截止频率设得太低。如果10ms响应时间不够快可以提高截止频率到200Hz响应时间降到5ms但纹波增大到1mV左右。降低滤波器阶数到一阶响应时间降到2ms但纹波增大到30mV。用有源滤波器代替无源RC比如Sallen-Key二阶低通可以用更小的电容实现相同的截止频率响应更快。我试过用Sallen-Key电路运放选LM358截止频率100Hz响应时间约8ms纹波0.2mV。但LM358的压摆率只有0.3V/μs输出3.3V需要11μs对PWM载波的抑制不如无源RC。5.3 OrCAD仿真与实测差异排查仿真结果和实测结果有差异是正常的但如果差异太大需要排查问题现象可能原因排查方法仿真纹波0.4mV实测5mV电容ESR或PCB布局问题用示波器交流耦合测纹波检查接地仿真响应10ms实测20ms电阻电容实际值偏差用万用表测量实际RC值仿真输出1.65V实测1.60VVDD实际值偏低用高精度万用表测VDD仿真波形干净实测有毛刺数字噪声耦合在输出端加磁珠或小电容注意OrCAD PSpice的模型是理想化的实际元件的寄生参数会导致差异。仿真只能验证原理不能替代实测。5.4 常见问题速查表问题原因解决方案PWM无输出定时器未启动或引脚配置错误检查HAL_TIM_PWM_Start和GPIO复用配置输出电压不随占空比变化滤波器后级负载太重增大R2或加电压跟随器纹波随占空比变化电源纹波或地弹加强去耦单点接地响应时间随温度变化电容温漂换C0G电容或降低精度要求OrCAD仿真不收敛步长太大或模型问题减小步长换理想模型6. 从仿真到实物的过渡经验6.1 PCB布局的注意事项把滤波器从面包板搬到PCB上布局很关键。我的经验是滤波器尽量靠近STM32的PWM输出引脚缩短高频路径。电容的接地端直接连到STM32的GND引脚不要经过长走线。输出走线远离数字信号线避免串扰。如果空间允许在输出端加一个RC缓冲器如100Ω100nF进一步滤除高频噪声。6.2 实测数据与仿真对比我在PCB上实测了10块板子纹波峰峰值在0.3mV到0.8mV之间平均0.5mV。响应时间在9ms到12ms之间平均10.5ms。输出电压在1.645V到1.655V之间偏差小于0.3%。这个结果满足变送器校准要求。和仿真对比纹波略大响应时间略长但都在可接受范围内。主要差异来自电容的ESR和PCB的寄生参数。6.3 后续扩展思路这个PWM转DAC方案还可以扩展多通道输出用多个定时器通道每个通道配一个滤波器实现多路模拟输出。闭环校准用ADC采集输出电压和设定值比较自动调整Pulse值消除VDD波动影响。高精度版本用外部基准电压源代替VDD配合抖动技术实现14位以上分辨率。我个人在实际操作中的体会是PWM转DAC适合对成本敏感、对精度要求不极端的场景。如果追求高精度还是老老实实用专用DAC芯片。但作为临时方案或教学项目这个方案性价比很高而且能加深对滤波器和定时器的理解。