PUYA ADC -- 内部温度传感器读取(HAL 库)

PUYA PY32F0xx_Firmware V1.5.0 ADC 内部温度传感器读取示例教学

ADC – 内部温度传感器读取(HAL 库)

难度: ⭐⭐(需要了解 ADC 基本概念) 固件库: PY32F0xx_Firmware V1.5.0 芯片: PY32F030 源文件: Projects/PY32F030-STK/Example/ADC/ADC_TempSensor/Src/main.c


这个实验是干嘛的?

PY32F030 芯片内部集成了一个温度传感器,不需要任何外部元件,通过 ADC 采集就能读取芯片当前温度。

有什么用?

  • 监测芯片工作温度,防止过热
  • 温度补偿(ADC 精度受温度影响)
  • 简单的环境温度测量(精度不高,约 ±5 度)

硬件准备

不需要任何外部硬件! 温度传感器在芯片内部。

你只需要:

  • PY32F030 开发板
  • USB 转串口模块(查看 printf 输出)

完整代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
#include "main.h"

/* 温度校准值(出厂写入 OTP,地址固定) */
#define HAL_ADC_TSCAL1  (*(uint32_t *)(0x1fff0f14))  /* 30 度时的 ADC 校准值 */
#define HAL_ADC_TSCAL2  (*(uint32_t *)(0x1fff0f18))  /* 105 度时的校准值 */
#define Vcc_Power       3.3l                          /* 实际供电电压,按实际修改 */

/* 校准值对应的电压(根据实际 VCC 换算) */
#define TScal1  (float)((HAL_ADC_TSCAL1) * 3.3 / Vcc_Power)
#define TScal2  (float)((HAL_ADC_TSCAL2) * 3.3 / Vcc_Power)

/* 温度计算公式参数 */
#define TStem1      30l   /* 校准点 1 的温度:30 度 */
#define TStem2_105  105   /* 校准点 2 的温度:105 度(高温版) */
#define Temp_k_105  ((float)(TStem2_105 - TStem1) / (float)(TScal2 - TScal1))

ADC_HandleTypeDef AdcHandle;
uint16_t aADCxConvertedData;
__IO uint16_t hADCxConvertedData_Temperature_DegreeCelsius = 0;

/* ADC 配置 */
static void APP_AdcConfig(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};

  /* 复位并使能 ADC 时钟 */
  __HAL_RCC_ADC_FORCE_RESET();
  __HAL_RCC_ADC_RELEASE_RESET();
  __HAL_RCC_ADC_CLK_ENABLE();

  AdcHandle.Instance = ADC1;

  /* ADC 校准(必须!) */
  if (HAL_ADCEx_Calibration_Start(&AdcHandle) != HAL_OK)
  {
    APP_ErrorHandler();
  }

  /* ADC 基础配置 */
  AdcHandle.Instance                   = ADC1;
  AdcHandle.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV4;  /* ADC 时钟 = PCLK/4 */
  AdcHandle.Init.Resolution            = ADC_RESOLUTION_12B;         /* 12 位分辨率 */
  AdcHandle.Init.DataAlign             = ADC_DATAALIGN_RIGHT;        /* 右对齐 */
  AdcHandle.Init.ContinuousConvMode    = DISABLE;                    /* 单次转换 */
  AdcHandle.Init.ExternalTrigConv      = ADC_SOFTWARE_START;         /* 软件触发 */
  AdcHandle.Init.SamplingTimeCommon    = ADC_SAMPLETIME_239CYCLES_5; /* 采样时间 239.5 周期 */

  if (HAL_ADC_Init(&AdcHandle) != HAL_OK)
  {
    APP_ErrorHandler();
  }

  /* 配置通道:内部温度传感器 */
  sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
  sConfig.Rank    = ADC_RANK_CHANNEL_NUMBER;

  if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)
  {
    APP_ErrorHandler();
  }
}

int main(void)
{
  HAL_Init();
  BSP_LED_Init(LED_GREEN);
  BSP_USART_Config();   /* 初始化串口(printf 重定向) */

  APP_AdcConfig();      /* 配置 ADC */

  /* ADC 校准 */
  if (HAL_ADCEx_Calibration_Start(&AdcHandle) != HAL_OK)
  {
    APP_ErrorHandler();
  }

  /* 启动 ADC */
  HAL_ADC_Start(&AdcHandle);

  while (1)
  {
    /* 等待转换完成 */
    if (HAL_ADC_PollForConversion(&AdcHandle, 2000) == HAL_OK)
    {
      /* 读取 ADC 值 */
      aADCxConvertedData = HAL_ADC_GetValue(&AdcHandle);

      /* 计算温度:线性插值公式 */
      hADCxConvertedData_Temperature_DegreeCelsius =
        (int16_t)(Temp_k_105 * aADCxConvertedData - Temp_k_105 * TScal1 + TStem1);

      printf("Temperature: %d\r\n", (int)hADCxConvertedData_Temperature_DegreeCelsius);
    }
    HAL_Delay(1000);  /* 每秒读一次 */
  }
}

void APP_ErrorHandler(void)
{
  while (1) {}
}

代码分段讲解

1. 温度校准值

1
2
#define HAL_ADC_TSCAL1  (*(uint32_t *)(0x1fff0f14))
#define HAL_ADC_TSCAL2  (*(uint32_t *)(0x1fff0f18))

PY32F030 出厂时,工厂会在 OTP(一次性可编程存储器)中写入两个校准值:

  • TSCAL1:30 度时的 ADC 采样值
  • TSCal2:105 度时的 ADC 采样值

这两个地址是固定的,直接读就行。

2. 温度计算公式

1
温度 = k * (ADC值 - TScal1) + 30

其中 k = (105 - 30) / (TScal2 - TScal1)

这是线性插值——已知两个点(30 度和 105 度),算出斜率 k,然后根据当前 ADC 值算温度。

3. ADC 配置要点

  • 通道ADC_CHANNEL_TEMPSENSOR(内部温度传感器)
  • 采样时间:239.5 周期(内部传感器阻抗高,需要长采样时间)
  • 触发方式:软件触发
  • 转换模式:单次转换

4. 校准

1
HAL_ADCEx_Calibration_Start(&AdcHandle);

必须校准! 芯片工艺偏差会导致 ADC 偏移,校准可以消除这个偏差。


实验现象

串口每秒打印一行:

1
2
3
Temperature: 28
Temperature: 29
Temperature: 30
  • 室温下一般显示 25~35 度
  • 用手捂住芯片,温度会升高
  • 精度约正负 5 度(内部传感器精度有限)

常见问题

Q: 温度值偏差很大?

  • 检查 Vcc_Power 是否与实际供电电压一致(USB 供电约 3.3V,电池可能不同)
  • 内部传感器精度有限,正负 5 度是正常的
  • 如果需要高精度,用外部传感器(如 DS18B20、SHT30)

Q: 读到的温度是 0 或负数?

  • 检查校准值地址是否正确
  • 检查是否选择了正确的芯片型号(PY32F030x8)

Q: 想读取外部引脚的电压?

  • ADC_CHANNEL_TEMPSENSOR 换成外部通道(如 ADC_CHANNEL_0
  • 去掉温度计算公式,直接用 ADC值 * 3.3 / 4095 算电压

小结

概念 说明
内部温度传感器 芯片自带,不需要外部元件
OTP 校准值 出厂写入,30 度和 105 度的 ADC 值
线性插值 用两个已知点推算任意点的温度
ADC 校准 必须做,消除工艺偏差
采样时间 内部传感器阻抗高,需要长采样时间

拓展应用

项目 原理 难度
芯片过热保护 温度超阈值时降低频率或关断 ⭐⭐
温度补偿 根据温度修正 ADC/RTC 等外设的偏差 ⭐⭐⭐
简易温度计 串口显示 + LCD 显示 ⭐⭐
温控风扇 温度高时 PWM 加速风扇 ⭐⭐⭐
环境监测 内部温度 + 外部传感器对比 ⭐⭐
世界是你们
使用 Hugo 构建
主题 StackJimmy 设计