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 ℃   ← 跑了一会儿,略有上升

用手按压芯片位置或用热风枪低温吹一下(小心),数值会明显上升。

posted @ 2026-05-25 16:20  徐中翼  阅读(18)  评论(0)    收藏  举报