一、硬件電路 六位數字鐘仿真原理圖如下(proteus仿真工程文件可到本帖附件中下載)
圖6-1總電路圖
2、時間調試 記時時間,如圖6-1為“01:13:38”,圖6-2所示20秒后的時間“01:13:38”,調試顯示正常。 圖6-1 01:13:38 圖 6-2 01:13:38 調整時間,如圖6-2到圖6-4所示,為修改位閃爍狀態,圖6-5和圖6-6為修改后的時間和退出修改3秒后的時間,調試顯示正常。 圖6-2修改秒個位閃爍“滅” 圖6-3修改秒個位閃爍“亮” 圖6-4修改分個位閃爍“滅” 圖6-5修改后時間 圖6-6退出修改
3、鬧鐘調試 如圖6-7所示,長按key2,右下角紅色led亮,表示開啟鬧鐘,并設置鬧鐘時間為“01:14:00”。若無開啟鬧鐘如圖6-8所示,到設定時間時,綠色LED無反應。如圖6-9和圖6-10所示,開啟鬧鐘,到設定時間,綠色led亮10s后熄滅。如圖6-11所示,鬧鐘“響起”后,按下key2取消“鬧鈴”,綠色led熄滅。 圖6-7 設定鬧鐘01:14:00 圖6-8無開啟鬧鐘,到時無反應 圖6-9到鬧鐘時間 圖6-10 鬧鐘10s后停止 圖6-11鬧鐘手動停止
4、日期調試 如圖6-12和圖6-13所示,在顯示模式下流動顯示8位數“2022年3月1日”。如圖6-14至圖6-16所示,當年分為閏年“2020”時,2月末為29日,過24點,變成“2020年03月01日”。如圖6-17至圖6-19所示,當年分為平年“2021”時,2月末為28日,過24點,變成“2021年03月01日”。
圖6-12 顯示日期2022年03月 圖6-13 顯示日期2022年03月01日 圖6-17 平年2月28日 圖6-18 設定當天23:59:49 圖6-19 24點后日
單片機源程序如下:
- #include<reg51.h> //包含51單片機頭文件
- #include<intrins.h> //包含標準函數庫
- sbit P_alarm =P1^6; //鬧鐘開啟與關閉指示燈,低電平有效,滅代表鬧鐘關閉
- sbit P_alarming=P1^7; //鬧鈴指示燈,到時則亮,低電平有效
- unsigned char code dis_7[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
- //七段共陰顯示數碼管“0~9”陰碼
- unsigned char data time[3]={23,59,58};
- //時間,從左到右分別為“小時、分鐘、秒”
- unsigned char data clock[3]={23,0,0};
- //鬧鐘,從左到右分別為“小時、分鐘、秒”
- unsigned char data date[4]={20,18,02,20};
- //日期, 從左到右分別為“年前兩位、年后兩位、月、日”
- unsigned char data day[12]={31,28,31,30,31,30,31,30,31,30,31,30};
- //12個月對應的天數
- unsigned char data *p; //指針變量,指向當前顯示地址
- unsigned char data mcs,ms_10; //計數器,mcs每250us加1次記錄40次,ms_10記錄mcs 100次
- unsigned char data position=1; //編輯模式下,記錄當前修改位“1~6”分別表示數碼管右到左標號
- unsigned char data screen=1; //顯示模式下,記錄當前顯示屏幕功能,如下:“1”代表time,“2”代表clock,“3”代表date
- unsigned int data fliker; //編輯模式下,修改位閃爍功能定時計數器
- unsigned int data flow; //顯示模式下,日期流動計數器
-
- bit edit; //“0”代表顯示模式,“1”代表編輯模式
- bit day_flag=0; //24點標志位
- bit disp_num; //顯示數據位數切換,“0”代表顯示6位,即時間,“1”代表顯示日期
- bit alarm,alarming; //鬧鐘狀態和鬧鈴狀態標志位
-
-
- void display(unsigned char *p); //顯示函數
- void clock_c(); //鬧鐘功能程序
- void date_c(); //日期程序
- unsigned char keyscan(); //鍵盤掃描程序,另外,K1長按進行模式切換
- void key2_function_d(); //顯示狀態,按鍵2功能程序,鬧鐘取消\啟動,鬧鈴消停
- void key2_function_e(); //編輯狀態,按鍵2功能程序,加1
- void key1_function_d(); //顯示狀態,按鍵2功能程序,顯示切換
- void key1_function_e(); //編輯狀態,按鍵2功能程序,修改位切換
- void delay(unsigned int i); //延時函數
-
-
-
- void main()
- {
- unsigned char keyboard,key;
- TMOD=0x02; //設置定時器0工作方式為2
- TL0=0x06; //設置低八位初始值
- TH0=0x06; //設置高八位初始值
- EA=1; //開啟總中斷
- ET0=1; //開啟定時器0中斷允許
- TR0=1; //啟動定時起0
- p=time; //指向時間地址
- while(1)
- {
- keyboard=P1&0x03; //讀取按鍵的值,并且屏蔽無關位
- display(p); //調用顯示函數
- clock_c(); //調用鬧鐘函數
- date_c(); //調用日期函數
- if((screen==1)&&(edit==1))TR0=0;else TR0=1;
- //修改時間時,關閉定時器
- if(keyboard!=0x03) //若有按鍵按下,則執行以下程序
- {
- display(p); //顯示函數消除按鍵抖動
- if(keyboard!=0x03) //再次檢測,若有,則執行以下程序
- {
- key=keyscan(); //調用按鍵掃描程序,讀取按鍵值
- if(!edit) //顯示模式
- {
- switch(key) //如果KEY1按下,則KEY1為1,若KEY2按下則為2
- {
- case 1:key1_function_d();break;//顯示屏幕切換
- case 2:key2_function_d();break;//鬧鐘功能設置,鬧鈴取消
- default:break;
- }
- }
- else //編輯模式
- {
- switch(key) //如果KEY1按下,則KEY1為1,若KEY2按下則為2
- {
- case 1:key1_function_e();break;//修改位切換
- case 2:key2_function_e();break;//加1
- default:break;
- }
- } } } } }
- void display(unsigned char *p) //顯示子程序,顯示參數為無符號字符型指針變量
- {
- unsigned char disp[8]={0,0,0,0,0,0,0,0};
- //顯示數據緩存變量
- unsigned char disp_com[6]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
- //數碼管位控端分別代表從左到右“1~6”號數碼管
- unsigned char a,b,d,dot;
- unsigned char c=0;
- if(!disp_num)a=6;else a=8; //顯示時間則a=6,否則a=8
- for(b=0;b<a;b++) //將顯示數據按位緩存與disp數據中
- {
- if(c==0)disp[b]=p[b/2]/10;
- else disp[b]=p[b/2]%10;
- c++;if(c==2)c=0;
- }
- if(!edit) //顯示模式
- {
- if(!disp_num) //顯示時間
- {
- for(b=0;b<6;b++)
- {
- if(b==1||b==3)dot=128;else dot=0;
- //1為小時個位,3為分鐘個位,其后面加小數點
- P0=0xff; //消影
- P2=~dis_7[disp[b]]+dot;//段碼刷新
- P0=disp_com[b]; //位選端有效,顯示輸出
- delay(5); //延時
- }
- }
- else //顯示日期
- {
- b=flow/50; //flow最大為400,每顯示50次流動一位
- for(c=0;c<6;c++)
- {
- d=c+b;if(d>7)d-=8; //每次顯示6位,但流動顯示起始位,第9位時返回第1位顯示
- if(d==3||d==5||d==7)dot=128;else dot=0;
- //年份、月份、日期個位加小數點
- P0=0xff; //消影
- P2=~dis_7[disp[d]]+dot;//斷碼刷新
- P0=disp_com[c]; //輸出位選端
- delay(5); //延時
- }
- flow++; //每顯示1次屏幕加1;
- if(flow==400)flow=0; //最大399
- }
- }
- else //編輯模式
- {
- fliker++;if(fliker>100)fliker=0;//修改位顯示閃爍功能
- if(!disp_num) //顯示時間
- {
- for(b=0;b<6;b++)
- {
- if(b==1||b==3)dot=128;else dot=0;
- P0=0xff;
- P2=~dis_7[disp[b]]+dot;
- if(b==(6-position)) //修改位閃爍,position=1代表修改第6位數碼管,最右邊數碼管
- {
- if(fliker<50)P0=disp_com[b]; //亮
- else P0=0xff; //滅
- }
- else P0=disp_com[b];
- delay(5);
- }
- }
- else //顯示日期
- {
- if(position>6)b=0;else b=2; //顯示日期移動,起始顯示狀態“年,月”
- for(c=0;c<6;c++)
- {
- d=c+b; //起始位置為disp[0或者2]
- if(d==3||d==5)dot=128;else dot=0;
- P0=0xff;
- P2=~dis_7[disp[d]]+dot;
- if(c==(6-position)||(c==(8-position)&&(position>6)))
- //修改位閃爍,年最大位開始修改(顯示“年、月”||顯示“年、月、日”)
- {
- if(fliker<50)P0=disp_com[c];
- else P0=0xff;
- }
- else P0=disp_com[c];
- delay(5);
- } } } }
- void clock_c() //鬧鐘子程序
- {
- if(alarm) //開啟鬧鐘則允許以下程序
- {
- int a,b,c;
- a=time[0]-clock[0];
- b=time[1]-clock[1];
- c=time[2]-clock[2]; //比較鬧鐘時間和當前時間
- if((a==0)&&(b==0)&&(c==0)) //時間到
- alarming=1; //啟動鬧鈴
- if(alarming) //鬧鈴10秒自動取消
- {
- if((b*60+c)>=10)alarming=0;
- }
- }
- P_alarm=!alarm; //鬧鐘狀態輸出
- P_alarming=!alarming; //鬧鈴狀態輸出
- }
-
- void date_c() //日期子程序
- {
- unsigned char a,b,c,d;
- d=date[0]*100+date[1];
- a=23-time[0];
- b=59-time[1];
- c=59-time[2]; //是否到24點
-
- if((d%400==0)||((d%100!=0)&&(d%4==0))) //閏年
- { day[1]=29;}
- else
- { day[1]=28;if(date[3]>28)date[3]=28;} //平年
-
- if((a==23)&&(b==59)&&(c==59)&&(day_flag==0)) //日期記時,24整點運行1次
- {
- date[3]++; //日加1
- if(date[3]>day[date[2]-1]) //月末
- {
- date[3]=1; //日歸1
- date[2]++; //月份加1
- if(date[2]>12) //年末
- {
- date[2]=1; //月歸1
- date[1]++; //年后兩位加1
- if(date[1]>99) //99年進位
- {
- date[0]++; //年前兩位加1
- date[1]=0; //年后兩位歸零
- if(date[0]>99) //最大9999年
- date[0]=0;
- }
- }
- }
- day_flag=1; //執行1次標志位置1,防止重復執行
- }
- if(day_flag){if(c!=59)day_flag=0;} //24整點過后,整點標志位復位
- }
-
- unsigned char keyscan() //鍵盤掃描子程序,無符號字符型子程序,返回鍵號
- {
- unsigned int a;
- unsigned char temp;
- temp=P1&0x03; //讀取按鍵輸入,屏蔽無關位
- for(a=0;a<100&&((P1&(0x07))==0x06);a++)
- //按鍵延時功能
- {
- display(p); //調用顯示程序
- if(a==99) //延時閾值
- {
- edit=!edit;
- if(screen==3)position=8;//進入編輯模式初始狀態設置
- else position=1;a=3;temp=0;P0=0xff;delay(1000);
- //進入編輯模式,則返回值為3不調用任何按鍵程序
- while((P1&(0x07))==0x06)display(p);
- //按鍵松開
- }
- }
- switch(temp) //根據按鍵狀態返回鍵號
- {
- case 0x06:a=1;break;
- case 0x05:a=2;break;
- default:break;
- }
- if(edit)while((P1&0x07)!=0x07)display(p);//按鍵松開
- return(a); //返回鍵號
- }
-
- void key2_function_d() //按鍵2,顯示狀態,鬧鐘啟動/停止,鬧鈴消停
- {
- unsigned int a;
- if(!edit) //顯示模式,執行以下程序
- {
- for(a=0;(a<100)&&((P1&(0x07))==0x05);a++)
- //按鍵延時功能
- {
- display(p); //調用顯示程序
- if(a==99) //到達延時閾值,執行以下程序
- {
- alarm=!alarm; //開啟或取消鬧鐘
- P_alarm=!alarm; //立即輸出反饋鬧鐘狀態
- while((P1&(0x07))==0x05)display(p);//按鍵松開
- }
- }
- if(alarm) //鬧鐘開啟
- {
- if(alarming)alarming=0; //手動取消,顯示模式下短按取消鬧鈴
- }else alarming=alarm; //鬧鐘關閉狀態 }
- }
-
- void key2_function_e() //編輯模式,按鍵2,加1功能
- {
- if(edit)
- {
- if(!disp_num) //顯示時間,則執行以下程序
- {
- switch(position) //根據位置進行加1
- {
- case 1:if(p[2]%10<9)p[2]++;else p[2]-=9 ;break; //秒個位
- case 2:if(p[2]<50)p[2]+=10;else p[2]-=50;break; //秒十位
- case 3:if(p[1]%10<9)p[1]++;else p[1]-=9 ;break; //分鐘個位
- case 4:if(p[1]<50)p[1]+=10;else p[1]-=50;break; //分鐘十位
- case 5:if((p[0]%10)<9&&(p[0]<23))p[0]++;else{if(p[0]<20)p[0]-=9;else p[0]-=3;};break; //小時個位
- case 6:if((p[0]/10)<2&&(p[0]<14))p[0]+=10;else{if(p[0]<20)p[0]-=10;else p[0]-=20;};break; //小時十位
- default: break;
- }
- }
- else //顯示日期,則執行以下程序
- {
-
- switch(position) //根據位置進行加
- {
- case 8:if(p[0]<99) p[0]+=10;else p[0]%=10;break; //年千位
- case 7:if(p[0]%10<9)p[0]++; else p[0]-=9;break; //年百位
- case 6:if(p[1]<99) p[1]+=10;else p[1]%=10;break; //年十位
- case 5:if(p[1]%10<9)p[1]++; else p[1]-=9;break; //年個位
- case 4:p[3]=1;if(p[2]==1)p[2]=10;else p[2]=1;break; //若進行月份的調整,需要重新設置日期,調整月份十位代表10以上和10以下切換
- case 3:p[3]=1;if(p[2]>=10){if(p[2]<12) p[2]++;else p[2]=10; } else if(p[2]<9) p[2]++;else p[2]=1;break; //調整月份個位
- case 2: //日期的十位
- p[3]+=10; //日期的十位加1
- if(p[3]>day[p[2]-1]) //如果超過月末
- {
- p[3]%=10; //保留日期個位
- if(p[3]==0)p[3]=10; //十位歸零
- }break;
- case 1: //日期的個位
- if((p[3]/10)==(day[p[2]-1]/10)) //如果與月末十位相
- 等則執行以下程序
- {
- if(p[3]==day[p[2]-1]){ p[3]=p[3]-(day[p[2]-1]%10);}
- //達到月末,減去月末個位數
- else
- p[3]++; //日期加1
- }
- else //不與月末十位相等
- {
- if(p[3]%10<9)p[3]++; //最大值為9
- else
- {
- p[3]-=9;
- if(p[3]==0)p[3]=1; //日期為0時則為1
- }
- }break;
- default: break;
- }
- }
- }
- }
-
- void key1_function_d() //按鍵1,顯示狀態,顯示切換
- {
- if(!edit) //顯示狀態下
- {
- screen++; //每調用1次,加1,范圍“1~3”
- switch(screen)
- {
- case 1:p=time;disp_num=0;break; //顯示時間
- case 2:p=clock;disp_num=0;break; //顯示鬧鐘
- case 3:p=date;disp_num=1;break; //顯示日期
- default:p=time;disp_num=0;screen=1;break; //顯示時間
- }
- }
- }
-
- void key1_function_e() //按鍵1,編輯模式下,移位
- {
- if(edit) //編輯模式下執行以下程序
- {
- if(!disp_num) //顯示時間狀態
- {
- position++;
- if(position>6)position=1; //position范圍“1~6”
- }else //顯示日期
- {
- position--; //從年千位開始修改
- if(position==0||position>8)
- position=8; //范圍“1~8”
- }
- }else position=1; //復位
- }
- void delay(unsigned int i) //延時程序
- {
- unsigned int k;
- for(;i>0;i--)
- for(k=0;k<54;k++);
- }
- void time_int0() interrupt 1 //定時中斷,中斷方式2
- {
- EA=0; //關閉總中斷,防止高級中斷影響定時
- if(mcs!=0x28) //10ms定時
- mcs++;
- else
- {
- mcs=0;
- if(ms_10!=100)ms_10++ ; //1s定時
- else
- {
- ms_10=0;
- if(time[2]!=59)time[2]++; //1s到則秒位加1
- else
- {
- time[2]=0; //60秒歸0
- if(time[1]!=59)time[1]++;//分鐘加1
- else
- {
- time[1]=0; //60分鐘歸0
- if(time[0]!=23)time[0]++; //小時加1
- else time[0]=0; //24小時歸0
- }
- }
- }
- }
- EA=1; //開放總中斷
- }
復制代碼
所有資料51hei提供下載:
時鐘.zip
(4.86 MB, 下載次數: 209)
2018-11-15 17:13 上傳
點擊文件名下載附件
|