简介:这份资源是面向C51单片机初学者与嵌入式爱好者的LCD1602显示例程合集,围绕字符型液晶屏与8051内核单片机的交互展开,覆盖矩阵按键键值、DS18B20温度、DS1302实时时钟以及ADC0832模数转换电压四类典型应用场景,帮助读者理解数据线、地址线及RS、RW、E控制信号的驱动逻辑。压缩包共86个文件,约4.13MB,包含c源码、h头文件、uvproj与uvopt工程配置、hex烧录文件、dsn与pdsprj仿真工程,以及mp4操作录屏和lst、m51编译列表等,兼顾代码实现与仿真验证。目前已有1076人学习下载。四个独立工程分别对应按键扫描、单总线测温、串行时钟读取和模拟量采集,读者可对照录屏观察仿真现象,再结合源码梳理显示格式化与接口时序,适合作为课程设计或电子制作的参考素材。1. 从一块“只会亮灯”的板子说起:LCD1602 到底是什么你手里可能有一块单片机开发板,上面焊着一块绿色或蓝色的屏幕,能显示两行字,每行十六个字符。你把它通上电,屏幕亮了,但要么一片空白,要么只有一排方块。你想让它显示“Hello”,却不知道从哪下手。这块屏幕就是 LCD1602,而让它听话的过程,就是这篇文章要带你走完的路。LCD1602 是一种字符型液晶显示模块,它不能显示图片,也不能显示中文,只能显示数字、英文字母和少量符号,一共两行,每行最多十六个字符。它便宜、稳定、接线简单,几乎是每个学单片机的人第一个真正“输出信息”的外设。你学它,不是为了做产品,而是为了跨过一道坎:让程序的结果不再只是闪烁的 LED,而是变成你能读懂的文字。C51 单片机是它的经典搭档,两者配合,是嵌入式入门最经典的组合之一。适合谁?适合刚学完点亮 LED、会写最简单的延时函数、但还没让单片机“说话”的你。2. 为什么值得先学它:从“看不见”到“看得见”的跨越2.1 它是调试的眼睛,也是信心的来源学单片机最难受的阶段,是程序跑没跑通你根本不知道。LED 只能告诉你“有电”或“没电”,按键只能告诉你“按了”或“没按”。但当你接上 LCD1602,你可以让程序把变量值、传感器读数、状态标志直接打印在屏幕上。比如你后面要学 DS18B20 温度传感器,温度到底读出来没有、读出来是多少,看一眼屏幕就知道。没有它,你只能靠猜,或者把数据传回电脑,那又是另一套麻烦事。我第一次学的时候,卡在 DS1302 时钟芯片上很久,读出来的时间全是乱码。后来把读到的原始字节直接显示在 LCD1602 上,才发现是时序里少了一个延时。屏幕不会骗你,它显示什么,程序就是什么。这种“看得见”的反馈,是新手坚持下去最重要的动力。2.2 它逼你搞懂“时序”这个玄学LCD1602 的驱动不复杂,但它有一套严格的读写时序。什么时候送命令、什么时候送数据、什么时候读忙标志,都有时间要求。你如果只是抄一段代码烧进去,可能也能显示,但一旦换一块屏幕、换一个晶振频率,就不亮了。真正自己写一遍驱动,你会被迫理解“使能脉冲”“建立时间”“保持时间”这些词到底在说什么。这些概念在后面学 DS1302、DS18B20 时全都会用到,它们都是靠一根线按特定节奏传数据的。提示:新手不要一上来就追求“无延时驱动”或“高速刷新”,先把标准时序跑通,哪怕延时给得保守一点。2.3 它连接了按键、传感器和时钟你拿到的这份例程,标题里除了 LCD1602 和 C51,还带了按键、DS18B20、DS1302。这不是随便凑的。按键用来切换显示页面,DS18B20 提供温度数据,DS1302 提供日期和时间。LCD1602 是最终的展示窗口。学完这个例程,你就有了一个最小的“环境监测站”雏形:按一下键,屏幕从时间跳到温度,再按一下跳回来。这个过程中,你会把之前单独学的按键扫描、单总线通信、SPI 通信全部串起来。这才是它真正的价值——不是一块屏幕,而是一个完整的入门项目骨架。3. 动手之前:接线、供电和第一个能亮起来的画面3.1 先认清引脚,别急着插线LCD1602 通常有 16 个引脚,编号从 1 到 16。你不需要记住全部,先记住这几组:引脚名称作用新手接法1VSS地接单片机 GND2VDD电源正接 5V3V0对比度调节接电位器中间脚4RS数据/命令选择接单片机某个 IO5RW读/写选择一般直接接地,只写不读6E使能信号接单片机某个 IO7-14D0-D7数据线接单片机一组 IO15A背光正串电阻接 5V16K背光负接 GNDV0 的对比度调节很关键。你如果发现屏幕亮了但看不到字,或者只有一排黑方块,八成是 V0 电压不对。拿一个 10K 电位器,一端接 5V,一端接 GND,中间脚接 V0,慢慢拧,直到字清晰出现。这个坑几乎每个人都会踩一次。3.2 第一个例程:让屏幕显示一行字假设你把 RS 接 P2.0,RW 接 GND,E 接 P2.1,数据线 D0-D7 接 P0 口。下面是最小化的显示代码,只做一件事:在第一行显示“Hello LCD”。#include <reg52.h> #define LCD_DATA P0 sbit RS = P2^0; sbit E = P2^1; // 短延时,让时序满足 LCD1602 的要求 void delay(unsigned int t) { while (t--); } // 写命令:RS=0,E 给一个高脉冲 void lcd_write_cmd(unsigned char cmd) { RS = 0; LCD_DATA = cmd; delay(100); E = 1; delay(100); E = 0; } // 写数据:RS=1,E 给一个高脉冲 void lcd_write_data(unsigned char dat) { RS = 1; LCD_DATA = dat; delay(100); E = 1; delay(100); E = 0; } // 初始化:按手册要求的顺序送命令 void lcd_init() { lcd_write_cmd(0x38); // 8 位数据,两行显示,5x7 点阵 lcd_write_cmd(0x0C); // 开显示,关光标,不闪烁 lcd_write_cmd(0x06); // 写一个字符后地址自动加一 lcd_write_cmd(0x01); // 清屏 delay(1000); } void main() { unsigned char i; unsigned char *str = "Hello LCD"; lcd_init(); lcd_write_cmd(0x80); // 设置显示地址为第一行第一个位置 for (i = 0; str[i] != '\0'; i++) { lcd_write_data(str[i]); } while (1); }这段代码里,0x38、0x0C、0x06、0x01是 LCD1602 的初始化命令,顺序不能乱。0x80是设置 DDRAM 地址的命令,第一行第一个位置对应地址 0x00,所以命令是 0x80 | 0x00。如果你想在第二行第一个位置显示,就写0x80 | 0x40,也就是0xC0。delay(100)里的 100 不是精确的微秒数,它只是让 E 脉冲足够宽,新手先不用纠结具体时间,能显示就行。注意:如果你用的是 4 位数据线接法,初始化命令和写数据的方式会不一样,但原理相同。建议第一遍先用 8 位接法,线多但逻辑直白。3.3 显示变量:让屏幕说出温度值只会显示固定字符串还不够,你真正需要的是把变量显示出来。比如 DS18B20 读到一个温度值temp,它是整数,你要把它变成字符显示。下面这个函数把 0 到 99 的整数显示在屏幕上。// 在指定位置显示一个两位整数 void lcd_show_num(unsigned char row, unsigned char col, unsigned char num) { unsigned char addr; if (row == 0) addr = 0x80 + col; else addr = 0xC0 + col; lcd_write_cmd(addr); lcd_write_data(num / 10 + '0'); // 十位 lcd_write_data(num % 10 + '0'); // 个位 }num / 10 + '0'是把数字转成对应字符的常用写法。比如 num 是 25,25/10 得 2,加 '0' 的 ASCII 码就变成字符 '2'。个位同理。你后面读 DS18B20 得到的温度、读 DS1302 得到的小时和分钟,都可以用这个函数显示出来。先让屏幕显示一个你手动改的变量,再把它换成传感器读到的值,这一步跨过去,例程就活了一半。4. 把按键、DS18B20、DS1302 串起来:一个能切换的显示界面4.1 按键扫描:别用延时死等按键接法通常是一个引脚接按键,按键另一端接地,引脚配置成输入并开启上拉。最简单的扫描方式是轮询:在主循环里不断读引脚,如果是低电平,就认为按下。但机械按键有抖动,你需要在检测到低电平后延时 10 到 20 毫秒再读一次,如果还是低电平,才确认按下。sbit KEY = P3^2; bit key_pressed() { if (KEY == 0) { delay(1000); // 约 10ms,消抖 if (KEY == 0) { while (KEY == 0); // 等待松手 return 1; } } return 0; }这个函数返回 1 表示有一次有效按下。你在主循环里调用它,按一次就切换一个显示模式。注意while (KEY == 0)会卡住程序,如果你后面要同时刷新时间和温度,就不能用这种死等松手的方式,而要改成状态机。但作为入门,先让按键能切换页面,再优化。4.2 DS18B20:一根线读出温度DS18B20 是单总线器件,一根数据线完成所有通信。它的时序要求比 LCD1602 更严格,延时必须准确。例程里通常包含复位、写字节、读字节、启动转换、读温度这几个步骤。你不需要从头写,但要知道读回来的温度是 16 位,高 5 位是符号位,低 4 位是小数部分。如果只显示整数,把读到的 16 位值右移 4 位再乘 0.0625 取整就行。// 简化示意:读取温度并返回整数部分 int ds18b20_read_temp() { unsigned char low, high; int temp; ds18b20_start(); // 启动一次温度转换 delay_ms(750); // 等待转换完成,12 位精度约 750ms ds18b20_reset(); ds18b20_write_byte(0xCC); // 跳过 ROM ds18b20_write_byte(0xBE); // 读暂存器 low = ds18b20_read_byte(); high = ds18b20_read_byte(); temp = (high << 8) | low; return temp >> 4; // 取整数部分 }0xCC是跳过 ROM 命令,总线上只有一个 DS18B20 时用它最省事。0xBE是读暂存器命令,读回的第一个字节是温度低 8 位,第二个是高 8 位。temp >> 4把小数部分丢掉,只留整数。你把这个返回值交给lcd_show_num,屏幕上就能看到温度了。4.3 DS1302:让屏幕显示日期和时间DS1302 是实时时钟芯片,它自己走时,掉电后靠纽扣电池维持。它用三根线通信:复位、时钟、数据。例程里会提供读写寄存器的函数。你需要做的是:初始化时写入一个起始时间,然后每隔一秒读一次当前时间,显示在屏幕上。// 读取 DS1302 的小时和分钟并显示 void show_time() { unsigned char hour, min; hour = ds1302_read(0x85); // 读小时寄存器 min = ds1302_read(0x83); // 读分钟寄存器 lcd_show_num(0, 0, hour); // 第一行显示小时 lcd_write_data(':'); lcd_show_num(0, 3, min); // 第一行显示分钟 }DS1302 的寄存器数据是 BCD 码,也就是说0x23表示 23 点,而不是十进制的 35。你如果直接当十进制显示,会看到乱码。例程里通常有 BCD 转十进制和十进制转 BCD 的函数,读出来先转换再显示。这个坑很隐蔽,现象是时间跳得不对,原因就是没做 BCD 转换。4.4 主循环:把三个功能拼在一起主循环里做三件事:扫描按键、根据当前模式决定显示什么、每隔一段时间刷新数据。下面是一个简化的框架。unsigned char mode = 0; // 0 显示时间,1 显示温度 void main() { lcd_init(); ds1302_init(); while (1) { if (key_pressed()) { mode = !mode; // 按键切换模式 lcd_write_cmd(0x01); // 清屏,准备重画 } if (mode == 0) { show_time(); } else { show_temp(); } delay(30000); // 简单延时,控制刷新速度 } }这个框架很粗糙,但能跑。你按一下键,屏幕从时间切到温度,再按一下切回来。delay(30000)是为了让屏幕不要刷新太快导致闪烁,实际项目中会用定时器中断来做,但入门阶段先用延时把逻辑跑通。5. 新手最容易翻车的几个地方:避坑与排查5.1 屏幕亮但没字,或者只有黑方块现象:背光亮了,但看不到任何字符,或者第一行全是黑色方块。原因:V0 对比度电压不对,或者初始化命令没发成功。解决:先调电位器,从一端慢慢拧到另一端,看字是否出现。如果怎么调都不行,检查 RS、E、数据线是否接错,尤其是数据线顺序有没有反。用万用表测一下单片机 IO 在写数据时有没有变化。5.2 显示乱码或只显示第一个字符现象:屏幕上出现奇怪的符号,或者只显示“H”后面就没了。原因:时序太快,LCD1602 还没处理完上一个字节,你就发了下一个。解决:在lcd_write_cmd和lcd_write_data里把延时加大,比如delay(500)。如果用的是 4 位模式,检查是不是先发高四位再发低四位,顺序不能错。5.3 DS18B20 读出来永远是 85 或 0现象:温度值不变,一直是 85 或者 0。原因:85 是 DS18B20 上电默认值,说明转换命令没发出去,或者等待时间不够。0 通常是读时序不对,读回来的全是 0。解决:检查启动转换后有没有等够 750 毫秒。用示波器看数据线有没有正确的复位脉冲和读写脉冲。如果没有示波器,就把读到的原始字节显示在 LCD 上,看看到底读回了什么。5.4 DS1302 时间不走或跳变现象:秒数不动,或者分钟突然跳好几分钟。原因:晶振没起振,或者 BCD 转换写错了。解决:DS1302 需要 32.768kHz 晶振,检查晶振是否焊好,负载电容是否匹配。读出来的时间先不做任何转换,直接显示十六进制,看看是不是 BCD 码。如果是,就加转换函数。5.5 按键按一次跳好几下现象:按一下键,模式切换了好几次。原因:按键抖动没消干净,或者松手检测没做。解决:把消抖延时加长到 20 毫秒,并且在确认按下后等待松手再返回。如果主循环里还有其他耗时操作,考虑用定时器中断扫描按键,避免死等。6. 下一步学什么:从能显示到能控制你现在已经能让屏幕显示固定字、显示变量、显示传感器数据,也能用按键切换页面。这个例程跑通之后,下一步不是去学更复杂的屏幕,而是把现有的东西用得更稳。第一,把延时换成定时器。你现在用的delay是空循环,它会阻塞程序,导致按键反应迟钝、时间显示不准。用定时器 0 或定时器 1 产生 1 毫秒中断,在中断里做按键扫描和时间刷新,主循环只负责显示。这是从“能跑”到“好用”的关键一步。第二,把显示内容做成菜单。现在只有两个模式,你可以扩展成三级菜单:主菜单显示时间,按一下进入温度,再按一下进入设置,设置里可以调时间。菜单结构用数组和状态机实现,不要用一堆 if-else 堆在一起。第三,把数据存起来。DS1302 有电池,掉电时间不丢。但你的温度数据掉电就没了。可以学 24C02 这类 EEPROM,把最高最低温度记录下来,上电后显示。这样你的小系统就有了“记忆”。第四,把通信协议整理成独立的文件。现在所有代码可能都在一个 .c 文件里,你可以把 LCD1602、DS18B20、DS1302 的驱动分别写成 .c 和 .h,主文件只调用接口。这是工程化的第一步,也是后面做更大项目的基础。我自己的习惯是,每学一个新外设,就把它单独写成一个最小工程,只做一件事:让它工作,并显示在 LCD 上。等所有外设都能单独跑了,再拼在一起。这样出问题的时候,你能快速定位是哪个模块的事。从那以后我每次调新模块,都强制先写一个只显示原始数据的测试程序,确认通信没问题,再去做业务逻辑。希望这个路径能帮到你,别急着一次写完,先让屏幕亮出第一个字。本文还有配套的精品资源,点击获取
简介:这份资源是面向C51单片机初学者与嵌入式爱好者的LCD1602显示例程合集,围绕字符型液晶屏与8051内核单片机的交互展开,覆盖矩阵按键键值、DS18B20温度、DS1302实时时钟以及ADC0832模数转换电压四类典型应用场景,帮助读者理解数据线、地址线及RS、RW、E控制信号的驱动逻辑。压缩包共86个文件,约4.13MB,包含c源码、h头文件、uvproj与uvopt工程配置、hex烧录文件、dsn与pdsprj仿真工程,以及mp4操作录屏和lst、m51编译列表等,兼顾代码实现与仿真验证。目前已有1076人学习下载。四个独立工程分别对应按键扫描、单总线测温、串行时钟读取和模拟量采集,读者可对照录屏观察仿真现象,再结合源码梳理显示格式化与接口时序,适合作为课程设计或电子制作的参考素材。1. 从一块“只会亮灯”的板子说起:LCD1602 到底是什么你手里可能有一块单片机开发板,上面焊着一块绿色或蓝色的屏幕,能显示两行字,每行十六个字符。你把它通上电,屏幕亮了,但要么一片空白,要么只有一排方块。你想让它显示“Hello”,却不知道从哪下手。这块屏幕就是 LCD1602,而让它听话的过程,就是这篇文章要带你走完的路。LCD1602 是一种字符型液晶显示模块,它不能显示图片,也不能显示中文,只能显示数字、英文字母和少量符号,一共两行,每行最多十六个字符。它便宜、稳定、接线简单,几乎是每个学单片机的人第一个真正“输出信息”的外设。你学它,不是为了做产品,而是为了跨过一道坎:让程序的结果不再只是闪烁的 LED,而是变成你能读懂的文字。C51 单片机是它的经典搭档,两者配合,是嵌入式入门最经典的组合之一。适合谁?适合刚学完点亮 LED、会写最简单的延时函数、但还没让单片机“说话”的你。2. 为什么值得先学它:从“看不见”到“看得见”的跨越2.1 它是调试的眼睛,也是信心的来源学单片机最难受的阶段,是程序跑没跑通你根本不知道。LED 只能告诉你“有电”或“没电”,按键只能告诉你“按了”或“没按”。但当你接上 LCD1602,你可以让程序把变量值、传感器读数、状态标志直接打印在屏幕上。比如你后面要学 DS18B20 温度传感器,温度到底读出来没有、读出来是多少,看一眼屏幕就知道。没有它,你只能靠猜,或者把数据传回电脑,那又是另一套麻烦事。我第一次学的时候,卡在 DS1302 时钟芯片上很久,读出来的时间全是乱码。后来把读到的原始字节直接显示在 LCD1602 上,才发现是时序里少了一个延时。屏幕不会骗你,它显示什么,程序就是什么。这种“看得见”的反馈,是新手坚持下去最重要的动力。2.2 它逼你搞懂“时序”这个玄学LCD1602 的驱动不复杂,但它有一套严格的读写时序。什么时候送命令、什么时候送数据、什么时候读忙标志,都有时间要求。你如果只是抄一段代码烧进去,可能也能显示,但一旦换一块屏幕、换一个晶振频率,就不亮了。真正自己写一遍驱动,你会被迫理解“使能脉冲”“建立时间”“保持时间”这些词到底在说什么。这些概念在后面学 DS1302、DS18B20 时全都会用到,它们都是靠一根线按特定节奏传数据的。提示:新手不要一上来就追求“无延时驱动”或“高速刷新”,先把标准时序跑通,哪怕延时给得保守一点。2.3 它连接了按键、传感器和时钟你拿到的这份例程,标题里除了 LCD1602 和 C51,还带了按键、DS18B20、DS1302。这不是随便凑的。按键用来切换显示页面,DS18B20 提供温度数据,DS1302 提供日期和时间。LCD1602 是最终的展示窗口。学完这个例程,你就有了一个最小的“环境监测站”雏形:按一下键,屏幕从时间跳到温度,再按一下跳回来。这个过程中,你会把之前单独学的按键扫描、单总线通信、SPI 通信全部串起来。这才是它真正的价值——不是一块屏幕,而是一个完整的入门项目骨架。3. 动手之前:接线、供电和第一个能亮起来的画面3.1 先认清引脚,别急着插线LCD1602 通常有 16 个引脚,编号从 1 到 16。你不需要记住全部,先记住这几组:引脚名称作用新手接法1VSS地接单片机 GND2VDD电源正接 5V3V0对比度调节接电位器中间脚4RS数据/命令选择接单片机某个 IO5RW读/写选择一般直接接地,只写不读6E使能信号接单片机某个 IO7-14D0-D7数据线接单片机一组 IO15A背光正串电阻接 5V16K背光负接 GNDV0 的对比度调节很关键。你如果发现屏幕亮了但看不到字,或者只有一排黑方块,八成是 V0 电压不对。拿一个 10K 电位器,一端接 5V,一端接 GND,中间脚接 V0,慢慢拧,直到字清晰出现。这个坑几乎每个人都会踩一次。3.2 第一个例程:让屏幕显示一行字假设你把 RS 接 P2.0,RW 接 GND,E 接 P2.1,数据线 D0-D7 接 P0 口。下面是最小化的显示代码,只做一件事:在第一行显示“Hello LCD”。#include <reg52.h> #define LCD_DATA P0 sbit RS = P2^0; sbit E = P2^1; // 短延时,让时序满足 LCD1602 的要求 void delay(unsigned int t) { while (t--); } // 写命令:RS=0,E 给一个高脉冲 void lcd_write_cmd(unsigned char cmd) { RS = 0; LCD_DATA = cmd; delay(100); E = 1; delay(100); E = 0; } // 写数据:RS=1,E 给一个高脉冲 void lcd_write_data(unsigned char dat) { RS = 1; LCD_DATA = dat; delay(100); E = 1; delay(100); E = 0; } // 初始化:按手册要求的顺序送命令 void lcd_init() { lcd_write_cmd(0x38); // 8 位数据,两行显示,5x7 点阵 lcd_write_cmd(0x0C); // 开显示,关光标,不闪烁 lcd_write_cmd(0x06); // 写一个字符后地址自动加一 lcd_write_cmd(0x01); // 清屏 delay(1000); } void main() { unsigned char i; unsigned char *str = "Hello LCD"; lcd_init(); lcd_write_cmd(0x80); // 设置显示地址为第一行第一个位置 for (i = 0; str[i] != '\0'; i++) { lcd_write_data(str[i]); } while (1); }这段代码里,0x38、0x0C、0x06、0x01是 LCD1602 的初始化命令,顺序不能乱。0x80是设置 DDRAM 地址的命令,第一行第一个位置对应地址 0x00,所以命令是 0x80 | 0x00。如果你想在第二行第一个位置显示,就写0x80 | 0x40,也就是0xC0。delay(100)里的 100 不是精确的微秒数,它只是让 E 脉冲足够宽,新手先不用纠结具体时间,能显示就行。注意:如果你用的是 4 位数据线接法,初始化命令和写数据的方式会不一样,但原理相同。建议第一遍先用 8 位接法,线多但逻辑直白。3.3 显示变量:让屏幕说出温度值只会显示固定字符串还不够,你真正需要的是把变量显示出来。比如 DS18B20 读到一个温度值temp,它是整数,你要把它变成字符显示。下面这个函数把 0 到 99 的整数显示在屏幕上。// 在指定位置显示一个两位整数 void lcd_show_num(unsigned char row, unsigned char col, unsigned char num) { unsigned char addr; if (row == 0) addr = 0x80 + col; else addr = 0xC0 + col; lcd_write_cmd(addr); lcd_write_data(num / 10 + '0'); // 十位 lcd_write_data(num % 10 + '0'); // 个位 }num / 10 + '0'是把数字转成对应字符的常用写法。比如 num 是 25,25/10 得 2,加 '0' 的 ASCII 码就变成字符 '2'。个位同理。你后面读 DS18B20 得到的温度、读 DS1302 得到的小时和分钟,都可以用这个函数显示出来。先让屏幕显示一个你手动改的变量,再把它换成传感器读到的值,这一步跨过去,例程就活了一半。4. 把按键、DS18B20、DS1302 串起来:一个能切换的显示界面4.1 按键扫描:别用延时死等按键接法通常是一个引脚接按键,按键另一端接地,引脚配置成输入并开启上拉。最简单的扫描方式是轮询:在主循环里不断读引脚,如果是低电平,就认为按下。但机械按键有抖动,你需要在检测到低电平后延时 10 到 20 毫秒再读一次,如果还是低电平,才确认按下。sbit KEY = P3^2; bit key_pressed() { if (KEY == 0) { delay(1000); // 约 10ms,消抖 if (KEY == 0) { while (KEY == 0); // 等待松手 return 1; } } return 0; }这个函数返回 1 表示有一次有效按下。你在主循环里调用它,按一次就切换一个显示模式。注意while (KEY == 0)会卡住程序,如果你后面要同时刷新时间和温度,就不能用这种死等松手的方式,而要改成状态机。但作为入门,先让按键能切换页面,再优化。4.2 DS18B20:一根线读出温度DS18B20 是单总线器件,一根数据线完成所有通信。它的时序要求比 LCD1602 更严格,延时必须准确。例程里通常包含复位、写字节、读字节、启动转换、读温度这几个步骤。你不需要从头写,但要知道读回来的温度是 16 位,高 5 位是符号位,低 4 位是小数部分。如果只显示整数,把读到的 16 位值右移 4 位再乘 0.0625 取整就行。// 简化示意:读取温度并返回整数部分 int ds18b20_read_temp() { unsigned char low, high; int temp; ds18b20_start(); // 启动一次温度转换 delay_ms(750); // 等待转换完成,12 位精度约 750ms ds18b20_reset(); ds18b20_write_byte(0xCC); // 跳过 ROM ds18b20_write_byte(0xBE); // 读暂存器 low = ds18b20_read_byte(); high = ds18b20_read_byte(); temp = (high << 8) | low; return temp >> 4; // 取整数部分 }0xCC是跳过 ROM 命令,总线上只有一个 DS18B20 时用它最省事。0xBE是读暂存器命令,读回的第一个字节是温度低 8 位,第二个是高 8 位。temp >> 4把小数部分丢掉,只留整数。你把这个返回值交给lcd_show_num,屏幕上就能看到温度了。4.3 DS1302:让屏幕显示日期和时间DS1302 是实时时钟芯片,它自己走时,掉电后靠纽扣电池维持。它用三根线通信:复位、时钟、数据。例程里会提供读写寄存器的函数。你需要做的是:初始化时写入一个起始时间,然后每隔一秒读一次当前时间,显示在屏幕上。// 读取 DS1302 的小时和分钟并显示 void show_time() { unsigned char hour, min; hour = ds1302_read(0x85); // 读小时寄存器 min = ds1302_read(0x83); // 读分钟寄存器 lcd_show_num(0, 0, hour); // 第一行显示小时 lcd_write_data(':'); lcd_show_num(0, 3, min); // 第一行显示分钟 }DS1302 的寄存器数据是 BCD 码,也就是说0x23表示 23 点,而不是十进制的 35。你如果直接当十进制显示,会看到乱码。例程里通常有 BCD 转十进制和十进制转 BCD 的函数,读出来先转换再显示。这个坑很隐蔽,现象是时间跳得不对,原因就是没做 BCD 转换。4.4 主循环:把三个功能拼在一起主循环里做三件事:扫描按键、根据当前模式决定显示什么、每隔一段时间刷新数据。下面是一个简化的框架。unsigned char mode = 0; // 0 显示时间,1 显示温度 void main() { lcd_init(); ds1302_init(); while (1) { if (key_pressed()) { mode = !mode; // 按键切换模式 lcd_write_cmd(0x01); // 清屏,准备重画 } if (mode == 0) { show_time(); } else { show_temp(); } delay(30000); // 简单延时,控制刷新速度 } }这个框架很粗糙,但能跑。你按一下键,屏幕从时间切到温度,再按一下切回来。delay(30000)是为了让屏幕不要刷新太快导致闪烁,实际项目中会用定时器中断来做,但入门阶段先用延时把逻辑跑通。5. 新手最容易翻车的几个地方:避坑与排查5.1 屏幕亮但没字,或者只有黑方块现象:背光亮了,但看不到任何字符,或者第一行全是黑色方块。原因:V0 对比度电压不对,或者初始化命令没发成功。解决:先调电位器,从一端慢慢拧到另一端,看字是否出现。如果怎么调都不行,检查 RS、E、数据线是否接错,尤其是数据线顺序有没有反。用万用表测一下单片机 IO 在写数据时有没有变化。5.2 显示乱码或只显示第一个字符现象:屏幕上出现奇怪的符号,或者只显示“H”后面就没了。原因:时序太快,LCD1602 还没处理完上一个字节,你就发了下一个。解决:在lcd_write_cmd和lcd_write_data里把延时加大,比如delay(500)。如果用的是 4 位模式,检查是不是先发高四位再发低四位,顺序不能错。5.3 DS18B20 读出来永远是 85 或 0现象:温度值不变,一直是 85 或者 0。原因:85 是 DS18B20 上电默认值,说明转换命令没发出去,或者等待时间不够。0 通常是读时序不对,读回来的全是 0。解决:检查启动转换后有没有等够 750 毫秒。用示波器看数据线有没有正确的复位脉冲和读写脉冲。如果没有示波器,就把读到的原始字节显示在 LCD 上,看看到底读回了什么。5.4 DS1302 时间不走或跳变现象:秒数不动,或者分钟突然跳好几分钟。原因:晶振没起振,或者 BCD 转换写错了。解决:DS1302 需要 32.768kHz 晶振,检查晶振是否焊好,负载电容是否匹配。读出来的时间先不做任何转换,直接显示十六进制,看看是不是 BCD 码。如果是,就加转换函数。5.5 按键按一次跳好几下现象:按一下键,模式切换了好几次。原因:按键抖动没消干净,或者松手检测没做。解决:把消抖延时加长到 20 毫秒,并且在确认按下后等待松手再返回。如果主循环里还有其他耗时操作,考虑用定时器中断扫描按键,避免死等。6. 下一步学什么:从能显示到能控制你现在已经能让屏幕显示固定字、显示变量、显示传感器数据,也能用按键切换页面。这个例程跑通之后,下一步不是去学更复杂的屏幕,而是把现有的东西用得更稳。第一,把延时换成定时器。你现在用的delay是空循环,它会阻塞程序,导致按键反应迟钝、时间显示不准。用定时器 0 或定时器 1 产生 1 毫秒中断,在中断里做按键扫描和时间刷新,主循环只负责显示。这是从“能跑”到“好用”的关键一步。第二,把显示内容做成菜单。现在只有两个模式,你可以扩展成三级菜单:主菜单显示时间,按一下进入温度,再按一下进入设置,设置里可以调时间。菜单结构用数组和状态机实现,不要用一堆 if-else 堆在一起。第三,把数据存起来。DS1302 有电池,掉电时间不丢。但你的温度数据掉电就没了。可以学 24C02 这类 EEPROM,把最高最低温度记录下来,上电后显示。这样你的小系统就有了“记忆”。第四,把通信协议整理成独立的文件。现在所有代码可能都在一个 .c 文件里,你可以把 LCD1602、DS18B20、DS1302 的驱动分别写成 .c 和 .h,主文件只调用接口。这是工程化的第一步,也是后面做更大项目的基础。我自己的习惯是,每学一个新外设,就把它单独写成一个最小工程,只做一件事:让它工作,并显示在 LCD 上。等所有外设都能单独跑了,再拼在一起。这样出问题的时候,你能快速定位是哪个模块的事。从那以后我每次调新模块,都强制先写一个只显示原始数据的测试程序,确认通信没问题,再去做业务逻辑。希望这个路径能帮到你,别急着一次写完,先让屏幕亮出第一个字。本文还有配套的精品资源,点击获取