当前位置: 首页 > news >正文

STM32实现LM19温度精准测量

参考资料

ADC采样:
【stm32-hal】ADC模拟-数字转换技术
电压转温度公式:
LM19温度传感器数据手册

一、LM19核心要点

  1. 供电:2.7~ 5.5V可测-55~ +130℃;2.4~ 2.7V仅-30~+130℃,STM32常用3.3V供电
  2. 输出:模拟电压信号,温度越高输出电压越小
    抛物线公式:V O = − 3.88 × 10 − 6 T 2 − 0.0115 T + 1.8639 V_O=-3.88×10^{-6}T^2-0.0115T+1.8639VO=3.88×106T20.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×1061.8639VO
  3. 引脚(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.配置

  1. 开启ADC时钟,配置IO为模拟输入
  2. ADC配置:单次/连续转换、12位分辨率、内部参考电压3.3V
  3. 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.关键代码逻辑

//1.ADC读取原始值uint16_tadc_val=HAL_ADC_GetValue(&hadc1);//2.换算输出电压(V)floatVo=adc_val*3.3f/4095.0f;//3.带入LM19反算公式求温度floattemp=-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.8605VO=0.01177×T+1.8605
T = ( 1.8605 − V O ) / 0.01177 T=(1.8605-V_O)/0.01177T=(1.8605VO)/0.01177
计算简单、单片机运算快,日常测温优先用此式。

四、误差优化

  1. 采样滤波:连续采10~20次ADC取平均,降低噪声
  2. 自发热忽略:LM19静态电流≤10μA,自升温<0.02℃,无需补偿
  3. 电源稳压:STM32 3.3V不稳会带来ADC误差,必要时校准ADC参考电压

五、示例:常用温度对照(快速校验)

温度Vo电压
0℃1.8639V
25℃1.574V
30℃1.515V
100℃0.675V

六、代码

根据【stm32-hal】ADC模拟-数字转换技术中配置ADC

  1. main()
/* Includes ------------------------------------------------------------------*/#include"main.h"#include"adc.h"#include"gpio.h"#include<math.h>/* Private includes ----------------------------------------------------------*//** * @brief The application entry point. * @retval int */intvalue=0;floatvoltage=0.0;floattemp=0.0;//ADC多次采样平均值,times:采样次数uint16_tGet_Adc_Average(uint8_ttimes){uint32_tadc_sum=0;for(uint8_ti=0;i<times;i++){// HAL_ADC_PollForConversion(&hadc1,HAL_MAX_DELAY);//已开启连续转换模式,只用写一次,可注释adc_sum+=HAL_ADC_GetValue(&hadc1);}returnadc_sum/times;}//LM19电压转温度floatLM19_GetTemp(floatVo){return-1481.96f+sqrtf(2196200.0f+(1.8639f-Vo)/3.88e-6f);}intmain(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 */}
  1. adc.c
/* Includes ------------------------------------------------------------------*/#include"adc.h"/* USER CODE BEGIN 0 *//* USER CODE END 0 */ADC_HandleTypeDef hadc1;/* ADC1 init function */voidMX_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 */}voidHAL_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 */}}voidHAL_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 */
  1. 时钟配置

将原来的

voidSystemClock_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();}}

改成

voidSystemClock_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=12MHzif(HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit)!=HAL_OK){Error_Handler();}}

否则,测到的电压偏大,温度偏低

核心原因:ADC时钟变了 → ADC采样精度变 → 电压算错 → 温度飘

STM32F1ADC最大时钟不能超过14MHz,这是关键限制:

1、原来配置
ADCPrescaler=DIV2 SYSCLK=HSE*9=72M → APB2=72M → ADC_CLK=72/2=36MHz

超标(>14M),ADC工作异常、采样跳变不准,温度乱飘。

2、修改后
ADCPrescaler=DIV6 → ADC_CLK=72/6=12MHz

12MHz<14MHz,ADC进入标准正常工作区间,采样真实电压,温度自然变准

快速总结
  1. DIV2:36M超规格 → ADC采样失真,温度错误
  2. DIV6:12M合规 → ADC读数真实,温度正确
配套优化(进一步稳温度)

ADC采样时间从1.5cycle改成长采样:

sConfig.SamplingTime=ADC_SAMPLETIME_239CYCLES_5;

七、现象

打开调试器可以观察到,temp等数据在不断变化。

http://www.jsqmd.com/news/959146/

相关文章:

  • 紧跟AI算法迭代节奏,178软文网动态优化运营方案实现长期稳定输出
  • 别再死记硬背了!用Multisim 14的瞬态仿真,5分钟搞定RC电路波形分析
  • 从课堂到项目:如何用Python面向对象思想重构你的机械臂运动仿真代码
  • 2026年口碑好的提花运动面料/运动面料生产厂家推荐 - 品牌宣传支持者
  • SAP PP/MM模块联动:物料版次(Revision Level)在生产订单和采购订单中的完整跟踪流程
  • 淘宝买的ST-Link V2在Keil 5.38和STM32CubeProgrammer 2.15上识别不了?别扔,试试这个暴力升级教程(附救砖指南)
  • 告别黑屏!手把手教你用ESP8266驱动1.44寸ST7735屏幕,从接线到显示第一个Hello World
  • Windows 11系统优化终极指南:如何用Win11Debloat让你的电脑跑得更快更干净
  • 别再甩锅给网络了!手把手教你为Android音视频App集成Ping诊断功能(附完整Kotlin代码)
  • 小程序毕业设计-基于Django的医院信息查询、疫苗信息及预约本地健康宝微信小程序系统的设计与实现(源码+LW+部署文档+全bao+远程调试+代码讲解等)
  • ESP32 TCP通信保姆级实战:从零搭建客户端,并用网络调试助手/Netcat测试
  • 3个维度重构阅读体验:如何通过开源书源实现内容自由?
  • 字符串匹配算法怎么选?从场景出发聊聊Horspool、KMP和Boyer-Moore的适用性
  • 从VGG16到ResNet18:何恺明当年到底解决了什么‘训练难题’?一个梯度消失的通俗比喻
  • AI与人类创造力协同进化模型(2024权威白皮书首发):基于全球87个跨学科实验数据
  • 从RTX_Config.h看RTX5内存管理:对象专用内存池 vs 全局内存池,你的选择是什么?
  • 从SPSS交叉表结果到论文报告:手把手教你解读“风险评估”表格
  • SAP EWM存储类型配置避坑指南:从‘标准’到‘灵活’,这18个参数你真的都懂了吗?
  • JSON差异比较对比指南
  • 告别静态Slave!用Jenkins Kubernetes插件打造多容器构建Pod(含Maven/Golang/Selenium实战)
  • 当屏幕休息时,如何让它变成一件数字艺术品?FlipIt翻页时钟屏保的优雅解决方案
  • 3步搞定金融数据获取:pywencai同花顺问财的Python自动化指南
  • 别再傻傻分不清!一张图看懂QPSK、OQPSK和π/4QPSK到底怎么选
  • 不止CuteCom!Ubuntu串口调试工具横评:Minicom、Picocom、Putty哪家强?
  • 别再买山寨ST-Link了!实测DAP-Link与自刷固件方案,告别Keil/CubeProgrammer兼容性烦恼
  • 老路由焕新记:给吃灰的小米路由器R2D刷上Misstar Tools,解锁广告过滤/内网穿透/离线下载
  • 015、Zephyr RTOS开发环境搭建(SDK安装与配置)
  • 别再只会用DS18B20了!用STM32驱动PT100实现0.2℃精度测温(附电桥与差分放大电路详解)
  • AI辅助开发:让快马AI解析版本需求并生成智能文件分类模块代码
  • 大模型时代必备技能,深度拆解Prompt工程、RAG调优与Agent编排的黄金三角组合