STM32 片上温度传感器与 VREFINT 校准:从原理到实战
👁 7 阅读 · 2026-08-18 · STM32 ADC
STM32 内置温度传感器和内部参考电压(VREFINT)为开发者提供了低成本、免外部硬件的温度监测方案。本文深入解析其工作原理,重点讲解如何利用 VREFINT 校准 ADC 供电电压,从而消除电源波动对温度测量的影响,提升精度。通过完整的代码示例,手把手教你配置 ADC 通道、读取数据并进行温度换算,同时分享常见陷阱与优化技巧,助你轻松掌握这一实用技能。
## 一、为什么需要片上温度传感器与 VREFINT?
在许多嵌入式应用中,温度监测是刚需。虽然外部传感器(如 DS18B20、NTC)精度高,但成本、布线和体积都是问题。STM32 内部集成了温度传感器和 VREFINT(内部参考电压),只需一个 ADC 即可同时测量芯片温度和供电电压,非常适合做过热保护、环境温度粗测等场景。
但片上温度传感器有一个痛点:其输出受供电电压影响。如果 VDD 不稳定,直接读取的 ADC 值会偏离真实温度。而 VREFINT 是一个固定的内部基准(典型值 1.2V),通过它我们可以反推出当前 VDD 的实际值,进而校准温度读数。这就是 VREFINT 校准的核心价值。
## 二、原理剖析:ADC 采样与温度换算
### 2.1 片上温度传感器
STM32 的温度传感器通过 ADC 的特定通道(如 STM32F1 的通道 16,F4 的通道 18)输出模拟电压,该电压与温度近似线性关系。数据手册给出典型公式:
```
Temperature (°C) = (V_sense - V_25) / Avg_Slope + 25
```
其中:
- `V_25`:25°C 时的典型电压(如 1.43V)
- `Avg_Slope`:温度系数(mV/°C,如 4.3 mV/°C)
注意:这些参数因芯片型号而异,务必查阅对应数据手册。
### 2.2 VREFINT 校准原理
ADC 转换结果是一个数字量,与输入电压和参考电压的关系为:
```
ADC_Value = (V_input / V_REF) * 4095 (12位ADC)
```
如果 V_REF 就是 VDD,那么当 VDD 波动时,同样的 V_input 会得到不同的 ADC_Value。而 VREFINT 的电压是固定的(约 1.2V),因此我们可以通过测量 VREFINT 通道的 ADC 值来反推 VDD:
```
VDD = (VREFINT_Cal / ADC_VREFINT) * 1.2V
```
其中 `VREFINT_Cal` 是工厂在 3.3V 下校准的 VREFINT ADC 值(通常存放在系统存储区)。然后,用这个 VDD 作为实际参考电压,重新计算温度传感器的电压:
```
V_sense = (ADC_Temp / 4095) * VDD
```
这样,即使 VDD 漂移,也能得到准确的 V_sense,从而正确换算温度。
## 三、硬件与软件准备
- 硬件:任意 STM32 开发板(本文以 STM32F103 为例)
- 软件:STM32CubeMX + HAL 库(或标准外设库)
- 无需外部元件,直接使用内部资源
## 四、配置步骤(以 STM32CubeMX 为例)
1. 打开 STM32CubeMX,选择芯片型号,配置时钟(如 HSI 8MHz)。
2. 在 `Analog` 选项卡中,启用 `ADC1`,将 `IN16`(温度传感器)和 `IN17`(VREFINT)均设为 `Enabled`。
3. 在 ADC 配置中,设置采样时间为最大(如 239.5 cycles),以提高精度。
4. 生成代码,工程会自动添加 ADC 初始化函数。
## 五、完整代码示例
以下代码基于 HAL 库,实现读取温度传感器和 VREFINT,并输出校准后的温度值。
```c
#include "main.h"
#include
ADC_HandleTypeDef hadc1;
// 从系统存储区读取 VREFINT 校准值(STM32F1 系列)
#define VREFINT_CAL_ADDR ((uint16_t*)0x1FFFF7BA) // 注意:不同型号地址不同
float read_temperature(void)
{
uint16_t adc_temp, adc_vrefint;
float vdd, v_sense, temperature;
uint16_t vrefint_cal = *VREFINT_CAL_ADDR;
// 读取温度传感器通道
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_16;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 100);
adc_temp = HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(&hadc1);
// 读取 VREFINT 通道
sConfig.Channel = ADC_CHANNEL_17;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 100);
adc_vrefint = HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(&hadc1);
// 计算实际 VDD(假设 VREFINT 典型值为 1.2V)
vdd = (vrefint_cal / (float)adc_vrefint) * 1.2f;
// 计算温度传感器电压
v_sense = (adc_temp / 4095.0f) * vdd;
// 温度换算(以 STM32F103 为例:V_25=1.43V, Avg_Slope=4.3mV/°C)
temperature = (v_sense - 1.43f) / 0.0043f + 25.0f;
return temperature;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_ADC1_Init();
while (1)
{
float temp = read_temperature();
printf("Temperature: %.2f °C\n", temp);
HAL_Delay(1000);
}
}
```
## 六、注意事项与优化技巧
- **校准地址**:不同 STM32 系列的 VREFINT 校准值地址不同,务必查阅参考手册。例如 F0 系列在 0x1FFFF7BA,F4 系列在 0x1FFF7A2A。
- **采样时间**:温度传感器输出阻抗较高,建议使用最大采样时间,否则读数会偏小。
- **连续转换**:如果频繁读取,可以开启 ADC 连续转换模式,减少启动开销。
- **滤波**:温度变化缓慢,可对多次采样取平均,或使用滑动滤波,以降低噪声。
- **误差来源**:VREFINT 典型值 1.2V 有 ±5% 误差,若需更高精度,可读取芯片出厂校准的 VREFINT 电压值(部分型号提供)。
- **功耗**:温度传感器和 VREFINT 在读取前需使能,读取后可关闭以省电(通过 ADC 控制寄存器)。
## 七、总结
通过 VREFINT 校准,我们可以在不增加硬件成本的情况下,显著提升 STM32 片上温度传感器的精度,使其在电源波动环境下依然可靠。本文的代码可直接移植到大部分 STM32 系列,只需调整通道号和校准参数。掌握这一技巧,你的嵌入式项目将多一个实用的“免费”传感器。