资讯动态

基于STC15的万年历程序:DS1302时序、LCD1602驱动与低功耗设计

发布时间:2026/10/15 2:31:41 来源:尧图企业网站定制
简介基于STC15的万年历程序是一份面向单片机学习者、电子竞赛选手和DIY爱好者的实践型资源聚焦实时时钟显示、温湿度监测报警与闹钟提醒三大功能可作为课程设计或项目原型直接参考。压缩包内共23个文件、约249KB核心包含Keil工程源码、编译生成的hex烧录文件、Protel格式电路图、Proteus仿真工程及PDF版原理图从软件到硬件覆盖了完整开发链路。程序选用DS1302实时时钟芯片保证断电走时准确可通过DHT11或DHT22传感器采集环境温湿度并做超限报警同时支持蜂鸣器定时闹钟帮助读者理解串行实时时钟和单总线传感器在单片机上的真实应用。其中仿真工程和电路原理图可配合源码一起使用便于先离线验证程序逻辑再动手搭焊对掌握电路连接、时序读写和调试排错都有直接帮助。目前已有351人学习或下载文件体量虽小但结构完整适合想快速上手单片机综合项目的人群。1. 基于STC15的万年历程序为什么这款单片机课设至今仍是练手首选如果你在搜索栏里敲下“基于STC15的万年历程序”大概率是接到了课程设计任务或者想给桌面添一个自己焊的时钟。这个题目在学生圈子里流传了很多年从来不是因为它的功能有多惊艳而是因为它把单片机开发里最容易被忽略的几件事——时序、电源、显示刷新、掉电保持——全部浓缩在一个巴掌大的板子上。很多开发者做完这个项目才真正搞懂什么是总线时序什么是内部上拉什么是看门狗。它适合两类人一类是刚学完51内核、想用一颗国产增强型芯片做点完整东西的新手另一类是想快速验证STC15系列在低功耗和掉电模式下表现的老手。前者能拿到一套能跑通、能答辩的完整流程后者能从中看到芯片手册里没写透的边界参数。这款程序的核心并不复杂STC15系列单片机负责读取实时时钟芯片的数据驱动LCD屏显示年、月、日、星期、时分秒再提供几个按键做校准。难点在于STC15的工作电压、IO口模式和传统的AT89C52完全不同直接把老代码搬过来会踩到不少暗坑。正文将从硬件选型、时序协议、驱动移植、避坑检查到最后的功能扩展一步步拆开来讲清楚。2. 硬件选型与接线STC15搭配哪款时钟芯片决定你后面少踩多少坑万年历硬件上只有一个核心决策点选什么实时时钟芯片以及STC15的IO口怎么跟它对接。这个决策直接决定了你后面是调两天还是调两个小时。2.1 时钟芯片DS1302还是DS3231市面上的万年历方案里出现频率最高的是DS1302和DS3231。DS1302是老牌选手三线串行接口成本低到几毛钱很多开发板出厂就焊好了它。DS3231则是带温补晶振的IIC芯片精度能到年误差几分钟以内。这份资源配套的例程默认按DS1302来写因为STC15单片机的IO口电平是5VDS1302也是5V供电两者直连最稳不用做电平转换。DS3231虽然精度高但它的大部分模块是3.3V供电STC15的IO口输出5V直接接上去会有超压风险。实际项目里我一般让开发者先看手头有什么。如果你想做桌面摆件DS3231省掉校时烦恼如果你是交课设DS1302足够跑分代码还更短。但注意一点STC15内部虽然有EEPROM但不要试图用它来保存时间数据因为掉电后时间还在走重新上电要读的是时钟芯片不是片内存储。2.2 STC15的IO口模式准双向口不是万能的STC15系列的IO口有四种模式准双向口、推挽输出、高阻输入、开漏输出。很多新手直接把老51代码烧进去发现LCD屏不亮、按键没反应问题往往出在IO配置上。STC15烧录后默认是准双向口模式这个模式带弱上拉读按键没问题但驱动LCD的数据口(D0-D7)时输出高电平的能力很弱如果LCD模块内部不带上拉屏幕就会显示一堆乱码。解决办法有两个把数据口配置成推挽输出模式或者在LCD模块的VDD和VSS之间确保共地且数据线上加10K上拉电阻。我习惯用STC-ISP软件里的“端口设置”工具烧录时直接勾选P0-P5是准双向口还是推挽输出。没有特殊需求时P0-P5全设为准双向口LCD数据口改用推挽这样接线最少。但这版例程为了兼容大多数板子代码里用的是P0口接LCD数据口P2.0-P2.2接控制口并在初始化函数里把P0M1、P0M0寄存器配置成推挽输出。如果你用的板子晶振是11.0592MHz那这个配置直接可用如果是其他晶振波特率和延时都可能要重新算。2.3 推荐接线表与电源注意下面这份接线表以STC15W408AS为主控芯片DS1302模块和LCD1602为例如果你手里的芯片型号不同大部分引脚可以照抄但P3.6、P3.7这类复用口要注意避开串口下载脚。模块引脚接入STC15说明LCD1602RSP2.0寄存器选择LCD1602RWP2.1读写选择接地或接P2.1LCD1602ENP2.2使能信号LCD1602D4-D7P0.4-P0.7四线模式数据DS1302CEP1.0片选高电平有效DS1302SCLKP1.1串行时钟DS1302DATP1.2数据线按键K1-K3P3.0-P3.2模式切换/加/减电源部分是最容易翻车的点。STC15系列支持2.4V到5.5V供电但DS1302模块上很多带一个3V电池座是为了掉电保持用的模块的VCC接5V问题时不大。如果发现时间每次上电都回到初始值先量一下VCC2引脚上是否有电压——DS1302有两个电源引脚VCC1接电池VCC2接主电源芯片优先使用VCC2供电。STC15掉电后DS1302由电池供电继续走时这时候需要把DS1302的VCC1接到电池正极否则掉电保持就是一句空话。提示如果你用的是带DS3231的模块注意模块上通常已经焊好了LDO稳压到3.3VSTC15的IO口输出高电平是接近5V的建议在数据线上串联1K电阻再接入避免长期过压损伤芯片。3. 软件设计从时序读取到显示刷新的分层实现硬件焊好之后软件的复杂度集中在两块怎么在STC15上把DS1302的寄存器读出来以及怎么把读到的BCD码在LCD1602上刷得干净不闪烁。3.1 DS1302的三线时序先摸清门道再写代码DS1302的三线接口由CE、SCLK、DAT组成。每次操作先拉高CE然后在SCLK上升沿把命令字节一位一位发出去再在后续的时钟沿读回数据。很多人在这里遇到“读出来的数据全是0xFF”或者“写着写着芯片就锁死”的情况原因有两个一是命令字节的位顺序搞反了DS1302是LSB first也就是最低位先发二是CE的控制太随意DS1302要求CE在整个读写过程中保持高电平中途不能跳变很多代码在字节间隙把CE拉低再拉高芯片就不认了。下面的代码是常用的DS1302单字节读函数注意看位顺序和CE处理。unsigned char DS1302_ReadByte(unsigned char addr) { unsigned char i; unsigned char temp 0; DS1302_CE 0; DS1302_SCLK 0; DS1302_CE 1; // CE拉高启动一次传输 // 发送地址字节, LSB first for (i 0; i 8; i) { DS1302_DAT addr 0x01; // 先取最低位送出 addr 1; DS1302_SCLK 1; // 上升沿DS1302采样数据 _nop_(); DS1302_SCLK 0; _nop_(); } // 读取数据字节, 仍然是LSB first DS1302_DAT 1; // 数据线切换为输入模式关键 for (i 0; i 8; i) { DS1302_SCLK 1; // 产生时钟沿DS1302在下降沿输出数据 _nop_(); temp 1; if (DS1302_DAT) { temp | 0x80; // 先移位再置位保证字节顺序正确 } DS1302_SCLK 0; _nop_(); } DS1302_CE 0; // 整个传输结束才能拉低 return temp; }这段代码里最容易被忽略的是读操作前先写DS1302_DAT 1。因为单片机的IO口在输出模式下会把引脚电位锁住直接去读是读不到外部信号的必须把它切换成输入状态。STC15的IO口如果配置成准双向口写1之后内部弱上拉会拉高引脚此时外部信号可以把它拉低所以能读。如果用推挽输出模式这个读操作就会永远读到高电平数据全是0xFF。这时只需要单独把P1.2设成高阻输入模式或者在代码里临时改一下P1M1和P1M0寄存器。读函数写好后还要有一个连续读时间的封装因为读星期和读时分秒需要连续操作。DS1302有一个“突发模式”Burst Mode一次发命令就能把所有时间寄存器全部读出来这在显示刷新频率高的时候特别有用。如果不想用突发模式也可以连续调用上面的读函数但每次调用之间需要至少几微秒的间隔否则时序太紧凑高低温环境容易丢数据。3.2 BCD码与十进制转换显示乱码的根源DS1302内部存储时间用的是BCD码比如0x23代表23秒0x59代表59分。如果你直接把读到的字节塞给LCD显示屏幕上会出现跳字母的情况比如显示“9C”而不是“23”。转换逻辑很直观十位乘以10加个位。但要注意读出来的字节自带高四位十位、低四位个位这跟十六进制数转十进制不是一回事不能用普通的printf(%d, hex)直接打印。下面这段转换函数是万年历里最基础的构件值得原样抄进工程里。unsigned char BCD2DEC(unsigned char bcd) { return ((bcd 4) * 10) (bcd 0x0F); } // 反过来把十进制数转成BCD码写给DS1302 unsigned char DEC2BCD(unsigned char dec) { return ((dec / 10) 4) | (dec % 10); }写时间时务必用DEC2BCD转换否则你给DS1302写16进制的12芯片会理解成十进制的180x12对应十位1个位2但如果直接拿十进制的12去赋值写入的其实是0x0C读回来变成了个位12。这个坑非常隐蔽表现为“校时之后分钟变乱”。除了BCD格式还有一个寄存器需要关心DS1302的小时寄存器第七位是12/24小时制标志位置1代表12小时制。如果初始化时不小心写成了12小时制下午的时间显示会从“13:00”跳到“01:00”很多人以为是芯片坏了其实是协议没理解透。3.3 LCD1602四线驱动如何省IO口还能稳定显示LCD1602标准接法需要8根数据线加3根控制线STC15IO口虽然多但如果你的板子还要接DS1302、按键、蜂鸣器IO口就会紧张。常用的做法是用四线模式只接D4-D7。初始化顺序跟八线模式不同必须先发送三次0x38八线接口指令再切换到四线模式发送0x32最后才是0x28。很多人套用八线模式的初始化代码屏幕只亮不显示字符就是这个顺序不对。初始化函数里有个关键延时LCD控制器的内部上电初始化需要至少15ms如果主频是11.0592MHz延时函数要写够短了就会出现屏幕偶尔白屏。下面这段是稳定版的初始化代码延时用的是STC15硬件定时器不依赖NOP空循环。void LCD1602_Init(void) { Delay_ms(50); // 等待LCD控制器上电稳定 LCD1602_WriteCmd(0x30); // 八线模式初始化第一步 Delay_ms(5); LCD1602_WriteCmd(0x30); // 重复第二次 Delay_ms(5); LCD1602_WriteCmd(0x30); // 重复第三次 Delay_ms(1); LCD1602_WriteCmd(0x20); // 切换到四线模式 Delay_ms(1); LCD1602_WriteCmd(0x28); // 4位数据2行显示5x7字形 LCD1602_WriteCmd(0x0C); // 显示开光标关 LCD1602_WriteCmd(0x01); // 清屏 Delay_ms(2); }注意这个初始化函数里的LCD1602_WriteCmd需要支持向高四位和低四位分别发送数据。在四线模式下如果你只写一次高四位LCD会一直等待剩余四位表现为屏幕上出来半个字符。很多资源里的例程是八线模式的你需要手动改成下面的写命令函数。void LCD1602_WriteCmd(unsigned char cmd) { LCD1602_RS 0; // 命令模式 LCD1602_EN 1; P0 (P0 0x0F) | (cmd 0xF0); // 先送高四位到P0.4-P0.7 LCD1602_EN 0; // 下降沿锁存 LCD1602_EN 1; P0 (P0 0x0F) | ((cmd 4) 0xF0); // 再送低四位 LCD1602_EN 0; LCD1602_RS 1; // 恢复数据模式为下一段做准备 }这个函数里的掩码操作(P0 0x0F)很关键。如果不做掩码直接对P0赋值会把P0的低四位也一起改掉如果低四位还接了矩阵键盘就会造成键盘误触发。很多开发者的经验是LCD占用的IO口最好专门留出一个完整的P口避免跟其他设备复用。STC15的P0口上电默认是高阻输入很多LCD模块不亮就是忘了在初始化前面加一行P0M1 0x00; P0M0 0xFF;把P0设为推挽输出。这行代码在STC15系列上是必须的老51默认开漏输出反而能靠外部上拉凑合用STC15如果忘记配置会造成LCD数据写不进去屏幕一直白屏。时间显示刷新方面不要在主循环里一直读时钟芯片。一方面DS1302的连续读耗时大约要几百微秒另一方面频繁刷新会让LCD的字符闪烁。我一般用STC15内部的定时器0做1Hz中断中断里更新内部时间变量主循环只负责把更新标志置位后刷新LCD。// 定时器0中断服务函数50ms一次累计20次为1秒 void Timer0_Isr(void) interrupt 1 { static unsigned int cnt 0; if (cnt 20) { cnt 0; time_updated 1; // 主循环检测到这个标志才去刷新显示 } }这样做的逻辑是显示刷新和按键扫描都是在主循环的间隙完成的中断只负责计时和置标志位避免在中断里调用耗时过长的LCD驱动函数。因为LCD的写命令需要几十微秒在中断里执行如果同时发生按键扫描和DS1302读取会导致中断处理时间超过下一次中断的触发间隔系统时钟就会漂移。这个问题在课堂作业里不会被发现但在持续跑24小时后时间会有几十秒的累积误差。3.4 按键消抖与菜单状态机万年历最少需要三个按键一个切换“正常显示/设置状态”两个用来加减数值。如果直接在主循环里检测按键按下瞬间的电平抖动会造成一次按下被识别成十几次年份会从2024直接飙到2050。正规做法是加入软件消抖——检测到按下后延时10ms再确认再等待释放。unsigned char Key_Scan(void) { static unsigned char key_state 0; unsigned char key 0; if (K1 0) key 1; else if (K2 0) key 2; else if (K3 0) key 3; // 状态机消抖 switch (key_state) { case 0: if (key) { key_state 1; } break; case 1: if (key) { // 确认按键稳定 key_state 2; return key; } else { key_state 0; // 抖动复位 } break; case 2: if (!key) { key_state 0; // 等待释放 } break; } return 0; }这段代码用了一个简单的三态状态机核心是“第一次检测到按下不立即响应第二次检测仍然按下才算数”。这样比单纯的延时消抖更省CPU时间而且不会出现长按键时重复触发。设置状态下的菜单切换我习惯用一个简单的switch-case状态变量而不是用函数指针或者复杂的环形队列代码量小且容易答辩时讲清楚。设置流程通常是正常显示状态下按K1进入年份调整数码管或者LCD上年份闪烁提示K2加一K3减一再按K1切到月份以此类推。这里的重点是在调整当前项的数值时要先把对应的BCD值读出来变成十进制再加减调整完再转回BCD码。如果直接在BCD码上加减会出现“加1到了9再加1变成16”的情况。这在秒表设置时分秒时尤其常见。4. STC15时钟源与低功耗策略把万年历从“能跑”做到“省电”很多课程设计的万年历只要求通电能用但如果考虑到桌面长期摆放功耗和掉电策略是真正的加分项。4.1 时钟源选择内部IRC还是外部晶振STC15系列芯片内部有一个IRC振荡器精度在常温下大约为正负1%温度变化时会漂移。如果你对它做万年历夏天和冬天的日误差可能差出几十秒。外部晶振可以显著改善这个情况但在PCB上需要多焊两个电容一个晶振。很多开发板没有把外部晶振的位置留出来所以大部分例程都是直接用内部IRC烧录时在STC-ISP软件里选好频率比如11.0592MHz就能跑。但这里有个细节内部IRC的精度不是生产时一次校准的STC15芯片出厂时的IRC频率有误差STC-ISP软件在连接时会自动校准并把这些频偏值写入非易失区。如果你自己用别的工具烧录没有做频率校准串口通信的波特率可能偏移5%以上和时间显示无关但后续你要通过串口打印调试信息时会收到乱码。万年历程序如果只做显示功能这个问题暂时不用管但如果你打算在程序里加串口日志必须给芯片用STC-ISP重新校准一次。4.2 进入掉电模式前时间数据怎么办STC15支持多种低功耗模式其中掉电模式Power Down的电流可以降到微安级别。如果想做一个桌面时钟晚上不需要显示时可以进入掉电模式按一下按键唤醒。此时DS1302的供电由电池负责时间不会丢这是整体方案里最合理的分工。程序上进入掉电模式很简单调用一条指令PCON | 0x02;即可。但唤醒之后有个大坑STC15掉电唤醒后代码从头开始执行所有的外设重新初始化如果你的某个IO口在主循环中是高电平输出比如控制背光的引脚唤醒后默认变成准双向口背光可能闪烁一下再关掉。解决办法是在初始化之后先恢复进入掉电前的IO状态再关闭背光。唤醒功能的设置还需要在代码里先把PCA模块或者定时器的唤醒功能关掉否则掉电模式下电流可能比预期高不少。低功耗设计对DS1302的电池寿命影响也很大。DS1302的VCC1接电池后主电源断电它的损耗大约只有几百纳安一颗2032纽扣电池能用好几年。但如果你的板子上还有LCD屏、LED灯珠这些外设的漏电会远大于DS1302本身电池很快就没电了。所以掉电模式下要把所有外设的VCC切断或者用MOS管做供电开关。很多现成的DS1302模块上有一颗二极管隔离VCC1和VCC2如果电池反而被主电源充电说明二极管方向或者封装脚下错了这种情况检测方法是主电源关闭后量电池两端电压如果缓慢上升就说明电路有反向漏电。4.3 看门狗的正确打开方式STC15内部集成了看门狗很多人写程序喜欢在初始化里顺手开起来防止程序跑飞。但在万年历这样的实时应用里看门狗如果不配合中断和循环周期来喂狗会频繁复位。常见的情况是进入设置菜单后长时间不按键主循环还卡在状态机里喂狗语句没执行到结果看门狗超时复位所有设置全部丢失。这不是程序跑飞是喂狗设计不合理。正确做法是把喂狗放在一个独立的高优先级中断里或者放在主循环的每个分支公共出口。如果设置了菜单超时自动退出可以在超时函数里喂狗。看门狗的超时时间也要跟主循环一次遍历的时间匹配如果主循环里有一次读取DS1302加刷新LCD需要50ms看门狗超时时间至少设500ms否则机器狗会误咬人。注意STC15看门狗一旦使能无法用软件关闭只能等复位。所以在调试阶段不要开启看门狗等所有功能稳定后再开否则每次修改代码重新烧录后旧程序里的看门狗可能还在运行会干扰ISP下载。5. 程序调试与避坑排查五个让新手最头疼的故障现象这里把万年历调试中最常见的五类问题整理一下。每一个都是实际踩过的坑其中有几个的排查过程花了大半天时间。按这个顺序去检查大多数情况十分钟内能定位。5.1 现象一LCD屏只亮背光没有任何字符现象屏幕背光亮对比度调到最大依然没有字符出现。原因1STC15的P0口默认是高阻输入LCD数据总线没有驱动能力。原因2LCD的3脚V0对比度电压没调整某些模块需要外接电位器分压。解决在软件初始化最开始加上P0M1 0x00; P0M0 0xFF;把P0切成推挽输出。硬件上V0脚接一个10K电位器中间抽头接到5V和GND之间调整到刚好看到字符为止。有些带背光的LCD模块内部已经焊好了对比度电阻就不需要这个电位器。5.2 现象二时间显示正常但每次断电重启都回到初始时间现象设置好时间后掉电再上电恢复到00:00:00 或者某个默认值。原因DS1302没有接备用电池或者VCC1悬空掉电后时钟芯片完全没有电源寄存器清零。解决检查模块上是否焊接了电池座电池是否装好以及VCC1引脚是否被模块上默认的跳线接到了一个固定电平。如果模块上VCC1已经和VCC2短接需要断开跳线再把电池正极接到VCC1。用万用表量VCC1对GND电压正常应该在3V左右。5.3 现象三秒数每次走两秒分钟跳变现象秒位显示01、02、04、05这样跳跃前进分钟有时跳变两分。原因定时器0的1秒中断不准确或者显示刷新时间大于1秒导致主循环在下一轮读到2秒后的数据。解决用示波器或者逻辑分析仪测量定时器中断引脚输出确认定时器重装值是否正确。常见错误是采用12MHz晶振却按11.0592MHz计算定时器初值。此外不要在中断里做耗时的LCD写入操作刷新放到主循环保证中断只做计数和置标志。5.4 现象四按键调整时数值乱跳现象按下K2加1年份从2024跳到2030甚至更大按下K3减1数值随机改变。原因按键消抖不彻底或者按键扫描函数在执行过程中被定时器中断打断导致同一物理按键多次触发。解决确认消抖逻辑是双状态确认而不是单纯延时函数。如果已经用了状态机消抖还乱跳检查按键扫描是否在定时器中断里被调用中断函数里如果调用了Key_Scan会反复进入状态机。把按键扫描全部移到主循环定时器中断里只置时间更新标志。5.5 现象五烧录程序时提示握手失败现象STC-ISP下载软件无法检测到单片机提示“仍在检测目标单片机”。原因STC15的P3.0和P3.1是串口下载脚如果你的程序初始化时把这几个引脚配置成推挽输出并拉高拉低会导致下载握手信号被干扰。另一个常见原因是板上没接晶振但STC15可以内部时钟下载。解决把P3.0和P3.1设成准双向口下载时断电重新上电让单片机在冷启动时进入ISP模式。如果板子上有其他外设把这几个引脚的外设暂时断开下载完再接回。6. 进阶玩法给万年历加上闹钟、温度与掉电时间保持基础功能跑通之后这个项目还有很大的扩展空间。如果能把这几个进阶功能加进去整个作品的完整度会提升很多也更容易用来支撑课程设计或竞赛报告。6.1 加入闹钟输出用STC15内部比较器或PWM产生提示音闹钟功能在程序逻辑上很简单——每天固定时刻触发一个标志位然后让蜂鸣器响一段时间。关键是提示音的实现方式。不要用主循环延时函数去控制蜂鸣器发声那样在闹钟响的时候整个系统的时间刷新和按键扫描都会卡住。常见做法是用一个定时器中断控制蜂鸣器的频率比如1kHz方波响5秒然后自动关闭并设置闹钟标志防止重复触发。如果芯片型号带PWM模块比如STC15W系列可以用PWM直接输出不占用定时器资源。闹钟时间需要和DS1302的时间做比较建议在每秒更新标志置位后做一次比较不要在每次循环都读取DS1302因为读写DS1302一次需要好几百微秒频繁操作会让主循环变慢。6.2 加入温度显示DS18B20的单总线读取用DS18B20给万年历加温度显示是很多课设的标配。但要注意STC15的IO口在驱动单总线设备时上拉电阻是必须的——DS18B20的数据线是开漏输出结构必须在外部接一个4.7K到10K的上拉电阻到VCC。有些开发板的P1口内部上拉比较弱会出现读温度偶尔出错的情况。读取DS18B20的时序最麻烦的是延时精度。DS18B20对时序要求比较苛刻读时隙必须在15微秒内完成采样。如果直接用STC15的软件延时因为内部IRC频率有误差有可能出现读出来全是85℃这种固定值。解决方法是在初始化时进行一次DS18B20的复位脉冲然后跳过ROM直接转换温度等待转换完成后读回。代码上要严格区分读0和读1的延时窗口。// 单总线读一位通过控制延时窗口区分读0和读1 unsigned char DS18B20_ReadBit(void) { unsigned char dat 0; IO_DAT 0; _nop_(); IO_DAT 1; // 拉高总线启动读时隙 _nop_(); if (IO_DAT) { dat 1; } Delay_us(50); // 等待时隙结束 return dat; }这个函数的关键点在于读取IO_DAT的电平必须在释放总线后极短的时间内完成一般是15微秒以内。如果延时超过这个窗口读到的就是时隙结束后的电平会出现连续读到相同数据的现象。6.3 掉电时间保持外部中断唤醒与EEPROM冷启动识别STC15内部有EEPROM可以在掉电前保存当前时间但DS1302的电池方案已经足够应付绝大多数场景片内EEPROM的用途可以放在保存闹钟设置和校时记录上。比如用户设置了一次闹钟时间下次上电希望它还在这时就需要从EEPROM读回闹钟配置而不是每次上电都恢复默认值。EEPROM的写操作有一点需要注意STC15的EEPROM写入前要先擦除扇区而且写入次数有寿命限制一般在10万次左右。在闹钟设置界面用户可能会反复修改此时每次修改都写入EEPROM会导致寿命快速消耗。我一般会在退出设置界面时统一写一次或者在修改完成后延时10秒没有再次修改才写入避免频繁擦写。掉电检测方面STC15的电源监控模块可以设置低压复位阈值。如果把阈值设在4.5V左右当主电源断电瞬间系统会进入复位或者触发外部中断此时可以抢在电压掉到DS1302电池工作电压之前把当前设置保存到EEPROM。这个功能需要一点硬件改动——在VCC和GND之间加一个合适的电容延长掉电缓冲时间到10ms以上否则电压下降太快中断都来不及执行。6.4 校时技巧和时间精度验证万年历做出来以后验证精度是最后一步。烧录程序后先用电波表或者手机上的网络时间来校准一次让它连续跑24小时再对比一次误差。如果误差在几秒以内说明晶振精度和定时器初值都是合理的如果误差达到一两分钟就要检查是不是用了定时器中断里刷LCD的问题。STC15内部IRC的温漂会导致冬天和夏天的日误差不同如果你要求长期精度还是建议用外部32.768kHz晶振给DS1302提供时钟源。但注意DS1302的晶振引脚非常敏感PCB走线要尽量短晶振外壳最好接地否则寄生的电容耦合会让时钟走快或走慢。如果手头没有示波器可以用一个简单的方法测精度用手机给电脑校时再把万年历的时间调成和电脑一致24小时后对比差值连续测三天取平均值。从我做过的多个类似项目的经验来看万年历的完成度往往不取决于功能有多花哨而在于掉电设置不丢、时间走得准、按键手感不飘。这三个基础体验做好再谈扩展才有意义。闹钟、温度、农历显示都是加分项但别让它们抢了核心时间显示的资源。这篇拆解里涉及的核心代码包括DS1302驱动、LCD1602四线显示、按键状态机、定时器校准都是在实际项目上验证过的直接可以编译进STC15工程。希望这份手记能帮你在调通它的路上少走些弯路。尤其按键消抖和P0口配置这两处都是让我当初折腾到深夜的细节预祝顺利。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑