简介基于STM32的红外测温系统设计文档面向嵌入式系统学习者、电子竞赛参赛者和相关专业毕业生提供从课题背景、技术现状到方案论证、软硬件实现的完整设计资料适用于医疗、工业等非接触测温场景。文档以STM32F103微控制器为处理核心采用MLX90614红外测温模块实现非接触测温并辅以DS18B20、LCD1602、按键、复位、电源和报警等模块详细说明了系统硬件电路、主程序流程、子程序逻辑和系统调试方法。整个资源包仅包含1个docx文档大小2.09MB目录结构完整内含硬件原理图、引脚说明、模块接线图、程序流程框图和部分关键程序能帮助读者快速理解红外测温原理、STM32外设使用以及软硬件联调流程。目前已有175人学习浏览尤其适合需要参考完整开发流程或撰写课程设计/毕业论文的读者是一份内容详实、结构清晰的典型案例。1. 项目要做什么红外测温系统的需求与整体设计思路如果你最近在准备STM32相关的毕业设计或者课程项目大概率绕不开测温这个方向。我最初接这个项目的时候需求也比较直接做一个非接触式的红外测温系统把测到的温度实时显示出来能设置报警阈值超温就响蜂鸣器。核心器件就是STM32单片机加一颗红外温度传感器。为什么选非接触式的红外测温而不是传统的NTC热敏电阻或者DS18B20这种接触式方案因为实际使用场景里有很多情况是不方便接触被测物的。比如测高速旋转的电机外壳、测电路板上功率管表面温度、测高温管道外壁甚至只是单纯想隔着一小段距离测个水温接触式传感器都很难办。红外测温的好处就是不用碰、不用贴传感器对准目标就能读温度响应还特别快。MLX90614这颗传感器的响应时间典型值在几十毫秒级别比DS18B20那种动辄几百毫秒的转换时间快得多。这个项目的受众我个人觉得主要是两类人一是正在做STM32课程设计/毕业设计的学生二是想快速上手单片机传感器完整开发流程的嵌入式入门者。项目本身不复杂但麻雀虽小五脏俱全从传感器选型、通信协议、数据处理到人机交互每个环节都覆盖到了。做完这个项目你对STM32的I2C通信、定时器、中断、低功耗这些常用外设基本都能有个实操层面的认识。在设计系统架构的时候我把它拆成几个模块来看主控模块负责核心控制和数据处理传感器模块负责采集红外温度显示模块负责把温度值和人机交互界面呈现出来报警模块负责超温提醒电源模块负责给整机供电。这样拆分的好处是每个模块可以独立调试出问题也容易定位。整机的工作流程就是上电初始化外设周期性读取红外传感器的温度数据经过滤波和校准换算成实际温度值刷新到OLED屏幕上同时判断当前温度有没有超过设定的阈值超了就触发蜂鸣器报警。2. 硬件选型与设计细节2.1 为什么选STM32F103C8T6当主控STM32型号很多F103系列可以称得上最经典的一个尤其是C8T6这颗芯片在学习和毕设里基本属于万金油级别。它内部有64KB Flash、20KB SRAM主频72MHz足够跑这个项目了。关键是这颗芯片在淘宝上价格便宜几块钱一片配套资料极多网上随便一搜就是一堆例程。从外设资源上看F103C8T6有3个USART、2个I2C、2个SPI、多个定时器还有ADC、DAC对于测温系统这种规模的应用绰绰有余。就算后续你想加个ESP8266把温度上传到云平台USART资源也够用。项目里我用到了它的I2C1来通信基本算是它的基础功能不存在资源紧张的问题。2.2 MLX90614红外测温传感器的选型理由与原理传感器选了MELEXIS的MLX90614这是目前做非接触测温最常用的一颗数字红外传感器。它内部集成了一个红外热电堆探测器和一颗信号处理芯片直接把红外辐射信号转换为数字温度值通过I2C实际上是SMBus接口输出省掉了复杂的模拟信号调理电路。这个数字输出的特性非常重要因为热电堆输出的模拟电压信号极其微弱如果自己搭放大电路放大器的零点漂移和噪声就够折腾很久直接选用带内部处理的传感器模块能大幅降低开发门槛。我得说说它怎么测温度的。红外测温的理论基础是斯特藩-玻尔兹曼定律物体辐射的能量和温度的四次方成正比。传感器内部的热电堆检测到红外辐射能量后根据热端和冷端的温差计算出目标表面的温度。MLX90614出厂前已经做了校准我们直接读它的RAM寄存器就行。它0x07地址存放的是被测物体的温度0x01地址存放的是传感器自身的环境温度数据格式为16位二进制补码单位是开尔文精度是0.02℃每比特换算公式是这个实际温度(℃) 原始数据 × 0.02 − 273.15这个公式在做数据处理的时候会用到后面代码部分我会再演示一遍。需要留心的是MLX90614出厂默认的I2C地址是0x5A7位地址通信协议兼容标准I2C但有些细节和标准I2C不完全一样比如它要求发送PEC校验字节不过我们用STM32的硬件I2C或者软件模拟I2C时基本把PEC忽略也能正常读写我实测是没问题的。2.3 显示与报警模块的设计显示模块我选了0.96寸的OLED屏驱动芯片是SSD1306接口是I2C的。之所以不用LCD1602一是OLED自带字库且对比度高二是它只需要两根线SDA、SCL就能通信接线超级简单三是显示内容可以灵活定义画个曲线、显示个图标都行。对于这个项目OLED可以显示当前的实时温度、环境温度、报警阈值还可以画一个简单的温度趋势条看起来比数码管和1602高级不少。SSD1306的I2C地址一般是0x3C也有部分是0x3D买的时候可以让卖家确认一下或者写个扫描程序扫一遍确定。报警模块就是蜂鸣器用三极管9012做开关驱动STM32的GPIO输出高电平时蜂鸣器响。注意蜂鸣器分有源和无源两种有源蜂鸣器内部带振荡电路通电就响程序里只需要给它高低电平就行无源蜂鸣器需要PWM信号才能发声。做这个项目买有源的就行省事。2.4 电源方案的考量电源这块值得单独说。MLX90614传感器既支持3.3V也支持5V供电但如果你要用STM32的ADC去量它的模拟输出型号那得配上合适的电平。好在我们用的是I2C数字接口直接统一用3.3V供电就行。整个系统的功耗也不高STM32F103在72MHz运行时的电流大概几十毫安传感器大概1.5毫安左右OLED屏幕点亮时也就十毫安上下。用USB线接入一个5V电源通过AMS1117-3.3稳压芯片降到3.3V给整个系统供电完全够用。如果是做便携版本可以上锂电池加TP4056充电板续航也足够。3. 软件设计与核心代码实现3.1 STM32CubeMX初始化配置软件部分我用的是STM32CubeMX生成工程框架代码逻辑自己在Keil里补充。为什么用CubeMX因为初始化代码手写太容易出错特别是时钟树和GPIO复用功能CubeMX可视化配置完自动生成初始化代码可以省去很多低级错误。而且它生成的代码是HAL库风格的可读性好、跨型号移植方便。时钟配置上把HSE外部高速时钟设为8MHz晶振系统时钟通过PLL倍频到72MHz这是STM32F103的最经典配置。I2C1选择PB6和PB7引脚分别对应I2C1_SCL和I2C1_SDA。I2C速度配置为100kHz标准模式就行MLX90614默认的SMBus通信速度上限是100kHz如果你想跑400kHz快速模式也可以但建议先按标准模式来稳定第一。GPIO方面PC13接蜂鸣器控制引脚配置为输出模式。定时器方面我开了一个TIM2做1ms时基用来做按键扫描和测温周期的调度——这个思路比单纯用delay死等要高效得多系统还能顺带响应用户操作。3.2 I2C读取MLX90614温度数据的完整流程接下来是核心的部分读温度。MLX90614的I2C读时序和普通I2C设备有一点差异它采用了写寄存器地址然后重启总线再读数据的流程。如果你直接调用HAL_I2C_Mem_Read这种标准函数可能读不出来数据因为HAL库的Mem_Read时序和MLX90614需要的时序不完全匹配。我自己实测遇到的情况是用HAL_I2C_Mem_Read读出来的数据一直是0xFFFF或者时不时报错后来改成手动组合I2C读写函数才稳定。下面是我实际使用的读取代码基于HAL库封装了一个独立的读温度函数// 从MLX90614指定RAM地址读取16位数据 uint8_t MLX90614_ReadRAM(uint8_t regAddr, uint16_t *data) { uint8_t buf[2]; HAL_StatusTypeDef status; // 发送起始信号 设备地址(写) 寄存器地址 status HAL_I2C_Master_Transmit(hi2c1, (MLX90614_ADDR 1) | 0, regAddr, 1, 100); if (status ! HAL_OK) return 1; // 重启总线发送设备地址(读)读取2字节数据 status HAL_I2C_Master_Receive(hi2c1, (MLX90614_ADDR 1) | 1, buf, 2, 100); if (status ! HAL_OK) return 2; *data (uint16_t)buf[0] | ((uint16_t)buf[1] 8); return 0; } // 计算实际温度单位℃ float MLX90614_GetTemp(uint8_t regAddr) { uint16_t rawData 0; float temp 0; if (MLX90614_ReadRAM(regAddr, rawData) 0) { temp (float)rawData * 0.02f - 273.15f; } return temp; }读出来的rawData是开尔文温度值的16倍放大值乘以0.02就是开尔文温度再减去273.15就得到摄氏温度。这个换算关系在MLX90614的数据手册里有明确说明属于常规操作。需要注意的是如果寄存器地址传0x07读到的是物体温度传0x01读到的是环境温度二者用途不同。我一般会在主循环里先读一次物体温度算完平均值再读一次环境温度用来辅助判断测量是否合理——如果环境温度读出来明显异常大概率是传感器接触不良或者I2C总线上有问题。3.3 平均滤波为什么需要以及怎么实现直接读出来的温度数据会有轻微波动尤其是近距离测量时如果目标温度本身不稳定读数会跳来跳去。解决办法就是做软件滤波。我用了最简单的滑动平均滤波维护一个长度为10的数组每来一个新数据就覆盖最旧的一个然后取平均值作为本轮的有效温度值。这种滤波方式可以很好地抑制随机噪声而且实现代码非常简单。滑动平均的效果我实测过在传感器对准一个稳定热源的情况下未滤波时读数波动可能有±0.5℃左右滤波后能稳定在±0.1℃以内效果很明显。代码如下#define FILTER_SIZE 10 float filterBuf[FILTER_SIZE]; uint8_t filterIndex 0; uint8_t filterCount 0; float Temperature_Filter(float newTemp) { float sum 0; uint8_t i 0; filterBuf[filterIndex] newTemp; filterIndex (filterIndex 1) % FILTER_SIZE; if (filterCount FILTER_SIZE) filterCount; for (i 0; i filterCount; i) { sum filterBuf[i]; } return sum / filterCount; }这段代码的思路很直白数组存满10个温度值后每来一个新值就把最旧的挤掉然后求平均。filterCount是为了在数组未填满时也能正常工作不至于除以0或者除以错误的长度。3.4 OLED显示与报警逻辑的实现OLED显示我用的是常用的SSD1306驱动网上有很多封装好的字库和画点函数。为了节省MCU资源我选择了I2C接口的OLED通信速率400kHz刷新一帧全屏大概需要几十毫秒对于这个项目来说完全够用。屏幕上除了显示温度数值我还在底部画了一个简易的进度条直观显示当前温度在设定阈值下的位置用一条白色竖线指示实测温度用户一眼就能看出温度离报警值还有多远。报警逻辑我用了一个施密特触发的思想当温度升高到设定报警值以上时蜂鸣器响当温度降到报警值以下的回差范围比如3℃后蜂鸣器才停止。为什么这么设计因为如果只在阈值点附近做简单比较当温度在阈值附近波动时蜂鸣器会反复开启关闭听起来就是嗞嗞嗞乱响很烦人。加上回差后报警一旦触发就要降下来几度才会解除消除了这种临界抖动。代码里就是维护一个报警状态标志进入报警状态后需要温度低于报警阈值减去回差值才复位。#define ALARM_THRESHOLD 38.0f #define ALARM_HYSTERESIS 3.0f uint8_t alarmState 0; void Alarm_Update(float currentTemp) { if (alarmState 0) { if (currentTemp ALARM_THRESHOLD) { alarmState 1; HAL_GPIO_WritePin(BUZZER_GPIO_Port, BUZZER_Pin, GPIO_PIN_SET); } } else { if (currentTemp (ALARM_THRESHOLD - ALARM_HYSTERESIS)) { alarmState 0; HAL_GPIO_WritePin(BUZZER_GPIO_Port, BUZZER_Pin, GPIO_PIN_RESET); } } }这个报警逻辑虽然简单但在实际使用中非常关键。我在调试阶段因为没有加回差就遇到过蜂鸣器在阈值附近疯狂抖动的问题后来查了资料才想到施密特触发这个方案实际用下来体验好了很多。4. 实操步骤与调试过程全记录4.1 硬件搭建与接线注意事项在实际动手之前我先在面包板上把电路搭了起来。接线关系比较简单STM32最小系统板我这里用的是C8T6最小核心板引出3.3V和GND给MLX90614供电PB6连SCLPB7连SDAOLED同样接3.3V、GND、SCL、SDA和MLX90614挂同一条I2C总线上。蜂鸣器模块的信号脚接PC13。接线时有几个新手常踩的坑我得提一下。第一I2C的SCL和SDA需要上拉电阻一般模块板上已经自带了4.7kΩ上拉电阻可以直接共用一个总线上。但如果你的传感器是纯裸芯片而非模块那就必须自己在总线上加上拉电阻否则I2C通信完全不通。第二MLX90614的VCC如果接的是5V那么I2C电平也是5V的这时候如果STM32的I2C引脚是容忍5V的倒还好但为了保险起见一律统一用3.3V供电省得逻辑电平不匹配出问题。第三OLED屏幕接I2C时SCL和SDA别接反了我见过不少因为这两根线接反导致屏幕一直不亮的案例。MLX90614模块在淘宝上有些是4针VCC、GND、SCL、SDA有些是5针多了一个SMBus地址选择脚。多出的地址选择脚是干嘛的它可以让你在一条I2C总线上挂多颗MLX90614通过电平来改地址。如果只用一个传感器这个脚悬空即可。4.2 Keil工程配置与下载调试用CubeMX生成工程后用Keil打开编译前务必确认芯片型号和Flash下载算法正确。F103C8T6的Flash是64KB如果误选了C8T6以外的型号可能导致下载失败。下载器我用的是ST-Link V2连接方式就是SWD模式占用的SWDIO、SWCLK、GND、3.3V四个引脚。这个模式下目标板其实不供电也行但为了保险我还是让板子自己供电ST-Link只负责烧录。第一次下载时如果报错Error: Flash Download failed - Cortex-M3先检查ST-Link和板子之间的接线。SWD接口只要SWDIO和SWCLK两根线接对就行不用接复位脚比JTAG的4根线简单多了。另外如果你板子上有别的外设把SWDIO或SWCLK占用了下载就可能失败这时候需要按住复位键再点下载等进度条动起来再松手能绕过这个问题。4.3 联调过程中的实测数据与心得整套系统跑起来后我做了几个实测。用传感器的探头对准一杯温水水温用玻璃温度计读数做参考。传感器距水面大概3厘米测温稳定后OLED显示温度和水银温度计差值在0.5℃以内考虑到玻璃温度计本身也有读数误差这个精度完全可以接受。我又把传感器对准一块刚从冰箱拿出来的冰袋读数从室温快速下降到2.3℃响应速度确实比接触式的传感器快很多。有一个细节值得注意MLX90614有一个固定的视场角FOV。根据型号不同FOV可能从35°到90°不等。这意味着传感器测的是视场范围内所有物体红外辐射的平均值。如果视场角内既有高温物体又有低温背景读数就会偏向两者的加权平均和你想测的目标温度有偏差。所以实际使用时传感器要尽量靠近被测目标让目标物体占满整个视场角读数才准确。我在测试时就发现距离很近5厘米内时读数稳定且准确拉远到30厘米以上读数就会有明显下降因为背景的冷物体参与了红外能量的积分计算。5. 常见问题与排查技巧5.1 I2C通信失败的排查思路这个项目里最常遇到问题的地方就是I2C通信。症状表现是读温度一直为0、读出的数据为0xFFFF、OLED黑屏不亮等。排查思路我建议按这个顺序来先确认供电——用万用表量一下MLX90614的VCC引脚电压是否正常。再确认接线——SCL/SDA是否和STM32配置的引脚一致是不是搞混了。然后确认地址——MLX90614默认地址0x5A但有些模块上如果有地址选择跳线帽接上后地址会变成0x5B或者别的值需要仔细看模块说明。最后就是确认上拉电阻——I2C总线必须要有上拉如果你用杜邦线连接两个模块而模块板载没有上拉电阻的话必须自己外接两个4.7kΩ上拉电阻到VCC。我在调试时经历过一次非常奇怪的现象同一套接线程序烧进去后第一次读取正常但等几秒后一直返回0。后来检查发现是杜邦线松了接触不良导致I2C总线被拉死。所以建议接线后可以先写一个简单的I2C扫描程序把总线上所有设备地址扫出来确认设备都在再继续下一步开发。5.2 温度数据异常的常见原因如果I2C通信正常但读出来的温度值不对比如读出来一个稳定的-270.15℃或者很高的异常值基本上可以判断是换算出了问题。最常见的原因是把开尔文和摄氏度的换算做错或者把0.02的精度系数写错。MLX90614的数据是开尔文温度放大50倍存储的也就是每一比特代表0.02K。所以换算公式是摄氏度 原始值 × 0.02 − 273.15。如果你忘了减273.15读数就会比正常值高273℃左右这种低级错误我调试的时候犯过一次后来在代码注释里写清楚这个换算关系才没再犯。另一个常见问题是传感器镜片脏了。MLX90614模块的感应区域上面有一个光学透镜或者滤光片如果它被灰尘、油污遮住了测量的温度就会严重不准。测试时手不要直接碰镜片脏了用棉签蘸少量酒精轻轻擦拭。还有如果被测目标表面是高度反射的亮金属表面它的发射率很低比如抛光铝只有0.04~0.1红外测温很可能就不准因为传感器默认目标发射率是1。这种场景下可以在目标表面贴一块黑色哑光胶带再测读数就能恢复正常。6. 从这个项目里可以延伸的后续方向做完这个基础版本后我一直在想怎么让它更有实用价值。思路可以扩展的方向挺多的。首先是增加无线传输功能用ESP8266模块通过串口连接STM32把温度数据上传到云平台或者局域网内的服务器配合手机App就能实现远程温度监控这个在冷链运输、机房温度监控场景里非常实用。其次是增加数据记录功能用SD卡模块定时存储温度数据配合RTC实时时钟记录时间戳就能形成一条温度历史曲线导出到PC端用Python做数据分析可以查看温度变化趋势这在食品储存和环境监测等领域很有用。目前这个项目做下来从硬件选型到软件实现再到问题排查算是一次完整的嵌入式开发实战训练。如果你也在做类似的STM32项目可以参考我这个整体思路先画出系统的功能框图和数据流再动手做硬件和代码能省去不少返工的麻烦。最后再分享一个我个人的开发习惯每完成一个模块的功能测试就提交一次代码版本加个注释说明当前状态。项目周期不长的还好如果同时改代码和调硬件没有版本管理很容易改着改着就不知道哪一版是能用的了。这个习惯帮我省了很多时间建议你也试试。本文还有配套的精品资源点击获取