程序: 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.zg4o1577.cn/f/1302cc.rar