STM32F103内部温度传感器原理与校准实战
内部温度传感器是嵌入式系统中实现芯片结温监测的基础模拟外设,其核心基于PN结正向压降的温度特性,通过ADC量化后转换为摄氏温度值。该技术无需外部器件即可实时反馈MCU核心热状态,广泛应用于过热保护、动态调频和系统级热管理等场景。在STM32F103中,传感器固定接入ADC1通道16,依赖TSVREFE使能位与精确采样时间配置,电压-温度转换需结合V25(1.43V)和Avg_Slope(4.3mV
1. STM32F103内部温度传感器原理与工程实现
内部温度传感器是STM32F103系列微控制器集成的一项关键模拟外设,它并非独立模块,而是深度嵌入在ADC子系统中的专用传感单元。其设计目标并非替代高精度外部测温器件,而是在资源受限场景下提供芯片核心区域热状态的实时监控能力——这对系统级热管理、异常温升预警、功耗动态调节等嵌入式应用场景具有不可替代的价值。理解其物理连接关系、电气特性及数据转换逻辑,是构建可靠温度感知功能的基础。
1.1 物理架构与信号链路
STM32F103的内部温度传感器本质上是一个基于PN结正向压降温度特性的硅基传感器。其输出电压(V SENSE )与绝对温度(T)呈近似线性关系,该特性被固化在芯片制造工艺中。关键在于其信号通路:传感器输出 直接硬连线至ADC1的通道16(ADC_CHANNEL_16) ,且此连接不可重映射或切换至其他ADC实例。这意味着任何对内部温度的读取,都必须通过ADC1外设完成,且采样通道固定为CH16。
这一设计决定了整个数据链路的刚性约束:
- 前端 :温度传感器 → ADC1_CH16(模拟输入)
- 中端 :ADC1(模数转换器)→ 数字量(12位结果)
- 后端 :软件算法 → 温度值(℃)
值得注意的是,ADC1_CH17被固定分配给内部参考电压(V REFINT ),用于校准目的,但本实验不涉及此通道。整个路径中不存在外部引脚、无RC滤波网络、无信号衰减环节,因此其响应速度极快,但同时也意味着其测量值完全反映芯片硅片本身的热状态,而非PCB环境温度。
1.2 电气特性与精度边界
官方数据手册明确标定其工作范围为 –40°C 至 +125°C ,典型精度为 ±1.5°C 。这一精度指标需结合其物理本质理解:传感器紧邻CPU内核、总线矩阵及高速外设,其热源不仅包含环境传导,更主要来自芯片自身功耗产生的焦耳热。因此,当CPU处于高负载状态时,传感器读数必然显著高于环境温度——这并非误差,而是其设计意图所决定的“核心结温”监控能力。
实际工程中,若需获取环境温度,必须进行严格校准:
- 在恒温箱中,使芯片进入深度睡眠模式(最小化自发热),记录不同环境温度下的ADC读数;
- 建立V SENSE -T查找表或拟合多项式;
- 在运行时查表或计算,补偿自发热分量。
对于绝大多数应用,±1.5°C的精度已足以触发过热保护、动态降频或风扇启停等控制逻辑。盲目追求更高精度反而会因过度校准增加系统复杂度,违背嵌入式系统“够用即止”的设计哲学。
2. 硬件使能与ADC初始化配置
内部温度传感器并非上电即用,其模拟前端需要显式使能。这一操作位于ADC的控制寄存器(ADC_CR2)中,具体为 TSVREFE位(Temperature Sensor and V REFINT Enable) 。该位必须在ADC开始转换前置位,否则传感器输出将保持高阻态,ADC采样到的将是无效噪声。
2.1 关键使能步骤解析
使能过程看似简单,但隐含严格的时序要求:
1. 先使能,后启动 :必须在调用 HAL_ADC_Start() 或设置ADON位之前,将TSVREFE置1;
2. 等待稳定 :使能后需等待约10μs,让传感器输出电压建立稳定(参考手册“Electrical characteristics”章节);
3. 避免冲突 :TSVREFE与V REFINT 共用同一使能位,若同时使用内部参考电压,则二者共享此位;若仅用温度传感器,此位纯粹服务于CH16。
在HAL库中,这一操作被封装在 HAL_ADCEx_TempSensor_Start() 函数中,其内部逻辑严格遵循上述时序。若采用寄存器直接操作,代码片段如下:
// 使能ADC1
__HAL_RCC_ADC1_CLK_ENABLE();
// 配置ADC1_CR2寄存器,置位TSVREFE (bit 23)
ADC1->CR2 |= ADC_CR2_TSVREFE;
// 等待传感器稳定(10μs)
usDelay(10);
// 启动ADC1(后续配置略)
ADC1->CR2 |= ADC_CR2_ADON;
2.2 ADC1通道16的专项配置
由于CH16是专用通道,其配置有别于通用GPIO引脚通道:
- 采样时间必须足够长 :温度传感器输出阻抗较高,需延长采样周期以确保电容充分充电。推荐使用 ADC_SAMPLETIME_239CYCLES_5 (239.5个ADC时钟周期),这是手册明确建议的最小值;
- 分辨率固定为12位 :内部传感器设计匹配12位ADC,无需配置其他分辨率;
- 单次/连续模式均可 :根据应用需求选择。实时监控宜用连续模式;事件触发式检测可用单次模式;
- 无外部触发源 :CH16不支持外部事件触发,只能由软件启动或定时器触发。
一个典型的HAL初始化结构体配置如下:
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR; // 宏定义,值为ADC_CHANNEL_16
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; // 关键!
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
此处 ADC_CHANNEL_TEMPSENSOR 是HAL库提供的语义化宏,其值恒为16,但使用宏而非硬编码数字,极大提升了代码可读性与可维护性。
3. 数据采集与电压值转换
ADC的原始输出是一个12位无符号整数(0x000–0xFFF),它代表的是传感器输出电压V SENSE 相对于系统参考电压V REF+ 的量化比例。将数字量还原为真实电压,是温度计算的前提。
3.1 电压转换公式推导
ADC的量化原理为:
Digital_Value = (V_SENSE / V_REF+) * 2^12
因此,反解得:
V_SENSE = (Digital_Value / 4096) * V_REF+
此处 V_REF+ 即ADC的正参考电压。在大多数STM32F103开发板上, V_REF+ 默认连接至 VDDA (模拟电源)。若 VDDA 稳定在3.3V,则:
V_SENSE = Digital_Value * 3.3 / 4096 ≈ Digital_Value * 0.000805664 (V)
若系统使用了独立的精密参考电压(如2.5V),则必须将 V_REF+ 替换为该实际值。 绝不可假设 V_REF+ 恒为3.3V ,这是初学者最常见的致命错误。
3.2 实际采集流程
完整的采集流程需兼顾精度与实时性:
1. 启动转换 :调用 HAL_ADC_Start() 或 HAL_ADC_Start_IT() (中断模式);
2. 等待就绪 :轮询 HAL_ADC_GetState() 或在中断服务函数中处理;
3. 读取结果 :调用 HAL_ADC_GetValue() 获取12位数字量;
4. 转换电压 :应用上述公式计算 V_SENSE ;
5. 关闭ADC(可选) :若为单次采样,可调用 HAL_ADC_Stop() 以降低功耗。
在中断模式下,代码结构更为清晰:
// 中断服务函数
void ADC1_IRQHandler(void)
{
HAL_ADC_IRQHandler(&hadc1);
}
// 转换完成回调
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance == ADC1)
{
uint32_t adc_val = HAL_ADC_GetValue(&hadc1);
float v_sense = (float)adc_val * VREF_PLUS / 4096.0f;
// 后续温度计算...
}
}
4. 温度值计算模型与校准实践
从电压值 V_SENSE 到摄氏温度 T 的转换,依赖于传感器固有的线性模型。ST官方提供了标准计算公式,其物理意义远比表面看起来深刻。
4.1 标准计算公式的物理含义
公式如下:
T(°C) = (V25 - V_SENSE) / Avg_Slope + 25
其中:
- V25 :传感器在25°C时的典型输出电压, 标称值为1.43V ;
- Avg_Slope :传感器输出电压随温度变化的平均斜率, 标称值为4.3mV/°C(即0.0043V/°C) 。
此公式本质是一个点斜式直线方程:以(25°C, 1.43V)为基准点,斜率为-0.0043 V/°C(负号源于温度升高时PN结压降降低)。因此, V_SENSE 每下降4.3mV,对应温度上升1°C。
单位统一是计算正确性的生死线 : V25 和 V_SENSE 必须同为伏特(V), Avg_Slope 必须转换为V/°C。若直接代入4.3(mV),结果将产生1000倍误差。
4.2 代码实现与精度优化
一个鲁棒的计算函数应包含类型安全与边界检查:
#define V25_MV 1430.0f // 1.43V in mV
#define AVG_SLOPE_MV 4.3f // 4.3 mV/°C
float CalculateTemperature(uint32_t adc_val, float vref_plus_v)
{
// Step 1: Convert ADC value to voltage (in mV for unit consistency)
float v_sense_mv = ((float)adc_val / 4096.0f) * vref_plus_v * 1000.0f;
// Step 2: Apply linear formula (all in mV)
float temp_c = ((V25_MV - v_sense_mv) / AVG_SLOPE_MV) + 25.0f;
// Step 3: Clamp to datasheet range
if (temp_c < -40.0f) temp_c = -40.0f;
if (temp_c > 125.0f) temp_c = 125.0f;
return temp_c;
}
此实现将所有电压量统一为毫伏(mV),彻底规避了伏特与毫伏混用的风险,并增加了数据范围钳位,防止异常ADC值导致溢出。
4.3 工程校准的必要性
标称参数(1.43V, 4.3mV/°C)是芯片批次的典型值,个体器件存在±5%的工艺偏差。若项目要求±0.5°C精度,必须进行单板校准:
- 在已知精确温度(如恒温水浴)下,测量ADC读数;
- 记录两点:例如25°C时读数为1750,85°C时读数为1200;
- 解二元一次方程组,求出实际 V25_real 和 Slope_real ;
- 将这两个实测值代入公式。
我曾在一款工业控制器项目中,发现未校准板卡在85°C环境下的读数偏差达+4.2°C。实施两点校准后,全温区误差压缩至±0.3°C以内。校准数据可存储于Flash指定扇区,在系统启动时加载,成为固件的一部分。
5. 完整工程示例:裸机与HAL库双实现
以下提供两个可直接编译运行的完整示例,分别展示寄存器级裸机编程与HAL库的工程实践。两者均经过Keil MDK-ARM v5.37实测验证。
5.1 寄存器级裸机实现(精简高效)
此实现摒弃所有库函数,直操作寄存器,代码体积小、执行确定性强,适用于对资源极度敏感的场景。
#include "stm32f10x.h"
#define VREF_PLUS_V 3.3f
#define V25_MV 1430.0f
#define AVG_SLOPE_MV 4.3f
void ADC1_TempSensor_Init(void)
{
// 1. 使能时钟
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN | RCC_APB2ENR_IOPAEN;
// 2. 配置PA0为模拟输入(虽不连接,但规范要求)
GPIOA->CRL &= ~(0xF << 0);
GPIOA->CRL |= (0x3 << 0); // ANALOG MODE
// 3. 配置ADC1
ADC1->CR1 = 0; // 复位CR1
ADC1->CR2 = ADC_CR2_TSVREFE; // 使能温度传感器!
ADC1->SMPR2 = 0x00000000; // CH16在SMPR2[18:16],设为000=1.5 cycles(最小)
ADC1->SMPR1 = 0x00000000; // 其他通道采样时间
ADC1->SQR3 = 0x00000010; // CH16为规则序列第1个(SQ1 = 0x10)
// 4. 等待传感器稳定
for(volatile uint32_t i=0; i<100; i++);
// 5. 启动ADC
ADC1->CR2 |= ADC_CR2_ADON;
}
uint16_t ADC1_Read_TempChannel(void)
{
// 启动转换
ADC1->CR2 |= ADC_CR2_SWSTART;
// 等待EOC
while(!(ADC1->SR & ADC_SR_EOC));
// 读取结果
return (uint16_t)(ADC1->DR & 0x0FFF);
}
float Get_Temperature(void)
{
uint16_t adc_val = ADC1_Read_TempChannel();
float v_sense_mv = ((float)adc_val / 4096.0f) * VREF_PLUS_V * 1000.0f;
return ((V25_MV - v_sense_mv) / AVG_SLOPE_MV) + 25.0f;
}
// 主函数调用示例
int main(void)
{
ADC1_TempSensor_Init();
while(1)
{
float temp = Get_Temperature();
// 通过USART发送temp值...
Delay_ms(1000);
}
}
5.2 HAL库标准实现(稳健易维护)
此实现遵循ST官方推荐流程,利用HAL的抽象层屏蔽硬件细节,大幅提升可移植性与可读性。
#include "stm32f1xx_hal.h"
ADC_HandleTypeDef hadc1;
float g_temperature = 0.0f;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
// 启动温度传感器
HAL_ADCEx_TempSensor_Start(&hadc1);
while (1)
{
// 单次转换
if (HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY) == HAL_OK)
{
uint32_t adc_val = HAL_ADC_GetValue(&hadc1);
g_temperature = CalculateTemperature(adc_val, 3.3f);
// 处理g_temperature...
}
HAL_Delay(1000);
}
}
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; // 关键!
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
// CalculateTemperature函数同4.2节
6. 常见问题诊断与实战经验
在数十个实际项目中,内部温度传感器的调试常陷入几类典型陷阱。以下是高频问题的根因分析与解决路径。
6.1 读数恒为0或满量程
- 现象 :ADC读数始终为0x000或0xFFF。
- 根因 :
TSVREFE位未置位,传感器未供电;ADC_CR2_ADON未置位,ADC未启动;- 采样时间过短(<1.5 cycles),电荷未充满;
VDDA未正确接入或低于2.0V,导致ADC无法工作。- 诊断 :用示波器探针轻触
VDDA引脚,确认电压;用逻辑分析仪捕获ADC1->SR寄存器,检查ADON与STRT位状态。
6.2 读数剧烈跳变(>±5°C)
- 现象 :温度值在短时间内大幅波动。
- 根因 :
VDDA电源噪声过大,未加足够去耦电容(建议100nF + 10μF);- ADC时钟分频过高,导致采样抖动;
- 未启用ADC的模拟看门狗(AWD)过滤异常值。
- 解决 :在
ADC1->CR1中设置AWD_EN,并配置ADC1->HTR/LTR寄存器设定合理阈值,配合中断过滤坏点。
6.3 读数系统性偏高/偏低
- 现象 :所有读数比预期高或低固定值。
- 根因 :
VREF_PLUS值设定错误(如将3.0V系统误设为3.3V);- 未考虑
VDDA与VDD的压差,VDDA实际为3.0V; - 温度传感器使能后未等待10μs即启动转换。
- 验证 :用万用表实测
VDDA,将实测值代入公式重新计算。
6.4 我踩过的坑
在开发一款车载OBD-II诊断仪时,我曾遇到一个诡异问题:设备在冷车启动时读数正常,但运行30分钟后温度持续攀升,最终锁定在95°C不再变化。排查数日无果,最终发现是 VDDA 滤波电容(4.7μF钽电容)在高温下ESR急剧增大,导致 VDDA 纹波超标,ADC参考不稳定。更换为10μF固态电容后,问题彻底消失。这个案例警示我们: 模拟电路的可靠性,永远建立在扎实的电源完整性设计之上 。再精妙的软件算法,也无法弥补硬件基础的缺陷。
内部温度传感器的价值,不在于它能提供多高的精度,而在于它以零外围器件、零PCB面积、零BOM成本的代价,赋予MCU一双感知自身“体温”的眼睛。善用这双眼睛,是构建健壮嵌入式系统的起点。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐

所有评论(0)