51单片机驱动MLX90614红外测温与LCD1602显示实战:I2C时序与防坑指南

发布时间:2026/9/11 18:16:29
51单片机驱动MLX90614红外测温与LCD1602显示实战:I2C时序与防坑指南 简介一份面向51单片机初学者的红外测温显示工程基于STC15F2K60S2芯片搭配Mlx90614红外传感器与LCD1602液晶屏实现非接触式温度测量与显示。压缩包共17个文件、体积1.33MB包含6个C源码文件、5个头文件具体涵盖Mlx90614驱动代码与头文件、I2C协议底层通信实现、LCD1602显示控制、延时函数以及主程序入口同时附有芯片寄存器的头文件、工程配置文件、说明文档和液晶指令集图片整体结构清晰可直接打开工程查看或按模块复用代码。项目完整呈现了通过I2C总线读取Mlx90614温度数据、再经LCD1602稳定显示的过程并在主循环中利用延时函数保证通信时序和显示刷新节奏能够帮助读者掌握单片机I/O控制、传感器数据采集、I2C通信协议以及字符液晶显示等基本技能。已有324人学习下载适合需要参考完整单片机测温代码、理解底层驱动并希望快速开展相关实验的开发者。1. 红外测温不上手就用最容易在 I2C 时序上翻车用 51 单片机做非接触测温MLX90614 是点名率最高的传感器一颗热电堆加一片数字信号处理芯片把物体温度换算成数字量通过 SMBus 接口输出不需要运放和冷端补偿。配套的 LCD1602 又是课程设计和项目里最普及的显示器件两者组合起来能覆盖人体测温、设备表面温度、红外测温枪原型等一长串场景。但很多人在 Proteus 里仿真一切正常一上实物就发现温度读成 0、液晶花屏、偶尔死机问题几乎都出在同一个地方51 单片机的 IO 时序与 MLX90614 的 SMBus 时钟拉伸没有处理好。这套方案真正的难点不是接线而是把 I2C 从机行为吃透后再去写驱动。下文按原理 → 接线 → 代码 → 校准的顺序把可以直接抄的驱动写法、关键时序参数和常见坑一次说清楚适合正在做课程设计或准备做产品原型的工程师参考。2. 读懂 MLX90614 和 LCD1602SMBus 寄存器与 HD44780 初始化2.1 MLX90614 的测温原理和寄存器分布MLX90614 内部把红外热电堆的微伏级信号做斩波放大、Σ-Δ 调制和 DSP 线性化直接输出与黑体辐射定律对应的温度值。这意味我们不需要做热敏电阻分压计算也不会遇到铂电阻的引线误差。传感器同时测两路温度目标物体温度RAM 地址 0x07和环境温度RAM 地址 0x06每个寄存器都是 16 位数据再加 8 位 PEC 校验码。RAM 地址数据内容换算方式0x06环境温度 T_A14 位有效T data × 0.02 − 273.150x07目标温度 T_obj16 位有效T data × 0.02 − 273.150x08目标温度红外原始值一般不直接使用温度分辨率是 0.02 °C出厂校准范围通常覆盖 −40 °C 到 125 °C。这两个寄存器就是整个项目的数据来源代码里只要连续读 0x06 和 0x07就能在 LCD 上同时显示环境温度和目标温度。2.2 为什么必须按 SMBus 时序读而不是用普通 I2CMLX90614 的物理层兼容 I2C但遵循 SMBus 读时序主机先发从机地址加写位0x5A 1再发要读取的寄存器地址然后重启动总线发从机地址加读位从机才会把数据送到 SDA 上。整个过程有三个细节和普通 I2C 外设不同。第一从机地址固定是 0x5A由模块内部的 SMBus 地址寄存器决定多数模块不支持地址跳线总线上不要挂第二颗同地址器件。第二每个字节后面带 PEC 校验位8 位 CRC 多项式是 0x07如果完全忽略 PEC在长线干扰情况下可能读到错值后面会给出校验函数。第三也是最容易被 51 单片机使用者忽略的MLX90614 在收到读命令后会拉低 SCL 做时钟拉伸表示它还在准备数据。主机必须在每一个 SCL 高电平阶段检测 SCL 是否被从机释放否则会产生额外时钟脉冲让读到的数据错位。2.3 LCD1602 的 4 位/8 位模式选择LCD1602 的核心控制芯片是 HD44780初始化本身不复杂但接口模式决定占用引脚数和驱动代码量。8 位模式需要 D0 到 D7 共 8 根数据线加 RS、RW、EN 三根控制线51 单片机引脚紧张时容易捉襟见肘。4 位模式只接 D4 到 D7初始化要把写命令拆成高 4 位和低 4 位两次发送代码量稍大但能省 4 个引脚。从稳定角度考虑我建议优先用 8 位模式。51 单片机 IO 数量足够时8 位模式的读写时序更直观排错时用示波器看数据线的逻辑也更容易。项目中如果必须省引脚再退化到 4 位模式。注意无论哪种模式初始化时都要按 HD44780 手册要求连续发送三次 0x38 设置命令因为显示器在上电后可能处于未知状态预先设置才能保证后续命令被正确识别。3. 51 单片机接 MLX906145V/3.3V 电平与接线表一次画对3.1 引脚对照与最小系统接线MLX90614 模块常见的是 4 引脚贴片封装或 GY-906 模块供电引脚和 I2C 引脚固定LCD1602 是 16 引脚直插接口。项目里用 STC89C52 作为主控晶振用 11.0592 MHz 或 12 MHz 都可以12 MHz 时delay_us的循环次数计算更直观。下面是一套经过验证的接线方式。MLX90614 引脚LCD1602 引脚51 单片机引脚VINVSS/GND3.3V 电源正极 / GNDGNDVDD/GNDGNDSCLD4 不接P2.1软件 I2C 时钟SDAD5 不接P2.0软件 I2C 数据不接RSP1.0不接RWP1.1不接ENP1.2不接D0-D7P0.0-P0.7MLX90614 必须先接 3.3V 电源5V 直接供电会烧毁模块。51 单片机是 5V 系统LCD1602 也是标准 5V 器件所以整个系统是双电源结构3.3V 给传感器5V 给单片机和液晶两块电源的地必须共地否则 SDA/SCL 上没有参考电位通信会随机失败。3.2 电平转换的三种做法MLX90614 的 SDA 和 SCL 是开漏输出外部必须接上拉电阻。麻烦在于它工作在 3.3V而 51 单片机的 P2 口是准双向口输出高电平能力弱直接和 3.3V 器件连接时要考虑高电平门限和灌电流问题。常见做法有三种串电阻隔离SCL、SDA 各串联 22Ω 到 100Ω 电阻再在 3.3V 侧接上拉电阻到 4.7kΩ。这种方案成本最低适合实验板。电平转换模块用 BSS138 或 TXS0102 等双向电平转换芯片SDA/SCL 分别接两端的 VCCA/VCCB5V 和 3.3V 之间是真正的高电平到低电平转换适合长线和工业环境。51 单片机配置成准双向模式后直接在 SDA/SCL 上接 3.3kΩ 上拉到 3.3V。51 的输入高电平阈值是 2.0V 左右3.3V 高电平可以直接识别但此时单片机的 IO 输出高电平时电压会被钳在 3.3V所以 SCL 高电平是 3.3V对 5V 单片机而言仍旧算合法高电平。我建议实验阶段用第三种做法省元件而且大部分模块已经自带 10kΩ 上拉到 3.3V接上就能跑。只要确保 51 单片机对应引脚没有输出强高电平脉冲就不会出现持续灌电。3.3 供电去耦和实测前的检查顺序MLX90614 的红外传感器对电源纹波敏感供电线上建议并一个 10μF 电解电容和一个 0.1μF 陶瓷电容位置尽量靠近模块 VIN 引脚。LCD1602 的背光供电如果从 5V 接记得在背光 LED 回路串一个 200Ω 左右的限流电阻直接接 5V 会烧背光。实测前用万用表确认三件事3.3V 和 5V 是否都正常输出地线是否连通SDA/SCL 上拉后的静态电压是否分别为 3.3V。这三个量没问题再上电烧程序能节省大半排查时间。4. 软仿 I2C LCD1602 显示从零写的 C51 驱动代码4.1 LCD1602 的底层驱动写命令、写数据、初始化先用宏定义接好引脚然后实现最底层的时序操作。LCD1602 的 RS 高电平代表数据低电平代表命令RW 低电平写、高电平读EN 靠下降沿锁存数据。很多现成驱动代码直接把延时写死这里用忙检测更可靠避免在快速执行时写入还没处理完的命令。#include reg52.h #include intrins.h #define LCD_DATA P0 sbit RS P1^0; sbit RW P1^1; sbit EN P1^2; void LcdBusyWait(void) { unsigned char temp; LCD_DATA 0xFF; // 数据口先置高准备读 RS 0; RW 1; EN 1; _nop_(); while ((LCD_DATA 0x80) ! 0) { EN 0; _nop_(); EN 1; _nop_(); } EN 0; } void LcdWriteCmd(unsigned char cmd) { LcdBusyWait(); RS 0; RW 0; LCD_DATA cmd; EN 1; _nop_(); EN 0; } void LcdWriteData(unsigned char dat) { LcdBusyWait(); RS 1; RW 0; LCD_DATA dat; EN 1; _nop_(); EN 0; }忙检测的核心是读 D7 位0x80掩码取出第 7 位为 1 表示 LCD 内部仍在工作循环等待直到变为 0。LCD_DATA在读取前必须写0xFF因为 51 单片机的 P0 口是开漏输出只有外部上拉才能读到正确的输入电平。这段代码适合 12 MHz 晶振如果换晶振_nop_()的延时比例不变不影响逻辑。初始化函数需要按 HD44780 手册要求的时序发送。void LcdInit(void) { LcdWriteCmd(0x38); // 8位模式2行显示5x8点阵 LcdWriteCmd(0x38); LcdWriteCmd(0x38); LcdWriteCmd(0x08); // 关闭显示只做初始化设置 LcdWriteCmd(0x01); // 清屏 LcdWriteCmd(0x06); // 光标右移整体不移动 LcdWriteCmd(0x0C); // 开显示关闭光标和闪烁 }前三条 0x38 是为了让 LCD 从上电随机状态强制进入 8 位模式。如果用 4 位模式这里的节奏完全不同需要在 0x33、0x32 之间反复切换。后面三条命令设置显示参数0x0C 是最常用的显示效果不显示光标适合测温显示界面。4.2 软件 I2C 的核心等待时钟拉伸MLX90614 的 SMBus 时序允许从机在 SCL 为高电平时把线拉低以申请等待时间。软件模拟 I2C 必须支持这个动作否则从机内部还未就绪就执行下一个时钟周期数据会丢失。下面是一组支撑读取温度的最小 I2C 函数SDA 和 SCL 都采用模拟开漏方式释放总线就是写 1。sbit SDA P2^0; sbit SCL P2^1; void I2C_Delay(void) { _nop_(); _nop_(); _nop_(); _nop_(); } void I2C_Start(void) { SDA 1; SCL 1; I2C_Delay(); SDA 0; I2C_Delay(); SCL 0; } void I2C_Stop(void) { SCL 0; SDA 0; I2C_Delay(); SCL 1; I2C_Delay(); SDA 1; I2C_Delay(); } bit I2C_WriteByte(unsigned char dat) { unsigned char i; bit ack; for (i 0; i 8; i) { SCL 0; if (dat 0x80) SDA 1; else SDA 0; I2C_Delay(); SCL 1; while (!SCL); // 等待从机释放 SCL I2C_Delay(); dat 1; } SCL 0; SDA 1; // 释放 SDA 去读 ACK I2C_Delay(); SCL 1; while (!SCL); ack SDA; SCL 0; return ack; } unsigned char I2C_ReadByte(bit ack) { unsigned char i, dat 0; SDA 1; // 释放 SDA从机才能驱动 for (i 0; i 8; i) { dat 1; SCL 0; I2C_Delay(); SCL 1; while (!SCL); // 每个高电平都做时钟拉伸检测 I2C_Delay(); if (SDA) dat | 0x01; } SCL 0; ack 1 ? (SDA 0) : (SDA 1); I2C_Delay(); SCL 1; while (!SCL); I2C_Delay(); SCL 0; SDA 1; return dat; }while (!SCL);是这套代码里最关键的循环当从机需要额外时间时它会主动把 SCL 拉低主机此刻不能继续发时钟只能原地等待。如果去掉这个检测MLX90614 偶尔返回的字节会整体错一位。注意 SCL 也是准双向口从机拉低后读到的值才是有效状态。4.3 读取两个温度并做 CRC 校验有了 I2C 读写原语读取 RAM 地址和换算温度的代码就很清晰。下面函数同时返回物体温度和环境温度并且对 PEC 做校验。unsigned char I2C_PEC(unsigned char *data, unsigned char len) { unsigned char crc 0; unsigned char i, bit; while (len--) { crc ^ *data; for (i 0; i 8; i) { bit crc 0x80; crc 1; if (bit) crc ^ 0x07; // CRC-8 多项式 } } return crc; } bit ReadMLX90614(unsigned char regAddr, unsigned int *value) { unsigned char buf[3]; unsigned char pec; bit ret 0; I2C_Start(); if (I2C_WriteByte(0x5A 1) 0) goto end; // 写地址 if (I2C_WriteByte(regAddr) 0) goto end; // 寄存器地址 I2C_Start(); if (I2C_WriteByte((0x5A 1) | 1) 0) goto end; // 读地址 buf[0] I2C_ReadByte(1); buf[1] I2C_ReadByte(1); buf[2] I2C_ReadByte(0); I2C_Stop(); pec I2C_PEC(buf, 2); if (pec ! buf[2]) goto end; *value ((unsigned int)buf[1] 8) | buf[0]; ret 1; end: I2C_Stop(); return ret; } float MLX90614_ReadTemp(unsigned char regAddr) { unsigned int raw; if (!ReadMLX90614(regAddr, raw)) return -100.0f; return (float)raw * 0.02f - 273.15f; }0x5A 1的结果是 0xB4这是对应的写地址读地址则是 0xB5。I2C_WriteByte返回 0 表示收到了从机的 ACK返回 1 表示 NACK如果地址写错或模块没上电都会在这里中止。CRC-8 校验用的多项式 0x07计算范围覆盖两个数据字节第三个字节必须是算出的 PEC 值否则认为本次读取不可靠。4.4 主循环格式化显示到 LCD1602Keil C51 的标准库对%f支持不完善直接调用sprintf(buf, %.2f, temp)在部分编译版本下显示不出来最稳的写法是拆出整数和小数分别转换。void DisplayTemp(float objTemp, float ambTemp) { int objInt, objDec, ambInt, ambDec; unsigned char buf[17]; objInt (int)objTemp; objDec (int)((objTemp - objInt) * 10); if (objDec 0) objDec -objDec; ambInt (int)ambTemp; ambDec (int)((ambTemp - ambInt) * 10); if (ambDec 0) ambDec -ambDec; sprintf((char *)buf, Obj:%3d.%d, objInt, objDec); LcdWriteCmd(0x80); // 第一行首地址 for (i 0; buf[i] ! \0; i) LcdWriteData(buf[i]); sprintf((char *)buf, Amb:%3d.%d, ambInt, ambDec); LcdWriteCmd(0xC0); // 第二行首地址 for (i 0; buf[i] ! \0; i) LcdWriteData(buf[i]); }把浮点数乘以 10 再取整得到小数位能避开软浮点库对格式化输出的坑。0x80和0xC0是 LCD1602 内部 DDRAM 的行首地址第一条命令定位到第一行第 1 列第二条定位到第二行第 1 列。主循环里每 300 ms 读一次温度并刷新显示读数就有了实时感。void main(void) { float obj, amb; LcdInit(); LcdWriteCmd(0x01); while (1) { obj MLX90614_ReadTemp(0x07); amb MLX90614_ReadTemp(0x06); DisplayTemp(obj, amb); delay_ms(300); } }delay_ms需要自己实现用定时器 0 做 1 ms 基准最准确。如果在 Proteus 里仿真可以把while (!SCL);的等待循环去掉一部分因为仿真模型经常忽略时钟拉伸但实物一定要保留。4.5 读数为 0、花屏、4.7 度不变的排查顺序电阻屏项目最常见的故障现象是 LCD 正常显示但温度永远是一个固定值如 -273.15 °C这种情况基本是 I2C 读取失败返回了默认错误值。先用示波器看 SCL 频率是否在 10 kHz 到 100 kHz 之间再用串口把ReadMLX90614返回的原始raw打印出来。如果raw是 0 或 0x8000说明读时序没对齐如果raw始终是某个固定数字多半是地址或寄存器地址错误。LCD 花屏则集中在LcdBusyWait的 P0 上拉问题上P0 口外部不接 10kΩ 上拉电阻读回的状态始终是 0x00忙检测会死循环。这些坑都不需要高级设备万用表和示波器就能定位。5. 调准温漂和发射率让读数和参考温度计对上MLX90614 出厂校准精度在 ±0.5 °C 到 ±1 °C 之间但这是对理想黑体辐射源而言。实际测量人体、铝壳、塑料表面时发射率不是 1读数会系统性偏低或偏高。成熟的做法是保留原始温度数据在软件里做标定补偿而不是去改传感器的 EEPROM。具体操作是找一台精度不低于 0.1 °C 的 PT100 测温仪或水银温度计在 0 °C 到 100 °C 之间取 5 到 6 个温度点把 MLX90614 的原始读数和参考值记录下来做一元线性回归。得到两个系数a和b后实际温度用T_real a * T_obj b修正写入程序末尾的标定常量里避免每次改代码重新烧录。如果测量目标是人体建议把传感器放在距离皮肤 1 cm 到 3 cm 的位置并保持几秒钟让传感器内部温度稳定。红外测温受被测物发射率影响很大软件里可以把目标温度和环境温度同时代入辐射公式做二次补偿。对于 51 单片机这种运算资源有限的平台用一个查表法和一个线性插值就足够。更简单的做法是把显示屏第二行同时显示环境温度因为当环境温度接近目标温度时发射率造成的误差会更小这样用户也能根据环境温度判断当前测量是否可信。最后一个技巧是对连续读数做滑动平均。每 50 ms 读一次温度保留最近 10 个有效值去掉最大值和最小值后取平均可以明显抑制电磁干扰带来的尖刺。注意不要在一次 I2C 读取失败后直接丢弃上一次的温度结果而是继续使用上一次有效值避免显示数字在 0 和真实温度之间来回跳。这样处理后用 51 单片机加 MLX90614 和 LCD1602 组成的测温系统就能在实验室条件下稳定显示到 0.1 °C 分辨率并且能被课程设计和实际项目直接采用。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询