STM32片内温度传感器读数偏差大(±5°C以上)的根本原因是未用实际VREFINT校准且采样时序不当;必须同一ADC周期内连续采样VREFINT与ADC_IN16、启用TSVREFE、满足最小采样时间、读取出厂校准值、单位统一为毫伏、禁用干扰外设、轮询确保原子性、避免自热,并明确结温≠外壳/PCB温度。

STM32的ADC_IN16通道必须配对VREFINT校准才可信
直接读ADC_IN16原始值套公式算温度,结果偏差常达±5°C以上——根本原因不是代码写错,而是忽略了VREFINT(内部参考电压)的实际值与标称值(通常1.2V)存在工艺偏差,而温度传感器的电压换算全程依赖这个基准。
实操要点:
-
VREFINT需和ADC_IN16在**同一ADC时钟周期内连续采样**(如先采VREFINT,再采ADC_IN16),避免基准漂移引入误差 - 必须启用ADC的
TSVREFE位(Temperature Sensor and VREFINT Enable),否则ADC_IN16通道无信号输入 - 采样时间不能低于数据手册要求(如STM32F4为17.1μs),否则
VSENSE电压建立不稳,读数跳变 - 公式中
V25和Avg_Slope是典型值,实际芯片的出厂校准值存于0x1FFF7A2C(VREFINT校准值)和0x1FFF7A2E(TS_CAL1,25°C时ADC值)等地址,应优先读取
为什么HAL_ADC_Start(&hadc1)之后要立刻HAL_ADC_PollForConversion()
片内温度传感器响应慢、噪声大,且ADC转换期间若被其他通道抢占或中断打断,会导致采样窗口错位、电荷未充分注入采样电容,最终读出的VSENSE偏低(表现为温度虚高)。轮询模式能确保单次转换原子完成,比中断或DMA更可靠。
关键动作:
立即学习“C++免费学习笔记(深入)”;
- 禁用所有可能触发ADC请求的外设(如定时器TRGO、EXTI线),防止意外启动转换
- 调用
HAL_ADC_Start()后**不加延时**,立即进入HAL_ADC_PollForConversion(),超时设为10ms足够 - 转换完成后,立刻调用
HAL_ADC_Stop(),避免传感器持续偏置导致自热(微安级电流积累也会让结温升高0.3°C以上)
T = (V25 - VSENSE) / Avg_Slope + 25里的单位必须统一为毫伏
公式看着简单,但新手常把VSENSE当成ADC原始值(0–4095)直接代入,或者把Avg_Slope当4.3代入却忘了单位是mV/°C。结果算出来可能是负200°C——这不是芯片冻住了,是单位没归一化。
正确换算链:
- 先用
HAL_ADC_GetValue()拿到ADC原始值raw - 算真实电压:
VSENSE_mV = raw * VREFINT_actual_mV / ADC_RES(例如F4系列ADC_RES=4095) -
V25从0x1FFF7A2E读出的是ADC值,同样要转成mV:V25_mV = TS_CAL1 * VREFINT_actual_mV / 4095 -
Avg_Slope查手册得4.3 mV/°C,直接参与计算,别自己除1000
NXP i.MX RT600这类多核MCU必须锁定LPADC时钟源
RT600的温度传感器走的是低功耗ADC(LPADC),但它默认时钟源可能来自PLL分频,而PLL在低功耗模式下会关闭——结果就是休眠唤醒后第一次读温度永远是0。这不是传感器坏了,是ADC根本没时钟。
绕过方式:
- 初始化时强制将
LPADC时钟切到XTAL_24M(外部晶振),不依赖PLL - 每次读温度前检查
LPADC->CFG寄存器的CLKEN位是否置1,未置1则手动开启 - RT600的温度寄存器地址是
0x400AD080(TEMPSENSE0),但该寄存器只更新当LPADC完成一次完整转换,不能靠轮询标志位就认为数据有效
最易被忽略的一点:所有片内温度传感器测的都是“结温”,不是PCB表面温度,也不是散热器温度。它反映的是芯片硅片核心区域的实时热状态,因此数值跳变快、滞后小——但也意味着你无法用它替代NTC去监控模块底板或IGBT壳温,二者物理位置和热阻路径完全不同。


















