程序: main函数: /* 程序功能:DS1302实时时钟显示 作者:朱波 时间:2012年2月3日 通信方式:3线串行通信 使用说明:本程序模仿家用万年历,能够进行时间的设定 时间设定方法:1.按下设置键进入设置模式 2.按选择键对要设置的项进行选择 3.按加一键对选择项加一 4.完成全部选择后出现"please check OK!" 此时按下确定键就回到时间模式 备注:可以通过主函数中的宏定义进行时间设置 */ #include <iom16v.h> #include <macros.h> #include "delay.h" #include "display.h" #include "ds1302.h" #include "key.h" #define uchar unsigned char #define uint unsigned int //用于校准时间 #define year 12//年份 #define week 4//星期 #define month 12//月 #define daytime 23//日 #define hour 12//小时 #define minute 59//分钟 #define seconds 55//秒 uint time_data[7]={year,week,month,daytime,hour,minute,seconds};//年星期月日时分秒(设置初始时间) void main() { int_init();//中断初始化 SEI();//开启总中断 LCD_init();//1602液晶初始化 SCK_OUT;//对时钟进行操作将SCK设置成输出 RST_OUT;//对时钟进行操作将RST设置成输出 set_rtc();//设置时间 hour_mode(24);//时间显示模式设定,12小时显示没有做好处理 //只是注意此函数的写法 while(1) { read_rtc();//读取时间 time_pro();//时间处理 display();//显示时间 } } delay.c: #include"delay.h" void delay(unsigned int ms) { unsigned int i,j; for(i=0;i<ms;i++) { for(j=0;j<1141;j++); } } delay.h: #ifndef delay_h #define delay_h extern void delay(unsigned int ms); #endif ds1302.c: #include <iom16v.h> #include <macros.h> #include "key.h" #include "display.h" #define uchar unsigned char #define uint unsigned int #define SCK_CLR PORTA&=~BIT(6) #define SCK_SET PORTA|=BIT(6) #define SCK_OUT DDRA|=BIT(6) #define RST_CLR PORTC&=~BIT(7) #define RST_SET PORTC|=BIT(7) #define RST_OUT DDRC|=BIT(7) #define IO_CLR PORTA&=~BIT(7)//IO引脚输出低电平 #define IO_SET PORTA|=BIT(7)//IO引脚输出高电平 #define IO_OUT DDRA|=BIT(7)//IO引脚设置为输出 #define IO_IN DDRA&=~BIT(7)//IO引脚设置为输入 #define IO_R PINA&BIT(7)//IO引脚进行读取操作 extern int time_data[7]; uchar write_add[7]={0X8C,0X8A,0X88,0X86,0X84,0X82,0X80};//写地址(最低位为0) uchar read_add[7]={0x8d,0x8b,0x89,0x87,0x85,0x83,0x81};//读地址(最低位为1) uchar table[];//用于计算 void write_ds1302_byte(uchar dat)//在ds1302中写指令字节 { uint i; IO_OUT; for(i=0;i<8;i++)//一位一位往里写,共写8位 { SCK_CLR;//开始时时序为0 //先准备好数据,在给上升沿 if(dat&0x01)//如果数据位为1 { IO_SET;//IO为1 } else//如果数据位为0 { IO_CLR;//IO为0 } SCK_SET;//SCLK拉高开始传输数据 dat=dat>>1;//移位进行下一个字节的传输(写的时候放到后面) } } uchar read_ds1302(uchar add)//单字节读 { uchar i,value;//定义变量 RST_CLR; SCK_CLR;//开始RET和SCLK时序为低 RST_SET; //根据时序要传输命令 write_ds1302_byte(add);//执行完此句后,SCLK时序到下降沿处开始读数据 IO_IN;//IO设置成输入 for(i=0;i<8;i++)//一个字节为8位,一位一位的读 { SCK_CLR;//时钟拉低开始传输 value=value>>1;//读的时候放到前面 if(IO_R)//读的结果如果为1 { value=value|0X80;//从低位开始读取 } SCK_SET;//时钟拉高结束传输 }//循环结束后SCLK时序到了最后,将RST拉低 RST_CLR; return value;//将数据返回 } void write_ds1302(uchar add,uchar dat)//单字节写(传递两个参数一个是地址一个是数据) { RST_CLR; NOP(); SCK_CLR;//开始RET和SCLK时序为低 RST_SET;//根据时序开始写入东西时将RST拉高 write_ds1302_byte(add);//写入地址 write_ds1302_byte(dat);//写入数据 RST_CLR;//根据时序,当写完东西时RST拉低 } void set_rtc(void)//设置时间(初始化时间) { uchar i,k; for(i=0;i<7;i++)//将十进制转换为16进制 { k=time_data[i]/10;//得到十位 time_data[i]=time_data[i]%10;//得到个位 time_data[i]=time_data[i]+k*16;//变成BCD码 } write_ds1302(0x8e,0x00);//去除写保护(control寄存器) for(i=0;i<7;i++)//对时 { write_ds1302(write_add[i],time_data[i]); } write_ds1302(0x8e,0x80);//(wp位置1)加入写保护 } void read_rtc(void)//读时钟 { uchar i; for(i=0;i<7;i++) { time_data[i]=read_ds1302(read_add[i]);//把时钟放到数组里 } } void time_pro(void)//时间处理函数(16进制转换为十进制) { table[0]=time_data[6]%16;//显示秒个位 table[1]=time_data[6]/16;//显示秒十位 table[2]=time_data[5]%16;//显示分个位 table[3]=time_data[5]/16;//显示分十位 table[4]=time_data[4]%16;//显示小时个位 table[5]=time_data[4]/16;//显示小时十位 table[6]=time_data[3]%16;//显示日个位 table[7]=time_data[3]/16;//显示日十位 table[8]=time_data[2]%16;//显示月个位 table[9]=time_data[2]/16;//显示月十位 table[10]=time_data[1]%16;//显示星期 table[12]=time_data[0]%16;//显示年个位 table[13]=time_data[0]/16;//显示年十位 } void hour_mode(uchar mode)//mode为12时为12小时格式,为24时为24小时格式 { uint value; write_ds1302(0x8e,0x00);//去除写保护(control寄存器) if(mode==12) { value=read_ds1302(0x85);//读取小时当前值 value=value|0x80; //将小时数据最高位置1 write_ds1302(0x84,value);//将置1后的小时数据写入小时寄存器 //变为12小时格式 } if(mode==24) { value=read_ds1302(0x85);//读取小时当前值 value=value&0x7F; //将小时数据最高位置0 write_ds1302(0x84,value);//将置1后的小时数据写入小时寄存器 //变为24小时格式 } } void int_init(void) { MCUCR |= 0x00; MCUCSR|= 0x00; GICR |= 0x40; } #pragma interrupt_handler int0_isr:2 void int0_isr(void)//在外部中断中进行时间设置 { uchar i=0,num=0,n=0,m=0,u=0,k=0,j=0,h=0; CLI();//进制中断 LCD_init();//清屏 showprompt();//显示提示信息 write_ds1302(0x80,0x80);//时钟停止 write_ds1302(0x8e,0x00);//去除写保护(control寄存器) while(!(num==4)) //num含义:1(加一),2(选择),3(设置),4(确定) { if(key_press()) //检测是否有按键按下 { num=key_scan(); //扫描并返回翻译后的键码 if(num==2)//进入选择状态 { i++; switch(i) { case 1://设置年 LCD_init();//清屏 &nbs设置年 LCD_init();//清屏 while(!(n==2))//再次按选择键就进入下个项目的设置 { if(key_press()) { n=key_scan(); //扫描并返回翻译后的键码 if(n==1) time_data[0]++;//按一下年加一 } //以下是时间微调,因为在DS1302中写入的是BCD码,所以在实际中 //可以用下面的调整方式,以下只处理到60年 if(time_data[0]==0x0a) { time_data[0]=0x10; } if(time_data[0]==0x1a) { time_data[0]=0x20; } if(time_data[0]==0x2a) { time_data[0]=0x30; } if(time_data[0]==0x3a) { time_data[0]=0x40; } if(time_data[0]==0x4a) { time_data[0]=0x50; } write_ds1302(write_add[0],time_data[0]);//写入地址和数据 time_pro();//时间处理 showyear();//显示年 } // break;不用终止程序,当程序跳出上边循环时,会进入下边程序 case 2://月设置 LCD_init();//清屏 while(!(m==2))//再次按选择键就进入下个项目的设置 { if(key_press()) { m=key_scan(); //扫描并返回翻译后的键码 if(m==1) time_data[2]++;//按一下月加一 if(time_data[2]==0x13)//一年有12个月 time_data[2]=1; } //BCD码处理 if(time_data[2]==0x0a) { time_data[2]=0x10; } if(time_data[2]==0x1a) { time_data[2]=0x20; } write_ds1302(write_add[2],time_data[2]); time_pro();//时间处理 showmonth();//显示月 } //break; case 3: //日设置 LCD_init();//清屏 while(!(u==2))//再次按选择键就进入下个项目的设置 { if(key_press()) { u=key_scan(); //扫描并返回翻译后的键码 if(u==1) time_data[3]++;//按一下日加一 if(time_data[3]==0x32)//一个月最多有31天 time_data[3]=1; } //BCD码处理 if(time_data[3]==0x0a) { time_data[3]=0x10; } if(time_data[3]==0x1a) { time_data[3]=0x20; } if(time_data[3]==0x2a) { time_data[3]=0x30; } write_ds1302(write_add[3],time_data[3]); time_pro();//时间处理 showdaytime();//显示日 } //break; case 4: //小时设置 LCD_init();//清屏 while(!(k==2))//再次按选择键就进入下个项目的设置 { if(key_press()) { k=key_scan(); //扫描并返回翻译后的键码 if(k==1) time_data[4]++;//按一下小时加一 if(time_data[4]==0x25)//一天24小时 time_data[4]=1; } //BCD码处理 if(time_data[4]==0x0a) { time_data[4]=0x10; } if(time_data[4]==0x1a) { time_data[4]=0x20; } write_ds1302(write_add[4],time_data[4]); time_pro();//时间处理 showhour();//显示小时 } //break; case 5: //分钟设置 LCD_init();//清屏 while(!(j==2))//再次按选择键就进入下个项目的设置 { if(key_press()) { j=key_scan(); //扫描并返回翻译后的键码 if(j==1) time_data[5]++;//按一下分钟加一 if(time_data[5]==0x5a)//一小时60分钟 time_data[5]=0x00; } //BCD码处理 if(time_data[5]==0x0a) { time_data[5]=0x10; } if(time_data[5]==0x1a) { time_data[5]=0x20; } if(time_data[5]==0x2a) { time_data[5]=0x30; } if(time_data[5]==0x3a) { time_data[5]=0x40; } if(time_data[5]==0x4a) { time_data[5]=0x50; } if(time_data[5]==0x5a) { time_data[5]=0x60; } write_ds1302(write_add[5],time_data[5]); time_pro();//时间处理 showminute();//显示分钟 } //break; case 6: //星期设置 LCD_init();//清屏 while(!(h==2))//再次按选择键就进入下个项目的设置 { if(key_press()) { h=key_scan(); //扫描并返回翻译后的键码 if(h==1) time_data[1]++;//按一下小时加一 if(time_data[1]==0x08)//一天24小时 time_data[1]=1; } write_ds1302(write_add[2],time_data[2]); time_pro();//时间处理 showweek();//显示星期 } default: LCD_init();//清屏 showmessage(); break; } } } } write_ds1302(0x8e,0x80);//(wp位置1)加入写保护 write_ds1302(0x80,0x00);//时钟开始 LCD_init();//清屏 SEI(); } key.c: #include <iom16v.h> #include <macros.h> #include "delay.h" #define uchar unsigned char #define uint&n月设置 LCD_init();//清屏 while(!(m==2))//再次按选择键就进入下个项目的设置 { if(key_press()) { m=key_scan(); //扫描并返回翻译后的键码 if(m==1) time_data[2]++;//按一下月加一 if(time_data[2]==0x13)//一年有12个月 time_data[2]=1; } //BCD码处理 if(time_data[2]==0x0a) { time_data[2]=0x10; } if(time_data[2]==0x1a) { time_data[2]=0x20; } write_ds1302(write_add[2],time_data[2]); time_pro();//时间处理 showmonth();//显示月 } //break; case 3: //日设置 LCD_init();//清屏 while(!(u==2))//再次按选择键就进入下个项目的设置 { if(key_press()) { u=key_scan(); //扫描并返回翻译后的键码 if(u==1) time_data[3]++;//按一下日加一 if(time_data[3]==0x32)//一个月最多有31天 time_data[3]=1; } //BCD码处理 if(time_data[3]==0x0a) { time_data[3]=0x10; } if(time_data[3]==0x1a) { time_data[3]=0x20; } if(time_data[3]==0x2a) { time_data[3]=0x30; } write_ds1302(write_add[3],time_data[3]); time_pro();//时间处理 showdaytime();//显示日 } //break; case 4: //小时设置 LCD_init();//清屏 while(!(k==2))//再次按选择键就进入下个项目的设置 { if(key_press()) { k=key_scan(); //扫描并返回翻译后的键码 if(k==1) time_data[4]++;//按一下小时加一 if(time_data[4]==0x25)//一天24小时 time_data[4]=1; } //BCD码处理 if(time_data[4]==0x0a) { time_data[4]=0x10; } if(time_data[4]==0x1a) { time_data[4]=0x20; } write_ds1302(write_add[4],time_data[4]); time_pro();//时间处理 showhour();//显示小时 } //break; case 5: //分钟设置 LCD_init();//清屏 while(!(j==2))//再次按选择键就进入下个项目的设置 { if(key_press()) { j=key_scan(); //扫描并返回翻译后的键码 if(j==1) time_data[5]++;//按一下分钟加一 if(time_data[5]==0x5a)//一小时60分钟 time_data[5]=0x00; } //BCD码处理 if(time_data[5]==0x0a) { time_data[5]=0x10; } if(time_data[5]==0x1a) { time_data[5]=0x20; } if(time_data[5]==0x2a) { time_data[5]=0x30; } if(time_data[5]==0x3a) { time_data[5]=0x40; } if(time_data[5]==0x4a) { time_data[5]=0x50; } if(time_data[5]==0x5a) { time_data[5]=0x60; } write_ds1302(write_add[5],time_data[5]); time_pro();//时间处理 showminute();//显示分钟 } //break; case 6: //星期设置 LCD_init();//清屏 while(!(h==2))//再次按选择键就进入下个项目的设置 { if(key_press()) { h=key_scan(); //扫描并返回翻译后的键码 if(h==1) time_data[1]++;//按一下小时加一 if(time_data[1]==0x08)//一天24小时 time_data[1]=1; } write_ds1302(write_add[2],time_data[2]); time_pro();//时间处理 showweek();//显示星期 } default: LCD_init();//清屏 showmessage(); break; } } } } write_ds1302(0x8e,0x80);//(wp位置1)加入写保护 write_ds1302(0x80,0x00);//时钟开始 LCD_init();//清屏 SEI(); } key.c: #include <iom16v.h> #include <macros.h> #include "delay.h" #define uchar unsigned char #define uint unsigned int uchar key_press()//判断按键是否被按下(按下返回1,没按下返回0) { uchar j; DDRD|=0X0F;//低四位为输出 PORTD|=0X0F;//低四位输出低电平 DDRD&=0XF0;//低四位为输入 j=PIND;//读一下输入寄存器 j&=0X0F;//屏蔽高四位 if(j==0X0F)//如果j没变化,则返回0,否则返回1 { return 0; } else { return 1; } } uchar key_scan()//按键扫描函数 { uchar key; delay(10); if(key_press())//有按键按下时key_press()的值为1 { key=PIND;//读取PIND值 key&=0X0F;//去掉高4位 switch(key)//开关语句,用于判断 { case 0X0E://1110连接PD0的按键按下 key=1;break; case 0X0D://1101连接PD1的按键按下 key=2;break; case 0X0B://1011连接PD2的按键按下 key=3;break; case 0X07://0111连接PD3的按键按下 key=4;break; default://都不是时随便返回一个值 key=16; } while(key_press());//在按键按下时执行完上边的程序后就执行此循环 //当按键松开时就跳出循环,目的是按一下就执行一次程序 } else//没有按下时随便返回一个值 { key=16; } return key; }
程序未完,完整代码下载: http://www.51hei.com/f/1302cc.rar