3. 万用表数据采集基础:ADC采样原理、万用表前端电路与STM32内部ADC配置
好,咱们进入第三章节。说实话,这一章是整个课程里最「硬核」的部分之一。你想想看,万用表测电压、测电流、测电阻,最终都要变成数字信号给蓝牙发出去。那这个「变」的过程,靠的就是ADC——模数转换器。
我当年第一次做手持仪表项目时,就栽在ADC的采样精度上。明明电路算得好好的,出来的数据就是跳来跳去。后来才发现,是前端电路和ADC配置没配合好。嗯,今天咱们就把这块彻底讲透。
3.1 ADC采样原理:从模拟到数字的桥梁
ADC说白了,就是把连续的模拟电压,变成离散的数字量。比如你测一节1.5V的电池,ADC会输出一个数字,比如3072(假设是12位ADC,参考电压3.3V)。
这里有个核心公式:
数字量 = (模拟电压 / 参考电压) × (2^N - 1)
其中N是ADC的位数。12位就是4096级,16位就是65536级。
重要概念:分辨率与精度
分辨率 = 参考电压 / 2^N。比如3.3V参考、12位ADC,分辨率就是3.3/4096 ≈ 0.8mV。但注意,分辨率不等于精度!我见过有人把12位ADC当16位用,结果数据全是噪声。
常见的ADC类型有几种:
- 逐次逼近型(SAR):STM32内部用的就是这种。速度快,精度适中,功耗低。
- 积分型(双斜率):万用表里常用。精度高,抗干扰强,但慢。
- Σ-Δ型:高精度测量仪器用。分辨率能做到24位,但速度慢。
我个人习惯,做便携设备首选SAR型。为什么?因为它在速度和功耗之间平衡得最好。你想想看,万用表要测电池,ADC本身不能太耗电,不然还没测完,电池先被ADC吃光了。
3.2 万用表前端电路:分压电阻与电流检测
ADC不能直接测高压,也不能直接测电流。所以前端电路就是干这个的——把被测信号「翻译」成ADC能接受的0~3.3V范围。
3.2.1 电压测量:分压电阻网络
万用表测电压,核心就是电阻分压。比如你要测0~100V,ADC只能接受0~3.3V,那分压比就是100:3.3 ≈ 30.3:1。
实际电路里,我一般用多档位切换:
档位1: 0~200mV → 直接进ADC(不衰减)
档位2: 0~2V → 1:1 分压
档位3: 0~20V → 10:1 分压
档位4: 0~200V → 100:1 分压
档位5: 0~1000V → 500:1 分压
注意:输入阻抗问题
分压电阻的取值很关键。电阻太大,噪声大;电阻太小,会拉低被测电压。我一般取1MΩ级别,这样对被测电路影响小。曾经有个项目,用了10kΩ分压,结果测高阻信号时,读数直接偏了20%。
3.2.2 电流测量:采样电阻+差分放大
测电流的原理更简单——让电流流过一个小电阻,测它两端的电压。比如1A电流流过0.1Ω电阻,产生100mV压降。
但这里有个坑:采样电阻的功率。1A×0.1Ω=0.1W,看起来不大。但如果是10A呢?10A×0.1Ω=10W,电阻会烫得能煎鸡蛋。
所以我建议:
- 小电流(mA级):用10Ω~100Ω采样电阻
- 中电流(A级):用0.1Ω~1Ω采样电阻
- 大电流(10A级):用mΩ级采样电阻,配合高精度运放放大
差分放大器的作用,就是把采样电阻两端的微小电压差,放大到ADC能识别的范围。我常用的运放是LMV321或MCP6001,便宜又好用。
3.2.3 电阻测量:恒流源法
测电阻,本质上是测电压。给被测电阻通一个已知电流,测它两端的电压,然后R=V/I。
实际电路里,我一般用恒流源芯片(比如LM334)或者运放搭的恒流源。注意档位切换:
| 档位 | 恒流电流 | 测量范围 |
|---|---|---|
| 200Ω | 1mA | 0~200mV对应0~200Ω |
| 2kΩ | 100μA | 0~200mV对应0~2kΩ |
| 20kΩ | 10μA | 0~200mV对应0~20kΩ |
| 200kΩ | 1μA | 0~200mV对应0~200kΩ |
| 2MΩ | 0.1μA | 0~200mV对应0~2MΩ |
小技巧:测高阻时,要注意漏电流。PCB板上的焊剂残留、潮湿环境,都会导致漏电流。我一般会在PCB上画一个「保护环」(Guard Ring),把高阻引脚围起来,效果立竿见影。
3.3 STM32内部ADC配置:从理论到代码
好,前端电路搞定了,信号已经调理到0~3.3V了。接下来就是STM32的ADC登场。
STM32的ADC模块,我常用的是:
- 12位分辨率(可配置为6、8、10、12位)
- 最多18个通道(16个外部+2个内部)
- 支持单次、连续、扫描、间断模式
- 内置电压参考(VREFINT,约1.2V)
3.4.1 基本配置步骤
我个人习惯用HAL库,但也会直接操作寄存器做优化。下面是一个典型的ADC初始化代码:
// ADC初始化配置
void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
// 1. 使能ADC时钟
__HAL_RCC_ADC1_CLK_ENABLE();
// 2. 配置ADC句柄
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; // 时钟分频
hadc1.Init.Resolution = ADC_RESOLUTION_12B; // 12位分辨率
hadc1.Init.ScanConvMode = DISABLE; // 单通道模式
hadc1.Init.ContinuousConvMode = DISABLE; // 单次转换
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 软件触发
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; // 右对齐
hadc1.Init.NbrOfConversion = 1; // 1个转换通道
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
// 3. 配置通道
sConfig.Channel = ADC_CHANNEL_0; // PA0引脚
sConfig.Rank = 1; // 转换顺序
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES; // 采样时间
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
采样时间的选择:
采样时间 = (采样周期数 + 12.5个周期) × ADC时钟周期。比如ADC时钟12MHz,采样周期3个周期,那么总时间 = (3+12.5)/12M ≈ 1.29μs。采样时间越长,精度越高,但速度越慢。我一般测直流信号用3~7个周期,测音频信号用1.5个周期。
3.4.2 读取ADC值
配置好之后,读取就简单了:
// 单次读取ADC值
uint16_t Read_ADC_Value(void)
{
HAL_ADC_Start(&hadc1); // 启动转换
HAL_ADC_PollForConversion(&hadc1, 100); // 等待转换完成
return HAL_ADC_GetValue(&hadc1); // 读取结果
}
// 多次采样取平均(我常用的方法)
uint16_t Read_ADC_Average(uint8_t times)
{
uint32_t sum = 0;
for(uint8_t i = 0; i < times; i++)
{
sum += Read_ADC_Value();
HAL_Delay(1); // 间隔1ms
}
return (uint16_t)(sum / times);
}
注意:参考电压的稳定性
ADC的精度直接取决于参考电压。如果VREF+有纹波,ADC结果就会有噪声。我一般会在VREF+引脚加一个10μF+0.1μF的滤波电容。如果要求更高,可以用外部基准电压源,比如TL431或REF3033。
3.4.3 校准与误差补偿
STM32的ADC有内置校准功能。我每次初始化后都会调用:
// ADC校准
HAL_ADCEx_Calibration_Start(&hadc1);
但即使校准了,实际测量还是有误差。我一般会在软件里做两点校准:
- 零点校准:将ADC输入端短接到GND,读取的值作为零点偏移
- 满量程校准:输入一个已知的精确电压(比如2.5V),计算增益误差
然后实际电压 = (ADC值 - 零点偏移) × (参考电压 / (满量程值 - 零点偏移))。
我曾经在一个项目中,没做校准就直接用,结果测出来的电压偏差了3%。后来加了校准,误差降到了0.1%以内。嗯,这个步骤千万别省。
3.5 实战中的坑与经验
最后,分享几个我踩过的坑:
- ADC输入阻抗不匹配:STM32的ADC输入阻抗约50kΩ,如果前端电路输出阻抗太高,会拉低电压。我一般会在ADC前加一个运放做电压跟随器。
- 数字噪声耦合:STM32的GPIO翻转时会产生噪声,耦合到ADC输入。我习惯把ADC引脚周围的GPIO设为模拟模式,并且远离高频信号线。
- 温度漂移:ADC的零点会随温度变化。如果产品工作温度范围宽,建议加温度传感器做补偿。
- 电源纹波:蓝牙发射时电流会突然增大,导致电源电压波动。我一般在ADC采样时关闭蓝牙发射,或者加一个足够大的储能电容。
我的个人习惯:每次画PCB时,ADC相关的模拟电路和数字电路严格分地。模拟地单独走线,在电源入口处单点连接。这个习惯帮我避免了很多噪声问题。
好了,这一章的内容就到这里。ADC采样看似简单,但要做好、做准,需要前端电路、ADC配置、软件校准三者配合。下一章,咱们会把这些数据通过蓝牙发出去,真正实现无线传输。
记住:理论是基础,实践出真知。建议你拿块开发板,接个电位器,实际测一测ADC的值,看看和万用表读数差多少。动手试试,比看十遍文章都管用。