|
/*******************************************************************************
* 實 驗 名 : 動態(tài)數(shù)碼管按鍵移動顯示試驗
* 實驗說明 :
* 連接方式 : 用排線將P0口和74H573旁邊的J12連接起來
* * 將138譯碼器的A、B、C端分別接P2.2、P2.3、P2.4
* * 用排線將P1口和矩陣鍵盤旁邊的JP4連接起來
* 注 意 :
*******************************************************************************/
#include<reg51.h>
/*=============
低層按鍵(I/0)掃描函數(shù),即低層按鍵設(shè)備驅(qū)動,只返回?zé)o鍵、短按和長按。具體雙擊不在此處判斷。參考本人教材的例9-1,稍微有變化。教材中為連_發(fā)。
===============*/
#define uchar unsigned char
#define uint unsigned int
sbit Chrg=P1^2;
sbit Done=P1^3;
sbit S1=P1^4;
sbit USBTest=P1^5;
sbit RED=P1^1;
sbit GREEN=P1^0;
sbit BLUE=P3^7;
sbit key_input_0=P3^6;
sbit Pout=P3^2;
#define N_key 0 //無鍵
#define S_key 1 //單鍵
#define D_key 2 //雙鍵
#define L_key 3 //長鍵
#define key_state_0 0
#define key_state_1 1
#define key_state_2 2
#define key_state_3 3
bit time_10ms_ok;
uchar key_driver()
{
static uchar key_state=key_state_0, key_time=0;
uchar key_press, key_return=N_key;
key_press=key_input_0; // 讀按鍵I/O電平
switch(key_state)
{
case key_state_0: // 按鍵初始態(tài)
if(!key_press) key_state=key_state_1; // 鍵被按下,狀態(tài)轉(zhuǎn)換到按鍵消抖和確認(rèn)狀態(tài)
break;
case key_state_1: // 按鍵消抖與確認(rèn)態(tài)
if(!key_press)
{
key_time=0;
key_state=key_state_2; // 按鍵仍然處于按下,消抖完成,狀態(tài)轉(zhuǎn)換到按下鍵時間的計時狀態(tài),但返回的還是無鍵事件
}
else
key_state=key_state_0; // 按鍵已抬起,轉(zhuǎn)換到按鍵初始態(tài)。此處完成和實現(xiàn)軟件消抖,其實按鍵的按下和釋放都在此消抖的。
break;
case key_state_2:
if(key_press)
{
key_return=S_key; // 此時按鍵釋放,說明是產(chǎn)生一次短操作,回送S_key
key_state=key_state_0; // 轉(zhuǎn)換到按鍵初始態(tài)
}
else if(++key_time>=100) // 繼續(xù)按下,計時加10ms(10ms為本函數(shù)循環(huán)執(zhí)行間隔)
{
key_return=L_key; // 按下時間>1000ms,此按鍵為長按操作,返回長鍵事件
key_state=key_state_3; // 轉(zhuǎn)換到等待按鍵釋放狀態(tài)
}
break;
case key_state_3: // 等待按鍵釋放狀態(tài),此狀態(tài)只返回?zé)o按鍵事件
if(key_press) key_state=key_state_0; //按鍵已釋放,轉(zhuǎn)換到按鍵初始態(tài)
break;
}
return key_return;
}
/*=============
中間層按鍵處理函數(shù),調(diào)用低層函數(shù)一次,處理雙擊事件的判斷,返回上層正確的無鍵、單鍵、雙鍵、長鍵4個按鍵事件。
本函數(shù)由上層循環(huán)調(diào)用,間隔10ms
===============*/
uchar key_read()
{
static uchar key_m=key_state_0, key_time_1=0;
uchar key_return=0,key_temp;
key_temp=key_driver();
switch(key_m)
{
case key_state_0:
if(key_temp==S_key)
{
key_time_1=0; // 第1次單擊,不返回,到下個狀態(tài)判斷后面是否出現(xiàn)雙擊
key_m=key_state_1;
}
else
key_return=key_temp; // 對于無鍵、長鍵,返回原事件
break;
case key_state_1:
if(key_temp==S_key) // 又一次單擊(間隔肯定<500ms)
{
key_return=D_key; // 返回雙擊鍵事件,回初始狀態(tài)
key_m=key_state_0;
}
else
{ // 這里500ms內(nèi)肯定讀到的都是無鍵事件,因為長鍵>1000ms,在1s前低層返回的都是無鍵
if(++key_time_1>=50)
{
key_return=S_key; // 500ms內(nèi)沒有再次出現(xiàn)單鍵事件,返回上一次的單鍵事件
key_m=key_state_0; // 返回初始狀態(tài)
}
}
break;
}
return key_return;
}
void delay_ms(uint sec)
{
uint i;
while(sec--)
{
for(i=0;i<1000;i++)
{
;//_nop();
}
}
}
/*
下面,根據(jù)程序分析按鍵事件的反映時間:
1。對于長鍵,按下超過1s馬上響應(yīng),反映最快
2。對于雙鍵,第2次按鍵釋放后馬上得到反映。
3。對于單鍵,釋放后延時拖后500ms才能響應(yīng),反映最慢。這個與需要判斷后面是否有雙擊操作有關(guān),只能這樣。實際應(yīng)用中,可以調(diào)整兩次單擊間隔時間定義,比如為300ms,這樣單擊的響應(yīng)回快一點,單按鍵操作人員需要加快按鍵的操作過程。如果產(chǎn)品是針對老年人的,這個時間不易太短,因為年紀(jì)大的人,反映和動作都比較慢。
當(dāng)然,上面兩段可以合在一起。我這樣做的目的,是為了可以方便的擴展為N擊(當(dāng)然,需要做修改)。可是最底層的就是最基本的操作處理短按和長按,不用改動的。至于雙擊,還是N擊,在中間層處理。這就是程序設(shè)計中分層結(jié)構(gòu)的優(yōu)點。
測試代碼環(huán)境如下:
*/
void main()
{
uchar key;
TMOD=0x01; //定時器模式選擇,選擇T0定時器,方式1
TR0=1; //1 打開定時器0
ET0=1; //定時器0中斷使能位
EA=1; //使能全局中斷
TH0=(65536-10000)/256;
TL0=(65536-10000)%256;
while(1)
{
if(time_10ms_ok) //每10ms執(zhí)行一次,
{
time_10ms_ok=0;
key=key_read(); //《====== 10ms一次調(diào)用按鍵中間層函數(shù),根據(jù)返回鍵值,點亮不同的LED燈,全面測試按鍵操作是否正常
if(key==L_key)
RED=1;Pout=0;
if(key==D_key)
GREEN=1;Pout=1;
if(key==S_key)
BLUE=1;Pout=0;
}
}
}
void time0() interrupt 1 // 定時器10ms中斷服務(wù)
{
TH0=(65536-10000)/256;
TL0=(65536-10000)%256;
time_10ms_ok=1;
}
|
|