全功能數(shù)字電子鐘(C51單片機應用開發(fā))
一、設計目的:
通過單片機應用產(chǎn)品的設計與調(diào)試過程,鞏固課程所學理論知識,初步了解單片機應用系統(tǒng)設計與調(diào)試的方法。
二、設計要求:
設計一個以AT89S51單片機為核心的數(shù)字電子鐘控制器,實現(xiàn)電子鐘的時間、日期交替顯示、鬧鐘功能,并可通過按鈕開關或鍵盤切換顯示內(nèi)容、調(diào)整參數(shù)、設置鬧鐘,在單片機實驗板上模擬調(diào)試實現(xiàn)控制器的功能。具體設計要求如下:
1.開機自檢,檢查相關接口及數(shù)碼管顯示器、指示燈、蜂鳴器等外設是否正常。
2.8位數(shù)碼管顯示器平常以一定的時間間隔、合適的格式顯示時間和日期信息,時間顯示時、分、秒;日期顯示年(2000~2099)、月、日;設置鬧鐘功能時顯示時、分、開/關狀態(tài)。
3.可通過按鍵設定時間、日期、鬧鐘等參數(shù)、手動切換顯示。按鍵可用獨立式按鍵或行列式鍵盤實現(xiàn)。設定參數(shù)過程有合適的方式指示當前可修改的內(nèi)容。
4.對開關量輸入進行軟件消抖動處理,參數(shù)的設定有容錯處理,如:小時不能超過23,日期中每月最大天數(shù)、閏年等。
5.用Protel設計可實現(xiàn)上述功能的控制器的原理圖(最小應用系統(tǒng))。
擴展功能(選做):
1.可設置多次鬧鐘。
2.顯示星期功能。
3.參數(shù)設定過程中,較長時間無操作,則自動恢復為正常顯示方式。。
4.其它自選的擴展功能。
三、總體方案設計及說明
總體功能框圖:
硬件:
8個LED采用動態(tài)掃描以節(jié)約驅動成本;
走時采用內(nèi)部T0計時中斷;
4x4矩陣鍵盤掃描采用線反轉法,以中斷掃描計數(shù)防止抖動;
……
軟件:
采用C語言實現(xiàn)。
四、系統(tǒng)資源分配說明(接口、存儲器分配)
1.接口:
89S51的P1口接8個LED小燈;
89S51的P3_2接蜂鳴器(低電平鳴響);
外擴一片8255:
89S51單片機的P0口是低8位地址與數(shù)據(jù)復用的,現(xiàn)在我們用74HC373分離出地址,89S51高位地址的P2_0(A8)接8255的片選端(/CS), 低位地址Q1Q0(A1A0)與8255的A1A0連接,數(shù)據(jù)位P0_7~P0_0分別接8255的D_7~D_0。 以此得到的8255端口的地址分別為:
PA:xxxxxxx0 xxxxxx00取0x0fefc; PB:xxxxxxx0 xxxxxx01取0x0fefd;
PC:xxxxxxx0 xxxxxx10取0x0fefd; CTL:xxxxxxx0 xxxxxx11取0x0feff;
8255的PA口控制LED數(shù)碼管的8個顯示段;PB口分別接8個LED數(shù)碼管的共陽極;
PC口分別接4x4矩陣鍵盤的行線和列線。
2.存儲分配:
struct{ //鬧鐘時、分、秒 ,共設6個鬧鐘(初始狀態(tài)默認:00-00-F1)
uchar hour;
uchar minute;
uchar isON;
}alarm[6]={{0,0,0}};
uchar hour=12,minute=0,second=0;//時、分、秒
uchar temp_second; //用于立即切換顯示時間/日期
uint year=2011;// 年
uchar month=12;// 月
uchar day=1; // 日
uchar week=6;// 星期
uchar Mdays[]={0,31,28,31,30,31,30,31,31,30,31,30,31};//各月天數(shù)
uchar alarm_isON=1; //鬧鐘總開關
uchar alarm_station=0; //鬧鐘狀態(tài)
uchar ano; //鬧鐘號(當前時間到的鬧鐘號)
uchar start_minute;//開始響鈴的時間(也就是所定鬧鐘的時間)
uint count_ms25=0; //軟件計數(shù)器(計數(shù)40個25毫秒達1s)
uchar show_model=0; // 顯示模式:[0]切換顯示時間/日期 [1]切換顯示日期/時間
const uchar fixtime=0x00;//時間修正量
uchar key=0xff;//獲得的當前鍵值
uchar last_key=0xff; //最后一次掃描到的按鍵(非0xff)
uchar key_count=0;//掃描到同一按鍵的次數(shù)
uchar Edown=0; //鬧鐘開關鍵是否按下
uchar led_buf[8]={24,24,24,24,24,24,24,24}; //時間日期顯示緩沖區(qū)
uchar code led_table1[]={0x0c0,0x0f9,0x0a4,0x0b0, 0x99,0x92,0x82,0x0f8,0x80,0x90,
0x88,0x83,0x0C6,0x0a1,0x86,0x8e,0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,
0x08,0x03,0x46,0x21,0x06,0x0e,0x7f,0x0bf,0xff};//數(shù)碼管段碼
uchar code KBTable[] = {'1','2','3','F','4','5','6','E','7','8','9','C','0','A','B','D'};//鍵值(可有可無)
五、軟件流程圖及說明
1.流程圖:
2.主要程序段說明:
(1)顯示:
動態(tài)顯示:即各位數(shù)碼管輪流點亮,對于顯示器各位數(shù)碼管,每隔一段延時時間循環(huán)點亮一次。利用人的視覺暫留功能可以看到整個顯示,但須保證掃描速度足夠快,人的視覺暫留功能才可察覺不到字符閃爍。顯示器的亮度與導通電流、點亮時間及間隔時間的比例有關。調(diào)整參數(shù)可以實現(xiàn)較高穩(wěn)定度的顯示。動態(tài)顯示節(jié)省了驅動和I/O口,降低了能耗。
void LED_show(uchar buf[])
{
uchar i,num,pLED=0x80;
for(i=0;i<8;i++)
{
num=buf[i];
PA=led_table1[num]; /*送字段碼*/
PB=pLED; /*送字位碼*/
pLED>>=1; /*右移一位*/
Delay(1); /*延時*/
}
}
(2)鍵盤(本次設計對下面兩種掃描方式都進行了實現(xiàn)):
a.行掃描法:依次從第一至最末行線上發(fā)出低電平信號, 如果該行線所連接的鍵沒有按下的話, 則列線所接的端口得到的是全“1”信號, 如果有鍵按下的話, 則得到非全“1”信號。
/*鍵盤掃描(行掃描法,延時消抖)********************************************************
uchar code KBTable[] = {
0xEE,'1',0xDE,'4',0xBE,'7',0x7E,'0',
0xED,'2',0xDD,'5',0xBD,'8',0x7D,'A',
0xEB,'3',0xDB,'6',0xBB,'9',0x7B,'B',
0xE7,'F',0xD7,'E',0xB7,'C',0x77,'D',
0x00,0xff};
uchar Get_key(void); // 獲取最終鍵值
{ uchar i;
uchar line, row, k_value;
static uchar lastkey=0xff;
CTL=0x88; //CH輸入,CL輸出 10001000
PC=PC & 0xf0; // PC0~PC3輸出0 , 輸入PC4~ PC7(默認1無鍵按下)
if ((PC & 0xf0) == 0xf0)
{
lastkey=0xff;
return 0xff; //無鍵按下
}
row = PC;
Delay(4); //延時,消除抖動
if (row != PC)
{
lastkey=0xff;
return 0xff; //判為抖動
}
line=0xFE;
for (i=0;i<4;i++)
{ PC = line; //輸出掃描信號
row=PC; //讀鍵盤口
if ((row & 0xf0) != 0xf0)
break;
line=(line<<1)+1;
}
if (i==4)
{ lastkey=0xff; return 0xff; }
k_value = (row & 0xf0) " (line & 0x0f) ;
for (i=0; i<32; i+=2)
if (k_value == KBTable[i])
break;
if(lastkey==KBTable[i+1])
return 0xff;
lastkey=KBTable[i+1];
return KBTable[i+1];
}
b.線反轉法:線反轉法也是識別閉合鍵的一種常用方法, 該法比行掃描速度快, 但在硬件上要求行線與列線外接上拉電阻。先將行線作為輸出線, 列線作為輸入線, 行線輸出全“0”信號, 讀入列線的值, 那么在閉合鍵所在的列線上的值必為0;然后從列線輸出全“0”信號,再讀取行線的輸入值,閉合鍵所在的行線值必為 0。這樣,當一個鍵被按下時, 必定可讀到一對唯一的行列值。再由這一對行列值可以求出閉合鍵所在的位置。
//一次鍵盤掃描(線反轉法,中斷掃描計數(shù)去抖)*********************************************************
uchar code KBTable[] = {'1','2','3','F','4','5','6','E','7','8','9','C','0','A','B','D'};
//key_index 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
uchar key_scan(void) //返回 '0','1','2'...'E','F',0xff
{ uchar key_index,temp=0;
CTL=0x88; //CH輸入,CL輸出 10001000
PC=PC & 0xf0; //將低四位置0
if(PC!=0xF0) //判斷按鍵是否按下 如果按鈕按下 會拉低CH其中的一個端口
{
temp=PC; //讀PC口
temp=temp&0xf0; //屏蔽低四位
temp=~