单片机复位后会先检测年份是否是2017年,不是就会复位1302,是2017年就不初始化1302。采用8位数码管来显示时间,日历,用的是ds1302时钟芯片,
S2是设置键,在任何时候按下超过2MS都可以进入设置函数。
S3在正常走时时按下,会显示日期,日期格式是2017.10.03;松开S3后日期会延时显示约6秒。
在设置功能里面,S3是数字减,S4是数字加。设置功能会在没有按下按键后开始延时,延时约6秒
即可退出设置。
S2设置的顺序是秒,分,时,日,月,年,退出,秒,分,。。。。循环,未按按键会自动退回到主界面
上机测试请用杜邦线将时钟模块的TSCLK,TIO,TRST,分别对应连接到P33,P34,P35,
连接方式不能与按键,串行通讯口,显示用的数据口相冲突,可自己在程序上方sbit的位置进行修改。
软件大都加了注解,不明白可以多交流。
本程序上只要稍加一点改动即可加入闹钟,有兴趣的朋友可以自己修改。
单片机源程序如下:
- //显示格式24-35-25 ,小时-分钟-秒钟, 按下S3后,显示年月日,2017.09.10,延时6秒后返回
- #include <reg52.h>
- #include <intrins.h>
- #define uchar unsigned char
- #define uint unsigned int
- #define sce timedate[1]
- #define min timedate[2]
- #define hour timedate[3]
- #define date timedate[4]
- #define week timedate[6]
- #define month timedate[5]
- #define year timedate[7]
- uchar displaychar1[8]={0,1,2,3,4,5,6,7},a,ya,ja ; //a结尾的字符都用来专门延时,a通用,ya日期延时程序用
- uchar commandchar[9]={0,0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e}; //与timedate对应,分别控制每个寄存器的写,读时数组+1,第8个数是控制写保护位
- uchar timedate[8],sdat,seta,m;
- bit s,s2,s3,s4,K,setbit,sk;
- sbit d = P2^6;
- sbit w = P2^7;
- sbit S2 = P3^0;
- sbit S3 = P3^1;
- sbit S4 = P3^2;
- sbit SCLK = P3^3;
- sbit TIO = P3^4;
- sbit TRST = P3^5;
- uchar LD = 1; //************数码管,亮度调节,0最小,10最高,越亮越闪 因会影响开始信号查询时间,建议最高设置为3**************
- unsigned char code duan[]=
- { 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40,0x00 }; //段选码0-9
- void timeout(uchar a)
- {
- uchar b;
- for(;a>0;a--)
- for(b=114;b>0;b--);
- }
- void timeout5us()
- {
- _nop_();
- }
- void display() //两个数,分别记录键值和重复码个数
- {
-
- P0 = 0xfe; w = 1; w = 0; //显示1位
- P0 = (displaychar1[0]); d = 1; d = 0;
- timeout(LD);
- P0 = 0; d = 1; d = 0; //清除段选
-
- P0 = 0xfd; w = 1; w = 0; //显示2位
- P0 = displaychar1[1]; d = 1; d = 0;
- timeout(LD);
- P0 = 0; d = 1; d = 0; //清除段选
- P0 = 0xfb; w = 1; w = 0; //显示3位
- P0 = displaychar1[2]; d = 1; d = 0;
- timeout(LD);
- P0 = 0; d = 1; d = 0; //清除段选
- P0 = 0xf7; w = 1; w = 0; //显示4位
- P0 = displaychar1[3]; d = 1; d = 0;
- timeout(LD);
- P0 = 0; d = 1; d = 0; //清除段选,否则下一个位选变化后由于段选没变会将此位信息显示到下一个位。
-
- P0 = 0xef; w = 1; w = 0; //显示5位
- P0 = displaychar1[4]; d = 1; d = 0;
- timeout(LD);
- P0 = 0; d = 1; d = 0; //清除段选
-
- P0 = 0xdf; w = 1; w = 0; //显示6位
- P0 = displaychar1[5]; d = 1; d = 0;
- timeout(LD);
- P0 = 0; d = 1; d = 0; //清除段选
- P0 = 0xbf; w = 1; w = 0; //显示7位
- P0 = displaychar1[6]; d = 1; d = 0;
- timeout(LD);
- P0 = 0; d = 1; d = 0; //清除段选
- P0 = 0x7f; w = 1; w = 0; //显示8位
- P0 = displaychar1[7]; d = 1; d = 0;
- timeout(LD);
- P0 = 0; d = 1; d = 0; //清除段选,否则下一个位选变化后由于段选没变会将此位信息显示到下一个位。
-
- }
- /************************************************************************************
- -------------------------------以下是与时钟芯片的数据时序---------------------------*/
- //写控制和一个字节
- void sendchar(uchar csdat,uchar sdat)
- { SCLK = 0;
- TRST = 1;
- for(a=0;a<8;a++)
- {
- SCLK = 0;
- TIO = (csdat%2);
- SCLK = 1;
- csdat >>=1;
- }
- for(a=0;a<8;a++)
- {
- SCLK = 0;
- TIO = (sdat%2);
- SCLK = 1;
- sdat >>=1;
- }
- SCLK = 0;
- TRST = 0;
- }
- //读一个字节
- uchar receviechar(uchar crdat)
- {
- uchar rdat;
- TRST = 1;
- // TIO = 1;
- TRST = 1;
- for(a=0;a<8;a++)
- {
- SCLK = 0;
- TIO = (crdat%2);
- SCLK = 1;
- crdat >>=1;
- }
- for(a=0;a<8;a++)
- {
- SCLK = 1;
- rdat >>= 1;
- SCLK = 0;
-
- if(TIO)
- rdat = rdat|0x80;
- }
- SCLK = 0; TRST = 0;
- return rdat ;
- }
- //读取时分秒
- void readtime()
- {
- sendchar(0x8e,0);
- hour = receviechar(0x85);
- min = receviechar(0x83);
- sce = receviechar(0x81);
- sendchar(0x8e,0x80); //
-
- }
- //读取年月日
- void readyear()
- {
- sendchar(0x8e,0);
- date = receviechar(0x87);
- month = receviechar(0x89);
- year = receviechar(0x8d);
- sendchar(0x8e,0x80); //
-
- }
- //=========================键盘扫描,用定时器延时================================
- bit jianpan(K)
- {
- if((!K)&TF0) //如果S2按下且计时器0溢出
- {
- TF0 = 0;
- ja++;
- if(ja>=2)
- {// P1 = 0;
- TR0 = 0; //每次按下后都会关闭定时器,后面的程度会检测松手,松开按键后会重新打开定时器,可以保护每次按下只起作用一次
- ja = 0;
- s = 1;
- }
- return s;
- }
- return s;
- }
- //===========================================================================================================
- //**********************************************设置时间日期子函数*******************************************
- //===========================================================================================================
- void settime()
- {
- // uchar sdat,seta;
- // bit sk;
- setbit=1; //设置控制标识,进本函数后先就让标识打开,在下面延时跳出前都会一直跳入设置功能函数
- if(TF1&(s2==s3==s4==0)) //没有按键按下时用定时器1来延时跳出
- {
- TF1=0;
- seta ++;
- }
- if(!(s2==s3==s4==0)) seta = 0; //延时时间超过一定值,或者有按键被按下(注意!这个符号)时,时间会被延长
- if(s3); //P1++; //P1++无用:调试软件时检测运行的次数
- if(seta>=70)
- {
- setbit=0; //关闭设置
- sdat = 0;
- }
- if(sk!=s2) //sk用来做s2的中间暂存数据,用来用来识别S2是否变化过
- {
- sk = s2;
- if(s2)
- sdat ++; //每次进行一次S2动作时才自加一次SDTA
- if(sdat ==6)sdat=7; //跳过星期,本程序不显示星期
- }
- m = timedate[sdat]/16*10+timedate[sdat]%16; //m用10进制存下
- if(s3) m--;
- if(s4) m++;
- switch (sdat) //每次进入设置的对应操作判断
- {
- case 1:
- if(m>59) m = 0; break;
- case 2:
- if(m>59) m = 0; break;
- case 3:
- if(m>23) m = 0; break;
- case 4:
- if(timedate[5]==4|timedate[5]==6|timedate[5]==9|timedate[5]==11) //小月
- {
- if(m>30) m = 1; break;
- }
- if(timedate[5]==2&(timedate[7]%4==0)) //闰年2月
- {
- if(m>29) m=1; P1++; break; //
- }
- if(timedate[5]==2&(timedate[7]%4!=0)) //平年2月
- {
- if(m>28) m=1; break;
- }
- if(m>31) m =1; break; //其它月分情况
- case 5:
- if(m>12) m = 1; break;
- case 6: sdat++; //跳过星期本程序不显示星期
- if(m>7) m = 1; break;
- case 7:
- if(m>99) m = 0; break;
- default : /****不在判断范围的进行初始化处理*/
- sdat = 0; sk=0; setbit = 0;s2 = 0;// P1++; //本程序所有P1++都是测试软件运行情况用 //每次在不满足条件时自动让数据初始化,
- break;
- //s2=0是优化点,在按下S2后按键标识不会清零,松手时程序已重复运行了很多遍,所以会重新跳回设置的第一位,加本命令可以让设置判断失效,不进设置页。
- }
- //==================================
- //setbit为1则说明以上有成立的条件,需要发送修改数据
- if(setbit)
- {
- sendchar(0x8e,0); //去写保护
- sendchar(commandchar[sdat],m/10*16+m%10); //将改好的数字发送到1302
- sendchar(0x8e,0x80); //加写保护
-
- if(seta%5>2&seta<50)
- timedate[sdat] = 0xbb; //设置延时操作期间控制闪烁时间
- } //显示空,对应段选的两位11;0xbb/16=11,0xbb%16=11 ,而上面段选数组中第12个数是显示空
- //写程序时用的测试语句,无用时未删供复习时看看当时的思路
- /* switch (sdat)
- {
- case 1:
- P1=0xfe;
- m = sce;
- if(s4) m++;
- if(s3) m--;
-
- break;
- case 2:
- P1=0xfd; break;
- case 3:
- P1=0xfb; break;
- case 4
- :P1=0xf7; break;
- case 5:
- P1=0xef; break;
- case 6:
- P1=0xdf; break;
- case 7:
- P1=0xbf; break;
- case 8:
- P1=0x7f; break;
- default :
- sdat = 0;sk=0; //每次在不满足条件时自动让数据初始化,
- break;
- } */
- }
- //===================================================================================
- //============================== 主函数==============================================
- main()
- {
-
- SCLK = 0 ;
- TRST = 0 ;
- readyear(); //读取年月日
- if(year!=0x17) //如果不是2017年,就初始化1302 ,给初值是17年10月03日,12:11:15
- {
- sendchar(0x8e,0);
- sendchar(0x80,0x15);
- sendchar(0x82,0x11);
- sendchar(0x84,0x12);
-
- sendchar(0x86,0x03);
- sendchar(0x88,0x10);
- sendchar(0x8c,0x17);
- sendchar(0x8e,0x80);
- }
- TMOD = 0x12; //设置定时器工作模式
- TR1 = 1;
- TR0 = 1;
- while(1)
- {
- if(!S2) s2 = jianpan(S2);
- if(!S3) s3 = jianpan(S3);
- if(!S4) s4 = jianpan(S4);
-
- TRST = 0;
- /*以下是开始年月日自动归零的问题修改,问题产生原因是开始没有加!setbit这个条件,程序在运行到设置年月日时依然会进
- 入下面这个判断,然后再进入设置,但是年月日在这里还没有读数,所以这里的设置就会进入M=0然后加或减,从而让设置值
- 出现错误
- */
- if((!ya&(!setbit)&(!s3))|(setbit&(sdat<4))) //如果S2没有被按下且不在显示年月日的延时范围,而且设置标识为0时,或者在标识为1但是设置值在123中时,则正常显示时间格式
- {
- readtime(); //P1++; //先读一下时间然后再判官设置程序,这样的主要目的是让初m的值由1302决定。
- if(s2|setbit) //按下S2或设置标识打开时会进入本判断
- {
- ya = 0;
- settime();
- }
- displaychar1[0] = duan[hour/16];
- displaychar1[1] = duan[hour%16];
- displaychar1[2] = duan[10];
- displaychar1[3] = duan[min/16];
- displaychar1[4] = duan[min%16];
- displaychar1[5] = duan[10];
- displaychar1[6] = duan[sce/16];
- displaychar1[7] = duan[sce%16];
- }
- if(((!setbit)&s3)|ya>0|(setbit&sdat>3))
- {
- if(TF1&!setbit)
- {
- ya++; //非设置页面才进去日期延时
- TF1 = 0;
- }
- readyear();
- if(s2|setbit) //按下S2或设置标识打开时会进入本判断
- {
- ya = 0; //进设置后会关闭日期显示延时
- settime();
- }
- displaychar1[0] = duan[2];
- displaychar1[1] = duan[0];
- displaychar1[2] = duan[year/16];
- displaychar1[3] = duan[year%16]|0x80;
- displaychar1[4] = duan[month/16];
- displaychar1[5] = duan[month%16]|0x80;
- displaychar1[6] = duan[date/16];
- displaychar1[7] = duan[date%16];
- if(ya>70) ya =0;
- //日期延时约6秒,大约时间,与计时器和程序的运行有关。
- } //P1=s; P1=~P1;
- s3 = s4 =0; // P1 = 255;
- if(S2==S3==S4==1)
- { // P1=255;
- ja = 0;
- TR0 = 1;
- s2= s3= s4= 0; //松开按键后恢复计时器并清零按键值
- s = 0; //为了快速跳时,s清零放在了松手检测中,s3和s4在按下不放后会重复跳值,想要每次按下只起作用一次
- } //的话,请将s放在本循环外面这个循环后面
- for(a=0; a<50;a++) //一个循环显示50次数码管
- display();
- }
- }
复制代码
所有资料51hei提供下载:
1302显示可调时间日期.rar
(218.59 KB, 下载次数: 181)
|