给51单片机加个“电子表”基于DS1302的简易时钟制作全记录最近在整理工作室时翻出一块落灰的STC89C52开发板突然想起大学时用DS1302时钟模块做的第一个独立项目——一个能显示完整时间的电子时钟。这次我决定重新设计这个经典项目不仅加入锂电池供电实现断电走时还优化了代码结构使其更易移植到其他51内核单片机上。下面就把这个实用小物件的完整制作过程分享给大家。1. 硬件选型与电路设计1.1 核心器件选型对比制作电子时钟首先需要选择合适的主控和时钟芯片。经过对比测试我最终确定了以下配置方案器件类型选型方案优势成本主控芯片STC89C52RC内置EEPROM支持在线编程5.8时钟芯片DS1302带电池备份精度±2ppm3.5显示模块LCD1602可显示字符接线简单6.9供电方案CR2032AMS1117主电源断开后仍可保持时钟运行2.3提示DS1302的典型计时误差为每月±2分钟若对精度要求更高可考虑DS3231±2ppm1.2 关键电路设计要点整个系统的电路连接需要注意以下几个关键点电源管理电路主电源采用USB 5V输入通过AMS1117-3.3稳压芯片为DS1302提供备份电源CR2032电池作为备用电源需串联1N4148二极管防反灌DS1302接口电路// 典型接线方式P1口复用较少 sbit DS1302_SCK P1^0; // 串行时钟 sbit DS1302_IO P1^1; // 数据线 sbit DS1302_RST P1^2; // 复位/片选显示模块选择数码管方案成本低但显示内容有限LCD1602方案可显示更多信息支持自定义字符本次选用LCD1602并采用4位数据线接法节省IO口2. DS1302驱动开发2.1 寄存器操作原理DS1302通过简单的三线接口进行通信其核心是掌握寄存器操作控制字节格式写操作示例7 6 5-1 0 1 | 0 | A4-A0 | 0(写)关键时间寄存器地址寄存器写地址读地址数据格式秒0x800x81BCD码分0x820x83BCD码小时0x840x85BCD码2.2 底层驱动实现以下是经过优化的驱动程序加入了错误检测机制// DS1302写一个字节 void DS1302_WriteByte(uint8_t dat) { uint8_t i; for(i0; i8; i) { DS1302_IO dat 0x01; DS1302_SCK 1; _nop_(); DS1302_SCK 0; dat 1; } } // DS1302读一个字节 uint8_t DS1302_ReadByte(void) { uint8_t i, dat 0; for(i0; i8; i) { dat 1; if(DS1302_IO) dat | 0x80; DS1302_SCK 1; _nop_(); DS1302_SCK 0; } return dat; }注意每次读写前必须先拉高RST引脚操作完成后立即拉低3. 时间显示功能实现3.1 LCD1602显示优化为了在LCD上显示更丰富的信息我设计了以下显示格式2024-07-15 MON 12:30:45对应的显示函数实现void DisplayTime() { uint8_t time[7]; // 年-月-日-星期-时-分-秒 DS1302_ReadTime(time); // 读取时间数据 LCD_SetCursor(0,0); printf(20%02x-%02x-%02x, time[6], time[4], time[3]); // 显示星期 const char *week[] {SUN,MON,TUE,WED,THU,FRI,SAT}; LCD_Print(week[time[5]%7]); LCD_SetCursor(4,1); printf(%02x:%02x:%02x, time[2], time[1], time[0]); }3.2 时间设置功能通过三个按键实现时间设置KEY1选择设置项年→月→日→时→分→秒KEY2数值增加KEY3确认保存核心设置逻辑void TimeSetting() { static uint8_t pos 0; if(KEY1_Pressed()) { pos (pos 1) % 6; } if(KEY2_Pressed()) { time_temp[pos] adjust_step[pos]; if(time_temp[pos] max_val[pos]) { time_temp[pos] min_val[pos]; } } if(KEY3_Pressed()) { DS1302_WriteTime(time_temp); pos 0; } }4. 系统优化与扩展4.1 低功耗设计为延长电池续航采取了以下措施主控休眠模式PCON | 0x01; // 进入空闲模式 // 通过外部中断唤醒显示背光控制无操作30秒后自动关闭背光按下任意键重新点亮动态刷新率正常模式1秒刷新1次省电模式10秒刷新1次4.2 温度补偿扩展功能通过DS18B20获取环境温度对DS1302进行简单补偿void TimeCompensation() { float temp DS18B20_ReadTemp(); if(temp 30.0) { // 高温环境下适当减秒 uint8_t sec DS1302_Read(0x81); if(sec 0) DS1302_Write(0x80, sec-1); } }4.3 整点报时功能利用蜂鸣器实现简易整点报时void CheckHourAlarm() { if(time[0]0 time[1]0) { // 整点 Buzzer_Beep(3, 200, 100); // 响3次 } }这个项目最让我满意的是成功实现了断电走时功能——即使拔掉USB线时钟也能依靠CR2032电池继续运行。实际测试显示一颗新电池可以维持时钟运行长达2年3个月。下次我准备尝试加入蓝牙模块用手机APP来调整时间。
给51单片机加个“电子表”:基于DS1302的简易时钟制作全记录(从原理图到代码)
发布时间:2026/6/10 6:03:18
给51单片机加个“电子表”基于DS1302的简易时钟制作全记录最近在整理工作室时翻出一块落灰的STC89C52开发板突然想起大学时用DS1302时钟模块做的第一个独立项目——一个能显示完整时间的电子时钟。这次我决定重新设计这个经典项目不仅加入锂电池供电实现断电走时还优化了代码结构使其更易移植到其他51内核单片机上。下面就把这个实用小物件的完整制作过程分享给大家。1. 硬件选型与电路设计1.1 核心器件选型对比制作电子时钟首先需要选择合适的主控和时钟芯片。经过对比测试我最终确定了以下配置方案器件类型选型方案优势成本主控芯片STC89C52RC内置EEPROM支持在线编程5.8时钟芯片DS1302带电池备份精度±2ppm3.5显示模块LCD1602可显示字符接线简单6.9供电方案CR2032AMS1117主电源断开后仍可保持时钟运行2.3提示DS1302的典型计时误差为每月±2分钟若对精度要求更高可考虑DS3231±2ppm1.2 关键电路设计要点整个系统的电路连接需要注意以下几个关键点电源管理电路主电源采用USB 5V输入通过AMS1117-3.3稳压芯片为DS1302提供备份电源CR2032电池作为备用电源需串联1N4148二极管防反灌DS1302接口电路// 典型接线方式P1口复用较少 sbit DS1302_SCK P1^0; // 串行时钟 sbit DS1302_IO P1^1; // 数据线 sbit DS1302_RST P1^2; // 复位/片选显示模块选择数码管方案成本低但显示内容有限LCD1602方案可显示更多信息支持自定义字符本次选用LCD1602并采用4位数据线接法节省IO口2. DS1302驱动开发2.1 寄存器操作原理DS1302通过简单的三线接口进行通信其核心是掌握寄存器操作控制字节格式写操作示例7 6 5-1 0 1 | 0 | A4-A0 | 0(写)关键时间寄存器地址寄存器写地址读地址数据格式秒0x800x81BCD码分0x820x83BCD码小时0x840x85BCD码2.2 底层驱动实现以下是经过优化的驱动程序加入了错误检测机制// DS1302写一个字节 void DS1302_WriteByte(uint8_t dat) { uint8_t i; for(i0; i8; i) { DS1302_IO dat 0x01; DS1302_SCK 1; _nop_(); DS1302_SCK 0; dat 1; } } // DS1302读一个字节 uint8_t DS1302_ReadByte(void) { uint8_t i, dat 0; for(i0; i8; i) { dat 1; if(DS1302_IO) dat | 0x80; DS1302_SCK 1; _nop_(); DS1302_SCK 0; } return dat; }注意每次读写前必须先拉高RST引脚操作完成后立即拉低3. 时间显示功能实现3.1 LCD1602显示优化为了在LCD上显示更丰富的信息我设计了以下显示格式2024-07-15 MON 12:30:45对应的显示函数实现void DisplayTime() { uint8_t time[7]; // 年-月-日-星期-时-分-秒 DS1302_ReadTime(time); // 读取时间数据 LCD_SetCursor(0,0); printf(20%02x-%02x-%02x, time[6], time[4], time[3]); // 显示星期 const char *week[] {SUN,MON,TUE,WED,THU,FRI,SAT}; LCD_Print(week[time[5]%7]); LCD_SetCursor(4,1); printf(%02x:%02x:%02x, time[2], time[1], time[0]); }3.2 时间设置功能通过三个按键实现时间设置KEY1选择设置项年→月→日→时→分→秒KEY2数值增加KEY3确认保存核心设置逻辑void TimeSetting() { static uint8_t pos 0; if(KEY1_Pressed()) { pos (pos 1) % 6; } if(KEY2_Pressed()) { time_temp[pos] adjust_step[pos]; if(time_temp[pos] max_val[pos]) { time_temp[pos] min_val[pos]; } } if(KEY3_Pressed()) { DS1302_WriteTime(time_temp); pos 0; } }4. 系统优化与扩展4.1 低功耗设计为延长电池续航采取了以下措施主控休眠模式PCON | 0x01; // 进入空闲模式 // 通过外部中断唤醒显示背光控制无操作30秒后自动关闭背光按下任意键重新点亮动态刷新率正常模式1秒刷新1次省电模式10秒刷新1次4.2 温度补偿扩展功能通过DS18B20获取环境温度对DS1302进行简单补偿void TimeCompensation() { float temp DS18B20_ReadTemp(); if(temp 30.0) { // 高温环境下适当减秒 uint8_t sec DS1302_Read(0x81); if(sec 0) DS1302_Write(0x80, sec-1); } }4.3 整点报时功能利用蜂鸣器实现简易整点报时void CheckHourAlarm() { if(time[0]0 time[1]0) { // 整点 Buzzer_Beep(3, 200, 100); // 响3次 } }这个项目最让我满意的是成功实现了断电走时功能——即使拔掉USB线时钟也能依靠CR2032电池继续运行。实际测试显示一颗新电池可以维持时钟运行长达2年3个月。下次我准备尝试加入蓝牙模块用手机APP来调整时间。