拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于STM32的电流电压检测模块设计:从硬件到标定

简介面向STM32嵌入式开发与电源监测场景这份资料提供了一套基于STM32F103的电流电压检测模块完整实现方案适用于工业控制、物联网设备中的实时电压电流采集与远程监控。压缩包共183个文件约25.51MB核心包含C源码h/c、Keil工程文件uvproj/uvprojx、已编译固件hex/axf以及设计文档pdf、PCB/截图jpg/png/bmp和调试脚本bat等从源码到硬件布局均有覆盖。目前已有2236人学习下载。资源围绕ADC采样、霍尔电流传感器/互感器信号处理、分压电压测量、软件滤波与UART/Modbus通信等关键环节展开固件中包含ADC初始化、通道配置和数据处理代码并附有Modbus Poll读取说明用户手册和测试报告可帮助快速接线、配置与验证。整体对想在真实项目中快速落地电压电流监测功能的开发者和学生有较高参考价值。 前阵子给一套户外电源系统做状态监测需要在主控端实时读取电池电压和充放电电流于是我顺手用STM32搭了一个电流电压检测模块。这个小模块看着简单——不就是ADC采样嘛——真正落地的时候从采样电路选型到软件标定每一步都有不少坑。这篇就把它完整拆一遍从方案选型、硬件设计到STM32端的采集、滤波与标定给正在做类似项目的朋友一个能直接参考的模板。1. 项目整体思路与方案选型1.1 先想清楚你要测的是什么信号动手之前我习惯先问自己三个问题被测信号的变化范围是多少要求的精度是多高现场有没有强干扰以我的场景为例电池电压大致在9V~14V之间波动充放电电流在0~3A范围分辨率希望做到电压0.01V、电流0.01A环境是户外机箱开关电源在旁边噪声不小。这些约束直接决定了后续所有方案的选择。如果是毕业设计或者智能家居小项目需求往往没这么严格比如只测个锂电池电压、检测设备是否过流那完全没必要上高精度仪表放大器普通的电阻分压加MCU内置ADC就够用。第一件事是把需求量化这决定了电路复杂度和成本。1.2 电流检测方案横向对比电流检测常见的方案就那几种我在不同项目里都试过各有取舍方案原理优点缺点适用场景采样电阻运放在回路串入低值电阻测压降换算电流成本低、响应快、精度可控插入损耗、需要运放、需要隔离中小电流、嵌入式板级霍尔电流传感器ACS712等磁场感应隔离、无插入损耗精度一般、温漂大、带宽有限大电流、粗暴场景专用电流检测芯片INA226等内部集成了放大和ADC精度高、带I2C接口成本高、发货周期长精密测量、通信模块我的项目电流只有3A对精度有要求但预算有限果断选了采样电阻加运放的方案。这里额外说一句别用那种标称1%的碳膜电阻做采样电阻温漂很感人尽量选低温漂的锰铜电阻或者精密合金电阻阻值根据电流和运放增益配合计算。1.3 电压检测方案与ADC采集基础电压检测比电流简单得多。STM32的ADC输入范围是0~3.3V所以外部电压必须先经过电阻分压网络把它降到ADC的安全输入范围内。这个网络的设计重点不是分压比本身而是等效阻抗对ADC采样精度的影响以及输入过压保护。STM32的ADC是逐次逼近型内部有采样保持电容。如果信号源阻抗太高采样时钟又很快电容来不及充到稳定值读数就会偏小。所以分压电阻总值不能太大一般控制在10k~100k之间必要时在ADC引脚并联一个0.1uF电容或者开启STM32内部的采样时间配置给它多留一点充电窗口。2. 硬件电路设计要点2.1 采样电阻选型与放大电路计算我用的是0.05Ω的锰铜电阻额定功率2W。最大电流3A时采样电阻上的压降是3×0.050.15V远低于运放的输出摆幅限制。但这0.15V给到ADC是不行的12位ADC在3.3V参考下每个LSB大约0.8mV直接测的话电流分辨率只有16mA达不到想要的0.01A。所以加了一级同相放大电路我用的运放是LMV358单电源3.3V供电轨到轨输出。放大倍数怎么定的先算需求要分辨率0.01A采样电阻压降变化是0.01×0.050.0005V0.5mV这个信号给到ADC至少要占据2~3个LSB才不会被噪声淹没。把放大倍数定在20倍0.5mV变成10mV对应12个LSB余量足够。放大倍数20倍怎么用电阻搭同相放大倍数公式是1Rf/Rg取Rf19kΩ、Rg1kΩ正好是20倍。注意这两个电阻要用1%精度低温漂的否则增益误差比运放本身还大。这里有一个澳门场外赌钱、在路上行驶的经验心得放的提示新手容易忽略运放的供电去耦电容一定要靠近电源引脚10uF钽电容并联0.1uF陶瓷电容靠近放置。开关电源带来的高频噪声如果不在这里滤掉后面ADC采出来的数据就是一团乱麻。2.2 电压分压网络与输入保护电压检测通道我用了两个精密电阻组成分压网络上臂100kΩ下臂10kΩ分压比是1/11。这样14V输入对应ADC引脚1.27V24V输入对应2.18V都在3.3V安全范围内。这两个电阻同样要求1%精度因为分压比精度直接决定了电压读数的绝对精度。分压电阻总值110kΩ在STM32 ADC标准采样时间比如1.5周期下阻抗偏高读数会偏低。我的解法是开启ADC的采样时间到最大239.5周期同时在ADC引脚对地并联一个0.1uF电容让采样保持电容能从外部储能电容取电而不是从高阻网络取电。实测这样处理后读数稳定度明显改善。保护电路不要省。我在ADC引脚和地之间并联了一对背靠背的BAV99把输入钳位在-0.7V~4V之间防止分压网络某个电阻虚焊导致高压直接灌进MCU。另外串了一个1kΩ的限流电阻即使外部误接入高压电流也被限制在毫安级不会烧片子。2.3 电源、参考电压与PCB布局STM32 ADC的参考电压引脚VREF如果条件允许尽量用独立的基准源而不是直接接3.3V电源。因为数字电路开关时的电流尖峰会在3.3V上产生纹波参考电压跟着晃ADC读数必然跳。我这个项目对成本不敏感用了REF3030输出3.0V基准精度0.2%。如果预算有限至少要在VREF脚旁边加强去耦并让模拟电路尽量远离数字部分。PCB布局上采样电阻一定要用四线开尔文连接即电流流过采样电阻电压检测线从电阻两端单独引出到运放输入端。不要直接把导线焊在采样电阻的同一焊盘上否则焊点阻抗和接触电阻会窜入测量回路精度损失严重。这是很多DIY项目测量不准的隐形原因。模拟地AGND和数字地DGND在PCB上单点连接位置选在MCU的焊盘处保证回流电流不会穿过模拟信号区域。开关电源的地线电流是比较脏的尽量让它直接从电源入口回到主电容别绕经模拟区。3. STM32软件实现与数据处理3.1 基于HAL库的ADC多通道配置我用的是标准外设库改HAL之后的工程看起来大家现在更喜欢HAL。其实核心逻辑都一样无非是配置ADC的采样通道、采样时间和转换模式。这里贴一下我用的关键配置代码ADC_HandleTypeDef hadc1; ADC_ChannelConfTypeDef sConfig {0}; // 使能ADC时钟和GPIO时钟 __HAL_RCC_ADC1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); // PA0 - 电压采样通道PA1 - 电流采样通道放大后 GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin GPIO_PIN_0 | GPIO_PIN_1; GPIO_InitStruct.Mode GPIO_MODE_ANALOG; GPIO_InitStruct.Pull GPIO_NOPULL; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); hadc1.Instance ADC1; hadc1.Init.ClockPrescaler ADC_CLOCK_SYNC_PCLK_DIV2; hadc1.Init.Resolution ADC_RESOLUTION_12B; hadc1.Init.ScanConvMode ENABLE; // 多通道扫描 hadc1.Init.ContinuousConvMode ENABLE; // 连续转换 hadc1.Init.NbrOfConversion 2; hadc1.Init.DiscontinuousConvMode DISABLE; HAL_ADC_Init(hadc1); sConfig.Channel ADC_CHANNEL_0; // PA0对应ADC1_IN0 sConfig.Rank 1; sConfig.SamplingTime ADC_SAMPLETIME_239CYCLES_5; // 拉长采样时间 HAL_ADC_ConfigChannel(hadc1, sConfig); sConfig.Channel ADC_CHANNEL_1; // PA1对应ADC1_IN1 sConfig.Rank 2; HAL_ADC_ConfigChannel(hadc1, sConfig);读取数据时要注意多通道扫描模式下数据寄存器只有一个转换完一个通道就会更新一个值所以必须在转换完成中断里按顺序读取或者用DMA。我一般用DMA方式一次性把两通道的结果搬到内存数组里简单省心uint16_t adc_buf[2]; HAL_ADC_Start_DMA(hadc1, (uint32_t*)adc_buf, 2); // 每次转换完成后会回调在回调里处理数据3.2 采样滤波噪声是常态不加滤波的白板数据没法用硬件上做了那么多防护但ADC原始数据仍然会有跳动尤其在开关电源附近。这是常态不要指望硬件把所有噪声都干掉软件滤波必须跟上。我用的组合拳是先做中值滤波去掉脉冲毛刺再做滑动平均平滑曲线。具体来说连续采集5次数据去掉最大值和最小值剩下3个求平均然后把这3次平均结果放到一个长度为8的滑动窗口里再取平均。这个组合对工频干扰和随机噪声的抑制效果都不错而且实时性损失很小。中值滤波可以滤掉偶发的尖峰比如电机刷火花、继电器吸合瞬间产生的干扰滑动平均则能把剩余的高频抖动平滑掉。两者配合我实测下来ADC读数的峰峰值抖动从±30个LSB降到了±3个LSB以内。这个数据对大部分应用都够用了。如果对实时性要求极高比如要做电流环PID控制就不建议用太长的滑动窗口否则相位滞后会很严重。那种场景下我建议把窗口长度压到4以内或者在滤波器和控制环路之间加一个前馈补偿。3.3 数据标定从ADC原始值到真实物理量ADC读回来的数字必须换算成真实的电压和电流这一步不是简单的乘一个系数就完事因为电路里每个环节都有误差分压电阻精度误差、运放增益误差、ADC参考电压误差。最靠谱的做法是逐点标定。我用的方法很简单用一台精度0.5%的万用表作为基准给电压通道输入几个已知电压比如0V、5V、10V、15V、20V记录对应的ADC读数然后用最小二乘法拟合出直线ykxb。电流通道也一样用电子负载设定不同电流记录ADC读数与设定电流的关系。拟合得到的k和b直接写进代码的配置参数里。这里有个实际经验我不吐不快标定点的范围一定要覆盖实际工作区间外推标定比内插标定误差大得多。比如你实际只用到0~3A就别只标定0~1A然后线性外推到3A误差会放大好几倍。具体代码里我一般把标定结果以结构体的方式存起来typedef struct { float k; // 斜率 float b; // 截距 float unit; // 物理量单位如V、A } CalibParam; CalibParam voltage_calib {0.00312, -0.0042, 1.0}; // 电压通道 CalibParam current_calib {0.01024, 0.0021, 1.0}; // 电流通道 float get_voltage(uint16_t adc_value) { return voltage_calib.k * adc_value voltage_calib.b; } float get_current(uint16_t adc_value) { return current_calib.k * adc_value current_calib.b; }4. 校准方法与误差分析4.1 两点校准法的实操步骤最小二乘拟合适合有时间慢慢做的朋友如果现场条件不允许两个点就能完成校准精度也可以接受。两个点怎么选选零点附近和工作区间的中上段这样的直线对区间内的拟合效果最好。我拿电压通道举例先把输入短接记录ADC读数为n0这叫零点校准对应0V。再用一个稳压电源调到12V精确输入记录ADC读数为n1。那么实际的换算关系就是[ V_{real} \frac{V_{known}}{n_1 - n_0} \times (n - n_0) ][ V_{real} \frac{12.00}{n_1 - n_0} \times (n - n_0) ]注意这里的n0不一定是一个ADC码如果硬件有零点偏置n0可能不是0。这个用代码实现非常简单现场操作也不费时间比完全依赖理论计算精度更高因为它把运放失调电压、参考电压误差等系统误差一并校准掉了。电流通道的标定方法相同只是需要一个恒流源或者电子负载。我在现场用了一个可以设定电流的电子负载分别设定0A和2A记录两组数据计算k和b。注意校准用的负载精度不要比你的待测设备低否则标定本身就没有意义了。4.2 误差来源与应对策略开始调试前先把可能引入误差的环节列了一遍这对定位问题很有帮助误差来源影响量级应对策略分压电阻精度1%每级1%用0.1%精度电阻或标定运放增益电阻精度每级1%用0.1%精度电阻或标定运放输入失调电压数十uV级别选低失调运放、软件校准ADC参考电压波动每mV约1LSB独立参考源、加强去耦采样回路压降取决于电流开尔文连接、减小接触电阻温度漂移视器件而定选低温漂器件、软件补偿这里特别提醒一点即使你所有电阻都选了0.1%精度系统总误差也不会是简单的相加因为有些误差是系统性的通过标定可以消除有些是随机性的只能通过滤波减少。做完两点校准之后系统性误差基本被消除剩下的主要是非线性误差和温度漂移。5. 常见问题与调试实录5.1 ADC采样值乱跳、不稳定怎么办这是大家问得最多的一个问题也是我刚开始踩过最深的一个坑。遇到采样值乱跳先别急着加滤波按照这个顺序排查先看电源纹波是不是太大。用示波器测量MCU的3.3V电源如果纹波超过50mVADC读数想稳定都难。解决方法是加强3.3V的去耦必要时串磁珠。再看参考电压VREF是否干净如果VREF和电源共用一条走线数字电路动作产生的电流尖峰会直接干扰参考电压ADC读数自然乱跳。最后才是看采样保持时间配置信号源阻抗大就增加采样周期。我当时遇到的情况是采样值周期性跳变用示波器一查发现3.3V上叠加了一个开关电源的开关频率纹波大概±20mV。在电源入口加了LC滤波问题就消失了。滤波和去耦是解决跳变的根本手段软件滤波只用来兜底。5.2 读数偏差比较大怎么定位问题读数的绝对值偏差往往比跳变更难定位因为涉及的因素更多。我的排查方法是用一个已知精度的信号源输入从传感器端到ADC引脚逐级测量电压对比理论值和实测值哪一级对不上就是哪一级的问题。比如电压通道读数比万用表低5%先用万用表量分压网络输出端电压是不是理论值如果分压网络输出电压正确那就是ADC参考电压或者标定参数的问题如果输出不正确那就是分压电阻本色的问题。电流通道类似先用万用表量采样电阻两端电压和运放输出电压再对比ADC读数逐级定位。我在调试一个项目时发现电流读数一直偏大8%查了很久才发现是采样电阻旁边一条走线经过了大电流PCB铜箔的压降被采样电路误当成采样电阻的压降了。这就是前面提到的开尔文连接问题的另一个案例——采样信号和功率回路在PCB上必须严格分开走线。5.3 调试中需要注意的几个细节最后分享几个调试中容易忽略的小问题。STM32的ADC引脚如果悬空读数会乱飘所以没有用到的通道要配置成关闭状态或者把引脚接固定电平。采样电阻的功率裕量要够我用0.05Ω电阻通3A电流时功耗是0.45W2W封装余量够足但如果长期满负荷运行热量积累也会带来温漂。再有一个经验是代码里别忘了对ADC DMA中断做防呆处理如果DMA传输出现半字对齐问题读数会很诡异。我习惯在DMA配置里明确指定数据宽度为半字16位避免默认配置下高低字节颠倒的问题。此外板子上电瞬间ADC还没稳定这个时候读出来的数据毫无意义一定要加一个上电延时逻辑比如延时100ms之后再开启ADC转换。这块检测模块从最开始的概念到最终稳定运行中间折腾了不少时间。但把每一个环节都吃透之后它其实就是一个很标准的“传感器信号链”设计感知、调理、采集、处理。后面你要是遇上类似的测量项目无论换什么MCU、改什么量程这条链路的设计思路都是相通的。纸上得来终觉浅还是建议大家自己亲手焊一块板子、写一遍代码、做一次完整的标定踩过的坑才会变成真正的经验。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门