资讯动态

51单片机OLED温度时钟:DS18B20与I2C显示实战

发布时间:2026/10/26 11:46:33 来源:尧图企业网站定制
简介这份资源面向51单片机初学者与嵌入式入门开发者围绕DS18B20数字温度传感器与OLED显示屏的联动展开解决温度采集、一线总线通信与屏幕实时显示的综合实践问题可作为小型物联网感知层原型的练手项目。压缩包共31个文件约107KB包含5个C源文件与4个头文件主程序、温度读取、IIC通信、延时与时间模块、Keil工程文件及编译产物另有hex烧录文件与一张效果图结构完整、可直接编译下载。目前已有2348人学习下载说明其在入门群体中具备一定参考价值。读者可从中获得DS18B20一线总线读写与温度解析、OLED初始化与字符数值显示、IIC时序实现以及51单片机多模块协同的完整代码框架便于对照理解硬件连接与程序组织并在此基础上扩展时间显示与功能调试。1. 从一块 0.96 寸屏说起这套温度时钟方案到底解决什么问题很多同学做单片机课设第一步就卡在“选什么显示方案”上。数码管便宜、驱动简单但只能显示数字想同时把温度和时间摆在一屏里位数一多就捉襟见肘LCD1602 能显示字符可对比度差、体积大装在成品盒子里显得笨重。反倒是 0.96 寸 OLED 屏I2C 两根线就能点亮128×64 的分辨率足够把温度、时间、日期分区排布视觉上也干净。这套基于 51 单片机的 OLED 温度时钟方案核心就是 DS18B20 单总线测温加 OLED 图形显示把“采集—处理—显示”这条链路走完整。它适合正在做单片机课程设计、电子工艺实习或者想拿一块最小系统板练手完整项目的读者。下面按需求、设计、实现、避坑、验收的顺序把这份方案拆开讲透。2. 需求与选型为什么是 51 DS18B20 OLED 这套组合2.1 功能需求先立住别急着写代码动手之前先把需求列清楚后面所有设计都围绕它展开。这套方案的功能边界通常是这样几条实时采集环境温度精度到 0.1℃用 OLED 分区域显示当前温度、当前时间时:分:秒和日期时间能通过按键手动校准系统上电后自动开始工作不需要额外操作。看起来简单但每一条都对应一个设计决策——温度精度决定了 DS18B20 的配置寄存器怎么设时间显示决定了要不要用定时器做时基按键校准决定了要不要做消抖和状态机。我一般会建议把需求写成一张表功能、输入、输出、约束四列答辩时直接拿出来讲比口头描述有说服力。约束里要写清楚供电电压、晶振频率、显示刷新频率这些硬指标它们会直接影响后面的选型和代码结构。2.2 主控选 51 的理由与代价选 STC89C52 这类 51 内核单片机理由很实在课程体系里最常见开发工具成熟最小系统板便宜引脚电平 5V 与 DS18B20、OLED 模块都能直接对接不需要电平转换。代价也要认51 的 RAM 只有 128 字节部分型号 256 字节跑 OLED 显存缓冲会很紧张所以显示部分通常不整屏缓存而是直接写屏主频 11.0592MHz 或 12MHz定时器精度有限做时钟时基要算准初值。提示如果手头是 STC12、STC15 这类增强型 51RAM 和主频都宽裕一些代码结构可以更从容但引脚和寄存器操作基本兼容方案不用大改。2.3 DS18B20 与 OLED 的接口取舍DS18B20 是单总线器件一根数据线完成供电寄生供电模式或两根线加电源通信靠严格的时序拉高拉低。它的好处是抗干扰尚可、测温范围宽-55℃ 到 125℃缺点是转换一次温度需要时间12 位精度下约 750ms所以采集不能太频繁通常 1 秒读一次就够。OLED 选 SSD1306 驱动的 0.96 寸 I2C 模块四针接口VCC、GND、SCL、SDA地址一般是 0x3C 或 0x3D。I2C 只用两根线省引脚代价是刷新速度比并口慢整屏刷新一次要几十毫秒所以显示代码要避免无谓的全屏重绘。器件接口关键参数选它的原因STC89C52—8 位11.0592MHz128B RAM课程通用、成本低DS18B20单总线12 位分辨率0.0625℃/LSB数字输出、免校准OLED SSD1306I2C128×64地址 0x3C两线驱动、显示信息量大独立按键GPIO23 个时间校准、模式切换3. 总体设计三层结构与四个模块的职责划分3.1 分层思路驱动层、服务层、应用层这套方案我习惯按三层来组织代码。驱动层负责和硬件打交道DS18B20 的复位、写字节、读字节时序OLED 的 I2C 起始、停止、写命令、写数据按键的引脚读取。服务层在驱动之上封装可复用的能力温度读取与换算、OLED 的字符与数字显示、定时器时基、按键消抖。应用层只做业务编排每隔一秒读温度、更新时间、刷新显示、扫描按键。这样分层的好处是调试时能快速定位问题——温度不对先查驱动层时序显示花屏先查 I2C 和显存写入时间走不准先查定时器初值。很多同学把所有代码堆在 main 里出了问题只能从头捋血泪经验就是分层越早做越省事。3.2 四个模块的职责与调用关系模块划分上这套方案通常拆成四个温度采集模块、时钟模块、显示模块、按键模块。温度采集模块对外只暴露一个DS18B20_ReadTemp()接口内部完成复位、跳过 ROM、启动转换、读暂存器、换算成实际温度。时钟模块基于定时器 0 的 50ms 中断累加凑满 20 次就是一秒维护时、分、秒、日、月、年。显示模块提供OLED_ShowTemp()、OLED_ShowTime()这类函数内部把数值拆成字符再写屏。按键模块负责模式切换和加减调整带 1020ms 消抖。调用关系是单向的应用层主循环依次调用温度采集、时钟更新、显示刷新、按键扫描模块之间不互相调用避免耦合。这种结构在 51 这种资源紧张的平台上尤其重要因为任何多余的函数调用都会吃栈空间。3.3 主循环与中断的分工主循环负责“慢活”读温度1 秒一次、刷新显示、扫描按键。定时器中断负责“快活”维护时基、给按键消抖提供节拍。分工原则是中断里只做计数和标志位不做耗时操作——DS18B20 转换要等 750ms绝不能放在中断里。// 定时器0初始化50ms 中断一次20 次凑 1 秒 void Timer0_Init(void) { TMOD 0xF0; // 清除 T0 模式位 TMOD | 0x01; // T0 工作在模式116位定时器 TH0 0x4C; // 11.0592MHz 下 50ms 初值高字节 TL0 0x00; // 低字节 ET0 1; // 开定时器0中断 EA 1; // 开总中断 TR0 1; // 启动定时器 } // 中断服务只累加计数置秒标志 void Timer0_ISR(void) interrupt 1 { TH0 0x4C; // 重装初值 TL0 0x00; if (tick50ms 20) { tick50ms 0; flag_1s 1; // 通知主循环过了一秒 } }这段代码的关键在初值计算11.0592MHz 晶振下机器周期是 12/11.0592MHz≈1.085μs50ms 需要约 46080 个机器周期65536-4608019456即 0x4C00。如果换成 12MHz 晶振初值要改成 0x3CB0否则时间会越走越偏。中断里只置标志位主循环看到flag_1s再去做读温度和刷新这样中断执行时间短不会影响其他时序敏感的操作。4. 关键模块实现从单总线时序到 OLED 分区显示4.1 DS18B20 时序与温度换算DS18B20 的通信全靠时序复位脉冲要求主机拉低至少 480μs然后释放器件在 1560μs 内拉低回应。写“1”和写“0”的区别在于拉低持续时间写 1 拉低 115μs 后释放写 0 拉低 60μs 以上。读时隙则是主机拉低 1μs 后释放在 15μs 内采样。// 复位拉低 480us 以上等待器件回应 bit DS18B20_Reset(void) { bit presence; DQ 0; DelayUs(480); // 拉低满足复位脉宽 DQ 1; DelayUs(60); // 等待器件拉低回应 presence DQ; // 0 表示器件在线 DelayUs(420); // 补足复位周期 return presence; } // 读温度跳过 ROM启动转换读两字节暂存器 float DS18B20_ReadTemp(void) { unsigned char tl, th; int raw; DS18B20_Reset(); DS18B20_WriteByte(0xCC); // 跳过 ROM单器件场景 DS18B20_WriteByte(0x44); // 启动温度转换 DelayMs(750); // 12 位精度需等待转换完成 DS18B20_Reset(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); // 读暂存器 tl DS18B20_ReadByte(); // 低字节 th DS18B20_ReadByte(); // 高字节 raw (th 8) | tl; return raw * 0.0625; // 12 位分辨率每 LSB 0.0625℃ }换算这里有个容易翻车的点raw是有符号数负温度时高字节是 0xFF 开头如果直接用unsigned int接负温会变成很大的正数。所以raw要声明成int显示时再判断正负。另外 0.0625 的乘法在 51 上会引入浮点库代码体积变大讲究一点的做法是整数运算temp raw * 625 / 10000显示时拆整数和小数部分。4.2 OLED 的 I2C 写命令与写数据SSD1306 的 I2C 协议里每次传输先发从机地址0x3C1然后一个控制字节区分命令还是数据0x00 表示后面是命令0x40 表示后面是数据。很多模块初始化不亮问题就出在控制字节写错或者地址不对。// 写一个命令字节 void OLED_WriteCmd(unsigned char cmd) { I2C_Start(); I2C_WriteByte(0x78); // 0x3C1写方向 I2C_WriteByte(0x00); // 控制字节命令 I2C_WriteByte(cmd); I2C_Stop(); } // 写一个数据字节 void OLED_WriteData(unsigned char dat) { I2C_Start(); I2C_WriteByte(0x78); I2C_WriteByte(0x40); // 控制字节数据 I2C_WriteByte(dat); I2C_Stop(); }地址 0x78 是 0x3C 左移一位的结果如果模块背面电阻焊的是 0x3D这里要改成 0x7A。初始化序列一般包括关显示、设时钟分频、设多路复用率、设显示偏移、设起始行、开电荷泵、设内存寻址模式、开显示这几步顺序不能乱尤其电荷泵不开屏是黑的。4.3 显示分区温度、时间、日期怎么排128×64 的屏按页寻址每页 8 行像素共 8 页。我一般这样分区第 01 页显示日期年-月-日第 24 页用大字号显示时间时:分:秒第 57 页显示温度和单位。字号用 16×16 的点阵一个汉字或两个数字占 16 像素宽一行能放 8 个字符。显示函数的设计要点是“只刷变化的部分”。时间每秒变一次温度也每秒变一次但日期基本不变如果每次都全屏重绘I2C 带宽吃不消屏幕还会闪。做法是给每个区域记一个上次的值值没变就跳过写屏。这个优化在 51 上很值能明显减少闪烁。// 只在温度变化时刷新减少 I2C 写入 void OLED_ShowTemp(float temp) { static int last_temp -9999; int now (int)(temp * 10); // 放大 10 倍转整数 if (now last_temp) return; last_temp now; OLED_SetPos(0, 6); // 第 6 页起始 OLED_ShowString(T:); OLED_ShowNum(now / 10); // 整数部分 OLED_ShowChar(.); OLED_ShowNum(now % 10); // 小数部分 OLED_ShowString(C); }last_temp用static保存上次值初值取一个不可能的温度保证第一次一定刷新。now放大 10 倍是为了避开浮点比较整数比较在 51 上快得多。4.4 按键校准与消抖按键模块通常设两个键一个切换模式正常显示 / 调时 / 调分一个加减。消抖不能靠delay死等那样会拖慢主循环。我一般用定时器节拍做状态机每 10ms 采样一次引脚连续两次相同才认同时记录按下沿触发一次动作。// 每 10ms 调用一次返回按键事件0 无1 短按 unsigned char Key_Scan(void) { static unsigned char last 1, cnt 0; unsigned char now KEY_PIN; // 读引脚按下为 0 if (now ! last) { if (cnt 2) { // 连续两次一致才确认 last now; cnt 0; if (now 0) return 1; // 按下沿 } } else { cnt 0; } return 0; }这个写法把消抖摊到多次调用里不阻塞主循环。模式切换用一个mode变量0 正常、1 调时、2 调分调的时候对应字段闪烁或加下划线提示答辩演示时很直观。5. 避坑与排查这套方案最容易翻车的五个点5.1 温度一直显示 85℃ 或 -127℃现象是上电后读数固定不变85 是 DS18B20 上电默认值-127 是通信失败。原因通常是复位时序不对或者延时函数在优化后被打乱。51 的DelayUs如果靠空循环实现编译器优化等级一高就可能被删掉。解决办法是把延时函数加volatile或者用定时器做精确延时同时用示波器或逻辑分析仪抓 DQ 线确认复位脉冲宽度够 480μs。5.2 OLED 不亮或花屏不亮先查三件事供电是否 3.3V部分模块 5V 也能工作但要看规格、I2C 地址是 0x3C 还是 0x3D、初始化里电荷泵命令有没有发。花屏多半是页地址和列地址设置错位或者写数据时控制字节写成了 0x00。排查方法是先只写一个固定图案比如全屏点亮确认底层通路没问题再往上叠字符显示。5.3 时间越走越慢或越走越快定时器初值算错是主因。11.0592MHz 和 12MHz 的初值不一样换晶振必须重算。另一个隐蔽原因是中断里做了耗时操作导致中断响应延迟累积。解决办法是中断里只置标志重活放主循环同时用秒表对时十分钟看偏差是否线性线性偏差就是初值问题非线性偏差多半是中断被阻塞。5.4 按键一次触发多次现象是按一下时间跳好几格。原因是机械抖动被当成多次按下或者消抖逻辑里cnt没有正确清零。按 5.4 节的状态机写法连续两次采样一致才确认基本能压住。如果还不行把采样周期从 10ms 调到 20ms或者加一个“释放后才允许下次触发”的标志。5.5 显示闪烁严重整屏重绘是闪烁的根源。I2C 速率有限全屏 1024 字节写一遍要几十毫秒人眼能看出来。解决办法就是 4.3 节的“变化才刷新”再配合局部清屏——只清要写的那几页不要OLED_Clear()全屏清。另外刷新频率别太高1 秒一次足够温度变化本来就不快。6. 验收与答辩怎么证明这套方案真的跑通了验收这件事我一般按“功能清单逐项过 边界条件抽查 现场演示”三步走。功能清单就是第 2 章那张需求表温度显示、时间显示、日期显示、按键校准逐项打勾每项都要能当场复现不能靠“刚才还是好的”。边界条件抽查是加分项用手捏住传感器看温度是否上升、把传感器放冰袋旁看是否降到零下并正确显示负号、断电再上电看时间是否从校准值继续走如果做了掉电保存或者归零后能否重新校准。答辩时老师最爱问的三个问题提前准备好答案一是“温度精度怎么保证的”答 12 位分辨率、0.0625℃ 每 LSB、整数换算避免浮点误差二是“时间准不准”答定时器初值怎么算的、实测十分钟偏差多少三是“如果换一个晶振要改哪里”答定时器初值和延时函数两处。能把这三问答利索基本就稳了。验收项操作方法合格标准温度显示常温下读数手捏传感器读数变化平滑精度 0.1℃负温显示传感器靠近冰袋正确显示负号与数值时间走时与手机秒表对时 10 分钟偏差在可接受范围内按键校准进入调时模式改分改完退出后时间正确显示稳定连续观察 5 分钟无明显闪烁、无花屏现场演示有个小技巧提前把系统通电跑十分钟让时间和温度都进入稳定状态演示时直接展示避免上电初始化那几秒的空白被追问。另外把代码按模块分文件存放答辩时老师翻代码能看到清晰的结构印象分不一样。从那以后我每次带这类课设都强制自己先画一张模块调用关系图再动手写代码图上一旦出现双向箭头就停下来重新想——单向调用是这套方案能顺利调试的底线。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑