尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

STC89C52+DS1302+LCD1602电子时钟设计:原理、接线与程序详解

STC89C52+DS1302+LCD1602电子时钟设计:原理、接线与程序详解 简介本资源是一套基于STC89C52单片机的实时时钟系统完整开发资料面向嵌入式初学者、电子类课程设计学生及单片机实训人员解决DS1302时钟芯片驱动、LCD1602动态显示与独立按键交互修改时间等典型实践难题。压缩包共64个文件含4个核心C源文件DS1302.c、lcd1602.c、common.c、ss.c、4个对应头文件、5个编译生成的OBJ目标文件、5个LST列表文件、1个可直接烧录的HEX固件以及原理图SchDoc文档和Keil工程文件uvproj/uvopt全面覆盖硬件设计、软件编程与调试验证环节。资源包大小为508KB结构清晰便于按模块学习与复现。已有1148人下载学习提供可运行的完整程序标准原理图关键注释代码助读者快速掌握实时时钟系统构建全流程并为扩展温度显示、闹钟功能等进阶开发奠定坚实基础。 做单片机课设或者毕设十个人里有八个会选做一个电子时钟。STC89C52、DS1302、LCD1602再加几个独立按键这条技术路线几乎成了单片机入门的“必修课”组合。我也跟着这套方案从零做过一个可以修改显示时间的电子日历现在把这套方案的原理、接线、程序和踩坑过程完整整理出来希望对正在做课设或者想入门单片机的朋友有帮助。这篇文章的重点不是“贴一堆代码让你抄”而是把 DS1302 时钟芯片怎么工作、LCD1602 怎么驱动、独立按键怎么设计、原理图应该注意哪些位置都讲清楚。看完之后你不仅能把程序跑起来还能知道出了问题该从哪里排查。1. 方案选型与整体设计思路1.1 这套组合解决什么问题先把这个项目要做的功能捋清楚。用 STC89C52 做主控DS1302 负责“真实世界的时间”LCD1602 负责把时间显示出来三个独立按键负责“修改时间”这就是一个非常典型的单片机综合练习项目。别小看这个组合它几乎覆盖了单片机入门阶段最重要的几块能力GPIO 读写、时序协议驱动、外设初始化、人机交互设计、状态机编程。用一套方案把这几样全部串起来比单独点个流水灯或者独立按键有学习价值得多。STC89C52 是 8051 内核单片机引脚兼容 AT89C52支持 ISP 串口下载不需要额外买编程器用一根 USB 转 TTL 线就能烧录这对学生党来说非常友好。DS1302 是串行实时时钟芯片功耗低走时靠外部 32.768kHz 晶振驱动断电后接一个 3V 纽扣电池可以继续计时。LCD1602 是字符型液晶显示模块16 列 2 行最多能显示 32 个字符显示时间日期完全够用。独立按键是最基础的人机交互输入设备一个按键、一个上拉或者下拉、几行消抖代码就能稳定触发。这个项目的核心价值在于DS1302 需要靠时序精确读写寄存器LCD1602 需要严格的初始化流程和命令/数据切换逻辑按键调整时间需要一套合理的状态切换逻辑。三样东西组合在一起难度刚好卡在“有点挑战但不至于劝退”的位置。1.2 为什么不用 DS3231 或者数码管很多人会问现在 DS3231 也不贵精度还高I2C 接口接线也简单为什么还要用 DS1302我的看法是课程设计和入门练手阶段选 DS1302 的目的不是为了“好用”而是为了“练底层能力”。DS3231 内置温补晶振精度确实很高但它用的是 I2C 接口在应用层上比 DS1302 的三线串行接口要“省事”很多。DS1302 的三线接口和标准 SPI 很像但又不完全一样你需要自己控制 CE、SCLK、IO 三根引脚的高低电平弄清楚数据是哪一位先发送、什么沿写入、什么沿读出这个过程对理解“时序”这个概念非常重要。以后不管做 SPI 还是 I2C 的外设核心逻辑都是一回事只是换了引脚名和协议规则而已。至于为什么不用数码管答案也很直接。数码管显示内容有限而且需要动态扫描不断刷新占 CPU 时间LCD1602 虽然初始化麻烦一点但显示内容灵活还能显示字母和符号信息密度高。做电子时钟这种需要同时显示日期、星期、时、分、秒的场景LCD1602 明显更合适。另外 LCD1602 的 HD44780 控制芯片非常经典以后接 OLED 或者 TFT 屏很多显示初始化的思想是相通的。2. 核心器件工作原理DS1302 时钟芯片和 LCD1602 显示屏2.1 DS1302 时钟芯片工作原理三线接口、寄存器与 BCD 码DS1302 内部结构可以简单理解成三部分时钟/日历寄存器、31 字节静态 RAM、一个涓流充电控制寄存器。我们平时用的主要是前面的时钟寄存器地址从 0x80 到 0x8C对应秒、分、时、日、月、星期、年这些时间字段。先讲三线接口。DS1302 有 CE有时标 RST、SCLK、IO 三根线。CE 拉高表示一次通信开始拉低表示结束。数据显示有一个关键点数据是在 SCLK 的上升沿被写入 DS1302在下降沿被 DS1302 输出而且都是低位在前。写一个字节的流程是这样的先把 IO 引脚设为输出按位把数据放到 IO 上每放一位就拉高 SCLK 再拉低产生一个上升沿DS1302 就把这一位收走。顺序从 bit0 开始一直发到 bit7。读一个字节的流程反过来SCLK 拉低之后DS1302 会把当前位送到 IO 上单片机读取 IO 引脚然后再给一个时钟脉冲推进到下一位。寄存器操作要先发一个命令字节。命令字节的格式有讲究最高位 bit7 固定是 1bit6 固定是 0bit5~bit1 是寄存器地址bit0 是读/写标志写为 0、读为 1。也就是说读秒寄存器要发 0x81写秒寄存器要发 0x80。写保护寄存器地址是 0x8Ebit7 是写保护位为 1 时禁止写操作为 0 时允许写。所以向 DS1302 写入时间前必须先向 0x8E 写入 0x00 关闭写保护写完时间后再向 0x8E 写入 0x80 开启写保护防止误修改。这里必须强调一个新手最容易搞错的东西DS1302 的时间字段不是普通十六进制或十进制存储而是用 BCD 码存储的。什么是 BCD 码就是每个十进制位用 4 位二进制表示。比如 59 秒BCD 码就是 0x59二进制是 0101 1001而不是十进制的 59 转成二进制的 0x3B。如果直接把 0x3B 写入秒寄存器那读回来的就是无效时间。所以代码里一般要写两个转换函数十进制转 BCD、BCD 转十进制。另外还有一个标志位要注意秒寄存器的 bit7 是时钟暂停标志 CH。如果这一位为 1DS1302 内部时钟会停止。很多人的板子做完之后时间不走往往不是晶振坏了而是秒寄存器 bit7 被写成了 1。设置时间时秒寄存器写入的数据 bit7 必须为 0时钟才会跑起来。2.2 LCD1602 显示原理8 线驱动、初始化流程与对比度配置LCD1602 内部用的是 HD44780 控制器它有三种存储器DDRAM 保存要显示的字符、CGRAM 可以自定义字符、CGROM 内置了 ASCII 字符和日文字符点阵。显示一个字符的基本流程是先发命令字设置 DDRAM 地址告诉液晶“这个字符显示在第几行第几列”再发数据字数据就是字符的 ASCII 码。比如要在第一行第一个位置显示字母 A就发送命令 0x80 设置地址然后发送数据 0x41。RS 引脚是命令/数据选择线RS 为低时发送的是命令RS 为高时发送的是数据。RW 是读写选择线对只写显示来说把 RW 直接接地即可这样可以少占一个 IO。EN 是使能信号一个下降沿会让 LCD 把当前引脚上的命令或数据锁存进去。所以每次写数据都要把 EN 拉高、延时一会儿、再拉低。LCD1602 上电后必须走一遍初始化流程这部分顺序错了显示屏就不会正常工作。标准初始化流程大致是延时等待内部复位完成、连续发送 3 次 0x30 设置 8 位模式、发送 0x20 或 0x28 设置功能、发送 0x0C 开启显示关闭光标、发送 0x01 清屏、发送 0x06 设置光标右移。如果后面发现屏幕只亮不显示或者显示乱码八成是初始化时序没走对。还有一个很容易被忽略的引脚是 V0也就是液晶对比度调节引脚。V0 必须接一个可调电压通常是一个 10k 电位器的中间抽头两端分别接 5V 和 GND。如果不接 V0 或者电压不对屏幕要么什么都看不到要么全是深色方块。这一步不是可选项很多入门者栽在这里。2.3 独立按键为什么要做消抖独立按键的原理很简单按键一端接单片机的 IO另一端接 GND单片机内部或外部上拉让引脚平时保持高电平按下后引脚被拉低检测到低电平就说明按键按下。但机械按键有一个物理特性按下和松开的瞬间金属触点不会一下子就完全接触或分离而是会在极短时间通常 5~10ms内反复抖动。如果代码不做处理单片机可能把一次按下识别成好几次触发导致调整时间时数字连续跳变。消抖有两种思路。硬件消抖是加 RC 滤波电路对入门项目来说会增加元件最常用的是软件消抖检测到引脚变低后延时 10~20ms再读一次引脚如果确实是低电平就确认按键按下直到检测到高电平再处理按键释放或触发一次动作。延时消抖虽然简单但要注意延时不能太长否则按键响应会变迟钝。3. 硬件电路搭建与原理图要点3.1 元器件清单做这个项目之前先把材料清单列清楚别焊到一半发现少了电位器或者晶振。元器件规格/型号数量用途说明单片机STC89C52RC1主控芯片晶振11.0592MHz1单片机外部晶振串口波特率常用电容30pF2单片机晶振负载电容电容10uF/电解1复位电路延时电阻10k1复位电路上拉电阻10k排阻1P0口上拉若用P0驱动LCD时需要时钟芯片DS13021实时时钟晶振32.768kHz1DS1302专用晶振电容6pF2DS1302晶振负载电容电池座CR20321DS1302备用电池显示屏LCD16021显示模块电位器10k1LCD1602对比度调节按键轻触式3~4时间调整交互面包板或洞洞板-1搭建电路这里多说一句STC89C52 的晶振用 11.0592MHz 是为了以后串口通信方便如果只是纯显示时钟不涉及通信用 12MHz 也可以。DS1302 的晶振必须是 32.768kHz因为这个频率是 2 的 15 次方内部经过 15 级分频就能精确得到 1Hz 的秒脉冲。3.2 核心接线分配与原理图解读原理图我没法直接用文字画出来但核心接线关系完全可以列成表。下面这张接线表是我用过的最稳的参考接法如果手里有现成的开发板引脚定义可能会不一样对应改一下就行。模块引脚接单片机引脚说明LCD1602VSSGND电源地LCD1602VDD5V正电源LCD1602V010k电位器中点对比度调节LCD1602RSP2.0命令/数据选择LCD1602RWGND只写模式接地LCD1602EP2.2使能信号LCD1602D0~D7P2.0~P2.7 中的对应位8位数据总线LCD1602A5V串100欧电阻背光正极LCD1602KGND背光负极DS1302VCC15V主电源DS1302VCC2电池正极备用电源DS1302GNDGND地DS1302CEP1.0使能DS1302SCLKP1.1串行时钟DS1302IOP1.2串行数据按键 K1~K3一端P3.2~P3.4按键输入按键 K1~K3另一端GND公共地原理图里最值得留意的就是 DS1302 的 VCC1 和 VCC2。VCC1 接主电源VCC2 接备用电池两个电源引脚内部有切换电路主电源掉电之后自动切换为电池供电。VCC2 建议接 3V 纽扣电池不要直接接 5V否则容易损坏芯片。如果模块上已经带电池座那就省事了如果是自己焊的芯片电池座必须焊进去否则时间断电就丢。还有一个容易被忽略的地方是 P0 口。如果是自己画原理图尽量避免把 LCD1602 的数据线接到 P0 口因为 STC89C52 的 P0 口是开漏输出内部没有上拉电阻驱动 LCD 的高电平会输出不了必须外接 10k 上拉排阻。很多开发板把液晶接口放在 P0 口所以板子上都带了排阻。自己搭电路的话建议数据线接到 P2 口省去上拉电阻的问题。3.3 最容易犯错的几个硬件位置第一个问题LCD1602 的 RW 如果不想接地而是接到单片机的某个 IO那这个 IO 必须正确控制否则读忙操作会出问题。我的建议是直接接地不用读忙标志每次写命令或写数据之后加一个延时即可简单可靠。第二个问题DS1302 的晶振非常容易受干扰。32.768kHz 晶振最好紧挨着芯片的 X1、X2 引脚走线尽量短不要从下面穿过其他信号线。如果晶振离得太远或者旁边有高频信号时钟容易出现走走停停或者误差很大的问题。第三个问题按键必须共地。独立按键一端接 IO另一端接 GND如果两端都接到单片机的两个 IO 上程序逻辑就会变得非常绕。同时要注意按键引脚不要和下载串口共用比如 P3.0、P3.1 是串口的 RXD 和 TXD下载程序时如果上面挂着按键或者别的外设可能会出现干扰所以按键建议选 P3.2、P3.3、P3.4 这种空闲口。第四个问题LCD 背光限流。A 引脚如果直接接 5V虽然一般不会立刻烧掉但长期工作会发热而且背光亮度太高也刺眼。串一个 100 欧左右的电阻限流更稳妥。4. 软件程序设计与核心代码实现4.1 工程结构规划写程序之前先想清楚工程结构不要一上来就在 main 函数里堆几百行代码。最简单的做法是分文件main.c 放主循环ds1302.c 放时钟芯片驱动lcd1602.c 放液晶驱动key.c 放按键扫描和消抖。每个模块配一个 .h 头文件把函数声明放进去。用 Keil uVision 建工程的时候把文件加进去编译一次确认没有语法问题再往板子上烧。这个思路看着麻烦但对后面的调试帮助很大。比如屏幕不亮你先怀疑 LCD 驱动单独拿一个测试程序初始化 LCD 并显示一行固定字符问题范围一下就缩小了。如果所有代码都混在一起出了问题就很难定位。4.2 DS1302 驱动代码读写函数与 BCD 转换DS1302 的驱动最核心就是两个函数写一个字节、读一个字节。注意引脚定义要根据自己的接线改下面先用 P1.0、P1.1、P1.2。sbit DS1302_CE P1^0; sbit DS1302_SCLK P1^1; sbit DS1302_IO P1^2; void DS1302_WriteByte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DS1302_IO dat 0x01; // LSB first DS1302_SCLK 1; DS1302_SCLK 0; dat 1; } } unsigned char DS1302_ReadByte(void) { unsigned char i, dat 0; for (i 0; i 8; i) { dat 1; if (DS1302_IO) dat | 0x80; DS1302_SCLK 1; DS1302_SCLK 0; } return dat; }写字节函数就是先把数据放到 IO 上然后拉高 SCLK 再拉低产生一个上升沿把数据写进去。读字节函数是在 SCLK 为低的时候读取 IO然后拉高再拉低推进到下一位。有了字节读写再封装寄存器读写和设置初始时间就很简单了。void DS1302_WriteReg(unsigned char addr, unsigned char dat) { DS1302_CE 0; DS1302_SCLK 0; DS1302_CE 1; DS1302_WriteByte(addr 0xFE); // 写命令 DS1302_WriteByte(dat); DS1302_CE 0; } unsigned char DS1302_ReadReg(unsigned char addr) { unsigned char dat; DS1302_CE 0; DS1302_SCLK 0; DS1302_CE 1; DS1302_WriteByte(addr | 0x01); // 读命令 dat DS1302_ReadByte(); DS1302_CE 0; return dat; }注意写寄存器时要把地址的 bit0 置 0读寄存器时置 1。设置时间时先关闭写保护写入秒分时日期月星期年再开启写保护。unsigned char DecToBCD(unsigned char dec) { return ((dec / 10) 4) | (dec % 10); } unsigned char BCDToDec(unsigned char bcd) { return (bcd 4) * 10 (bcd 0x0F); } void DS1302_SetTime(unsigned char year, unsigned char month, unsigned char day, unsigned char week, unsigned char hour, unsigned char min, unsigned char sec) { DS1302_WriteReg(0x8E, 0x00); // 关闭写保护 DS1302_WriteReg(0x80, DecToBCD(sec)); DS1302_WriteReg(0x82, DecToBCD(min)); DS1302_WriteReg(0x84, DecToBCD(hour)); DS1302_WriteReg(0x86, DecToBCD(day)); DS1302_WriteReg(0x88, DecToBCD(month)); DS1302_WriteReg(0x8A, DecToBCD(week)); DS1302_WriteReg(0x8C, DecToBCD(year)); DS1302_WriteReg(0x8E, 0x80); // 开启写保护 }读取时间的思路就是分别读各个寄存器的地址然后用 BCD 转十进制把数据显示出来。一个常用的判断逻辑是上电后读秒寄存器如果读到的值大于 0x59 或者 bit7 为 1说明芯片可能没有初始化或时钟停了此时调用一次设置时间。4.3 LCD1602 驱动和显示刷新LCD1602 的驱动分为写命令和写数据两个函数。如果是 8 位接线模式一次发送一个完整的字节如果是 4 线模式需要先发高 4 位再发低 4 位。下面以 8 位模式为例。sbit LCD_RS P2^0; sbit LCD_RW P2^1; sbit LCD_EN P2^2; #define LCD_DATA P0 // 如果接在P0口必须接上拉 void LCD_Delay(unsigned int t) { while (t--); } void LCD_WriteCommand(unsigned char cmd) { LCD_RS 0; LCD_RW 0; LCD_DATA cmd; LCD_EN 1; LCD_Delay(100); LCD_EN 0; } void LCD_WriteData(unsigned char dat) { LCD_RS 1; LCD_RW 0; LCD_DATA dat; LCD_EN 1; LCD_Delay(100); LCD_EN 0; } void LCD_Init(void) { LCD_Delay(15000); LCD_WriteCommand(0x30); LCD_Delay(5000); LCD_WriteCommand(0x30); LCD_Delay(5000); LCD_WriteCommand(0x30); LCD_Delay(5000); LCD_WriteCommand(0x38); // 8位数据、2行、5x7点阵 LCD_WriteCommand(0x0C); // 显示开、光标关 LCD_WriteCommand(0x01); // 清屏 LCD_Delay(5000); LCD_WriteCommand(0x06); // 光标右移 }显示时间的时候可以先自己拼一个显示缓冲区把数字拆成字符。比如显示 14 时 30 分 25 秒就在第二行固定位置分别写入 1、4、:、3、0、:、2、5。这个方法比 sprintf 省代码空间在 C51 里更推荐。void LCD_ShowTime(unsigned char hour, unsigned char min, unsigned char sec) { LCD_WriteCommand(0x80 | 0x40); // 第二行起始地址 LCD_WriteData(0 hour / 10); LCD_WriteData(0 hour % 10); LCD_WriteData(:); LCD_WriteData(0 min / 10); LCD_WriteData(0 min % 10); LCD_WriteData(:); LCD_WriteData(0 sec / 10); LCD_WriteData(0 sec % 10); }显示刷新频率不要太高因为 51 单片机主频有限每次刷屏都占用大量时间。在主循环里加一个 200ms 左右的延时判断或者用一个定时器标志每 200ms 读一次 DS1302 并刷新显示肉眼看起来足够流畅也不会影响按键扫描。4.4 按键状态机与时间调整逻辑按键调整时间首先要设计状态。我建议用三个按键K1 进入设置模式并切换设置项K2 加 1K3 减 1。不要贪多三个键足够完成所有功能。状态设计大致是正常显示状态、设置秒状态、设置分状态、设置时状态、设置日期状态、设置月状态、设置年状态、退出。用一个 unsigned char 变量 mode 表示当前状态0 是正常显示1 到 7 分别对应不同的设置项。按键扫描函数的核心是消抖和边沿检测。消抖的写法很多入门阶段最直观的是检测到低电平后延时 10~20ms再次确认低电平然后等待按键释放再执行一次动作。这样可以避免按键按住时重复触发。关键代码逻辑如下if (key1_pressed()) { mode; if (mode 7) mode 0; if (mode 0) { // 退出设置模式把当前缓冲区的时间写入DS1302 DS1302_SetTime(buf_year, buf_month, buf_day, buf_week, buf_hour, buf_min, buf p a hrefhttps://download.csdn.net/download/yangpanpan111/85782832 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p
返回列表