资讯动态

LCD1602+C51显示例程:整合DS18B20温度与DS1302时钟的完整驱动拆解

发布时间:2026/10/17 11:10:26 来源:尧图企业网站定制
简介这是一套面向C51单片机初学者的LCD1602显示例程合集围绕字符液晶与常用外围模块的联动覆盖矩阵按键键值、DS18B20温度、DS1302时钟、ADC0832电压转换四种典型场景。资源共86个文件核心包含Keil工程C源码/uvproj/hex、Proteus仿真电路DSN以及四段演示mp4压缩包仅4.13MB按矩阵按键、DS18B20、DS1302、ADC0832分目录存放便于对照学习。已有1074人浏览学习。代码中涉及单总线通信、串行时序、按键扫描与模数转换等知识点视频录屏配合实际工程能帮助新手理解从硬件连接到数据显示的完整流程也可作为课程设计或电子设计竞赛的参考模板。1. 这个例程包解决的不只是“点亮LCD”它把显示、按键、温度、时钟串成了一条完整链路半夜调一块 LCD1602 出不来字应该是很多做 C51 课设的人共同的记忆。插上开发板、烧进点灯程序、满怀期待地把 16 针排线怼上去结果屏幕要么全黑要么只有第一行冒出一排方块。多数人卡在这里就放弃了但真正的问题往往不是液晶坏了而是初始化时序、数据口电平、E 脚的下降沿锁存这些细节没对齐。这份 LCD1602C51 单片机显示例程我拆完之后的感觉是它没有停留在“让屏幕亮起来”这一步。例程里同时带了按键切换、DS18B20 温度采集和 DS1302 时钟芯片读写四个模块共用一套 51 的 GPIO 口最后在 LCD 上动态显示时间和温度。对正在做课程设计、或者想在 51 开发板上体验“一板多用”的开发者来说这份例程是把裸机外设驱动整合进一个状态机的完整参照——不是跑马灯 demo而是一台能看夜晚温度的桌面信息屏。2. 硬件接线与工程结构四个模块怎么在 C51 上共存拿到例程的第一步不是打开源码而是先看它的文件和引脚规划。跨模块项目最怕的其实是引脚冲突LCD 已经占了 11 个引脚DS18B20 还要一根单总线DS1302 又是三根线加上三个按键一个 40 脚的 AT89C52 都显得紧张。这份例程能把它们理顺关键在“复用”与“错开”。2.1 例程包文件结构与代码组织解压后的工程目录不算复杂但划分得很清楚典型的模块化写法lcd1602_demo/ ├── main.c // 主循环、状态机、按键调度 ├── delay.c / delay.h // 软件延时us 和 ms 两级 ├── lcd1602.c / lcd1602.h // LCD 驱动命令与数据函数 ├── ds18b20.c / ds18b20.h // 单总线时序温度读取 ├── ds1302.c / ds1302.h // 三线时序时间读写 └── README.txt // 接线说明与引脚定义每个外设一个 .c/.h 文件对头文件里只暴露接口函数不暴露全局变量这是 51 裸机工程里比较容易忽视的好习惯。main.c 里不堆驱动代码只做初始化、按键扫描和显示调度这样后续把显示逻辑换成 OLED 或者加一个按键菜单不需要动 DS18B20 那部分。delay.c是这段例程的地基。LCD 和单总线时序对延时的依赖非常大常见的写法是用_nop_()做微秒级延时用循环嵌套做毫秒级延时。注意这里没有用定时器中断做延时——对这三块外设来说阻塞式延时反而更可控中断会打断时序的连续性这是 51 外设驱动里一个很核心的原则。2.2 LCD1602 并口接线P0、P2 的选择与上拉电阻看 README 里的引脚定义数据口用的是 P0控制口用 P2 的低三位。这个选择有讲究P0 口内部没有上拉电阻是开漏结构接 LCD 数据线时必须在外部加上 4.7kΩ-10kΩ 的上拉排阻否则高电平会飘LCD 读出花字。P1、P2、P3 内部自带弱上拉如果想把数据口改到 P1 或 P2可以省掉排阻但改到 P0 是大多数人开发板默认的布局。// 引脚宏定义集中在一个文件里方便改线 #define LCD_DATA P0 // 数据口 D0-D7 接 P0.0-P0.7 sbit LCD_RS P2^0; // RS: 0命令, 1数据 sbit LCD_RW P2^1; // RW: 一般直接接地或拉低 sbit LCD_E P2^2; // E: 使能脚下降沿锁存数据RS、RW、E 三条控制线的分工要记牢RS 决定当前传的是命令还是要显示的字符数据RW 决定读还是写E 的下降沿让 LCD 内部把总线上的数据锁存进去。RW 在只写不读的场景下可以直接接地省一个引脚。但这份例程里保留了 RW 接 P2.1 的写法是为了兼容“读忙标志”的扩展——虽然实际代码里用的是固定延时等待没有去读 BF 位但这种留余地的做法值得学。2.3 DS18B20、DS1302 与按键的引脚分配单总线、时钟三线、按键这三组外设例程把它们分别放到了 P3 的不同引脚。DS18B20 的 DQ 接在 P3.4DS1302 的 CE、SCLK、IO 接在 P3.5-P3.7按键接在 P3.0-P3.2。这一排引脚最大的特点是它们跟串口下载引脚 P3.0/P3.1 错开了调试时不会互相干扰。模块引脚说明DS18B20DQ → P3.4单总线需要 4.7kΩ 上拉DS1302CE → P3.5 / SCLK → P3.6 / IO → P3.7三线 SPI 变体按键S1 → P3.0 / S2 → P3.1 / S3 → P3.2接 GND按下为低电平DS18B20 的 DQ 脚必须加 4.7kΩ-10kΩ 上拉到 VCC。这是很多第一次用这款传感器的人最容易翻车的点单总线在空闲时靠上拉电阻维持高电平器件应答时靠拉低总线产生低电平窗口。没有上拉复位脉冲之后总线浮空读回来的存在应答就变成随机值。2.4 电源与电平匹配5V 与 3.3V 模块混接要小心51 开发板供电多是 5V而不少 LCD1602 模块和 DS1302 模块自带 3.3V 稳压或逻辑电平转换。混接时最典型的坑是DS1302 的数据脚 IO 被 5V 电压拉高后如果模块是 3.3V 电平逻辑可能永久性损伤。可靠的做法是给 DS1302 的 CE、SCLK、IO 三条线都串一个 1kΩ 限流电阻或者直接选择带电平转换的模块例程 README 里也专门提示了这一点。上电前的检查顺序我一般固定走一遍先量 VCC 和 GND 是否短路再查每个模块的背光跳线最后用万用表确认 DQ 引脚上的高电平确实是接近 VCC 的而不是一个悬空导致的 2V 漂浮电平。这一步能省掉后面整整一个晚上的排查时间。3. 核心模块拆解从按键按下的 0.5 秒到屏幕刷出的那一行字这章是整份例程最值钱的部分。我之前拆过不少 51 显示例程LCD 驱动基本都大同小异真正的分水岭在于单总线的读时序卡得准不准、DS1302 的写保护有没有关干净、以及按键切换和刷屏之间会不会互相拖累。3.1 LCD1602 写命令与写数据HD44780 时序的实现先看最底层的写函数。HD44780 控制器的时序要求是RS 和 RW 电平先稳定下来然后 E 拉高数据在总线上稳定一段时间后E 降下去这个下降沿就是数据被锁存的时刻。// 写命令向 LCD 写入控制命令字 void LCD_WriteCmd(unsigned char cmd) { LCD_RS 0; // RS0 代表命令模式 LCD_RW 0; // 写模式 LCD_DATA cmd; // 命令字放到数据口 LCD_E 1; // 使能拉高 delay_us(5); // 等待数据稳定 LCD_E 0; // 下降沿锁存命令 delay_us(20); // 命令执行时间 } // 写数据向 LCD 写入要显示的字符 ASCII void LCD_WriteData(unsigned char dat) { LCD_RS 1; // RS1 代表数据模式 LCD_RW 0; LCD_DATA dat; LCD_E 1; delay_us(5); LCD_E 0; delay_us(20); }这两个函数的区别只在 RS 那一位的设置上。HD44780 规定 RS0 时写入被当作命令解析RS1 时数据直接进显存 DPRAM所以 RS 的电平必须在 E 下降沿之前稳定下来。代码里先置 RS、再放数据、再拉 E这个顺序就是时序规范里说的“建立时间”。delay_us(5)和delay_us(20)两个参数实际上闲庭信步地满足了 HD44780 的需求E 高电平最少要维持 450ns命令执行最长要 1.64ms这里的 20us 只覆盖短命令长命令如清屏、光标归位实际需要 1.64ms所以初始化序列里对这两条命令单独做了 5ms 延时。这是例程里一个容易忽略但非常关键的细节——如果你把它们统一缩短到 500us屏幕就会表现为经常闪一下然后丢字。初始化序列也是同样的套路依次发送 0x388 位模式、两行、5x7 点阵、0x0C开显示、关光标、0x01清屏每条命令之间做足延时// LCD 初始化上电后必须等待内部自检完成 void LCD_Init(void) { delay_ms(15); // 上电等待 LCD_WriteCmd(0x38); // 8位模式、两行显示、5x7字体 delay_ms(5); LCD_WriteCmd(0x38); LCD_WriteCmd(0x0C); // 开显示 LCD_WriteCmd(0x01); // 清屏 delay_ms(5); }重复发送两次 0x38 是老工程师留下的习惯。因为 51 上电后电源爬升速度一旦偏慢LCD 内部状态机会进到一个未知初值第一次命令可能被吞掉。有了这条“冗余训练”容错性明显好了实测在电压波动大的同学开发板上屏幕出字的概率高了很多。3.2 DS18B20 单总线时序复位、存在应答与温度拼接DS18B20 是整个例程里最敏感的模块它的时序是以微秒为单位卡的。看它的复位函数核心是“拉低 480us → 释放 → 采样存在应答”这组动作sbit DQ P3^4; // 数据线定义 // 复位 DS18B20返回 0 表示器件存在1 表示未检测到 unsigned char DS18B20_Reset(void) { unsigned char presence; DQ 0; // 主机拉低总线 delay_us(500); // 复位脉冲要求 480us-960us DQ 1; // 释放总线进入接收状态 delay_us(60); // 等待器件应答窗口 presence DQ; // 器件会拉低总线 60-240us 表示“我在” delay_us(400); // 等待应答结束 return presence; }这段逻辑里最容易出问题的就是delay_us(500)这个时长。标准规定复位脉冲在 480us-960us 之间太短器件不认太长会把器件误判成读时序。另外释放总线后 60us 才开始采样这个窗口是器件应答的最前端错过了就永远读不回存在应答。我从例程里学到的技巧是把delay_us(60)放到presence DQ的前面而不是先采样再延时顺序反了读到的永远是高电平。读一个字节的时序则是另一种套路每个 bit 都要占 60us 到 120us// 读取一个字节LSB 在前 unsigned char DS18B20_ReadByte(void) { unsigned char i, dat 0; for (i 0; i 8; i) { dat 1; DQ 0; // 起始信号 delay_us(2); // 主机拉低 2us 左右 DQ 1; // 释放总线 delay_us(8); // 在 15us 内采样 if (DQ) dat | 0x80; // 高电平读 1低电平读 0 delay_us(60); // 完成一个时隙 } return dat; }读时序的关键窗口是主机拉低总线后从 15us 开始采样到 45us 之前必须读完。代码里用delay_us(2)拉低然后只等delay_us(8)就采样总共 10us落在安全区间内。如果把采样前的延时加长到 30us实际上就读到了器件释放总线后的高电平结果全变成 0xFF。这个坑我在自己项目里踩过一次最后是靠示波器抓波形才看出是采样点太靠后了。温度读取的完整流程是跳过 ROM → 启动温度转换 → 等待 750ms → 跳过 ROM → 读两个字节。例程里没有死等 750ms而是采用定时查询的方式在DS18B20_ReadTemp里只发启动命令转换结果在下一秒轮询时读回来这样主循环不用傻等LCD 刷新不会被卡住。温度拼接的逻辑才是这章最“值钱”的 5 行代码// 拼接温度值高字节左移8位后与低字节合并 temp_raw (ds18b20_read_byte() 8) | ds18b20_read_byte(); if (temp_raw 0x8000) // 负温度判断 celsius ((temp_raw 0x7FFF) 4) * -1; else celsius (temp_raw 4);DS18B20 的 12 位分辨率表明低 4 位是小数部分整数部分是从第 4 位开始的 8 位。正温度直接右移 4 位就是整数负温度存的是补码不能直接右移要先去掉符号位再取负否则你会读到一串 120 多度的诡异数字。这里的 4是真正关键的一步。3.3 DS1302 三线接口寄存器映射与写保护开关DS1302 的时序比 DS18B20 温和不少它的三线接口是靠时钟线 SCLK 的上升沿和下降沿来区分读写方向的。每次写寄存器的第一步是先往命令寄存器写一个字节告诉芯片“我要写哪个地址、接下来数据往哪放”。// 向 DS1302 写一个字节从最低位开始 void DS1302_WriteByte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DS1302_IO dat 0x01; // 最低位先出 DS1302_SCLK 0; DS1302_SCLK 1; // 上升沿写数据 dat 1; } }DS1302 的命令字格式是0x80 | 寄存器地址 | 读写位写地址时最低位要为 0读时为 1。整个例程里最容易让我这种新手翻车的是写保护寄存器DS1302 默认写保护是开启的直接写时间寄存器根本没反应必须先向 0x8E 这个地址写入 0x00 关掉写保护改完时间之后再把 0x80 写回去打开写保护防止意外改写。时间数据在 DS1302 内部以 BCD 码存放这是个隐蔽的坑。比如 7 点整存的是 0x07到 7 点过 10 分存的是 0x10。如果你直接按十进制格式去写 10芯片里存的是 0x0A读取回来会被解析成“零十分”时钟就彻底乱了。例程里专门写了十进制和 BCD 的互相转换// 十进制转 BCD秒、分、时都按两位 BCD 处理 unsigned char DecToBcd(unsigned char val) { return ((val / 10) 4) | (val % 10); } // BCD 转十进制给显示层直接用 unsigned char BcdToDec(unsigned char val) { return (val 4) * 10 (val 0x0F); }这两个函数写出来以后DS1302 驱动的基本功就完成了。读时钟时只需要按秒、分、时、日、月、周的地址顺序把寄存器各摸一遍经过 BcdToDec 转换后塞进显示缓冲区。它的寄存器地址表不需要背驱动文件头部的注释里都列了每次用直接查。3.4 主状态机按键扫描、模式切换与显示刷新策略前面三个模块都是底层驱动真正让例程像“产品”而不是“测试程序”的是 main.c 里的状态机。三个按键 S1、S2、S3 分别负责“切换显示页”“加一调整”“减一调整”按下时的扫描必须消抖否则一次按键会被判定成十几次。// 按键扫描10ms 消抖返回有效按键号 unsigned char KeyScan(void) { if (Key1 0) { delay_ms(10); // 消抖 if (Key1 0) { while (Key1 0); // 等待松手 return 1; } } return 0; }消抖 10ms 这个数值不是拍脑袋定的。机械按键的抖动期通常持续 5ms 到 15ms10ms 消抖能把绝大多数抖动滤掉同时不会让快速连按失效。while (Key1 0)等待松手这一步很关键它保证一次物理按下只产生一个有效事件——如果省掉你会看到按一下键屏幕连跳三页的“飞页”现象。主循环的显示调度策略是这份例程最成熟的地方它没有粗暴地在每次按键后全屏刷新while (1) { key KeyScan(); if (key 1) { page; if (page 3) page 0; LCD_WriteCmd(0x01); // 切换页时清屏 } if (page 0) // 温度页 { ReadAndShowTemp(); } else if (page 1) // 时间页 { ReadAndShowTime(); } delay_ms(200); }每 200ms 跑一次主循环DS18B20 的温度转换需要 750ms所以温度页实际上每 200ms 查询一次最多经过四次轮询才读到一个新值但屏幕上的温度变化已经足够流畅。时钟页则每秒从 DS1302 读一次避免频繁占用 I/O 口。最关键的是只有切换页面时才调用清屏指令平时刷新只使用LCD_WriteData覆盖变化字符清屏指令非常耗时动不动就是 1.64ms高频调用会肉眼可见地看到屏幕闪烁。4. 编译烧录与避坑指南把例程从 Keil 工程变成实物上的显示代码读得再细不烧进板子里都是纸上谈兵。这一章我把从新建工程、编译出 HEX 文件到实物点亮的完整过程走一遍再列出五条拆机过程中实测踩过的坑。4.1 Keil 工程组织文件添加、目标芯片设置与 HEX 生成拿到例程要在 Keil C51 里跑起来第一步是新建工程选择目标芯片型号。不同开发板的 51 内核芯片配置略有差别但基本都是 8051 内核编译链接结果通用性很强这里不需要过于纠结具体型号关键是把优化等级设置好。工程配置里两个地方必须检查一是 Output 选项卡里勾选 “Create HEX File”否则编译成功后只在目录里生成.obj文件烧录器认不了二是 C51 选项卡里的优化选择建议选 Level 8 并且关掉 “Variables in Overlay” 选项否则单总线函数里的局部变量可能被优化到寄存器里运行到一半被中断打飞。添加文件到工程时典型的错误是只添加 main.c 而漏掉 delay.c、lcd1602.c 等其它文件。Keil 不会自动把同目录的 .c 文件拉进工程每个源文件的编译都是独立任务。正确做法是在工程面板里挨个添加再把头文件所在的目录路径填进 C/C 选项卡的 Include Paths 里。编译后看到的 0 Error(s) 并不是终点真正要等到 HEX 文件出现在输出目录里才算成功。4.2 烧录与上电的排错流程烧录的第一步是确认下载器驱动装好了设备管理器里能看到串口。接下来选择 HEX 文件、设置波特率、冷启动下载——这对于那些需要冷启动的下载电路来说是唯一可靠的烧录方式先点软件里的“下载”按钮再把开发板断电重新上电。烧录完成后上电最稳的验证顺序不是直接看 LCD而是先打一个 LED 闪烁的最小程序确认最小系统正常。然后逐步挂载外设先只接 LCD 和电位器调出两行蓝色方块再接 DS18B20最后接 DS1302。一次只接一个模块哪一步挂了立刻就能定位到是接线还是代码问题这条顺序能让你绕开整个“全部接上后集体沉默”的排查泥潭。4.3 避坑记录五条拆机过程实测踩过的坑现象 1LCD 第一行全是一排方块第二行黑块原因初始化时序里的命令被吞了大概率是延时太短或者对比度电位器根本没有调到位。 解决拆开例程的 LCD_Init核对每条命令后面的延时是否充足特别是清屏后的 5ms同时用螺丝刀微调背光板背后的电位器调到第一行出现清晰的两个小光标块。现象 2DS18B20 读出来永远是 85℃原因85℃ 是 DS18B20 上电复位后的默认温度寄存器值意味着从未成功触发一次温度转换。 解决重点检查复位脉冲时长是否落在 480us-960us读取字节时每位的采样窗口是否过早或过晚。最常见到的情况是上拉电阻没有焊DQ 引脚悬空例程的延时参数本身没有问题大概率被连接线背了锅。现象 3按键按下去屏一口气跳两三个页面原因没有等待松手或者没有消抖导致一次按下触发多次有效扫描。 解决确认消抖延时至少 10ms并且按键释放后才返回有效键值。我在这份例程的 main.c 里特意找到while (Key1 0);这一行在它前面注释里标了“等待松手”这行一个字都不能删。现象 4DS1302 时间写上去了但断电重新上电后倒退回初始值原因DS1302 的备用电池没有接掉电后时钟芯片连振荡器都停转寄存器内容全部复位。 解决查看 5V 主电源之外是否给 DS1302 的 VCC2 引脚接了 3V 纽扣电池接好备用电池后代码里还要检查写保护寄存器是否做了软件关断开合。现象 5烧录时软件永远提示“连接失败请给 MCU 上电”原因开发板的下载引脚 P3.0/P3.1 被外部器件占用比如接在旁边的 DS18B20 或按键电路拉低了串口电平。 解决把占用 P3.0/P3.1 的跳线帽拔掉再烧录。如果你发现了例程里把 DS18B20 放在 P3.4、按键放在 P3.0-P3.2 但跳过 P3.1也正是它留了一手给下载电路。5. 二次开发指引把它改造成一台能显示三路数据的环境信息屏把这份例程吃透之后最值得做的事不是去背诵驱动函数而是自己动手改造成套逻辑。我的习惯是拿到任何一块没玩过的屏幕或传感器先跑通裸例程再往里加自己的业务逻辑。这次也一样我只打算把它的架构往两个方向延伸扩展传感数据、优化显示调度。5.1 第一步加一个 DHT11 湿度传感器DHT11 也是单总线协议但时序跟 DS18B20 完全不同可以完全不占用新的引脚直接复用 P3.4 旁边的 P3.3。在驱动文件里仿照 ds18b20.c 写一套时钟启动脉冲、读 40 个 bit 的函数然后把湿度值并存进显示缓冲区。注意 DHT11 每个 bit 的时隙是 26us-28us 宽主频 12MHz 下用 nop 延时精度刚好所有人都可以照抄。// 读取 DHT11 湿度值并拼进显示缓冲区 void ReadAndShowHumidity(void) { unsigned char humi DHT11_ReadByte(); LCD_WriteData(H); LCD_WriteData(:); LCD_WriteData(humi / 10 0); LCD_WriteData(humi % 10 0); LCD_WriteData(%); }写到这里我需要提醒一点LCD 写数据函数里每个数字字符都要先转换成 ASCII 码再加0而不是直接把整数往LCD_WriteData里塞。如果是负数或超过两位的数字还要额外处理符号位和取整逻辑——这是新手改显示代码时最容易出现的黑匣子问题。5.2 第二步把按键翻页改成自动轮播现在三个键控制四个页面是手动切换。改造成自动轮播的思路很清晰在主循环里加一个计数器page_count每次循环page_count当它累计到 5000 时自动切换page (page 1) % 4再清屏刷新按键只保留“手动翻页”优先级。关键是让自动轮播和按键触发走同一条切换逻辑不要写两份重复代码这样任何一个页面切换 bug 都能从一处修复。5.3 第三步验证改造是否成功的三个标准改造完了不能只看屏亮不亮我给自己定三个验证标准第一温度页从 25℃ 加热到 35℃ 的过程中数字只需在 1 秒内完成一次跳变同时不能出现 25 和 76 这种乱码第二拔掉 DS1302 的备用电池等待 10 秒后重插上电时间应正确回到初值而非随机值第三连续按 200 次翻页键页面不能卡死、不能漏帧。这三个标准过了这个信息屏才能算稳定可交付。做嵌入式开发这些年我也不是没在单总线和时钟芯片上翻过车自从第一次半夜排查 LCD 花屏到天亮之后每次拿到新的显示例程我都会强制自己走一遍“先量电平、再看时序、最后怀疑代码”的排查顺序。希望这篇拆解能帮你在点亮这块屏幕的时候少走几个我走过的弯路也少看几个凌晨两点的满天星。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑