51 单片机电子钟实现设置闹钟、闹钟播放音乐、时间
1. 项目功能24 小时制电子时钟小时、分钟可手动调节秒自动计时不可设置独立存储闹钟时分与系统当前时间互不干扰分开设置四段式设置模式调节对应数码管数字 500ms 闪烁提示三首内置蜂鸣乐曲小星星 / 两只老虎 / 生日快乐按键切换闹钟音乐到达闹钟时间自动播放选中乐曲设置模式下屏蔽闹钟4 路独立按键切换档位、增减数值、退出设置8 位共阴极数码管动态扫描显示带消隐防重影11.0592MHz 晶振定时器 0 1ms 中断精准计时 闪烁节拍。2. 硬件资源主控STC89C52/AT89C52晶振11.0592MHz二、硬件引脚分配表格器件 引脚 说明数码管段选 P0 8 位段码输出数码管位选 LSA P2^2 3-8 译码器 A 脚数码管位选 LSB P2^3 3-8 译码器 B 脚数码管位选 LSC P2^4 3-8 译码器 C 脚无源蜂鸣器 P2^5 低电平驱动发声独立按键 K1 P3^1 切换设置档位独立按键 K2 P3^0 数值 1 / 下一首音乐独立按键 K3 P3^2 数值 - 1 / 上一首音乐独立按键 K4 P3^3 退出设置返回正常走时三、变量与宏定义说明1. 数据类型重定义c运行typedef unsigned int u16; // 16位无符号整型typedef unsigned char u8; // 8位无符号字符型2. 按键宏c运行#define KEY1_PRESS 1#define KEY2_PRESS 2#define KEY3_PRESS 3#define KEY4_PRESS 4#define KEY_UNPRESS 0 // 无按键按下3. 全局存储变量1系统当前时间独立存储c运行u16 timer_cnt 0; // 定时器1ms计数累计10001秒u8 hour 12; // 当前小时 0~23u8 min 30; // 当前分钟 0~59u8 sec 0; // 当前秒 0~59仅自动走时不可手动修改2闹钟参数独立存储与当前时间完全隔离c运行u8 alarm_h 12; // 闹钟触发小时 0~23u8 alarm_m 50; // 闹钟触发分钟 0~59u8 music_sel 0; // 闹钟音乐选择 0小星星 1两只老虎 2生日快乐3模式与闪烁控制c运行u8 set_mode 0;// 模式说明// 0正常计时// 1设置当前小时// 2设置当前分钟// 3设置闹钟小时// 4设置闹钟分钟// 5切换闹钟音乐u16 blink_cnt 0; // 闪烁计时计数器bit blink_flag 0; // 闪烁标志 1全亮 0调节位熄灭4. 常量数组smg_code[10]共阴极数码管 0~9 段码tone[]蜂鸣器音调频率表star_music/star_time小星星乐谱 节拍tiger_music/tiger_time两只老虎乐谱 节拍birth_music/birth_time生日快乐乐谱 节拍disp_buf[8]数码管显示缓存存储 8 位数码管段码。四、函数接口说明1. 延时函数void delay_10us(u16 ten_us)功能10us 基础延时用于数码管动态扫描、按键消抖入参ten_us 延时倍数void delay_ms(u16 ms)功能毫秒级长延时用于乐曲音符间隔void DelayUs2x(unsigned char t) / void DelayMs(unsigned char t)功能短延时工具函数供蜂鸣器发声调用2. 按键驱动 u8 key_scan (u8 mode)功能独立按键扫描带软件消抖入参mode0 单次扫描mode1 重置按键状态返回值KEY1~KEY4_PRESS 对应按键按下KEY_UNPRESS 无按键逻辑静态变量防抖松手后重置按键状态3. 数码管驱动 void smg_display (u8 dat [])功能8 位数码管动态扫描输出带闪烁逻辑判断、段码消隐入参dat [] 显示缓存数组流程依次选中每一位→输出段码→延时→段码清零消隐闪烁逻辑blink_flag0时当前调节位输出 0x00 熄灭。4. 显示缓存刷新 void refresh_display (void)功能根据当前设置模式填充显示缓存模式 0/1/2显示当前时间 HH MM SS模式 3/4显示闹钟时间 HH MM秒位熄灭区分模式 5最后一位显示当前选中音乐编号5. 定时器初始化 void timer0_init (void)配置 T0 为模式 116 位定时器11.0592MHz 定时 1ms开启定时器中断、总中断启动定时器。6. 定时器 0 中断服务函数 void timer0_isr () interrupt 1中断周期1ms重装初值 TH00xFC TL00x18累加计时计数器 timer_cnt、闪烁计数器 blink_cnt500ms 翻转闪烁标志实现 0.5s 闪烁累计 1000ms 秒自增自动进位秒→分→时小时 24 循环。7. 蜂鸣器底层发声 void beep_play (u16 freq, u16 time)功能输出指定频率方波驱动无源蜂鸣器发声freq音调频率time发声时长。8. 乐曲播放 void play_music (void)根据music_sel选择对应乐谱循环播放音符间加停顿。9. 按键逻辑处理 void key_handle (u8 key_val)根据按键值执行对应操作K1切换设置档位循环 0~5切换时重置闪烁K2当前档位数值 1溢出归零K3当前档位数值 - 1下溢归最大值K4直接退出设置回到正常计时模式。10. 闹钟检测 void alarm_scan (void)仅正常模式set_mode0检测闹钟当前时分 闹钟时分时播放选中音乐其余时刻关闭蜂鸣器。11. 主函数 void main (void)系统主循环按键扫描 → 按键处理 → 刷新显示缓存 → 数码管扫描 → 闹钟判断。五、操作流程说明档位切换K1 循环切换档位 0正常走时数码管显示 HH MM SS全部常亮到闹钟时间自动播放乐曲。档位 1设置当前小时小时两位闪烁K2 小时 1K3 小时 - 1秒自动走时不变。档位 2设置当前分钟分钟两位闪烁K2 分钟 1K3 分钟 - 1秒自动走时不变。档位 3设置闹钟小时数码管显示闹钟 HH MM小时闪烁修改仅改变闹钟不影响系统时间。档位 4设置闹钟分钟数码管显示闹钟 HH MM分钟闪烁独立存储闹钟分钟。档位 5选择闹钟音乐最后一位显示 0/1/2 并闪烁K2/K3 切换三首乐曲。按键通用规则K2数值加一到上限自动归零24 小时 / 60 分钟 / 3 首乐曲K3数值减一到下限自动归最大值K4任意档位一键退出回到正常时钟界面六、核心特性说明时间与闹钟完全分离存储hour/min 系统时间、alarm_h/alarm_m 闹钟时间两组独立变量修改闹钟不会改变当前时钟设置闹钟时数码管隐藏秒位直观区分。秒不可手动调节无任何档位操作 sec 变量仅依靠定时器自动进位。闪烁交互提示每 500ms 切换亮灭仅当前正在修改的数字闪烁其余数字常亮交互直观。闹钟屏蔽机制处于任意设置档位时不会触发闹钟音乐避免调时被蜂鸣干扰。动态数码管消隐每一位扫描结束后段码置 0消除拖影、重影。软件按键消抖按键检测延时 10ms 消抖无长按误触发。七、关键时序参数定时器 0 中断1ms秒计时1000 次中断 1s闪烁周期500ms 翻转一次数码管单段扫描延时1000us音符间隔停顿30ms。八、代码如下#include reg52.h #include intrins.h typedef unsigned int u16; typedef unsigned char u8; // 硬件引脚定义 #define SMG_SEG_PORT P0 sbit LSA P2^2; sbit LSB P2^3; sbit LSC P2^4; sbit BEEP P2^5; // 无源蜂鸣器低电平发声 // 独立按键 sbit KEY1 P3^1; sbit KEY2 P3^0; sbit KEY3 P3^2; sbit KEY4 P3^3; // 按键宏 #define KEY1_PRESS 1 #define KEY2_PRESS 2 #define KEY3_PRESS 3 #define KEY4_PRESS 4 #define KEY_UNPRESS 0 // 共阴极数码管段码 0-9 u8 code smg_code[10] {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; u8 disp_buf[8] {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // 当前系统时间独立存储 u16 timer_cnt 0; u8 hour 12; u8 min 30; u8 sec 0; // 闹钟时间独立存储和当前时间完全分开 u8 alarm_h 12; u8 alarm_m 50; u8 music_sel 0; // 选中音乐编号 0/1/2三首曲子 // 设置模式标记 u8 set_mode 0; // 0:正常计时 1:调当前小时 2:调当前分钟 3:闹钟小时 4:闹钟分钟 5:选择闹钟音乐 // 闪烁相关 u16 blink_cnt 0; bit blink_flag 0; // 0熄灭 1点亮 // 音乐频率表(无源蜂鸣器音调) u16 code tone[] { 0, 330,370,414,440,494,554,622,659,740,831,880,988 }; // 小星星乐谱 u8 code star_music[] {1,1,5,5,6,6,5,4,4,3,3,2,2,1,0}; u8 code star_time[] {2,2,2,2,2,2,4,2,2,2,2,2,2,4,0}; // 两只老虎乐谱 u8 code tiger_music[] {1,2,3,1,1,2,3,1,3,4,5,0}; u8 code tiger_time[] {2,2,2,2,2,2,2,2,2,2,4,0}; // 生日快乐乐谱 u8 code birth_music[] {5,5,6,5,8,7,0,5,5,6,5,9,8,0}; u8 code birth_time[] {2,2,4,4,4,6,2,2,4,4,4,6,6,0}; /************************* 延时函数 *************************/ void delay_10us(u16 ten_us) { while(ten_us--); } void delay_ms(u16 ms) { u16 i,j; for(ims;i0;i--) for(j110;j0;j--); } void DelayUs2x(unsigned char t) { while(--t); } void DelayMs(unsigned char t) { while(t--) { DelayUs2x(245); DelayUs2x(245); } } /************************* 按键扫描 *************************/ u8 key_scan(u8 mode) { static u8 key1; if(mode)key1; if(key1(KEY10||KEY20||KEY30||KEY40)) { delay_10us(1000); key0; if(KEY10) return KEY1_PRESS; else if(KEY20) return KEY2_PRESS; else if(KEY30) return KEY3_PRESS; else if(KEY40) return KEY4_PRESS; } else if(KEY1KEY2KEY3KEY4) { key1; } return KEY_UNPRESS; } /************************* 数码管动态扫描带闪烁判断 *************************/ void smg_display(u8 dat[]) { u8 i; u8 show_data; for(i 0; i 8; i) { switch(i) { case 0: LSC1;LSB1;LSA1; break; case 1: LSC1;LSB1;LSA0; break; case 2: LSC1;LSB0;LSA1; break; case 3: LSC1;LSB0;LSA0; break; case 4: LSC0;LSB1;LSA1; break; case 5: LSC0;LSB1;LSA0; break; case 6: LSC0;LSB0;LSA1; break; case 7: LSC0;LSB0;LSA0; break; } if(blink_flag) show_data dat[i]; else { switch(set_mode) { case 1: //调当前小时 0、1闪烁 if(i0 || i1) show_data 0x00; else show_data dat[i]; break; case 2: //调当前分钟 2、3闪烁 if(i2 || i3) show_data 0x00; else show_data dat[i]; break; case 3: //闹钟小时 0、1闪烁 if(i0 || i1) show_data 0x00; else show_data dat[i]; break; case 4: //闹钟分钟 2、3闪烁 if(i2 || i3) show_data 0x00; else show_data dat[i]; break; case 5: //选音乐 第6位闪烁 if(i6) show_data 0x00; else show_data dat[i]; break; default: show_data dat[i]; break; } } SMG_SEG_PORT show_data; delay_10us(100); SMG_SEG_PORT 0x00; } } /************************* 更新显示缓存时间/闹钟分开显示 *************************/ void refresh_display(void) { if(set_mode 0 || set_mode 1 || set_mode 2) { // 正常走时 / 设置当前时间显示系统时分秒 disp_buf[0] smg_code[hour / 10]; disp_buf[1] smg_code[hour % 10]; disp_buf[2] smg_code[min / 10]; disp_buf[3] smg_code[min % 10]; disp_buf[4] smg_code[sec / 10]; disp_buf[5] smg_code[sec % 10]; } else if(set_mode 3 || set_mode 4) { // 设置闹钟显示闹钟时分秒位熄灭区分 disp_buf[0] smg_code[alarm_h / 10]; disp_buf[1] smg_code[alarm_h % 10]; disp_buf[2] smg_code[alarm_m / 10]; disp_buf[3] smg_code[alarm_m % 10]; disp_buf[4] 0x00; disp_buf[5] 0x00; } if(set_mode 5) { disp_buf[6] smg_code[music_sel]; disp_buf[7] 0x00; } else { disp_buf[6] 0x00; disp_buf[7] 0x00; } } /************************* 定时器0初始化 1ms中断 *************************/ void timer0_init(void) { TMOD | 0x01; TH0 0xFC; TL0 0x18; ET0 1; TR0 1; EA 1; } /************************* T0中断计时 闪烁节拍 *************************/ void timer0_isr() interrupt 1 { TH0 0xFC; TL0 0x18; timer_cnt; blink_cnt; if(blink_cnt 500) { blink_cnt 0; blink_flag !blink_flag; } // 秒自动走时无法手动修改 if(timer_cnt 1000) { timer_cnt 0; sec; if(sec 60) { sec 0; min; if(min 60) { min 0; hour; if(hour 24) hour 0; } } } } /************************* 蜂鸣器播放单音 *************************/ void beep_play(u16 freq, u16 time) { u16 i; u16 t 10000 / freq; for(i0; itime; i) { BEEP 0; delay_10us(t); BEEP 1; delay_10us(t); } } /************************* 播放选中音乐 *************************/ void play_music(void) { u8 i0; switch(music_sel) { case 0: //小星星 while(star_music[i]!0) { beep_play(tone[star_music[i]], star_time[i]*100); delay_ms(30); i; } break; case 1: //两只老虎 while(tiger_music[i]!0) { beep_play(tone[tiger_music[i]], tiger_time[i]*100); delay_ms(30); i; } break; case 2: //生日快乐 while(birth_music[i]!0) { beep_play(tone[birth_music[i]], birth_time[i]*100); delay_ms(30); i; } break; } } /************************* 按键处理函数时间、闹钟变量完全独立操作 *************************/ void key_handle(u8 key_val) { if(key_val KEY1_PRESS) { set_mode; if(set_mode 5) set_mode 0; blink_cnt 0; blink_flag 1; } else if(key_val KEY2_PRESS) { switch(set_mode) { case 1: hour; if(hour24) hour0; break; // 当前小时 case 2: min; if(min60) min0; break; // 当前分钟 case 3: alarm_h; if(alarm_h24) alarm_h0; break; //闹钟小时 case 4: alarm_m; if(alarm_m60) alarm_m0; break; //闹钟分钟 case 5: music_sel; if(music_sel2) music_sel0; break; } blink_cnt 0; blink_flag 1; } else if(key_val KEY3_PRESS) { switch(set_mode) { case 1: hour--; if(hour0) hour23; break; case 2: min--; if(min0) min59; break; case 3: alarm_h--; if(alarm_h0) alarm_h23; break; case 4: alarm_m--; if(alarm_m0) alarm_m59; break; case 5: music_sel--; if(music_sel255) music_sel2; break; } blink_cnt 0; blink_flag 1; } else if(key_val KEY4_PRESS) { set_mode 0; blink_cnt 0; blink_flag 1; } } /************************* 闹钟判断 *************************/ void alarm_scan(void) { if(set_mode ! 0) return; if(hour alarm_h min alarm_m) { play_music(); } else { BEEP 1; } } /************************* 主函数 *************************/ void main(void) { u8 key_val 0; BEEP 1; timer0_init(); while(1) { key_val key_scan(0); if(key_val ! KEY_UNPRESS) { key_handle(key_val); } refresh_display(); smg_display(disp_buf); alarm_scan(); } }