简介:这份资源面向51单片机初学者与嵌入式入门开发者,围绕OLED显示屏与DS18B20数字温度传感器,实现环境温度与时间的实时采集与显示,属于典型的小型物联网人机交互实践项目。压缩包共31个文件,约107KB,包含5个C源文件与4个头文件,分别对应主程序、IIC通信、温度读取、延时与时间处理等模块;另有uvproj、uvopt工程配置、hex烧录文件及obj、lst等编译中间产物,可直接用Keil打开编译下载。资源已有2348人学习下载,说明其在入门实践中具有一定参考价值。读者可从中获得完整的工程框架与模块划分思路,理解一线总线协议读写、OLED初始化与字符显示、IIC时序等关键环节,并借助现成工程快速搭建硬件调试环境,对照源码排查显示异常与温度读取问题,适合作为课程设计或自学练手的参考样例。
1. 一块 0.96 寸 OLED 加一颗 DS18B20,为什么值得单独拆一遍
手里这份oled显示屏+ds18b20 显示温度和时间.zip,本质是一个 51 单片机的最小可运行工程:DS18B20 单总线采温,OLED 屏(常见 SSD1306 驱动、I2C 或 SPI 接口)负责把温度和时间刷出来。听起来像入门作业,但真正把它跑稳的人不多——单总线时序差几个微秒就读出 85.0,I2C 上拉电阻没算对就整屏不亮,时间走时靠软件延时就会越走越飘。这份资源的价值不在“点亮”,而在它把采温、显示、计时三条链路塞进了一个能直接烧录的工程里,适合刚上手 51 单片机、想一次把外设驱动打通的新手,也适合拿它当模板改造成温控器、时钟屏的老手。下面按“这是什么 → 怎么跑起来 → 坑在哪 → 怎么改得更狠”的顺序拆。
2. 工程结构与三条数据链路:先看清代码在干什么
拿到压缩包别急着烧,先花十分钟把目录和主循环读一遍。这类工程通常包含主程序、DS18B20 驱动、OLED 驱动、延时函数、字库文件几块,理解它们之间的调用关系,比盲目改一行编译一次高效得多。
2.1 目录里每个文件对应哪条链路
典型结构大致是这样(文件名可能略有差异,按功能对号入座即可):
| 文件/目录 | 作用 | 改动频率 |
|---|---|---|
main.c | 初始化、主循环、时间与温度调度 | 高 |
ds18b20.c/.h | 单总线复位、写字节、读字节、温度换算 | 中 |
oled.c/.h | I2C/SPI 底层写、清屏、光标定位 | 低 |
oledfont.h | ASCII 与中文点阵字库 | 低 |
delay.c/.h | 微秒/毫秒延时,单总线时序的命根子 | 中 |
工程文件(.uvproj等) | 编译配置、芯片型号、晶振 | 低 |
读代码的顺序建议反过来:先看main.c的主循环,确认它多久采一次温、多久刷一次屏、时间变量怎么自增;再回头看ds18b20.c的时序函数;最后才碰oled.c。很多人一上来就改 OLED 显示内容,结果温度根本没读对,白折腾。
2.2 主循环的调度逻辑与时间自增
主循环一般长这样,核心是“采温”和“计时”两个节奏不能互相拖死:
#include "ds18b20.h" #include "oled.h" #include "delay.h" unsigned char hour = 12, min = 0, sec = 0; unsigned int ms_count = 0; void main(void) { int temp_raw; float temp; OLED_Init(); // 屏先初始化,避免上电花屏 OLED_Clear(); DS18B20_Init(); // 单总线拉高释放 while (1) { temp_raw = DS18B20_ReadTemp(); // 读原始值,单位 0.0625℃ temp = temp_raw * 0.0625; // 换算成摄氏度 OLED_ShowString(0, 0, "TEMP:"); OLED_ShowFloat(40, 0, temp, 1); // 保留一位小数 OLED_ShowString(0, 2, "TIME:"); OLED_ShowNum(40, 2, hour, 2); OLED_ShowChar(56, 2, ':'); OLED_ShowNum(64, 2, min, 2); OLED_ShowChar(80, 2, ':'); OLED_ShowNum(88, 2, sec, 2); Delay_ms(500); // 刷新间隔,别设太小 ms_count += 500; if (ms_count >= 1000) // 软件累加走时 { ms_count = 0; sec++; if (sec >= 60) { sec = 0; min++; } if (min >= 60) { min = 0; hour++; } if (hour >= 24) hour = 0; } } }逻辑说明:DS18B20_ReadTemp()返回的是 12 位原始值,乘0.0625才是摄氏度,这一步漏了就会显示几千度。时间用ms_count累加而不是靠Delay_ms(1000)单独走一秒,是为了让采温和走时共用一个循环,避免屏刷新把秒针拖慢。参数上,Delay_ms(500)是刷新节奏,DS18B20 一次转换最长约 750ms,所以刷新间隔不能低于这个量级,否则读到的是上一次的旧值。
提示:如果你的工程里
DS18B20_ReadTemp()直接返回浮点,说明作者已经在驱动里做了换算,主循环就别再乘 0.0625,否则温度会小得离谱。
2.3 DS18B20 单总线时序为什么必须抠到微秒
DS18B20 只用一根数据线,靠“拉低多久”来区分写 0、写 1、读 0、读 1。以常见的 12MHz 或 11.0592MHz 晶振为例,一个写 1 时隙要求拉低 1~15μs 后释放,写 0 要拉低 60μs 左右,读时隙则要在拉低后 15μs 内采样。这些窗口全靠delay.c里的微秒延时撑住,延时函数一旦被编译器优化掉或者循环次数算错,读出来就是固定的 85.0(复位值)或者全 0。
常见做法是把延时写成带volatile或者加_nop_()的形式,防止优化;同时用示波器或者逻辑分析仪抓一次复位脉冲(拉低 480μs 以上再释放),确认宽度对得上再往下调。没有仪器的话,就先用“读到的温度是否随手指捏住传感器而变化”做粗验——捏住能升到 30℃ 以上,说明时序基本通了;一直 85.0,先查延时和上拉。
3. 从零跑通:编译、烧录、上电自检的完整步骤
工程能编译不代表能跑,能跑不代表显示对。这一章按“环境 → 编译 → 烧录 → 自检”四步走,每步给出可对照的现象,方便你定位卡在哪一环。
3.1 编译环境与芯片型号对齐
这类 51 工程多数用 Keil C51 打开。第一步不是点编译,而是确认三件事:工程目标芯片型号和手里板子一致(比如 STC89C52 与 AT89C52 的下载方式不同)、晶振频率填对(直接影响delay.c的微秒精度)、头文件路径包含oledfont.h所在目录。
# 以命令行方式做一次干净编译(Keil 的 UV4 支持批处理) UV4.exe -b project.uvproj -o build_log.txt # 编译完先看 log 里有没有 warning,尤其是 "unreferenced" 和 "overflow"逻辑说明:-b是批处理编译,-o把日志落盘,方便 grep。重点看两类告警——DATA space overflow说明 51 的片内 RAM 不够用了,字库或缓冲区太大;unreferenced variable可能是某个驱动函数根本没被调用,主循环里漏了初始化。参数上,如果用的是 STC 系列,Keil 里选的型号只影响编译,实际烧录靠 STC-ISP 工具,别在 Keil 里死磕下载配置。
3.2 烧录与上电自检清单
烧录前先断电,接好下载线(串口或 USB 转 TTL),冷启动下载。上电后按下面顺序看现象,哪一步不对就停在哪:
- 屏是否整体点亮或有雪花——不亮先查供电和 I2C 上拉,花屏查初始化命令顺序。
- 是否显示
TEMP:和TIME:字样——有字说明 OLED 驱动通了,问题在传感器。 - 温度值是否在合理区间(室温 15~35℃)——显示 85.0 查单总线时序,显示 0.0 查上拉电阻和数据线接触。
- 秒数是否在跳——不跳查主循环是否卡在某个
while里等 DS18B20 转换完成。
注意:DS18B20 数据线必须接 4.7kΩ 上拉到 VCC,很多模块板载了这颗电阻,裸传感器直接飞线的话十有八九读不到,这是新手最高频的翻车点。
3.3 用串口打印做“黑匣子”
屏上只能看结果,看不到中间值。调试阶段建议把原始值从串口吐出来,比盯着屏猜快得多:
// 在读取温度后加一段串口输出,波特率 9600 printf("raw=%d temp=%.1f\r\n", temp_raw, temp);逻辑说明:temp_raw是未换算的整数,正常室温下大约在 320 左右(20℃ ÷ 0.0625);如果它恒为 1360(对应 85℃)或 0,就能立刻判断是时序问题还是接线问题。串口初始化记得配 Timer1 的工作模式 2 和 TH1/TL1 重装值,9600 波特率在 11.0592MHz 下是0xFD,12MHz 下误差偏大,这也是为什么这类工程偏爱 11.0592MHz 晶振。
4. 避坑与排查:五个真实会卡住你的问题
这一章全是血泪经验,每条按“现象 → 原因 → 解决”写,遇到对得上的直接照做。
4.1 温度恒为 85.0
现象:屏上温度一直显示 85.0,捏传感器也不变。原因:85.0 是 DS18B20 上电复位后的默认寄存器值,说明主机根本没成功发起转换,多半是复位脉冲宽度不够或读时隙采样点错了。解决:先用逻辑分析仪抓复位脉冲,确认拉低 ≥480μs;再检查delay.c的微秒函数在目标晶振下是否准确,必要时用_nop_()手工凑时序。
4.2 整屏不亮或只亮一半
现象:上电后 OLED 全黑,或只有上半屏有内容。原因:I2C 的 SCL/SDA 没接上拉电阻,或者初始化命令里对比度、复用率参数和实际屏尺寸(0.96 寸常见 128×64)不匹配。解决:补 4.7kΩ 上拉;核对初始化序列里的0xA8(复用率)和0xD3(显示偏移)参数,128×32 的屏用 64 的初始化会只亮一半。
4.3 时间越走越慢
现象:放一晚上,时钟比手机慢好几分钟。原因:软件累加走时依赖Delay_ms的精度,而Delay_ms本身受晶振误差、中断干扰影响,误差会累积。解决:短期用定时器中断替代主循环累加,长期精度要求高就外挂 DS1302 或 DS3231 这类专用 RTC 芯片,别指望软件延时走准。
4.4 编译通过但下载后无反应
现象:Keil 编译 0 error,烧录提示成功,板子毫无动静。原因:工程里选的芯片型号和实际芯片的 RAM/Flash 容量不符,程序跑飞;或者看门狗没关,主循环被复位。解决:核对芯片型号与容量;在main开头确认是否需要喂狗或关闭 WDT,STC 系列可在 ISP 工具里设置不上电启用看门狗。
4.5 显示中文乱码或缺字
现象:想加“温度”两个汉字,结果显示方块或错位。原因:oledfont.h里没有对应汉字的点阵,或者取模方式(阴码/阳码、逐行/逐列)和驱动函数不匹配。解决:用取模软件按驱动要求的格式重新生成,16×16 汉字占 32 字节,确认OLED_ShowCN的坐标是按页还是按像素计算,两者差一个换算就会错位。
5. 进阶改造:把 demo 变成能用的桌面温时钟
跑通只是起点,这份工程真正好用的是它的可改造性。下面三个方向按难度递增,改完基本就是一台能摆在桌上的成品。
5.1 加按键做时间校准
在main.c里加两个按键扫描(接 P3.2、P3.3),一个进设置模式、一个加数值。关键是设置模式下暂停ms_count自增,退出时清零重新计时,否则校准完秒数会跳。扫描要加 10~20ms 消抖,别用死循环等按键释放,否则采温会停。
5.2 温度报警阈值与蜂鸣器联动
读到的temp和设定阈值比较,超限拉低蜂鸣器引脚。注意 DS18B20 转换期间总线忙,报警判断放在换算之后、刷新之前,避免读到半截数据。阈值建议做成宏定义放头文件,改起来不用翻主函数。
5.3 精度与刷新率的取舍表
| 分辨率设置 | 转换时间 | 温度步进 | 适用场景 |
|---|---|---|---|
| 9 位 | 93.75ms | 0.5℃ | 刷新快、精度要求低 |
| 10 位 | 187.5ms | 0.25℃ | 一般室温显示 |
| 11 位 | 375ms | 0.125℃ | 需要小数两位 |
| 12 位 | 750ms | 0.0625℃ | 默认,精度最高 |
改分辨率要写 DS18B20 的配置寄存器(命令0x4E写暂存器第 5 字节),改完刷新间隔必须大于对应转换时间,否则读到旧值。我一般默认用 12 位,刷新设 800ms 留余量。
从那以后我每次拿到这类“传感器 + 屏”的工程,都强制先抓一次单总线复位波形、再串口打一遍原始值,两步过了才动显示代码——省下的返工时间远比买逻辑分析仪的钱多。希望帮到你。
本文还有配套的精品资源,点击获取