ARTICLE DETAIL

资讯详情

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

STM32无DAC怎么办?用PWM加RC滤波实现低成本模拟输出

STM32无DAC怎么办?用PWM加RC滤波实现低成本模拟输出 搞嵌入式这些年几乎每个用STM32的项目都会撞上同一个需求想输出一个可调的模拟电压。ADC采样大家都熟顺手就配好了但一说到模拟输出就尴尬。尤其是 STM32F103C8T6 这类最常用的芯片本身不带DAC外设翻手册翻了半天只能默默接受现实。有人会换芯片有人会加外部SPI DAC但对于大量不需要极端精度、只求稳定可控的项目用一个定时器输出PWM、再搭一级RC低通滤波器就是性价比最高的解法。这套方案不需要额外专用芯片不占用I2C/SPI总线一颗电阻一颗电容就能把PWM变成直流电压配合软件查表甚至能输出正弦波、语音这类连续信号。调试得当等效分辨率能做到接近10到12位日常做可调电压源、LED亮度曲线、传感器基准偏置、简易信号发生器都够用。这篇内容围绕“STM32 PWM DAC”展开适合已经会用定时器输出PWM、但还没把PWM和模拟输出真正打通的朋友也希望给正在为“没有DAC”挠头的开发者一个完整的参考思路。1. 为什么非要用PWM来做DAC1.1 手里这颗芯片没有DAC怎么办很多朋友第一次发现STM32F103C8T6没有DAC是在画原理图的时候。项目需要一路可调的基准电压心想片上不是有DAC吗结果翻数据手册才发现F103系列里带DAC的型号集中在Cx系列以上C8T6这种性价比之王根本没有。这时候可行的方案就几条换芯片、外挂DAC、用PWM滤波。换芯片牵扯硬件改动外挂DAC芯片成本上去了通信时序也比PWM复杂。PWM方案则几乎零成本——定时器本来就要用GPIO本来就要引RC滤波两个元件加起来不到一毛钱。更重要的是PWM输出天然适合大动态范围调节占空比从0到100%对应满量程电压控制思路非常直观。1.2 PWM怎么变成模拟电压一句话原理一个方波信号如果周期远小于负载回路的响应时间负载看到的就不是高高低低的变化而是在高低电平之间的一个平均值。这个平均值等于占空比乘以幅值。电压 PWM高电平幅值 × 占空比 PWM低电平幅值 × (1 - 占空比)对于3.3V供电、占空比50%的PWM输出理想值是1.65V占空比75%就是2.475V。RC低通滤波器在这个场景里干的活就是平滑——方波的高频成分被电容吸收掉剩下的直流分量保留下来。1.3 与片上真实DAC的差距在哪想用PWM模拟DAC得知道它和真实DAC的区别。片内真实DAC响应速度极快输出建立时间在微秒级而且没有原理性的纹波。PWM方案输出是被滤波的必然存在残余纹波和较长的建立延迟。可以用一个不完美的比喻真实DAC像一位熟练的画家拿起笔精确调色PWM加RC更像一个人用水龙头往桶里放水水量是平均流量但水面始终有一点波动而且水位到达目标需要时间。所以PWM-DAC适合用于对精度和带宽要求不那么苛刻的场景比如静态偏置、慢速波形、音频重放这类信号频率远低于PWM频率的应用。2. RC滤波电路的完整设计思路2.1 低通滤波的本质从方波里“捞”出直流分量RC低通滤波器是电阻和电容串联的简单结构信号输入到R的一端从C两端取输出。它的核心指标是截止频率fc 1 / (2 × π × R × C)高于截止频率的信号会被衰减低于截止频率的信号基本无损通过。PWM方波里包含直流分量、PWM频率基波、各次谐波设计目标就是让直流分量顺利通过、PWM频率及以上的成分被大幅压制。打个比方方波进入RC滤波器就像把石子扔进水池水面会泛起波纹但离落点越远波纹越平。RC的截止频率越低相当于水池越大波纹越平代价是水位变化也越慢。2.2 截止频率怎么算PWM频率、纹波与响应速度的权衡这里有个逃不开的三角关系PWM频率、RC截止频率、响应速度三者互相牵扯。PWM频率越高滤波相对容易但单片机能提供的占空比分辨率可能下降RC截止频率越低纹波越小但输出电压建立时间越长。纹波电压有个工程近似估算公式ΔVpp ≈ Vpwm / (2 × π × fPWM × R × C)举个例子PWM频率70.3kHz幅值3.3VR取1kΩ、C取100nF算下来纹波大约在75mV左右。这个数值对许多场合仍然偏大继续加大电容到1μF纹波能降到7.5mV。但注意R×C变大了时间常数变成1ms输出从0V跳到3.3V需要约5ms才能稳定。所以在定截止频率前先问自己两个问题允许的纹波是多少输出允许的建立时间是多少。2.3 一阶、二阶到三阶到底该用几级RC一阶RC最简单但滤波斜率只有-20dB每十倍频程也就是说提高PWM频率十倍纹波只降到原来的十分之一。工程上一阶RC适合对纹波不敏感、或对成本面积极度敏感的场景。二阶RC就是把两级一阶RC串联起来滤波斜率到-40dB每十倍频程。同样的截止频率二阶对PWM高频分量的压制能力强得多剩余纹波明显小于一阶。代价是相位延迟更大、阶跃响应可能出现轻微过冲。三级以上滤波收益递减元件误差还会带来不必要的一致性风险。设计二阶RC时有个关键细节两级参数不能完全相同。第二级RC会从第一级输出抽取电流形成负载效应。一个常见做法是第一级取较小的R、较大的C第二级取较大的R、较小的C同时R2取R1的10倍左右减小后级对前级的影响。2.4 输出级要不要加运放RC滤波网络直接输出电压会受负载阻抗影响。你接一个10kΩ负载和一个100Ω负载得到的电压完全不同因为负载电阻和RC网络构成了并联分压。如果需要稳定的电压输出或者后端电路输入阻抗不高就必须加一级运放缓冲。最常用的是电压跟随器输入接运放正端输出直接反馈到负端。运放输入阻抗极高几乎不从RC网络抽取电流输出阻抗极低带载能力强。选运放时优先选轨到轨输入输出、供电电压能覆盖0到3.3V的型号否则输出范围两端会削掉一部分。3. STM32端的软件实现与参数配置3.1 定时器PWM的硬件机制STM32的PWM输出由定时器产生。以STM32F103的TIM3为例内部有一个从0向上计数的计数器CNT还有一个自动重载寄存器ARR决定计数上限以及捕获比较寄存器CCR决定输出翻转点。当CNT小于CCR时输出有效电平CNT到达CCR且未到ARR时输出另一个电平CNT到达ARR后清零重新计数如此形成周期性的PWM波形。一句话记忆ARR决定PWM频率CCR决定占空比。PWM频率与占空比分辨率的换算关系很关键fPWM TIM_CLK / ((ARR 1) × (PSC 1))占空比 CCR / (ARR 1)3.2 用CubeMX快速生成工程的关键参数假设目标频率70.3kHz主频72MHz想得到整10位分辨率即1024级占空比可以直接令PSC0ARR1023。这时fPWM 72MHz / (1023 1) 70.3125kHz这个组合非常规整ARR1正好是1024占空比步进是1/1024约等于满量程3.3V下的3.2mV每级。CubeMX里配置TIM3为PWM Generation CH1PSC填0Counter Period填1023Pulse填512作为初始占空比保持PWM模式1和High polarity直接生成代码。启动输出的代码极简单HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1);想改变输出电压就更新CCR__HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, duty_value);注意duty_value范围是0到1023对应0到3.3V。这个方案的分辨率已经比很多8位DAC芯片更精细日常使用足够。3.3 分辨率、PWM频率和系统时钟的三角关系有朋友会问既然定时器ARR最大能到65535能不能直接做16位分辨率理论可以但工程上不现实。ARR越大PWM频率越低。比如ARR4095时PWM频率只有17.58kHz要滤掉这么低的基波RC截止频率就要压到几百Hz输出建立时间会慢到无法接受而且12位分辨率下元件温漂和噪声带来的误差会淹没低几位。所以工程上的最优解是“够用就好”。10位PWM配合精心设计的RC网络已经能输出稳定平滑的模拟电压成本极低。要真正追求12位以上稳定输出更专业的做法是外挂高精度DAC芯片而不是死磕PWM方案。3.4 从固定电压到任意波形DMA持续更新占空比PWM-DAC不仅能输出固定电压还能输出变化波形。思路是把一段波形的幅度值预先量化成占空比序列通过定时器更新事件逐点刷新CCR。如果每秒更新8k个点就能得到8kHz采样率的模拟波形。一个更高效的实现是用DMA搬运数据。把正弦波查找表放在数组里配置TIM3的更新事件触发DMA把数组内容循环传输到CCR寄存器。CPU只需要在改变波形时重新配置波形输出期间完全不用参与uint16_t sine_wave[256]; // 预先计算正弦波占空比表 for (int i 0; i 256; i) { sine_wave[i] 511 (int)(511.0f * sinf(2 * 3.14159f * i / 256.0f)); } HAL_TIM_PWM_Start_DMA(htim3, TIM_CHANNEL_1, (uint32_t *)sine_wave, 256); // 配置DMA为circular模式用这个方式输出正弦波RC截止频率要适当提高一些否则波形高频部分会被过度削弱但也不能太高否则纹波会变大。一个实践经验是让RC截止频率处于输出信号最高频率的3到5倍同时尽量远低于PWM频率。4. 精度考量与校准方法4.1 误差来源拆解谁在吃掉你的LSB你以为占空比算得准输出电压就一定准实际测下来才发现偏差比预期大不少。PWM-DAC的误差来源要分开看一是占空比到平均电压的数学误差二是RC网络引入的元件误差三是系统基准电压误差四是电容吸收噪声带来的动态误差。数学层面公式本身是准确的占空比定了平均电压就定了。真正出问题的往往是3.3V供电本身不精确。很多开发板上的3.3V是LDO输出标称3.3V实际可能是3.28V也可能是3.32V。这个偏差会直接反映在输出电压上10位精度下满量程偏差就能达到几十mV。RC元件的容差也同样直接贡献误差碳膜电阻5%容差、X7R电容在直流偏压下电容量变化20%都是不可忽视的因素。4.2 电源与地线被忽略的第一噪声源PWM-DAC输出纹波除了RC滤波不彻底很多时候来自地线和电源干扰。当PWM引脚在高低电平间切换时瞬间电流会在电源和地线上产生毛刺这些毛刺被后级电路拾取后呈现在输出端。想改善这个问题首先在单片机的VDD和GND之间就近放一个100nF陶瓷电容加一个10μF电解电容去耦这个电容要靠近电源引脚而不是放远了。其次RC滤波部分的“GND”要从单片机的地线附近单独引出不要和电机、继电器这类大电流回路的GND混在一起。最后如果PWM输出频率较高可以考虑降低GPIO的翻转速度STM32引脚有速度等级设置在CubeMX的GPIO配置里可以选Low、Medium、High低速应用没必要选High Speed。4.3 两点校准与软件补偿元件误差和供电偏差造成的是一个近似线性的增益误差和偏置误差完全可以用两点校准修正。实测中选定两个点占空比较低处和占空比较高处分别用万用表测出实际电压再线性拟合出真实比例关系。比如设置duty100实测电压0.336V设置duty900实测电压2.910V。理想比例是3.3V/1023≈3.23mV/级。实际计算k (2.910 - 0.336) / (900 - 100) 2.574 / 800 3.2175mV/级b 0.336 - 100 × 3.2175mV 0.01425V于是任意目标电压V对应占空比duty (V - b) / k把这两个参数存储在Flash或EEPROM里每次开机读取并在软件中补偿就能把系统精度提升到受限于3.3V长期稳定性和万用表精度的程度。实测下来校准后10位系统在常温下做到满量程0.2%以内的偏差并不难。5. 元件选型指南电阻和电容怎么挑5.1 电阻精度、温漂与功率电阻选型主要看三个指标精度、温漂和功率。RC滤波对电阻精度要求不高因为最终会有软件校准但如果想降低校准前的硬件偏差选1%精度的E96系列电阻是基础操作。温漂方面普通厚膜电阻温漂大约在100ppm/℃到200ppm/℃环境温度变化30℃就能带来0.3%到0.6%阻值变化直接影响截止频率和输出建立特性。要做尽量稳定的电压输出可以选薄膜电阻或低温漂型号25ppm/℃的电阻在多数场景下足够。功率通常不是问题RC滤波电路电流极小1kΩ电阻在3.3V下功率只有10mW左右0603封装完全扛得住。但高PWM频率下端接的电感类负载要另算那种场景下PWM直接驱动MOS管发热是另一个话题。5.2 电容介质类型背后的坑电容是最容易踩坑的元件。同样是1μF用C0G介质和X7R介质表现完全不同。C0GNP0电容温度特性极好、容值几乎不随电压和温度变化但容量做不大1μF的C0G电容体积和价格都不友好。X7R电容容量大、价格低但存在显著的直流偏压特性——加上额定直流电压后实际容量可能缩水到标称值的一半甚至更低。选电容的思路是小容量低于100nF尽量用C0G大容量用X7R或X5R同时在计算截止频率时留出30%到50%裕量。输出端如果需要更平滑的低频表现可以考虑在RC网络后再并一个低ESR电解电容或钽电容但它会影响响应速度需要实测确认。5.3 常用组合速查清单根据不同的PWM频率和纹波目标这里给一个快速参考组合。说明一下这些都是我在常用项目里实际验过的搭配未必适合所有场景但可以作为起点。目标PWM频率滤波级数电阻R电容C估算纹波适用场景70.3kHz一阶1kΩ100nF约75mV对纹波不敏感成本优先70.3kHz二阶R11kΩR210kΩC1100nFC2100nF约几mV常规可调电压输出17.58kHz二阶R110kΩR2100kΩC11μFC2100nF较低追求更低噪声偏置电压70.3kHz一阶运放缓冲1kΩ1μF C0G约7.5mV带载能力需求高使用这个表的时候注意二阶RC的截止频率并不是简单套用一个公式两级彼此有负载效应最好用Multisim或LTspice仿真验证或者干脆焊出来实测。仿真软件里把实际电容的ESR和容差模型也带上否则和真实板子差距还是有的。6. 常见问题与排查方法6.1 高发问题排查速查表故障现象可能原因排查思路输出电压始终接近0VPWM引脚没输出、GPIO配置错误、PWM未启动先用示波器看PWM引脚波形确认频率和占空比输出电压明显低于理论值占空比参数溢出、供电电压偏低、负载过重万用表测3.3V实际值确认负载阻抗是否过高输出电压纹波大RC截止频率过高、PWM频率过低、电源去耦不足增大C或增加滤波级数检查电源电容输出响应太慢RC时间常数过大减小R或C或考虑用运放加提升电路输出波形存在周期性毛刺软件在中断里更新CCR造成竞争、DMA配置错误加临界区保护确认DMA传输宽度和缓冲区对齐接上后级电路电压立刻跌落缺少运放缓冲负载直接并联到RC网络加一级电压跟随器6.2 实测的几个排查技巧我在调试中遇到过最典型的案例是输出直接接了一个10kΩ的负载电阻设定占空比后电压比理论值低了50mV。一量发现RC网络的输出阻抗和负载构成分压调整R的阻值或者加运放就能解决。很多朋友会忽略输出阻抗这个隐藏参数实际上RC滤波电路本质上是高阻源驱动能力极弱。另一个常见坑是PWM极性。CubeMX里Polarity选中High还是Low直接决定了CCR增大时输出电压是升高还是降低。极性配置反了的话代码里调占空比越大电压反而越小容易误判成硬件故障。最后提醒一句调试PWM-DAC离不开示波器但不要用示波器探头直接跨接在RC网络输出和地之间就完事。探头输入电容约10pF在高阻输出节点上会引入额外负载尤其用1MΩ级别的高阻RC网络时影响明显。需要精确测量时把探头打到10x档衰减同时接一个缓冲级再测。7. 关于这套方案的一点扩展想法如果项目后续需要更高精度的输出电压又不方便换带DAC的芯片还可以在现有PWM方案的基础上做两个升级一是把RC滤波后信号接入外部基准源驱动的运放用增益修正抵消供电误差二是改成Σ-Δ调制方式输出相当于用更高频的脉冲密度模拟电压配合低通滤波能达到比占空比方案更好的精度。我个人在实际操作中的体会是PWM-DAC这套方案最值钱的地方不在于替代DAC而是在于它让你重新理解了“模拟输出”的本质。占空比、滤波、响应时间、噪声来源这些概念一通百通。下次再有人问我低成本模拟输出怎么搞我的回答永远是先算清楚你的纹波预算和时间常数再动手画板子不然焊好了也是乱调。
返回列表