STM32F407 内部温度传感器实验
一、基本原理
STM32F407 内部集成了一个温度传感器,本质上是一个基于带隙电压(Bandgap)结构的模拟电路,靠近 CPU 核心区域,其输出电压 VSENSE 随芯片结温(Die Temperature)线性变化。
关键信息
| 项目 | 说明 |
|---|---|
| 连接方式 | 固定在 ADC1 的通道 16(ADC1_IN16) |
| 温度范围 | −40 ℃ ~ +125 ℃ |
| 精度 | 约 ±1.5 ℃ |
| 25℃ 典型 VSENSE | ≈ 0.76 V(760 mV) |
| 斜率 Avg_Slope | ≈ 2.5 mV/℃(温度越高,电压越低) |
| 需要额外硬件? | 零外部元件,纯内部资源 |
重要:这测的是芯片内部硅片温度(结温),不是环境温度!运行时会比室温偏高(芯片自身发热),不能当气象温度计用,但非常适合做过热保护/热管理。
二、温度计算公式
根据 RM0090 参考手册,公式为:
\[\boxed{T(℃) = \frac{V_{SENSE} - V_{25}}{Avg\_Slope} + 25}
\]
其中:
- VSENSE:传感器当前输出电压(由 ADC 值换算)
- V25:25℃ 时的典型电压 ≈ 0.76 V
- Avg_Slope:斜率 ≈ −2.5 mV/℃(负号已隐含在公式中)
用 ADC 原始值直接算(更实用)
12-bit ADC,参考电压 VREF+ = 3.3 V 时:
V_SENSE = ADC_RawValue × 3.3 / 4096
T(℃) = (V_SENSE - 0.76) / (-0.0025) + 25
= 25 + (0.76 - V_SENSE) / 0.0025
三、使用步骤(核心流程)
① 开启 ADC1 时钟
② 配置 ADC1(12位、单次/连续、右对齐等)
③ ★ 使能内部温度传感器 —— 置位 ADC_CCR 的 TSVREFE 位(bit23)
④ 配置规则序列:选通道16,采样时间 ≥ 17.1μs(建议 239.5 周期)
⑤ 校准 ADC → 启动转换 → 读 ADC 值
⑥ 代入公式算温度
最容易踩的坑:忘了调用
ADC_TempSensorVrefintCmd(ENABLE)或没设置足够长的采样时间,导致读出来是随机噪声。
四、代码实现
方案 A:标准外设库(StdPeriph)版本
/*********************** temp_sensor.h ***********************/
#ifndef __TEMP_SENSOR_H
#define __TEMP_SENSOR_H
#include "stm32f4xx.h"
void TempSensor_Init(void);
uint16_t TempSensor_ReadADC(void);
float TempSensor_GetTemp_C(void);
#endif
/*********************** temp_sensor.c ***********************/
#include "temp_sensor.h"
/*-------------------------------------------------------------
初始化 ADC1 + 内部温度传感器(通道16)
-------------------------------------------------------------*/
void TempSensor_Init(void)
{
ADC_InitTypeDef ADC_InitStruct;
ADC_CommonInitTypeDef ADC_CommonInitStruct;
/* 1. 开时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
/* 2. ADC 公共配置 */
ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div4; // ADCCLK = PCLK2/4 = 84M/4 = 21MHz
ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStruct);
/* 3. ADC 基本配置 */
ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStruct.ADC_ScanConvMode = DISABLE; // 单通道
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; // 单次触发
ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStruct.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStruct);
/* 4. ★ 配置通道16,采样时间必须足够大(≥17.1μs)*/
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_239Cycles5);
/* 5. ★ 使能内部温度传感器 */
ADC_TempSensorVrefintCmd(ENABLE); // 设置 ADC_CCR 的 TSVREFE 位
/* 6. 使能 ADC 并校准 */
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));
}
/*-------------------------------------------------------------
启动一次转换并读取 ADC 原始值(通道16)
-------------------------------------------------------------*/
uint16_t TempSensor_ReadADC(void)
{
ADC_SoftwareStartConv(ADC1); // 软件触发
while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); // 等待转换完成
return ADC_GetConversionValue(ADC1);
}
/*-------------------------------------------------------------
读取多次取平均,返回温度(℃)
sample_cnt: 平均值次数(如 10~16)
-------------------------------------------------------------*/
float TempSensor_GetTemp_C(void)
{
uint32_t sum = 0;
const uint8_t sample_cnt = 16;
for (uint8_t i = 0; i < sample_cnt; i++)
sum += TempSensor_ReadADC();
uint16_t adc_avg = sum / sample_cnt;
/* ADC值 → 电压 */
float vsense = (float)adc_avg * 3.3f / 4096.0f;
/* 代入温度公式 */
float temp = 25.0f + (0.76f - vsense) / 0.0025f;
return temp;
}
方案 B:HAL 库版本(CubeMX 风格)
/*********************** main.c 片段 ***********************/
ADC_HandleTypeDef hadc1;
/* ADC1 初始化 */
void MX_ADC1_Init(void)
{
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
HAL_ADC_Init(&hadc1);
/* 配置温度传感器通道 —— HAL会自动帮你设置TSVREFE位 */
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR; // = 通道16
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES; // 越长越稳
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}
/* 读温度(℃) */
float Get_InternalTemp(void)
{
uint32_t sum = 0;
const uint8_t N = 10;
for (uint8_t i = 0; i < N; i++) {
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 5);
sum += HAL_ADC_GetValue(&hadc1);
}
uint16_t adc_avg = sum / N;
float vsense = (float)adc_avg * 3.3f / 4096.0f;
return 25.0f + (0.76f - vsense) / 0.0025f;
}
五、进阶技巧
1. 使用出厂校准值(推荐,个体差异更小)
ST 出厂时在两个温度点做了芯片个体校准,数据存在 OTP 地址:
| 地址 | 内容 |
|---|---|
0x1FFF7A2C |
30℃ 对应的 ADC 原始值(TS_CAL1) |
0x1FFF7A2E |
110℃ 对应的 ADC 原始值(TS_CAL2) |
校准公式(来自 STM32F4 数据手册):
\[T(℃) = \frac{110 - 30}{TS\_CAL2 - TS\_CAL1} \times (ADC_{raw} - TS\_CAL1) + 30
\]
float Get_Temp_WithFactoryCalib(void)
{
uint16_t TS_CAL1 = *((uint16_t *)0x1FFF7A2C); // 30°C 校准值
uint16_t TS_CAL2 = *((uint16_t *)0x1FFF7A2E); // 110°C 校准值
uint16_t adc_raw = TempSensor_ReadADC(); // 你的单次读值
return ((110.0f - 30.0f) / (TS_CAL2 - TS_CAL1)) *
(adc_raw - TS_CAL1) + 30.0f;
}
用校准值后精度明显更好,强烈建议使用!
2. 软件滤波
// 简单滑动窗口 / 中位值平均
#define TEMP_WIN 8
static uint16_t win[TEMP_WIN];
// 填充窗口后取排序去头尾再平均...
3. 实测值偏高的原因
| 现象 | 原因 |
|---|---|
| 读出来 35~45℃(室温才 25℃) | 正常——芯片自身功耗导致结温 > 环境温度 |
| 数值跳动 ±3℃ | 采样时间太短 / 没做平均滤波 |
| 读出来接近 0 或全 0xFF | 忘了 ADC_TempSensorVrefintCmd(ENABLE) |
参考代码 STM32F407内部温度传感器实验 www.youwenfan.com/contentcnv/72886.html
六、实验现象预期
上电运行后,串口/屏幕大概显示:
Internal Temp: 38.50 ℃ ← 芯片刚跑起来(比室温高,正常)
Internal Temp: 41.20 ℃ ← 跑了一会儿,略有上升
用手按压芯片位置或用热风枪低温吹一下(小心),数值会明显上升。

浙公网安备 33010602011771号