DS1302 时钟实验:数码管显示改 LCD1602 显示
实验平台 :普中(PRECHIN)51 开发板 · STC89C52RC
开发环境 :Keil5 C51(c51v954a)+ STC-ISP
原始工程 :
21-DS1302(数码管显示)时钟实验完成日期:2026-08-31
目录
- [1. 实验目标](#1. 实验目标)
- [2. 硬件基础](#2. 硬件基础)
- [2.1 DS1302 实时时钟](#2.1 DS1302 实时时钟)
- [2.2 LCD1602 液晶](#2.2 LCD1602 液晶)
- [2.3 接线与引脚分配](#2.3 接线与引脚分配)
- [3. 改造思路](#3. 改造思路)
- [4. 软件实现](#4. 软件实现)
- [4.1 新增 LCD1602 驱动](#4.1 新增 LCD1602 驱动)
- [4.2 重写主程序](#4.2 重写主程序)
- [4.3 修改 Keil 工程](#4.3 修改 Keil 工程)
- [5. 关键知识点](#5. 关键知识点)
- [5.1 LCD1602 写操作时序](#5.1 LCD1602 写操作时序)
- [5.2 BCD 码转 ASCII](#5.2 BCD 码转 ASCII)
- [5.3 DDRAM 地址与光标定位](#5.3 DDRAM 地址与光标定位)
- [6. 编译与烧录](#6. 编译与烧录)
- [7. 常见问题排查](#7. 常见问题排查)
- [8. 可扩展方向](#8. 可扩展方向)
1. 实验目标
原实验通过动态数码管 显示 DS1302 的时分秒(格式 XX-XX-XX)。本实验在保留 DS1302 驱动逻辑不变的前提下,将显示部分替换为 LCD1602 字符液晶:
- 第一行显示标题
DS1302 Clock - 第二行显示时间
Time: HH:MM:SS

2. 硬件基础
2.1 DS1302 实时时钟
DS1302 是三线制(RST/CLK/IO)串行实时时钟芯片,本工程沿用原有驱动:
| 信号 | 单片机引脚 |
|---|---|
| RST(复位) | P3.5 |
| CLK(时钟) | P3.6 |
| IO(数据) | P3.4 |
时间数据以 BCD 码 存储,gDS1302_TIME[7] 数组顺序为:秒、分、时、日、月、星期、年。
2.2 LCD1602 液晶
- 16 字符 × 2 行字符型液晶,5×7 点阵
- 8 位并行数据接口 + 3 根控制线(RS、RW、E)
- 内置字符发生器(CGROM),写入 ASCII 码即可显示对应字符
2.3 接线与引脚分配
普中开发板 LCD1602 插座引脚分配:
| LCD1602 信号 | 单片机引脚 | 说明 |
|---|---|---|
| D0--D7 | P0.0--P0.7 | 8 位数据总线(与数码管共用 P0) |
| RS | P2.6 | 0=命令,1=数据 |
| RW | P2.5 | 0=写,1=读 |
| E | P2.7 | 使能,上升沿锁存 |
注意:LCD1602 与数码管段选共用 P0 口,插上 LCD 后数码管不显示属正常现象。对比度由板上电位器调节。
3. 改造思路
整体采用模块替换策略,DS1302 部分零改动:
原工程:main.c → smg.c(数码管动态扫描) ┐
├→ ds1302.c(不变)→ public.c(不变)
新工程:main.c → lcd1602.c(字符液晶) ┘
改动三处:
- 新增
App/lcd1602/lcd1602.c与lcd1602.h------ LCD1602 驱动 - 重写
User/main.c------ 显示逻辑从"查段码表"改为"BCD 转 ASCII" - 修改
template.uvproj------smg.c移出编译、加入lcd1602.c、包含路径增加.\App\lcd1602
4. 软件实现
4.1 新增 LCD1602 驱动
头文件 lcd1602.h ------ 引脚与接口定义:
c
#define LCD1602_DATAPORT P0 // 数据总线
sbit LCD1602_RS = P2^6; // 0=命令 1=数据
sbit LCD1602_RW = P2^5; // 0=写 1=读
sbit LCD1602_EN = P2^7; // 使能
void lcd1602_write_cmd(u8 cmd); // 写命令
void lcd1602_write_data(u8 dat); // 写数据
void lcd1602_init(void); // 初始化
void lcd1602_show_string(u8 x,u8 y,u8 *str); // 定位显示字符串
void lcd1602_show_num(u8 x,u8 y,u8 num,u8 len); // 显示数值(高位补0)
void lcd1602_refresh_time(void); // 刷新时间显示
写命令函数(写数据函数仅 RS 电平不同):
c
void lcd1602_write_cmd(u8 cmd)
{
LCD1602_RS = 0; // 选择写命令
LCD1602_RW = 0; // 选择写
LCD1602_EN = 0; // 使能拉低,准备产生上升沿
LCD1602_DATAPORT = cmd; // 命令字送上数据总线
delay_ms(1);
LCD1602_EN = 1; // 上升沿,LCD 锁存命令
delay_ms(1);
LCD1602_EN = 0;
}
初始化函数:
c
void lcd1602_init(void)
{
lcd1602_write_cmd(0x38); // 8位总线、两行显示、5×7点阵
lcd1602_write_cmd(0x0c); // 开显示、关光标
lcd1602_write_cmd(0x06); // 写入后地址指针自动+1
lcd1602_write_cmd(0x01); // 清屏
delay_ms(2);
}
字符串定位显示 (第一行基准地址 0x80,第二行 0xC0):
c
void lcd1602_show_string(u8 x, u8 y, u8 *str)
{
u8 i = 0;
if (y == 0)
lcd1602_write_cmd(0x80 | x); // 第一行
else
lcd1602_write_cmd(0xC0 | x); // 第二行
while (str[i] != '\0')
lcd1602_write_data(str[i++]);
}
时间刷新函数 ------ 核心:BCD → ASCII:
c
void lcd1602_refresh_time(void)
{
u8 buf[9];
buf[0] = '0' + (gDS1302_TIME[2] >> 4); // 时十位(BCD高4位)
buf[1] = '0' + (gDS1302_TIME[2] & 0x0f); // 时个位(BCD低4位)
buf[2] = ':';
buf[3] = '0' + (gDS1302_TIME[1] >> 4); // 分十位
buf[4] = '0' + (gDS1302_TIME[1] & 0x0f); // 分个位
buf[5] = ':';
buf[6] = '0' + (gDS1302_TIME[0] >> 4); // 秒十位
buf[7] = '0' + (gDS1302_TIME[0] & 0x0f); // 秒个位
buf[8] = '\0';
lcd1602_show_string(5, 1, buf); // 第二行第5列起显示
}
4.2 重写主程序
c
#include "public.h"
#include "lcd1602.h"
#include "ds1302.h"
void main()
{
lcd1602_init(); // 初始化LCD1602
ds1302_init(); // 初始化DS1302
lcd1602_show_string(0, 0, " DS1302 Clock "); // 第一行标题
lcd1602_show_string(0, 1, "Time: "); // 第二行前缀
while (1)
{
ds1302_read_time(); // 读取DS1302时间
lcd1602_refresh_time(); // 刷新"HH:MM:SS"
}
}
与原数码管版本对比:动态扫描需要每约 1ms 刷新一次才能维持视觉暂留,而 LCD1602 内部自带显存(DDRAM),只需在数据变化时重写,主循环反而更简单。
4.3 修改 Keil 工程
对 template.uvproj 的三处修改:
| 项目 | 修改内容 |
|---|---|
| 编译文件 | 删除 .\App\smg\smg.c,新增 .\App\lcd1602\lcd1602.c |
| 包含路径 | IncludePath 加入 .\App\lcd1602 |
| 磁盘文件 | App\smg\ 文件夹保留不删,仅移出编译 |
命令行编译验证:
bash
D:\Keil_v5\UV4\UV4.exe -b template.uvproj -j0 -o build.log
结果:0 Error / 2 Warning ,生成 Obj\template.hex(code=769 字节)。两条 L16: UNCALLED SEGMENT 警告来自未被调用的 lcd1602_show_num 与 delay_10us,属无害警告。
5. 关键知识点
5.1 LCD1602 写操作时序
RS ──┐设置(0=命令/1=数据)
RW ──┤拉低(写)
数据──┤送上 P0 总线
E ──┘先低 → 拉高(上升沿锁存) → 拉低
E 引脚的上升沿 是 LCD 采样数据的时刻,两次 delay_ms(1) 保证建立/保持时间,对 11.0592/12MHz 晶振均足够。
5.2 BCD 码转 ASCII
DS1302 输出 BCD 码(如 0x59 表示 59),LCD1602 需要 ASCII 显示。利用 ASCII 字符 '0'~'9' 连续的特性:
十位 = '0' + (bcd >> 4) // 高4位右移到低4位
个位 = '0' + (bcd & 0x0f) // 屏蔽高4位
对比原数码管方案:数码管用 BCD 查段码表 gsmg_code[],LCD 直接用字符加法,无需查表。
5.3 DDRAM 地址与光标定位
| 显示位置 | DDRAM 地址 | 写命令 |
|---|---|---|
| 第一行第 x 列(x=0~15) | 0x00 + x | `0x80 |
| 第二行第 x 列 | 0x40 + x | `0xC0 |
两行地址不连续(相差 0x40),所以 show_string 需按行选择基准地址。每写一个字符,指针因 0x06 命令自动 +1,实现连续输出。
6. 编译与烧录
- 打开 Keil 工程
template.uvproj,Project → Rebuild all target files - 确认
Obj\template.hex生成 - STC-ISP:芯片选 STC89C52RC,选串口/USB,加载 HEX,冷启动下载
- 下载完成后 LCD 第一行显示标题,第二行从
Time: 13:51:47(初始值)开始走时
7. 常见问题排查
| 现象 | 原因 | 解决 |
|---|---|---|
| 屏幕只亮无字符 | 对比度不当 | 调板上 LCD 旁电位器 |
| 整屏无反应 | E/RS/RW 接线错或 LCD 未插紧 | 核对 P2.7/P2.6/P2.5 |
| 只显示第一行 | 初始化命令时序过快 | 确认 delay_ms 足够、0x38 已写入 |
| 时间不走 | DS1302 接线/电池问题 | 核对 P3.4~P3.6,检查晶振 32.768kHz |
| 数码管不亮 | 与 LCD 共用 P0,正常现象 | 无需处理 |
8. 可扩展方向
- 显示日期 :第一行右侧用
lcd1602_show_num显示YY-MM-DD与星期 - 按键调时 :结合独立按键(K1~K4)实现时/分/加/减,写入
gDS1302_TIME后调用ds1302_init重新写入芯片 - 自定义字符:利用 CGRAM(0x40 命令)显示℃、爱心等 5×8 自绘符号
- 秒闪烁冒号 :每秒交替显示
:与空格,增强时钟观感