一、为什么需要片上温度传感器与 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 为例)
- 打开 STM32CubeMX,选择芯片型号,配置时钟(如 HSI 8MHz)。
- 在
Analog选项卡中,启用ADC1,将IN16(温度传感器)和IN17(VREFINT)均设为Enabled。 - 在 ADC 配置中,设置采样时间为最大(如 239.5 cycles),以提高精度。
- 生成代码,工程会自动添加 ADC 初始化函数。
五、完整代码示例
以下代码基于 HAL 库,实现读取温度传感器和 VREFINT,并输出校准后的温度值。
#include "main.h"
#include <stdio.h>
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 系列,只需调整通道号和校准参数。掌握这一技巧,你的嵌入式项目将多一个实用的“免费”传感器。