简介:MLX90614红外测温系统是嵌入式开发中的常见场景,这份驱动资源面向STM32开发者,用于解决红外测温模块的快速移植与驱动编写问题。ZIP压缩包共2个文件,包含C源码与头文件各一个,整体仅2KB,便于直接嵌入工程阅读;源码侧重初始化、I²C通信、寄存器配置和温度数据读取,头文件则提供函数原型与相关常量定义。内容还涉及Object1/Object2温度寄存器、发射率设置、16位原始数据的温度换算公式,以及通信失败、超时等异常处理的排错思路;对ear9fp封装特点和实时测温场景也有补充说明。通过封装好的函数接口可快速完成初始化与温度读取,减少对照数据手册逐条配置的重复工作,用作模块接入与调试的参考笔记非常合适。目前已有1139人学习下载,适合正在学习STM32外设驱动或需要快速集成MLX90614的开发者。 MLX90614配STM32做红外测温,这两年几乎成了嵌入式项目的标配。毕设要做非接触测温手环、测温门禁、智能晾衣杆热源感应,很多人的第一反应就是搜“STM32 MLX90614”,找到代码、烧进去、串口能打印出温度,就算跑通了。但等你真拿这东西去测一个具体物体,就会发现事情没那么简单:为什么测金属不准?为什么读数跳来跳去?为什么别人能跑通的代码在我板子上就是NACK?这篇文章就围绕STM32加MLX90614这套组合,把原理、接线、驱动、标定和排坑一次性讲完,给正在做红外测温的兄弟们一份真正能照着干的手册。
1. MLX90614凭什么能做非接触测温
1.1 热电堆原理:温度怎么变成数字
MLX90614内部核心是红外热电堆传感器。任何有温度的物体都在向外辐射红外线,辐射强度跟物体表面温度的四次方成正比,温度越高辐射的红外能量越强。热电堆由上百个微型热电偶串联而成,吸收红外辐射后产生与辐射强度成正比的微小电压信号,这个信号经过芯片内置的运放放大、24位ADC数字化,再通过DSP做环境温度补偿和非线性校正,最后输出数字温度值。
所以它测的是物体表面辐射温度,而不是空气温度。这一点决定了很多使用场景的差异:比如测一杯热水,MLX90614测的是水面或杯壁的表面温度,而DS18B20测的是水本身的温度。两者各有用途,做红外测温项目首先要搞清楚你到底要测哪个温度。
1.2 型号怎么选:不是所有MLX90614都一样
这块比较容易踩坑。MLX90614的型号后缀决定了物体测温量程和视场角:
- ESF/BAA:最常见的消费和医疗版,物体测温范围约-40到115℃,体温场景到50℃足够用,环境温度范围-40到85℃,90°视场,价格最低,适合人体测温、非接触测温门禁这类项目。
- A系列(如A01、A02):工业版,物体测温范围可达-70到380℃,适合测电机外壳、加热管、热水管路温度。
- DCI版本:窄视场版本,大概1°左右的视场角,适合距离稍远、目标尺寸较小的点温场景。
很多做毕业设计的同学上来就买ESF/BAA,结果拿去测几百度的加热管,读到的最高温度被芯片量程限制住,白折腾一场。选型第一步就把量程和视场定好,后续能省掉大量返工。
1.3 SMBus接口和寄存器结构
MLX90614对外提供两个接口:PWM和SMBus(兼容I2C)。PWM模式直接把温度映射成占空比,分辨率10位;SMBus模式下分辨率16位,0.02℃每LSB,所以我一直推荐用SMBus读数据。出厂默认7位I2C地址是0x5A,换算成8位写地址0xB4、读地址0xB5,HAL库配置时DevAddress要填0xB4(即0x5A左移一位),这是新手最容易搞混的地方。
芯片内部主要分两块存储:RAM是运行数据区,0x07地址存环境温度TA,0x06地址存物体温度TObj;EEPROM是配置区,存发射率、地址、量程校准参数等,出厂写死,一般不轻易动。读数据时主机向芯片发送寄存器地址,芯片返回3个字节:低字节、高字节、PEC校验字节。前两字节拼成16位原始值,换算后就是温度。
2. 硬件连接与布局细节
2.1 供电和引脚
MLX90614的引脚在SMBus模式下一共4个:VIN、GND、SCL、SDA。VIN供电电压要看你手里是散片还是模块:散片按数据手册的标称电压来,常见的是3.3V供电;成品模块大多板载稳压,5V供电也能正常工作。我建议拿不准的时候先看模块背面丝印,或者直接查你购买页的原理图,别凭感觉接。
去耦电容不能省。VIN和GND之间加一个100nF陶瓷电容,位置尽量靠近芯片的VIN引脚,条件允许再并一个10uF电容。很多散片用户直接飞线,电源纹波一大,I2C通信就时好时坏,表象是读数据偶尔成功偶尔失败,实际根子在供电。
2.2 上拉电阻不能省
SMBus规范要求总线有上拉电阻,MLX90614的SDA、SCL是开漏输出,必须外部上拉。推荐4.7k到10k,总线速度快就选小一点的阻值,400kHz时4.7k比较稳。STM32内部上拉不要指望,那个阻值普遍在30k以上,驱动能力弱,总线上升沿会变得很慢,数据就容易出错。
我第一次用的时候就犯了没加上拉的错:SCL/SDA直接接STM32的PB6/PB7,把内部上拉打开了,读回来的数据隔三差五就是0xFFFF,当时还以为是芯片坏了。后来示波器一抓,发现总线上根本没有干净的高电平,波形边缘塌得一塌糊涂。加上两个4.7k电阻之后,问题立刻消失。从那以后我养成了一个习惯:凡是I2C项目,画板子的第一步就是把上拉电阻预留好,哪怕之后用不到,也比飞线补焊强。
2.3 探头安装和视场角的影响
传感器自身会发热,而且芯片封装本身就有温漂,所以安装时要让传感器周围有空气流通,尽量避免直接贴在发热的大功率器件或主控芯片上。这就是为什么原厂文档都会说TA读数是传感器附近的环境温度——TA不准,会直接连累TObj的补偿精度。
视场角影响更大。MLX90614ESF是90°视场,测量距离越远,采集到的目标区域越大。距离10cm测到的是一个直径接近18cm的圆,如果目标物体只有5cm大小,读到的温度会混合背景温度,结果偏低。做体温枪这种近距离测量问题不大,但做设备监测时就要算好距离和目标尺寸,或者选窄视场版本。安装的时候也要注意视场角内不要有塑料外壳、遮挡物,不然测的就是遮挡物的温度。
3. STM32驱动代码实现
3.1 用CubeMX把I2C配好
用STM32CubeMX配置I2C1:标准模式和快速模式都可以,建议先从100kHz起步,跑通了再提到400kHz。关键是GPIO要配成开漏输出,CubeMX里可以把PB6/PB7的内部上拉开起来,但外部4.7k上拉才是正解,内部上拉只是辅助。初始化配置代码大致如下:
I2C_HandleTypeDef hi2c1; hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 100000; // 100kHz,先低速跑通 hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); }如果你用的是标准库而不是HAL库,原理完全一样,只是寄存器操作方式不同。标准库新建工程的时候记得把I2C外设时钟打开,GPIO模式配成开漏复用,漏掉任何一步都会让总线工作异常。
3.2 读RAM寄存器:三步完成
读MLX90614温度数据其实很简单,核心就一条HAL_I2C_Mem_Read。芯片内部寄存器短地址是8位,所以MemAddressSize用I2C_MEMADD_SIZE_8BIT。
/** * @brief 从MLX90614读取寄存器数据 * @param hi2c: I2C句柄 * @param reg: 寄存器地址,RAM区0x06=物体温度,0x07=环境温度 * @param data: 返回16位原始数据 * @retval 0=成功,1=超时或错误 */ uint8_t MLX90614_ReadReg(I2C_HandleTypeDef *hi2c, uint8_t reg, uint16_t *data) { uint8_t buf[3] = {0}; // 注意DevAddress是7位地址左移一位:0x5A << 1 = 0xB4 if (HAL_I2C_Mem_Read(hi2c, 0x5A << 1, reg, I2C_MEMADD_SIZE_8BIT, buf, 3, 200) != HAL_OK) { return 1; } // buf[0]为数据低字节,buf[1]为数据高字节,buf[2]为PEC,实际使用可忽略 *data = ((uint16_t)buf[1] << 8) | buf[0]; return 0; }温度换算:
/** * @brief MLX90614原始数据转为摄氏度 * @note 内部以0.02℃/LSB分辨率存储开尔文温度 * @param raw: 16位原始值 * @retval 温度(℃) */ float MLX90614_ToCelsius(uint16_t raw) { return raw * 0.02f - 273.15f; }25℃室温附近,读到的raw应该接近0x3A3C(十进制14908,乘0.02减273.15刚好约25℃)。如果读到的原始值离这个范围很远,先别急着调代码,多半是硬件连接问题。
3.3 不稳定的硬件I2C,换成软件I2C
STM32F103系列在早期标准库时代,硬件I2C被很多人调侃是“bug集合体”,原因是SMBus时序跟标准I2C有细微差别,尤其在总线超时检测、PEC字节上容易翻车。用HAL库以后稳定性好了不少,但如果你发现硬件I2C读MLX90614时第一次总是失败、重试几次又能成功,那基本还是外部上拉不足或者总线电容过大导致边沿过缓,跟芯片本身关系不大。
排查顺序建议是:先按100kHz跑,确保稳定了再提速;若还是不稳定,直接上软件I2C。软件I2C本质就是两个GPIO模拟时序,代码量不大,而且可以任意指定引脚,硬件接线更灵活。核心读写思路如下:
#define MLX90614_ADDR 0x5A #define MLX90614_WRITE (MLX90614_ADDR << 1) // 0xB4 #define MLX90614_READ ((MLX90614_ADDR << 1) | 1) // 0xB5 // GPIO模拟I2C:SCL/SDA配置为开漏输出,拉高必须依赖外部上拉电阻 static void I2C_Start(void) { SDA_H(); SCL_H(); delay_us(5); SDA_L(); delay_us(5); SCL_L(); } static void I2C_Stop(void) { SDA_L(); SCL_H(); delay_us(5); SDA_H(); delay_us(5); }完整模拟I2C代码网上例程一大把,这里只提关键坑:软件I2C的延时函数必须准确。用for循环空转延时或者定时器延时都行,但绝对不能在一个会被高优先级中断打断的地方做位翻转,否则时序一乱,芯片就不响应了。之前有人遇到“STM32延时函数delay卡死”的问题,排查半天发现是中断里调用了会等待的延时函数,导致主循环的I2C时序被打断。所以软件I2C的延时函数要选用不依赖中断的忙等待版本。
3.4 完整读取流程
实际项目里不可能只读一次,我用的是“多次读取加中值滤波”的流程:
- 读TObj(寄存器0x06),连续读5次,每次间隔10ms。
- 去掉最大值和最小值,剩下的取平均。
- 再读TA(寄存器0x07),判断传感器自身环境温度是否正常。
- 如果TObj和TA很接近,说明传感器前方可能没有目标物体,或者目标物体温度与环境一致,属于正常现象,不要误判成传感器故障。
滤波的主要作用是消除偶发的毛刺,尤其是工业现场有电机、变频器干扰的时候,单次读数的可信度很低。平均之后的数值虽然也会有波动,但至少趋势是稳定的,拿去做阈值判断就不会频繁误触发。
4. 温度数据换算与精度调校
4.1 0.02和273.15这两个数怎么来的
MLX90614内部把温度以0.02摄氏度的分辨率存成16位数据,单位是开尔文。所以拿到raw值之后,先乘以0.02得到开尔文温度,再减去273.15就是摄氏度。不少人看到0.02会疑惑,这个分辨率其实是芯片出厂就定死的,换算公式全球通用,不需要自己标定。
细节上要注意:raw本身在正常量程内不会超过0x7FFF,所以直接用uint16_t运算、转float后再减273.15即可,不需要特意处理负数。如果某些异常情况下raw大于0x8000,大概率是芯片配置异常或读数错误,代码里可以做一次范围过滤,超出量程的数据直接丢弃,防止把错误值送进控制逻辑。
4.2 发射率:为什么测金属会不准
所有红外测温的“天敌”都是发射率。发射率代表物体表面辐射红外能量的能力,黑体发射率是1,人体皮肤约0.98,普通油漆表面约0.9,氧化铝约0.3,而抛光铝只有0.04左右。MLX90614出厂默认按发射率1.0来计算,测高发射率物体误差不大,但测金属、镜面、反光面就会严重偏低。
调整方法:写EEPROM地址0x04,写入值等于目标发射率乘以0xFFFF。人体测温场景建议写0.95左右,也就是约0xF332(十进制62258)。写入时要注意EEPROM写入次数有限且有写保护时序,操作前把出厂值备份好,弄错可能把芯片配置搞乱甚至整个芯片变砖。如果不是对特定材料测温,大多数场景保持默认1.0就行。
你会发现一个典型现象:拿红外测温测打磨后的铝块,读出来只有40℃,实际上铝块已经80℃了;贴上黑色电工胶带再测,温度瞬间就上来了。这不是传感器坏了,是被测面的发射率变了。所以测金属目标,最省事的办法就是贴黑胶带,而不是折腾EEPROM。
4.3 现场标定三步走
我给母排测温项目做标定时,是拿K型热电偶同时贴在同一个铜排上,读两组数据做差值补偿。操作流程分三步:
- 准备一个恒温源,或者直接用热水、恒温烙铁设定几个不同温度点。
- MLX90614对准目标表面,稳定后记录读数,同时记录热电偶的参考值。
- 多测几个点后做线性拟合,得到一个offset值写入代码里。
注意测量距离每次都要保持一致,90°大视场下距离差1cm,结果就可能差好几度。这个方法虽然土,但在工程现场反而最实用。需要提醒的是,标定精度并不会超过热电偶本身的精度,想要绝对准确就得上黑体炉,那个成本就不是几十块钱能搞定的了。
5. 常见问题与排查技巧实录
5.1 问题速查表
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 读数固定0xFFFF | SDA/SCL接线错误、芯片未上电、上拉电阻缺失 | 检查供电,万用表量VIN电压;补上外部上拉电阻 |
| HAL_I2C_Mem_Read返回NACK或超时 | 从机地址填错、总线被拉死、SCL/SDA接反 | 确认地址是0x5A左移一位;逐个量SCL/SDA电平;复位总线 |
| 数据能读到但偶尔跳变很大 | 总线干扰、电源纹波大、测量距离不稳定 | 加去耦电容、降低I2C时钟、固定测量距离 |
| TObj和TA几乎一样 | 传感器前方没有被测对象,或被测物体温度与环境相同 | 把传感器对准目标物体或用手挡一下测试 |
| 测金属温度明显偏低 | 金属表面发射率低,红外测温测的是辐射温度而非真实温度 | 表面贴黑胶带或涂黑漆后再测,或调整发射率 |
| 硬件I2C读取时好时坏 | SMBus时序兼容性、上拉电阻不匹配、总线电容过大 | 降到100kHz;检查上拉电阻;不行直接换软件I2C |
5.2 开发环境的小坑
调试MLX90614的时候,如果程序烧录不进去,不要光盯着传感器代码,也要检查开发工具链这边。ST-Link连接丢失报“error: no stm32 target found”这类错误时,很多时候是PA13/PA14被代码复用成普通GPIO了,而你的I2C引脚恰好又离得很近,飞线毛刺把调试端口干扰了。解决办法是把Boot0拉高进ISP模式擦除,或者按住复位键点烧录、在芯片复位瞬间松开,这些土办法实测都有效。
还有一次项目拷给别人,对方Keil里编译报错找不到芯片,最后发现是芯片支持包没装好。所以拿到一个STM32工程,第一件事先确认芯片型号和设备支持包跟你本机环境对得上,不然排查半天全是环境问题。另外Keil5兼容C51和STM32的安装方式也不一样,要分别装对应的芯片包,混用工程模板经常会报奇怪的宏定义错误。
5.3 我买到假芯片了吗
最近市面上MLX90614散片鱼龙混杂,很多人买回去读到的温度明显偏高或偏低。一个简单的验证方法:把发射率保持默认,用黑胶带贴在待测物体表面,如果胶带区域读数和周围裸面读数差得很远,说明裸面发射率低,是正常现象。反过来,如果怎么测都读不准,最高温度还明显超量程或者读数存在跳表,那才要考虑是不是翻新片或打磨片。
正品芯片的RAM数据读取连续10次,原始值波动不会超过3到5个LSB,波动太大说明芯片本身或电源有问题。另外MLX90614的SMBus地址默认是0x5A,如果I2C扫描时在别的地址也有响应,那多半不是原装芯片,后面各种怪问题也就不奇怪了。
最后再说一个我个人的习惯:做这类传感器项目,一定先把典型场景的正常读数范围记录下来。比如25℃室温下,MLX90614的0x07寄存器大概读到0x3A3C附近。以后不管代码怎么改、硬件怎么换,只要读数偏离这个基准太多,就能立刻判断是环境变化还是系统故障。这个习惯帮我省掉了大量排查时间。
红外测温这东西,原理不复杂,真正的功夫都在细节里:供电、上拉、发射率、测量距离、视场角,每一个都能让数据面目全非。如果你也正在调MLX90614,按照上面的顺序从硬件到软件排查一遍,大概率能把问题解决掉。
本文还有配套的精品资源,点击获取