做一台电压电流表,很多人第一反应是买现成的电压电流表模块或者数码管表头来拼。我之前也这么干过,但真正想把它做进自己的便携设备里时,拼装方案的短板就全出来了:体积大、整机功耗高、电流档采样压降大,精度还没办法做系统级校准。后来我把主控换成CW32L012,把模拟采样前端重新设计了一遍,才终于把手持双显电压电流表做进了巴掌大的外壳里,电池供电也能长期在线工作。这篇文章就把完整的设计思路拆开讲,从选型、硬件链路、参数计算到固件实现和校准流程,一步一步地还原我自己的做法,给想用CW32L012这类低功耗MCU做小型仪表的朋友一个可以照抄的参考。
1. 为什么选CW32L012这颗MCU来做仪表
1.1 先把需求清单列出来
做表之前,我一般强迫自己先把指标写死,不然选型没法谈。这次需求是这样的:一路电压测量、一路电流测量,两个量同时显示在LCD屏上。电压范围0到30V,电流范围0到5A,精度目标是电压±0.5%,电流±1%。分辨率至少要有0.01V和0.001A。整机采用锂电池供电,要求能连续运行较长时间,所以整机平均电流必须压得很低。外壳尺寸不超过10cm×6cm,也就是必须单颗芯片完成采集、处理、显示驱动,不能再用独立的ADC芯片加主控的方案。
这些需求听起来不复杂,但组合起来其实很麻烦。0到5A的电流采样属于大电流路径,处理不好会发热;0到30V的电压采样属于高阻路径,处理不好会丢精度;而整机又要电池供电,MCU选大了浪费,选小了怕外设不够。这个矛盾点恰恰决定了主控选型的思路:低功耗、集成ADC、外设够用、价格别太离谱。
1.2 CW32L012的核心资源配置
CW32L012是Cortex-M0+内核的32位低功耗MCU,对我这个项目来说最核心的三个点是:片上12位SAR ADC、多档低功耗运行模式、足够的Flash和RAM空间。具体到这款型号,几十KB级别的Flash和几KB的RAM做仪表完全够用,不会出现写不下程序的问题。12位ADC可以直接吃电压电流采样前端的信号,省掉一颗外部ADC,成本和布线面积都下来了。
低功耗模式是这颗料最值钱的地方。仪表不是一直在满速跑的东西,大部分时间在等待采样间隔和刷新周期。我需要MCU在两次采样之间能睡过去,醒来马上能采集、计算、刷新显示。CW32L012的低功耗模式支持这种间歇工作方式,实测下来整机平均电流能压到非常低的水平,这对电池续航的影响是决定性的。开发环境也很好解决,Keil MDK直接支持,官方固件库上手不费劲,而且这款芯片现在是国内供应链里比较好拿到货的存在。
1.3 和同级别替代方案的对比
选型的时候我当然也看过其他的料。STM32L031低功耗确实强,但价格和供货情况不太适合做这种成本敏感的小仪表;GD32E230性能不错,主频高、Flash大,不过静态功耗比CW32L012高,对电池供电的要求不够友好;新唐的N76E003更便宜,但Flash小、ADC精度一般,而且后续做软件校准时灵活性差一点。整体对比下来,CW32L012在功耗、集成度、价格三者之间取了一个比较舒服的平衡点,尤其是12位ADC和多种低功耗模式的组合,正好命中手持仪表的需求。
| 对比维度 | CW32L012 | STM32L031 | GD32E230 | N76E003 |
|---|---|---|---|---|
| 内核 | Cortex-M0+ | Cortex-M0+ | Cortex-M0+ | 8051 |
| 片内ADC | 12位 | 12位 | 12位 | 12位 |
| 低功耗模式 | 多档,适合间歇工作 | 强 | 一般 | 一般 |
| 价格区间 | 低 | 较高 | 低 | 最低 |
| 开发难度 | 低 | 低 | 低 | 中等 |
我不是说其它料不能用,而是说在这个具体项目里,CW32L012的综合成本最低。如果你的项目对功耗要求没那么苛刻,GD32E230也完全能做,只是电池容量要加一点而已。
2. 电压电流采样前端:一份可以照抄的电路设计
2.1 电压采样链路的搭法
电压采样我采用的是电阻分压方案,这是最简单也最可靠的做法。被测电压从0到30V,不能直接进ADC,因为CW32L012的ADC输入范围是0到参考电压,参考电压我选的是3.3V。所以分压网络要把30V映射到3V左右,留一点余量防止意外过压。
具体阻值我是这么选的:分压网络采用上臂90kΩ、下臂10kΩ,分压比就是10:1,30V输入时下臂得到3V。为什么要用这么大的阻值?因为电压测量端不希望从被测电路吸取太多电流,90k加10k总共100k,30V下只流过0.3mA,对绝大多数被测点都是可以接受的负载。
这里有一个容易犯的错:分压电阻值不能一味取大。ADC采样时内部的采样电容会从分压网络抽取电荷,如果源阻抗太高,采样电容还没充满就被断开了,读到的值就会偏低。CW32L012的ADC采样时间可以通过寄存器配置,但阻抗太高时误差仍然难以完全消除。我实测下来,用100k级别源阻抗,配合足够长的采样时间,误差能压到很小;但如果用1MΩ以上分压网络,即使加长采样时间,高速采样时依然能看到明显波动。所以如果你的系统对功耗没那么敏感,分压电阻可以小一点;如果非要追求低功耗而用大电阻,那采样时间必须对应拉长。
2.2 电流采样链路的搭法
电流采样我采用的是低侧采样方案,也就是在负载回路的低端串一颗采样电阻,测它两端的电压。低侧采样的好处是共模电压很低,放大电路好设计,用一颗普通运放就能搞定。高侧采样需要专门的高边电流检测放大器,虽然能避免地线干扰,但成本高一些,这个项目里的测量场景用低侧完全够了。
采样电阻选20mΩ,额定功率至少0.5W。5A电流流过时压降只有100mV,功耗是0.5W,刚好在电阻的额定功率范围内,不会过热漂移。这个100mV信号太小,直接进ADC的话分辨率不够,需要放大。我用一颗轨到轨运放搭同相放大,放大倍数设为20倍,也就是把100mV放大到2V。这样5A时ADC输入是2.0V,在3.3V参考电压范围之内,留出了30%的余量防止瞬态过流。
运放选型要注意的是输入失调电压。不要买便宜货,失调电压大的运放会让零点读数偏掉几十毫伏,折算成电流就是好几安的误差。我建议选用输入失调电压在微伏级别、输入偏置电流低的轨到轨运放,价格也就高几毛钱,但对精度的影响是根本性的。
2.3 保护与抗干扰处理
模拟前端电路不是把信号送进ADC就行,还要考虑过压、静电和噪声。电压采样端口外面可能会碰到超过30V的电压,分压网络虽然能降压,但如果误触到更高的电压,比如直接接到220V交流,分压电阻后面的器件还是可能被打穿。我在下臂电阻后面加了一颗3.3V的TVS管,把ADC输入钳位在安全范围。
电流采样运放输出端同样加了一颗3.3V稳压二极管做钳位。整条采样链路,从传感器到ADC引脚之间,各加了一级RC低通滤波,截止频率大概在几十kHz,把高频噪声滤掉,减少混叠误差。这里有一个体验很深的心得:RC滤波的时间常数不能太大,否则信号的响应速度会变慢。你调一次负载电流,要等个一两秒读数才稳定下来,这在实际使用中很难受。所以滤波电容我控制在0.1μF级别,配合几kΩ的电阻,既滤掉了高频毛刺,又保持了快速的响应。
3. 画板之前先算账:量程、分压比和增益的计算
3.1 电压量程与分压比的推导
先用10:1分压比反推一下整个量程:下臂电压V_adc = V_in / 10,当V_in = 30V时,V_adc = 3.0V,而ADC参考电压是3.3V,所以满量程理论上可以达到33V。留10%的余量是故意的,因为电池电压会有波动,如果基准电压略微偏高一点,导致V_adc超过参考电压,ADC读数会直接饱和,表现出来就是电压值平头。与其卡着量程极限,不如留点空间。
下臂电阻的精度直接影响测量精度。普通5%精度的电阻偏差太大,我全部使用0.1%精度的低温漂电阻,虽然贵一些,但这是保证全温度范围内精度的基础。如果预算紧张,至少也要用1%的金属膜电阻,然后靠软件单点校准把固定偏差补回来。
3.2 电流采样电阻功耗与运放增益的核算
电流采样电阻的功耗计算非常关键,这是很多人容易忽略的。P = I²R,5A的平方乘以20mΩ等于0.5W,选额定功率0.5W的电阻属于贴着极限跑。我建议选1W额定功率的,第一是留热余量,第二是功率降额后电阻的温漂会更小。电阻温漂会直接变成测量误差,我实测过普通功率电阻在大电流工作一段时间后,阻值能漂0.5%以上,这在电流采样中是不可接受的。
运放增益我选了20倍,是这么算出来的:满量程采样电阻压降100mV,要映射到ADC量程的60%左右(2V/3.3V),所以增益 = 2V / 0.1V = 20倍。如果你希望满量程时更贴近参考电压,可以选22倍,但那样离饱和太近,瞬态冲击容易削波。工程上稳妥一点,我建议留20%到30%的余量。
3.3 12位ADC的有效分辨率评估
CW32L012内部ADC是12位的,理论上分辨率是参考电压除以4096,也就是3.3V / 4096 ≈ 0.806mV。结合电压分压比10:1,ADC每个码字对应的被测电压是8.06mV,刚好满足0.01V的分辨率要求。电流链路同理,5A对应2V,那么每个码字对应电流是5A / (2V / 0.806mV) ≈ 2.02mA,满足0.001A的分辨率要求。
但是这里我要说一个经验之谈:不要被“12位ADC”这个数字骗了。在实际模拟链路中,噪声、基准漂移、PCB布局干扰会把有效位数往下拖。我实测过,不做任何软件滤波时,ADC最低两位基本是跳的,有效位数只有10位左右。所以固件层面的滤波不是可选项,而是刚需,这一点在下一章细说。
4. 固件实现:采样、滤波和显示刷新
4.1 ADC初始化与多通道采集
CW32L012的ADC实现我用的是常规的规则组多通道采集。电压通道和电流通道各占一个ADC输入引脚,每次采集时依次转换两个通道。初始化代码大致是这样的:
void adc_init(void) { // 使能ADC时钟,配置引脚为模拟输入 // 设置ADC时钟分频,确保采样时钟在芯片允许范围 // 配置采样时间为最大值,降低高阻源带来的误差 // 开启ADC输入通道:电压通道和电流通道 // 校准ADC(如果有硬件校准寄存器) }这里最关键的是采样时间的配置。前文说过,电压分压网络的源阻抗有100kΩ,如果采样时间不够,ADC内部采样电容充不满,读数就会系统性偏低。我把采样时间设成ADC时钟的几十个周期,实际测试下来能明显改善高阻源的读数准确性。这个坑我一开始没注意,直接用的默认采样配置,结果测3.3V电压时读数稳定偏低几十个码字,排查了很久才想到是采样时间的问题。
4.2 软件滤波:中值与滑动平均的组合
软件滤波我采用的是两层方案。第一层是中值滤波,连续采集5次数据,排序后取中间值。这一层的作用是把偶发的尖峰干扰、电源毛刺造成的异常点剔除掉。第二层是滑动平均滤波,维护一个长度为8的窗口,每次新的采样结果进入窗口,取窗口平均值作为最终显示值。中值滤波负责“去毛刺”,滑动平均负责“平滑抖动”,两者配合的效果远比单一滤波好。
uint16_t get_filtered_adc(uint8_t channel) { uint16_t buf[5]; for (int i = 0; i < 5; i++) { buf[i] = read_adc(channel); } // 简单排序取中值 uint16_t mid = median(buf, 5); // 送入滑动平均窗口 add_to_window(channel, mid); return window_average(channel); }滤波窗口长度要控制好。窗口太短,平滑效果差;窗口太长,响应速度慢,负载突然变化时读数要过很久才跟上。我试过32点窗口,稳定是稳定,但每次改负载要等将近一秒读数才到位,太影响体验。最终8点窗口配合约50ms的采样周期,响应和稳定兼得。
4.3 显示刷新与主循环逻辑
主循环的逻辑我设计成:读取滤波后的ADC值,转换成电压和电流的工程值,刷新LCD显示,然后进入低功耗睡眠,等待下一次定时唤醒。整个循环周期大概50到100ms,也就是显示刷新率在10到20Hz。这个刷新率在数字仪表里算是比较舒服的,既不会显得卡顿,也不至于因为刷新过快导致显示数字乱跳。
电压和电流的工程值换算是在固件里用比例系数做的。电压值 = ADC码值 × 参考电压 / 4096 × 分压比;电流值 = ADC码值 × 参考电压 / 4096 / 运放增益 / 采样电阻阻值。这些系数我全部定义成浮点数常量,后续校准时只需要改两个系数就能完成重新标定,不用改电路。
5. 校准和实测:让读数对得上万用表
5.1 两点校准的原理与实现
即使使用了0.1%的电阻,分压比和运放增益的实际值依然和标称值有偏差,而且ADC本身的增益误差和失调误差也存在。所以批量生产或者做原型验证时,必须做软件校准。我采用的方法是两点校准:记录零点和满量程点,也就是同时记录基准仪表的真实值和本设备的ADC原始值,然后算出比例系数和偏移量。
校准流程具体是这样的:把输入短路,记录电流通道的零点码值,这代表了整个链路的失调;然后接入一个精确的满量程基准源,记录对应码值。有了这两组数据,就能算出线性映射关系的斜率和截距,固件里把这两个参数存到Flash里。之后每次开机读取校准参数,工程值换算时直接套用。这个流程做完,设备的精度就能挣脱元器件标称精度的限制,实测误差能压到0.3%左右。
5.2 长时间工作时的温度漂移问题
校准做完之后测出来是准的,但放一段时间或者环境温度变化之后,读数会慢慢漂。这个漂移主要来自运放的失调电压和基准电压的温度系数。运放方面,我选的是低温漂型号,实测漂移量很小;基准方面,CW32L012内部参考电压本身就带温度系数,如果项目对精度的要求更高,比如长期误差要小于0.5%,建议外挂一颗精密基准芯片,比如常见的TL431或者专用基准源。
对便携仪表来说,还有一点值得注意:锂电池电压会随着电量下降而降低,如果ADC参考电压直接取电池电压,那测量结果也会跟着漂。所以我在设计时特意让参考电压独立,不直接用电池电压,同时在校准时把参考电压的实际值作为一个固定标定量写到Flash里,这样即使锂电池电压波动,ADC量化结果也依然准确。
5.3 实测数据记录
我拿这台表的原型和一只四位半台式万用表做了对比测试。电压档位,输入从3.3V到30V之间取了10个点,最大偏差出现在27V附近,约0.12V,相对误差0.44%。电流档位,从0.2A到5A之间也取了10个点,最大偏差约0.04A,相对误差0.8%。这个结果虽然还没到我最终的目标,但已经证明了设计思路是走得通的。进一步压差主要靠优化校准点分布和改用更精密的基准源。
6. 踩坑记录与后续改进
6.1 ADC参考电压的选择是个大坑
第一次打样时我直接用3.3V电源作为ADC参考电压,结果发现读数不稳定,误差有1%多。排查下来发现,3.3V电源在LCD刷新时会有一点点跌落,参考电压跟着波动,ADC结果自然就乱了。后来我把参考电压改成内部基准,并向串一个高质量的去耦电容,读数立刻稳了。所以项目一开始就应该先把参考电压的选路决策做在硬件设计里,不要指望软件能弥补参考源的不稳定。
6.2 上电瞬间的异常指示值
还有一个困扰我很长时间的问题是上电瞬间屏幕会显示一个很大的电流值,像是系统刚上电时出现错误读数。查到最后是运放在MCU还没稳定工作时就已经通电,输出进入了一个不确定态,被ADC读到了一个满偏码值。解决办法是在固件里加一个启动延时,等所有外设都稳定后再开始第一轮ADC采集,并且在启动后丢弃前几次采样结果。简单说就是模拟电路比数字电路先稳定,软件必须主动“等一等”硬件。
6.3 下一步可以做的小改进
这版做完之后,我试着在固件里加入滑动窗口的自动零点校准:在不测量时如果检测到输入端短路,自动更新零点偏移值。这在长时间在线监测场景里很好用,能对抗运放失调随温度的缓慢漂移。后续也考虑加数据记录功能,利用CW32L012的低功耗定时唤醒,每小时存一组电压电流值到Flash。调试的时候我还发现一个规律:凡是涉及到模拟采样的项目,PCB布线的地线走向往往比电路原理本身更影响精度。模拟地和数字地要单点连接,采样电阻的地端必须直接连到ADC的地参考点,这算是所有电压电流表项目里最值得注意的一条布局经验。