资讯详情

STM32驱动MLX90614红外测温:从I2C时序到OLED显示的完整落地

📅 2026/10/14 1:33:31 | 华诺云谱 👁 阅读
STM32驱动MLX90614红外测温:从I2C时序到OLED显示的完整落地
简介基于STM32的MLX90614红外测温显示工程面向嵌入式初学者与物联网项目开发者提供完整的非接触式测温与OLED显示方案。项目以STM32F103C8T6为主控通过I2C接口读取MLX90614红外传感器数据经SPI驱动OLED屏实时显示环境及物体温度涵盖底层I2C/SPI驱动、传感器寄存器配置、温度转换与屏幕刷新逻辑并涉及电源管理和抗干扰等实用考虑。压缩包内共有157个文件以.c/.h源码文件和Keil工程文件(.uvprojx)为主包含编译生成的.o/.d/.crf等中间文件及调试配置整体大小3.05MB目录结构清晰可方便导入开发环境查看或直接烧录。目前已有818人学习下载。压缩包内附完整可编译的工程源码可帮助读者深入理解MLX90614的I2C通信时序、OLED屏幕的SPI控制方式以及温度数据标定与显示格式处理亦可作为课程设计、毕业设计或物联网测温产品的快速参考模板。1. 基于STM32的MLX90614红外测温从I2C时序到OLED显示的完整落地用STM32驱动MLX90614红外测温模块并通过OLED显示是很多开发者入门非接触式测温的首选方案但这个看起来简单的项目里其实藏着不少坑MLX90614的I2C时序兼容性问题、SMBus协议与标准I2C的差异、EEPROM校准参数读取顺序、以及OLED显示刷新与温度采样之间的时序冲突。这套资源把完整的工程代码、原理图说明和调试记录打包在一起能帮你避开大部分新手会踩的坑。适合正在做测温手环、智能家居门禁测温或者医疗辅助设备的开发者也适合课程设计需要快速出DEMO的学生。我拿到这套资源后第一件事就是看I2C初始化代码和EEPROMM读取逻辑因为这决定了你后续调试是半小时搞定还是要熬一整晚。2. 硬件搭建与选型MLX90614的I2C地址问题和传感器配置2.1 模块选型与引脚连接别只看VCC和GNDMLX90614虽然封装小但内部集成了一颗红外热电堆传感器和一颗信号处理芯片对外提供SMBus兼容的数字接口。市面上常见的模块有两种一种是纯传感器芯片需要自己搭外围电路另一种是集成模块带稳压和滤波电容后者更适合直接对接STM32。这套资源用的是集成模块引脚定义是VCC、GND、SCL、SDA四根线部分模块还会引出SMBus的报警引脚ALM但标准驱动用不上。连线要注意三个细节。第一MLX90614的SMBus接口需要上拉电阻模块上如果有4.7k到10k的上拉就不要再额外加否则总线负载过重会导致通信时序变形。第二STM32的I2C引脚如果复用为开漏输出必须确认GPIO配置正确很多人在这里用了推挽输出导致总线拉不低。第三电源要干净MLX90614对电源纹波比较敏感供电不要和电机或继电器共用一个电源否则温度数据会在目标温度附近跳变。我一般会把SCL接PB6、SDA接PB7STM32F103的I2C1引脚OLED用I2C2或者软件I2C都可以。如果OLED也用硬件I2C注意两条总线的地址不要冲突MLX90614的默认7位地址是0x5BOLED屏幕常见地址是0x3C分开总线最省心。2.2 MLX90614的寄存器架构RAM和EEPROM才是关键MLX90614内部有两块存储区域需要区分清楚。RAM区存放的是测量结果地址0x06是红外传感器原始数据IR地址0x07是环境温度Ta这两个地址读取频率最高。EEPROM区存放的是校准参数和配置字包括发射率校准值地址0x24、传感器ID地址0x25以及一些出厂校准参数。很多初学者上来就读RAM的0x06和0x07发现数据不对就怀疑传感器坏了其实是因为没有先读EEPROM的校准参数。MLX90614的出厂校准数据存储在EEPROM里芯片上电后会自动加载到内部寄存器但如果你要计算实际温度需要用到发射率值默认是0x95对应人体测温场景。有些模块出厂时发射率被改成别的值读出来的温度会整体偏移这时候就要通过写EEPROM的0x24地址来校正。这套资源里的代码直接封装了读取EEPROM和RAM的函数你不用自己从头写I2C时序但建议理解一下SMBus的读字流程先发设备地址加写位然后发寄存器地址再重新发送设备地址加读位最后连续读两个字节加PEC校验字节。这里的PEC就是SMBus特有的CRC-8校验MLX90614会计算并返回这个校验字节如果你忽略它也不影响读取数据但总线在某些情况下可能会出现错位。3. 核心代码实现SMBus兼容读取、温度计算和OLED刷新策略3.1 初始化I2C和GPIO复用开漏加时钟配置先看GPIO和I2C的初始化代码这是整个项目的地基。代码用的是STM32标准外设库如果你改用HAL库逻辑是一样的只是函数名不同。void I2C_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能GPIOB时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // 使能I2C1时钟 GPIO_InitStructure.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; // PB6SCL, PB7SDA GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_OD; // 复用开漏输出 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; // 速度50MHz GPIO_Init(GPIOB, GPIO_InitStructure); // I2C1配置 I2C_InitTypeDef I2C_InitStructure; I2C_InitStructure.I2C_Mode I2C_Mode_I2C; I2C_InitStructure.I2C_ClockSpeed 100000; // 100kHz标准模式 I2C_InitStructure.I2C_DutyCycle I2C_DutyCycle_2; I2C_InitStructure.I2C_Ack I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_OwnAddress2 0x00; I2C_Init(I2C1, I2C_InitStructure); I2C_Cmd(I2C1, ENABLE); }这段代码的关键点有两个。一是GPIO模式必须配置为复用开漏GPIO_Mode_AF_OD因为I2C总线协议要求开漏输出配合外部上拉电阻才能实现线与功能如果你配成推挽输出SDA线在多个设备通信时会出现短路风险。二是I2C时钟频率设置为100kHzMLX90614在SMBus模式下支持10kHz到100kHz的时钟频率虽然它也能跑400kHz快速模式但官方手册推荐工作在100kHz以下更有保障尤其是当你的线材比较长或者模块质量一般的时候。3.2 读取MLX90614 RAM数据的核心函数读取温度数据是整个项目的核心这里给出读取RAM地址的完整函数实现。// 向MLX90614指定寄存器地址发起写命令用于设置EEPROM等操作 uint8_t MLX90614_WriteReg(uint8_t reg_addr, uint16_t data) { // 1. 发送起始信号 I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); // 2. 发送设备地址写位 I2C_Send7bitAddress(I2C1, MLX90614_ADDR, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); // 3. 发送寄存器地址 I2C_SendData(I2C1, reg_addr); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 4. 发送数据低字节SMBus规定先发低字节 I2C_SendData(I2C1, (uint8_t)(data 0xFF)); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 5. 发送数据高字节 I2C_SendData(I2C1, (uint8_t)((data 8) 0xFF)); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 6. 发送PEC校验字节CRC-8启用校验功能时必须发送 uint8_t pec 0; pec MLX90614_PEC_Calc(MLX90614_ADDR 1, reg_addr, data); I2C_SendData(I2C1, pec); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 7. 停止信号 I2C_GenerateSTOP(I2C1, ENABLE); return 0; } // 读取MLX90614指定寄存器地址的16位数据用于RAM和EEPROM读取 uint16_t MLX90614_ReadReg(uint8_t reg_addr) { uint16_t result 0; uint8_t data_l 0, data_h 0; // 1. 发送起始信号 I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); // 2. 发送设备地址写位指定要读取的寄存器地址 I2C_Send7bitAddress(I2C1, MLX90614_ADDR, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1, reg_addr); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 3. 重新发送起始信号SMBus读字操作要求restart I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); // 4. 发送设备地址读位 I2C_Send7bitAddress(I2C1, MLX90614_ADDR, I2C_Direction_Receiver); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); // 5. 读取低字节SMBus数据格式低字节在前并发送ACK I2C_AcknowledgeConfig(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); data_l I2C_ReceiveData(I2C1); I2C_AcknowledgeConfig(I2C1, ENABLE); // 继续ACK // 6. 读取高字节发送NACK表示这是最后一个字节 I2C_AcknowledgeConfig(I2C1, DISABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); data_h I2C_ReceiveData(I2C1); // 7. 读取PEC校验字节如果启用PEC——用NACK结束后读取 uint8_t pec 0; I2C_AcknowledgeConfig(I2C1, DISABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); pec I2C_ReceiveData(I2C1); // 8. 停止信号 I2C_GenerateSTOP(I2C1, ENABLE); result (data_h 8) | data_l; return result; }这段代码的逻辑说明SMBus读字操作和标准I2C读操作的最大区别在于第二步和第三步之间的restart信号。SMBus在写完寄存器地址后必须重新发送起始信号repeated START然后切换为读模式这样才能读取指定寄存器的内容。如果你用标准I2C的连续读函数比如HAL库的HAL_I2C_Mem_Read它内部可能不会严格按照SMBus的时序要求来实现导致读回来的数据错位。参数说明MLX90614_ADDR是7位设备地址0x5B但在发送时要左移一位变成8位最低位是读写标志。reg_addr是要读取的寄存器地址读温度时传0x06读环境温度时传0x07读发射率时传0x24。PEC校验字节可以忽略不读但如果你的总线干扰比较大建议启用校验具体的CRC-8算法函数在资源包里有现成实现。3.3 温度计算原始数据到摄氏度的转换公式读取到的16位原始数据只是ADC数值需要转换成实际温度。MLX90614内部已经做了线性化处理所以转换公式很简单但一定要分清读取的是哪个寄存器。// 将MLX90614原始数据转换为摄氏度 float MLX90614_GetTemp(uint8_t reg_addr) { uint16_t raw MLX90614_ReadReg(reg_addr); // 温度计算公式温度(°C) 原始值 * 0.02 - 273.15 // 原始值是开尔文温度乘以50后的结果 return (float)raw * 0.02f - 273.15f; } // 读取目标物体温度红外传感器温度 float MLX90614_GetObjectTemp(void) { return MLX90614_GetTemp(0x06); } // 读取环境温度传感器自身附近的环境温度 float MLX90614_GetAmbientTemp(void) { return MLX90614_GetTemp(0x07); }这个转换公式的原理是MLX90614内部以0.02摄氏度的分辨率表示温度值且内部以开尔文为单位存储。举个例子如果读取到的原始值是0x0B91即2961那么温度就是2961×0.02-273.1513.07摄氏度。这个公式是固定的不需要额外校准。需要注意的一个坑是如果你用手直接捏住传感器外壳或者传感器靠近发热元件读取的环境温度会比实际环境高好几度这时候目标温度的计算也会跟着偏移。所以传感器的安装位置要远离主控芯片和电源模块必要时加一个隔热垫。3.4 OLED显示刷新策略温度采样和显示不能互相阻塞OLED显示如果用I2C接口在刷新一屏数据时需要几百毫秒的时间如果在这期间去读取MLX90614的温度IRQ时序会被拉长导致温度数据读取失败或数据跳变。这套资源给出的方案是分时复用温度采样和OLED刷新放在同一个循环里但用简单的状态机控制。// 主循环采样温度 OLED显示 int main(void) { // 初始化系统、GPIO、I2C、OLED等 SystemInit(); I2C_GPIO_Init(); OLED_Init(); uint16_t sample_count 0; float obj_temp 0.0f; char display_buf[16]; while(1) { // 每10次循环采样一次温度其余时间用于OLED刷新 if(sample_count % 10 0) { obj_temp MLX90614_GetObjectTemp(); // 格式化显示字符串 sprintf(display_buf, Obj: %.2f C, obj_temp); } // OLED每100ms刷新一次 if(sample_count % 5 0) { OLED_Clear(); OLED_ShowString(0, 0, display_buf); // 刷新环境温度 float amb_temp MLX90614_GetAmbientTemp(); sprintf(display_buf, Amb: %.2f C, amb_temp); OLED_ShowString(0, 2, display_buf); OLED_Refresh(); } delay_ms(10); sample_count; if(sample_count 100) sample_count 0; } }这种分时刷新策略的目的是避免长时间占用I2C总线。OLED刷新一屏通常需要发送1024字节的数据128×64分辨率在100kHz的I2C时钟下大约需要100ms这段时间内如果连续读取MLX90614的多个寄存器时间会被拉长到200ms以上实际测温的实时性就变差了。所以采样间隔选10次循环一次100ms显示刷新选5次循环一次50ms两者交错进行。4. 避坑指南MLX90614调试中五个最容易踩的坑4.1 I2C总线卡死程序卡在等待事件标志现象程序运行一段时间后卡在while(!I2C_CheckEvent(...))这个循环里CPU占用率100%看门狗超时复位。原因MLX90614在异常掉电或者总线干扰时内部状态机可能进入错误状态不再响应主机的通信请求。更常见的情况是I2C总线上有毛刺导致从设备在收到一个不完整的字节后把SDA线拉死。解决在每次I2C通信失败后主动产生一个停止信号来复位总线状态或者连续发送9个SCL时钟脉冲让从设备释放SDA线。这套资源里提供了一个I2C_Bus_Reset函数可以在通信超时后调用。另外建议在初始化I2C前先对SCL和SDA引脚做一次软复位拉低SDA再拉高SCL发送GPIO模拟的STOP信号。4.2 读到的温度数据恒定不变或始终是-273.15现象不管目标物体是手还是冰水读到的温度始终不变或者一直显示-273.15。原因-273.15是绝对零度的摄氏表示出现这个值说明读取到的原始数据是0x0000也就是说MLX90614根本没有响应或者读取的寄存器不包含有效数据。常见原因有两个一是I2C设备地址不对向错误地址发起了读操作二是在使用I2C_Mem_Read之类的库函数时寄存器地址被当成了内存地址导致从设备的地址指针指向无效区域。解决先用I2C扫描程序确认MLX90614的实际设备地址。在总线上搜索0x00到0x7F的所有地址看看哪个地址有ACK响应。如果扫描到0x5B以外的地址就要在代码的MLX90614_ADDR宏中改过来。另外检查I2C读取函数的第二个参数是否传入了正确的寄存器地址比如读取温度应该传0x06而不是0x07。4.3 读取温度数据时好时坏加上OLED显示后更严重现象单独读温度很稳定但接上OLED后温度数据偶尔出现跳变比如从36度突然变成5度再跳回来。原因OLED刷新时占用了大量总线带宽如果OLED和MLX90614挂在同一条I2C总线上OLED刷新期间MLX90614的通信时序被拉长。MLX90614的SMBus协议有一个超时机制如果从启动到停止的时间超过某个阈值从设备会放弃本次通信返回无效数据或产生NACK。解决最简单的方案是让OLED和MLX90614分用两条I2C总线STM32F103有两个硬件I2C控制器OLED接I2C1MLX90614接I2C2互不干扰。如果只能用一条总线那就把OLED刷新频率降低比如从50Hz降到20Hz同时把温度采样放在OLED刷新间隙里比如OLED刷新完一屏后延迟2ms再触发温度采样。4.4 发射率校准错误导致体温测量偏低现象用MLX90614测量人体额头温度读数是33度额温枪是36.5度差了3度以上。原因MLX90614出厂默认发射率校准值是0x95即0.95这是针对黑体辐射源的通用值。但人体皮肤的有效发射率通常高于0.95部分人体部位接近0.98。当被测物体发射率高于传感器的预设值传感器会低估实际温度。解决通过写EEPROM的地址0x24来修改发射率。写入公式是发射率值乘以200比如0.98对应的写入值是1960xC4。修改之后要重新上电才能生效而且EEPROM的写入次数有限不要频繁改写。另外如果只是临时做个演示不改EEPROM也可以在读取温度后加一个软件补偿比如在最终结果上加上1到2度。4.5 传感器刚上电时温度数据缓慢漂移现象刚上电的几分钟内显示的温度逐渐上升大约5分钟后才稳定。原因MLX90614内部有一个热质量上电瞬间传感器自身温升还没建立稳定热电堆传感器读到的红外辐射能量包含了一部分传感器自身的热辐射。同时OLED屏幕发热和主控芯片发热也会影响传感器附近的环境温度。解决上电后先让系统预热2分钟再开始采样显示或者在前100次采样中丢弃前20个数据点。更彻底的做法是读环境温度寄存器0x07然后与目标温度做差分计算。MLX90614手册里也有提到传感器靠近电路板热源时数据会偏高0.5到1度安装时尽量让传感器探头伸出PCB边缘。5. 进阶校准技巧发射率修正和温度补偿让数据更可信如果你不满足于能出数想让你的测温设备达到额温枪级别的准确度这一步值得研究。核心技巧就两个发射率修正和两点校准。先看发射率修正。前面提到人体发射率约0.98但不同皮肤状态和测量角度会有差异。更严谨的做法是用黑体炉校准来确定实际发射率值。操作方法是找一台有温度显示的黑体炉设定一个目标温度比如37度等黑体炉稳定后用你的设备读取目标温度T_measured然后调整发射率值重新测量直到读数稳定在37.0度。记录此时EEPROM里的发射率值这个就是你的设备针对当前应用场景的准值。如果找不到黑体炉也可以用冰水混合物和沸水做两点校准。冰水混合物在标准大气压下是0度沸水是100度。把MLX90614分别对准这两种目标读取温度记录偏差ΔT1和ΔT2。然后做一个线性补偿// 线性温度补偿示例 float compensated_temp measured_temp slope * (measured_temp - reference_temp) offset;其中slope和offset通过上述两组校准点计算。比如冰水混合物测到2度沸水测到97度那么偏差分别是2度和-3度slope (-3-2)/(100-0) -0.05offset 2。这样在50度时补偿量为-0.5度。这个补偿方法不依赖发射率设置直接在软件层做修正适合快速现场校准。这套资源里附带了一个校准工具代码读取EEPROM发射率参数、写入指定值、以及验证温度读取的完整流程。实际项目中我一般会把校准结果保存在EEPROM的备用区域产品模式下自动加载校准参数而不是每次上电都重新校准。另外关于显示界面的一个细节建议如果你在OLED上同时显示目标温度和环境温度建议把环境温度字体缩小放在角落目标温度用大字号居中显示因为使用者关心的永远是目标温度环境温度数字太大反而容易分散注意力。最后说一个我自己的习惯每次改完代码烧录后我会用固定距离比如10cm对准同一个目标连续读取30次温度看最大值和最小值的差值如果超过0.5度说明滤波没做好需要增加滑动平均窗口。从那以后我每次做测温项目都强制走一遍这个流程能提前暴露很多通信和供电问题。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑