参考资料
ADC采样:
电压转温度公式:
一、LM19核心要点
- 供电:2.7~ 5.5V可测-55~ +130℃;2.4~ 2.7V仅-30~+130℃,STM32常用3.3V供电
- 输出 :模拟电压信号,温度越高输出电压越小
抛物线公式: V O = − 3.88 × 10 − 6 T 2 − 0.0115 T + 1.8639 V_O=-3.88×10^{-6}T^2-0.0115T+1.8639 VO=−3.88×10−6T2−0.0115T+1.8639
测温反推: T = − 1481.96 + 2.1962 × 10 6 + 1.8639 − V O 3.88 × 10 − 6 T=-1481.96+\sqrt{2.1962×10^6+\frac{1.8639-V_O}{3.88×10^{-6}}} T=−1481.96+2.1962×106+3.88×10−61.8639−VO - 引脚(TO92) :1=VCC、2=VOUT、3=GND

二、硬件接线

| LM19引脚 | STM32 | 备注 |
|---|---|---|
| V+(1脚) | 3.3V | 严禁5V超压可选 |
| GND(3脚) | GND | 共地 |
| VOUT(2脚) | STM32 ADC输入引脚(PA0/PA1等) | |
| 外围推荐:电源端加0.1μF去耦电容;长线干扰大时:VOUT串200Ω+1μF到GND滤波 |
注意:LM19最大输出约2.485V(-55℃)<3.3V,可直接接入3.3V量程ADC
三、STM32软件思路(HAL库为例)
1.配置
- 开启ADC时钟,配置IO为模拟输入
- ADC配置:单次/连续转换、12位分辨率、内部参考电压3.3V
- ADC采样值换算电压:
V o u t ( V ) = A D C v a l × 3.3 4095 V_{out}(V)=\frac{ADC_{val}×3.3}{4095} Vout(V)=4095ADCval×3.3
2.关键代码逻辑
c
//1.ADC读取原始值
uint16_t adc_val=HAL_ADC_GetValue(&hadc1);
//2.换算输出电压(V)
float Vo = adc_val * 3.3f / 4095.0f;
//3.带入LM19反算公式求温度
float temp = -1481.96f + sqrt(2196200.0f + (1.8639f-Vo)/3.88e-6f);
3.简化方案(小量程近似线性)
-30~100℃适用: V O = − 0.01177 × T + 1.8605 V_O = -0.01177×T+1.8605 VO=−0.01177×T+1.8605
T = ( 1.8605 − V O ) / 0.01177 T=(1.8605-V_O)/0.01177 T=(1.8605−VO)/0.01177
计算简单、单片机运算快,日常测温优先用此式。
四、误差优化
- 采样滤波:连续采10~20次ADC取平均,降低噪声
- 自发热忽略:LM19静态电流≤10μA,自升温<0.02℃,无需补偿
- 电源稳压:STM32 3.3V不稳会带来ADC误差,必要时校准ADC参考电压
五、示例:常用温度对照(快速校验)
| 温度 | Vo电压 |
|---|---|
| 0℃ | 1.8639V |
| 25℃ | 1.574V |
| 30℃ | 1.515V |
| 100℃ | 0.675V |
六、代码
根据【stm32-hal】ADC模拟-数字转换技术中配置ADC
- main()
c
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "gpio.h"
#include <math.h>
/* Private includes ----------------------------------------------------------*/
/**
* @brief The application entry point.
* @retval int
*/
int value = 0;
float voltage =0.0;
float temp =0.0;
//ADC多次采样平均值,times:采样次数
uint16_t Get_Adc_Average(uint8_t times)
{
uint32_t adc_sum = 0;
for(uint8_t i=0; i<times; i++)
{
// HAL_ADC_PollForConversion(&hadc1,HAL_MAX_DELAY);//已开启连续转换模式,只用写一次,可注释
adc_sum += HAL_ADC_GetValue(&hadc1);
}
return adc_sum / times;
}
//LM19电压转温度
float LM19_GetTemp(float Vo)
{
return -1481.96f + sqrtf(2196200.0f + (1.8639f - Vo)/3.88e-6f);
}
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_ADC1_Init();
/* USER CODE BEGIN 2 */
HAL_ADCEx_Calibration_Start(&hadc1);
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1,HAL_MAX_DELAY);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
value = Get_Adc_Average(20); //20次平均
voltage = value * 3.3f / 4095.0f;
temp = LM19_GetTemp(voltage);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
- adc.c
c
/* Includes ------------------------------------------------------------------*/
#include "adc.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
ADC_HandleTypeDef hadc1;
/* ADC1 init function */
void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
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();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_15;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
/* USER CODE END ADC1_Init 2 */
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspInit 0 */
/* USER CODE END ADC1_MspInit 0 */
/* ADC1 clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
/**ADC1 GPIO Configuration
PC5 ------> ADC1_IN15
*/
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* USER CODE BEGIN ADC1_MspInit 1 */
/* USER CODE END ADC1_MspInit 1 */
}
}
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspDeInit 0 */
/* USER CODE END ADC1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ADC1_CLK_DISABLE();
/**ADC1 GPIO Configuration
PC5 ------> ADC1_IN15
*/
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_5);
/* USER CODE BEGIN ADC1_MspDeInit 1 */
/* USER CODE END ADC1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
- 时钟配置
将原来的
c
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
改成
c
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;//ADCPrescaler = DIV6 → ADC_CLK=72/6=12MHz
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
否则,测到的电压偏大,温度偏低
核心原因:ADC时钟变了 → ADC采样精度变 → 电压算错 → 温度飘
STM32F1 ADC最大时钟不能超过14MHz,这是关键限制:
1、原来配置
c
ADCPrescaler = DIV2
SYSCLK=HSE*9=72M → APB2=72M → ADC_CLK =72/2=36MHz
超标(>14M),ADC工作异常、采样跳变不准,温度乱飘。
2、修改后
c
ADCPrescaler = DIV6 → ADC_CLK=72/6=12MHz
12MHz<14MHz,ADC进入标准正常工作区间,采样真实电压,温度自然变准。
快速总结
- DIV2:36M超规格 → ADC采样失真,温度错误
- DIV6:12M合规 → ADC读数真实,温度正确
配套优化(进一步稳温度)
ADC采样时间从1.5cycle改成长采样:
c
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
七、现象
打开调试器可以观察到,temp等数据在不断变化。
