一、为什么需要片上温度传感器与 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,并输出校准后的温度值。

#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 系列,只需调整通道号和校准参数。掌握这一技巧,你的嵌入式项目将多一个实用的“免费”传感器。