//DS1302时钟程序 #include <stc89c52.h> #include <intrins.h> #define uchar unsigned char #define uint unsigned int /********************************************************************/ //少占鱼制作 河北正定欢迎您 长沙航空职业技术学院 //2010 年QQ:411656434 //版权所有:www.51hei.com #define LCD1602_RS P2_5 //定义引脚 #define LCD1602_RW P2_6 #define LCD1602_E P2_7 #define LCD1602_IO P0 #define Busy 0x80 //用于检测LCD1602状态字中的Busy标识 const uchar num[]=" 3456789+"; uchar code a[] = {" E N v"}; void WriteData(uchar DAT); void WriteCommand(uchar command,BuysC); uchar ReadData(void); void Readbusy(void); void LCD1602_Init(void); void DisplayOneChar(uchar X, uchar Y, uchar DData); void Displaystring(uchar X, uchar Y, uchar *DData); void Delay5Ms(void); void Delay400Ms(void); void delayms(uint); void delays(uint m) ; void delayus(uchar x) ; sbit DS1302_CLK =P2^0;// P1^0; sbit DS1302_IO =P2^1;// P1^1; sbit DS1302_RST =P2^2;// P1^2; uchar shuju; unsigned char time[]={0x11,0x03,0x28,0x01,0x12,0x39,0x00};// 初始化时间年月日星期时分秒 uchar Display_Buffer[12]={"12:25:00"};//时、分、秒 的格式 uchar riqi[12]={"11-03-28"};//年、月、日 的格式 uchar xingqi[5]="week";//星期 unsigned char second,minute,hour,week,day,month,year; /******************1602函数********************************/ /********************************************************************/ void delays(uint m) { uint i,j; for(i=0;i<m;i++) { for(j=0;j<1000;j++) {;} } } void Delay(void) { unsigned char i; unsigned int j; for(i=0;i<200;i++) for(j=300;j>0;j--); } /***************************************************************************** 函数功能:向DS1302送一字节数据子程序 入口参数: 出口参数: *****************************************************************************/ void InputByte(unsigned char BYTE) { char i; for(i=8;i>0;i--) { //前面在read_ds1302()函数里已经把CLK清0了 DS1302_CLK=0;//这里再清0一次,看着清楚。呵呵 _nop_(); DS1302_IO=(bit)(BYTE&0x01);//取出低位数据给数据引脚 _nop_(); _nop_(); DS1302_CLK=1;//置1,时钟上升沿,引脚上的数据传入1302寄存器中 _nop_(); _nop_(); BYTE>>=1;//因为这里移位的BYTE不是最后要的结果,虽然最后循环完会多移一位,但没影响。 _nop_(); //循环最后一次后,时钟状态是高电平。 } _nop_(); _nop_(); } /***************************************************************************** 函数功能:读DS1302一个字节子程序,读数据是下降沿读 入口参数: 出口参数: *****************************************************************************/ unsigned char OutputByte(void) { unsigned char i; unsigned char ucdat=0; for(i=8;i>0;i--) { //前面时钟状态是高电平。 ucdat>>=1; //移位注意,移位不能放在循环语句末,否则最后循环完会多移一位。得到结果就错了。 DS1302_IO=1;//单片机端置高防止破坏1302传来的数据,开始接收1302端的高低数据对数据线的改变。 DS1302_CLK=0;//紧接着时钟变低,下降沿数据输出。 if(DS1302_IO)//数据已经输出,判断是1还是0. ucdat|=0x80;//如果引脚是高则此位保存1。低不保存,右移的时候自然补0了。 _nop_(); DS1302_CLK=1;//读完一位数据,再置高时钟,准备读下一个。 _nop_();//延时一下。 } //读完数据还是把时钟清0,下次好直接用。 DS1302_CLK=0;//读完数据还是把时钟清0,下次好直接用。 _nop_(); _nop_(); DS1302_RST=0; _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); DS1302_CLK=0; _nop_(); _nop_(); _nop_(); _nop_(); DS1302_RST=0; _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); return(ucdat); } /***************************************************************************** 函数功能:向DS1302某地址写一字节数据子程序 入口参数:addr,TDat 出口参数: *****************************************************************************/ void write_ds1302(unsigned char addr,unsigned char TDat) { DS1302_RST=0; _nop_(); _nop_(); DS1302_CLK=0; _nop_(); _nop_(); DS1302_RST=1; _nop_(); _nop_(); _nop_(); _nop_(); InputByte(addr);//先写地址 _nop_(); _nop_(); InputByte(TDat);//再写数据 DS1302_IO=1;//单片机端置高防止破坏1302传来的数据,开始接收1302端的高低数据对数据线的改变。 DS1302_RST=0; delayus(10); DS1302_CLK=0; delayus(10); } /***************************************************************************** 函数功能:读DS1302地址子程序 入口参数:add 出口参数:timedata *****************************************************************************/ unsigned char read_ds1302(unsigned char addr) { unsigned char timedata; DS1302_RST=0; _nop_(); DS1302_CLK=0; _nop_(); DS1302_RST=1; InputByte(addr);//先写入读命令 timedata=OutputByte();//再读出数据 DS1302_IO=1;//单片机端置高防止破坏1302传来的数据,开始接收1302端的高低数据对数据线的改变。 DS1302_RST=0; delayus(10); DS1302_CLK=0; delayus(10); return(timedata); } /***************************************************************************** 函数功能:初始化DS1302子程序 入口参数:time[](全局变量) 出口参数: *****************************************************************************/ void initial_ds1302() { DS1302_IO=1;//单片机端置高防止破坏1302传来的数据,开始接收1302端的高低数据对数据线的改变。 DS1302_RST=0; delayms(100); DS1302_CLK=0; delayms(100); write_ds1302(0x8e,0x00); //写保护寄存器,在对时钟或RAM写前WP一定要为0 write_ds1302(0x8c,time[0]); //年 write_ds1302(0x88,time[1]); //月 write_ds1302(0x86,time[2]); //日 write_ds1302(0x8A,time[3]); //星期 write_ds1302(0x84,time[4]); //时 write_ds1302(0x82,time[5]); //分 write_ds1302(0x80,time[6]); //秒 write_ds1302(0x8e,0x80); //写保护寄存器 } /***************************************************************************** 函数功能:读DS1302时间子程序 入口参数: 出口参数:全局变量(second,minute,hour,week,day,month,year) *****************************************************************************/ void read_time() { second=read_ds1302(0x81); //秒寄存器 _nop_(); minute=read_ds1302(0x83); //读分 _nop_(); hour=read_ds1302(0x85); //读时 _nop_(); week=read_ds1302(0x8B); //读星期 _nop_(); day=read_ds1302(0x87); //日 _nop_(); month=read_ds1302(0x89); //月 _nop_(); year=read_ds1302(0x8d); //年 _nop_(); } void main(void) {uchar shuju; delayms(2); Delay400Ms(); //启动等待,等LCM讲入工作状态 delays(1); LCD1602_Init(); //LCM初始化 Delay5Ms(); //延时片刻(可不要) DS1302_RST=0; delayms(1000); DS1302_CLK=0; delayms(1000); shuju=ReadData(); Displaystring(2, 0, a); Delay();Delay(); delayms(2000); Displaystring(3,0,riqi); Displaystring(12,0,xingqi); delayms(4000); // initial_ds1302(); //初始化DS1302 delayms(1200); while(1) { read_time();//读取时间 Display_Buffer[0]=hour/16+'0';//存储下时的高位进数组 Display_Buffer[1]=hour%16+'0';//存储下时的低位进数组 Display_Buffer[3]=minute/16+'0';//注意+'0'后存储的是ASCII。用于下面液晶显示。 Display_Buffer[4]=minute%16+'0';//注意+'0'后存储的是ASCII。用于下面液晶显示。 Display_Buffer[6]=second/16+'0'; Display_Buffer[7]=second%16+'0'; riqi[0]=year/16+'0'; riqi[1]=year%16+'0'; riqi[3]=month/16+'0'; riqi[4]=month%16+'0'; riqi[6]=day/16+'0'; riqi[7]=day%16+'0'; Displaystring(3,0,riqi); delayms(1); DisplayOneChar(14,1,week+'0'); Displaystring(3,1,Display_Buffer); } } //写数据 void WriteData(uchar DAT) { Readbusy(); LCD1602_RS = 1; LCD1602_RW = 0; LCD1602_IO = DAT; LCD1602_E = 0; //若晶振速度太高可以在这后加小的延时 LCD1602_E = 0; //延时 LCD1602_E = 1; LCD1602_E=1; LCD1602_E=0; } //写指令 void WriteCommand(uchar command,BuysC) //BuysC为0时忽略忙检测 { LCD1602_IO=0x00; if (BuysC) Readbusy(); //根据需要检测忙 LCD1602_RS = 0; LCD1602_RW = 0; LCD1602_IO = command; LCD1602_E = 0; LCD1602_E = 0; LCD1602_E = 1; LCD1602_E = 1; LCD1602_E=0; } //读数据 uchar ReadData(void) { Readbusy(); LCD1602_RS = 1; LCD1602_RW = 1; LCD1602_E = 0; LCD1602_E = 0; LCD1602_E = 1; delayms(1); LCD1602_E = 0; return(LCD1602_IO); } //读忙 void Readbusy(void) { LCD1602_IO = 0xff; LCD1602_RS = 0; LCD1602_RW = 1; LCD1602_E = 0; LCD1602_E = 0; LCD1602_E = 1; delayms(2); while (LCD1602_IO&0x80); //检测忙信号 LCD1602_E = 0; } //初始化 void LCD1602_Init(void) //LCM初始化 { Delay400Ms(); LCD1602_E = 0; LCD1602_IO=0x00; WriteCommand(0x38,0); //三次显示模式设置,不检测忙信号 Delay5Ms(); WriteCommand(0x38,0); Delay5Ms(); WriteCommand(0x38,0); Delay5Ms(); WriteCommand(0x38,1); //显示模式设置,开始要求每次检测忙信号 Delay5Ms(); WriteCommand(0x08,1); //关闭显示 ] Delay5Ms(); WriteCommand(0x01,1); //显示清屏 Delay5Ms(); WriteCommand(0x06,1); // 显示光标移动设置 Delay5Ms(); WriteCommand(0x0f,1); // 显示开及光标设置 Delay5Ms(); } //按指定位置显示一个字符 void DisplayOneChar(uchar X, uchar Y, uchar DData) { Y &= 0x01; X &= 0x0f; //限制X不能大于15,Y不能大于1 if (Y) X |= 0x40; //当要显示第二行时地址码+0x40; X |= 0x80; // 算出指令码 WriteCommand(X, 0); //这里不检测忙信号,发送地址码 WriteData(DData); } //按指定位置显示一串字符 void Displaystring(uchar X, uchar Y, uchar *DData) { uchar ListLength; ListLength = 0; Y &= 0x01; X &= 0x0f; //限制X不能大于15,Y不能大于1 while (DData[ListLength]>0x20) //若到达字串尾则退出 { if (X <= 0x0f) //X坐标应小于0x0f { DisplayOneChar(X, Y, DData[ListLength]); //显示单个字符 ListLength++; X++; } } } //5ms延时 void Delay5Ms(void) { unsigned int t = 5552; while(t--); } //400ms延时 void Delay400Ms(void) { uchar i = 5; uint j; while(i--) { j=7269; while(j--); } } // void delayms(uint k) { uint data i,j; for(i=0;i<k;i++) { for(j=0;j<121;j++) {;} } } void delayus(uchar x) { while(--x); }