引言
本设计以AT89S52单片机为控制核心,时钟芯片DS1302提供时钟源,配合LCD1602液晶显示模块,组成基本硬件系统,同时利用HC-05嵌入式蓝牙串口通讯模块,可在手机端进行日期、时间的校准。具有走时精确,功耗低,显示直观,调整简单方便等优点。
基本显示:
第一行为日期,依次为:年-月-日-星期
第二行为时间,依次为:时:分:秒
说明:
每月的天数,闰年的天数可自动调整
蓝牙校准日期:
命令d+年月日星期+#,如设置2016年12月8日星期四,手机发送命令:d16120804
蓝牙校准时间:
命令t+时分秒+#,如设置15:11:46,手机发送命令:t151146#
硬件电路设计
AT89S52单片机介绍:AT89S52_百度百科
DS1302时钟芯片介绍:DS1302_百度百科
LCD1602液晶模块介绍:LCD1602_百度百科
HC-05嵌入式蓝牙串口通讯模块介绍:HC-05
1 #include2 #include 3 #include 4 #include 5 void serial_initial() 6 { 7 EA = 1; //开总中断 8 ES = 1; //开串口中断 9 TMOD = 0x20; //定时器1,方式2,8位自动重装,作波特率发生器 10 TH1 = 0xfa; //波特率9600 11 TL1 = 0xfa; 12 TR1 = 1; 13 SCON = 0x50; //串口工作方式1,允许接收 14 PCON = 0x80; //SMOD=1,bps倍增9600 15 } 16 void clock_initial() 17 { 18 lcd_initial(); //1602初始化 19 serial_initial(); //中断初始化 20 Set_DS1302_Time(0x80); //先写入寄存器起始地址设置时钟日历初值 21 dis_string(0x80,"Date:11-11-11-11"); 22 dis_string(0xc0,"Time: 11:11:11 "); 23 flag_date = 0; 24 flag_time = 0; 25 bt_flag = 0; 26 } 27 28 void main() 29 { 30 clock_initial(); 31 while(1) 32 { 33 if(flag_date == 1&&cmd_end==1) 34 date_set(); 35 if(flag_time == 1&&cmd_end==1) 36 time_set(); 37 Read_DS1302_Time(0x81); 38 dis_twonum (5, 0, TimeData[6]); //年 39 dis_twonum (8, 0, TimeData[4]); //月 40 dis_twonum (11, 0, TimeData[3]); //日 41 dis_twonum (14, 0, TimeData[5]); //星期 42 dis_twonum (6, 1, TimeData[2]); //时 43 dis_twonum (9, 1, TimeData[1]); //分 44 dis_twonum (12, 1, TimeData[0]); //秒 45 Delay_Ms(1000); //延时 46 } 47 } 48 49 50 void serial_int() interrupt 4 51 { 52 uchar temp; //定义串口接收数据变量 53 54 ES = 0; //关串口中断 55 RI = 0; //令接收中断标志位为0(软件清零) 56 temp = SBUF; //将接收到的数据送入变量 temp 57 data_deal(temp); //对收到数据进行处理 58 ES = 1; //开串口中断 59 /*SBUF = temp; //将接收的数据发送回去 60 while(TI == 0); 61 TI = 0; */ 62 }
1 sbit TSCLK = P2 ^ 0; //时钟 2 sbit TIO = P2 ^ 1; //数据 3 sbit TRST = P2 ^ 2; //使能 4 5 //时钟日历暂存数组,秒、分、时、日、月、周、年 初值为= 16年2月15日 周1 23:58:50 6 uchar TimeData[7] = {50, 58, 23, 15, 2, 1, 16}; 7 void Delay_Ms(uint ms) 8 { 9 uint i; 10 do { 11 i = 11059200 / 96000; 12 while(--i); //96T per loop 13 } while(--ms); 14 } 15 /*写一字节*/ 16 void DS1302_W_Byte(uchar dat) 17 { 18 uchar i; 19 for(i = 0; i < 8; i++) //每次写1bit,写8次 20 { 21 TSCLK = 0; //拉低时钟总线 22 TIO = dat & 0x01; //从一字节最低位开始写 23 TSCLK = 1; //拉高时钟总线,DS1302把数据读走 24 dat >>= 1; //数据右移一位 25 } 26 } 27 /*读一字节*/ 28 uchar DS1302_R_Byte() 29 { 30 uchar i, dat; 31 for(i = 0; i < 8; i++) 32 { 33 TSCLK = 0; 34 dat >>= 1; 35 if(TIO) dat |= 0x80;//读取数据 36 TSCLK = 1; 37 } 38 return dat; //返回读取的数据 39 } 40 /*写数据,命令*/ 41 void DS1302_W_DAT(uchar cmd, uchar dat) 42 { 43 TRST = 0; 44 TSCLK = 0; 45 TRST = 1; 46 DS1302_W_Byte(cmd); 47 DS1302_W_Byte(dat); 48 } 49 /*读数据*/ 50 uchar DS1302_R_DAT(uchar cmd) 51 { 52 uchar dat; 53 TRST = 0; 54 TSCLK = 0; 55 TRST = 1; 56 DS1302_W_Byte(cmd); 57 dat = DS1302_R_Byte(); 58 return dat; 59 } 60 61 /*清除写保护*/ 62 void DS1302_Clear_WP() 63 { 64 DS1302_W_DAT(0x8e, 0x00); 65 } 66 /*设置写保护*/ 67 void DS1302_Set_WP() 68 { 69 DS1302_W_DAT(0x8e, 0x80); 70 TRST = 0; 71 TSCLK = 0; 72 } 73 /*时间预设*/ 74 void Set_DS1302_Time(uchar addr) 75 { 76 uchar i, j; 77 DS1302_Clear_WP(); //清除写保护 78 for(i = 0; i < 7; i++) //写入7个字节的时钟初始值 79 { 80 j = TimeData[i] / 10; //BCD码转换 81 TimeData[i] %= 10; 82 TimeData[i] += j * 16; 83 DS1302_W_DAT(addr, TimeData[i]); 84 addr += 2; 85 } 86 DS1302_Set_WP(); //开写保护 87 } 88 /*读取1302时间*/ 89 void Read_DS1302_Time(uchar addr) 90 { 91 uchar i; 92 DS1302_Clear_WP(); 93 for(i = 0; i < 7; i++) //从DS1302读取7个字节的时钟日历数据 94 { 95 TimeData[i] = DS1302_R_DAT(addr); 96 addr += 2; 97 } 98 DS1302_Set_WP(); 99 } 100
1 #define uint unsigned int 2 #define uchar unsigned char 3 sbit RS = P2 ^ 3; 4 sbit RW = P2 ^ 4; 5 sbit EN = P2 ^ 5; 6 7 void check_busy() //检测是否忙 8 { 9 uchar dat; 10 P0 = 0xff; 11 RS = 0; 12 RW = 1; 13 do 14 { 15 EN = 1; 16 dat = P0; 17 EN = 0; 18 } while(dat & 0x80); 19 } 20 21 void write_cmd(uchar cmd) //写命令 22 { 23 check_busy(); 24 RS = 0; 25 RW = 0; 26 P0 = cmd; 27 EN = 1; 28 EN = 0; 29 } 30 31 void write_data(uchar dat) //写数据 32 { 33 check_busy(); 34 RS = 1; 35 RW = 0; 36 P0 = dat; 37 EN = 1; 38 EN = 0; 39 } 40 /*显示一位数字*/ 41 void dis_onenum (uchar X, uchar Y, uchar dat) 42 { 43 if(Y) X |= 0X40; 44 X |= 0X80; 45 write_cmd(X); 46 write_data(dat); 47 } 48 /*显示两位数字*/ 49 void dis_twonum (uchar X, uchar Y, uchar dat) 50 { 51 dis_onenum (X, Y, dat / 16 + '0'); 52 dis_onenum (X+1, Y, dat % 16 + '0'); 53 } 54 /*显示字符串*/ 55 void dis_string(uchar location,uchar *p) 56 { 57 write_cmd(location); 58 while(*p) 59 { 60 write_data(*p); 61 p++; 62 } 63 } 64 65 void lcd_initial() 66 { 67 write_cmd(0x38); 68 write_cmd(0x0c); 69 write_cmd(0x06); 70 write_cmd(0x01); 71 } 72
1 uchar date[20],time[20]; 2 uchar cmd_index, cmd_start, cmd_end, bt_flag; 3 bit flag_time, flag_date; 4 5 void time_set() 6 { 7 uchar a; 8 uchar i, j, addr = 0x80; 9 for(a = 1; a < 7; a++) 10 time[a] = time[a] - 0x30; //数据处理 11 TimeData[2] = (time[1] * 10 + time[2]) % 24; //小时 12 TimeData[1] = (time[3] * 10 + time[4]) % 60; //分钟 13 TimeData[0] = (time[5] * 10 + time[6]) % 60; //秒 14 15 DS1302_Clear_WP(); //清除写保护 16 for(i = 0; i < 3; i++) //写入时间设置值 17 { 18 j = TimeData[i] / 10; //BCD码转换 19 TimeData[i] %= 10; 20 TimeData[i] += j * 16; 21 DS1302_W_DAT(addr, TimeData[i]); 22 addr += 2; 23 } 24 DS1302_Set_WP(); //开写保护 25 flag_time = 0; 26 } 27 28 void date_set() 29 { 30 uchar a; 31 uchar i, j, addr = 0x86; 32 for(a = 1; a < 9; a++) 33 date[a] = date[a] - 0x30; //对数据处理 34 TimeData[3] = (date[5] * 10 + date[6]) ; //日 35 TimeData[4] = (date[3] * 10 + date[4]) ; //月 36 TimeData[6] = (date[1] * 10 + date[2]) ; //年 37 TimeData[5] = (date[7] * 10 + date[8]) ; //周 38 39 DS1302_Clear_WP(); 40 for(i = 3; i < 7; i++) //写入日期设置值 41 { 42 j = TimeData[i] / 10; //BCD码转换 43 TimeData[i] %= 10; 44 TimeData[i] += j * 16; 45 DS1302_W_DAT(addr, TimeData[i]); 46 addr += 2; 47 } 48 DS1302_Set_WP(); //开写保护 49 flag_date = 0; 50 } 51 52 void data_deal(uchar data_buf) 53 { 54 switch(data_buf) 55 { 56 case 'd': 57 cmd_start = 1; 58 cmd_end = 0; 59 flag_date = 1; 60 cmd_index = 0; 61 bt_flag = 1; 62 break; 63 case 't': 64 cmd_start = 1; 65 cmd_end = 0; 66 flag_time = 1; 67 cmd_index = 0; 68 bt_flag = 2; 69 break; 70 case '#': 71 cmd_start = 0; 72 cmd_end = 1; 73 break; 74 default:break; 75 } 76 if (cmd_start == 1) 77 { 78 switch (bt_flag) 79 { 80 case 1: 81 date[cmd_index] = data_buf; 82 cmd_index++; 83 break; 84 case 2: 85 time[cmd_index] = data_buf; 86 cmd_index++; 87 break; 88 default:break; 89 } 90 if (cmd_index >= 20) 91 cmd_index = 0; 92 } 93 }
2016.12.05——2016.12.07三天的单片机课程设计结束了,我的课程设计是——《基于AT89S52单片机和DS1302时钟芯片的电子钟(可手机蓝牙校准)》,此次设计让我对AT89S52单片机有了更深的认识,为了完成设计,我又学习了DS1302时钟芯片时间写入,时间读取等基本的读写操作时序,对DS1302的操作方法有了一定了解。
当液晶时钟第一次被成功的校准时,我很激动和兴奋,也很有成就感。由于使用了蓝牙串口校准时间,所以又复习了串口通讯的原理,在调试的过程中,对串口数据的处理也有了一定的经验:由于串口是一个一个字符接收的,当接收到下一个字符时,上一个字符就会丢失,为了保存接收到的每个字符,先将接收到的数据存放到数组中,然后数组指针加一,这样串口接收的每一个数据就会按照接收的先后顺序有序的存放到数组中,然后数组中的元素就可以被我们拿来进行各种操作了,如本设计中的设置时间和日期。
由于课程设计的时间较短,所以此设计仅在时间显示的基础上,增加了蓝牙串口校准时间功能,此设计还有很多可以完善和改进的功能,如:
1.增加闹钟功能,增加按键校准时间日期、设置闹钟功能,
3.增加星期计算功能,调整时只需调整年月日,调整完毕后自动计算出星期几,
4.增加DS18B20温度传感器实时显示温度,手机可远程获取温度,
5.星期显示为英文缩写,如周一:Mon,
6.简单报警功能, 等其他功能,这些功能都是可以实现的。
虽然单片机课程设计结束了, 但我的学习之路还没有结束,我将继续学习51单片机的其他扩展功能,以及其他的通讯协议,如IIC通讯,SPI通讯,红外通信,WiFI无线通讯,为以后学习性能更强大的单片机做个准备,MCS-51系列单片机虽然是低功耗可编程处理器,但它也只是8位微处理器,它的指令执行速度、驱动能力、程序存储等其他片内资源都是非常有限的,已经不能满足当今科技发展的需要,所以为了更好的发展,还要学习其他性能更强大的单片机。课程设计结束了,还要感谢老师的的指导我才能完成课程设计,衷心的感谢老师的帮助和提出的建议。
今天开通了博客园的个人博客,因为之前是用的新浪博客,没有插入代码的功能,所以排版非常乱,现在有了这个博客就可以美美的插入代码了,哈哈,就把上学期做的课程设计作为新家的第一篇文章吧!
2017-03-12 18:24:01
欢迎大家关注我的个人博客 http://www.wangchaochao.top/
微信扫码关注我的公众号
不定期更新个人学习笔记和技术总结,欢迎大家互相学习交流!