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