網(wǎng)友評論:支持一下ayb_ice。
網(wǎng)友評論:“若鍵盤數(shù)量比較多,并且要讀寫EEPROM”
若鍵盤數(shù)量比較多,就更加應該放到主循環(huán)當中做,老占著中斷不放,不是好事。
讀EEPROM,一般不怎么耗時,
寫EEPROM,采取中斷方式,不能傻等。
一句話,程序當中,不要為一件事長時間傻等。
網(wǎng)友評論:不會為一件事長時間傻等,是OS的一個巨大的優(yōu)點
網(wǎng)友評論:終于有人支持了
網(wǎng)友評論:通用的,都能插的上嘴
網(wǎng)友評論:寫eep本身,就幾條指令,不怎么耗時,
但是為了確信寫完成,啟動下一次寫,需要等待可能若干ms。
對于確信寫完成,簡單的就是延時查詢傻等,這樣就比較耗時。
如果是芯片內(nèi)部的eep,寫完成,會有中斷發(fā)生,置啟動寫標志,
主循環(huán)里,檢測到標志,啟動下一次寫。
或者在主循環(huán)里查詢,寫完成中斷標標志,啟動下一次寫
對于外部的eep,啟用定時器延時,時間到設置一個寫標志,
主循環(huán)里,檢測到標志,啟動下一次寫。
這樣處理的話,就不會有傻等的情況發(fā)生
網(wǎng)友評論:我喜歡放在主程序掃鍵盤,其它任務都用中斷,置標志位,主程序處理,這樣假設所有任務都發(fā)生主程序處理時間也不會太長(不到100ms,對我做過的來說),而人對鍵盤的反應比較遲鈍
網(wǎng)友評論:和twz8858877思路完全一樣。
而且,
在一個循環(huán)的,所有任務都發(fā)生的可能性,一般情況下非常小.
網(wǎng)友評論:即可所有任務同時發(fā)生也可以面對,因為不是所有任務必須及時處理,可以先處理最緊急的任務。
網(wǎng)友評論:放定時中斷里絕對是很好的方法
網(wǎng)友評論:因為有個程序每31.25us中斷一次,每次最少18us,平均25uS,最大30uS~~~
還要跑很多程序,特別還要檢測頻率,動態(tài)測試IO的好壞,芯片是否被振落,LED刷新等~~~
網(wǎng)友評論:hotpower的0耗時,沒看過。
不知道是個啥概念。
但估計十之八九是在消抖延時上做文章,消抖延時在定時器里定時,完成后再進行鍵盤值的確定,除此外,想不出,還有什么地方可以接見時間。
如果是這種思路的話,主程序執(zhí)行鍵盤掃描,一樣可以0耗時.
網(wǎng)友評論:完全封裝,做成標準庫。
外部只提供和
硬件有關的掃描標志數(shù)組。
可每個KEY單獨識別PUSH、DOWN、DOUBLECLICK、LONGCLICK,強大啊。
網(wǎng)友評論:這樣可以保證掃描的周期性,尤其是對數(shù)碼管。
如果放在主程序中,很難保證其周期性,處理事務是還要瞻前顧后,有點麻煩。
放在定時器中斷中,很容易做到掃描的周期性,同時也很容易實現(xiàn)所長所說的“PUSH、DOWN、DOUBLECLICK、LONGCLICK”,
以及自動連擊等。甚至還可以用一個隊列保存按鍵,這樣在主程序任務時間長時,按鍵也能很好的響應。
如果掃描放在主程序中,做這些就有難度了。
網(wǎng)友評論:所長技癢難忍啊。
實現(xiàn)每個KEY單獨識別PUSH、DOWN、DOUBLECLICK、LONGCLICK,
工作量很大啊。
網(wǎng)友評論:當然,這和最早的電路是使用鍵盤掃描IO和LED復用有關。
我是一直使用鍵盤0耗時,這和我笨有關,我最早的時候,對著書本就是沒學會寫去抖程序。
其實,0耗時是檢測keydown事件,然后把去抖放在檢測keyup事件,如此而已。沒什么神秘
掃描程序和鍵盤處理程序分開,那掃描放中斷還是放主程序的問題就基本可以終結(jié)。
網(wǎng)友評論:去抖也很方便的
任何按鍵都有三個動作,即三個屬性:按下、按住、彈起
按下和彈起是瞬間的,按住可以任意時長...
網(wǎng)友評論:為每個鍵,設置一個隊列,記錄每個鍵的動作,和持續(xù)的時間,
對隊列進行分析,
各種鍵的狀態(tài),應該在主程序里面實現(xiàn)PUSH、DOWN、DOUBLECLICK、LONGCLICK,
問題不大吧?
網(wǎng)友評論:具有按鍵按下、按住、彈起等功能。在這個代碼的基礎上,再增加幾條語句,就可以實現(xiàn)長按、按鍵連發(fā)等功能。
/********************************************************************
函數(shù)功能:定時器0中斷處理。
入口參數(shù):約5ms中斷一次。
返回:無。
備注:無。
********************************************************************/
voidTimer0Isr(void)interrupt1
{
uint8temp;
#ifndefLCD
staticuint8LedDigit=0;
#endif
TH0=(65536-Fclk/1000/12*5+22)/256;//定時器0重裝
TL0=(65536-Fclk/1000/12*5+22)%256;
SystemTick++;
#ifndefLCD//沒有定義LCD,則使用數(shù)碼管顯示
switch(LedDigit)
{
case0:
LED_3=1;
LED_SEG=Table[LedBuffer][0]];
LED_0=0;
LedDigit=1;
break;
case1:
LED_0=1;
LED_SEG=Table[LedBuffer][1]];
LED_1=0;
LedDigit=2;
break;
case2:
LED_1=1;
LED_SEG=Table[LedBuffer][2]];
LED_2=0;
LedDigit=3;
break;
case3:
LED_2=1;
LED_SEG=Table[LedBuffer][3]];
LED_3=0;
LedDigit=0;
break;
default:
LedDigit=0;
break;
}
#endif
if(!KeyCanChange)return;//如果正在處理按鍵,則不再掃描鍵盤
//開始鍵盤掃描
//保存按鍵狀態(tài)到當前按鍵情況
//KeyCurrent總共有8個bit
//當某個
開關按下時,對應的bit為1
P2_4=0;//掃描第一列
temp=~KeyIO;
P2_4=1;
temp&=0x0F;
P2_5=0;//掃描第二列
KeyCurrent=~KeyIO;
P2_5=1;
KeyCurrent<<=4;
KeyCurrent|=temp;
if(KeyCurrent!=KeyOld)//說明按鍵情況發(fā)生了改變
{
KeyNoChangedTime=0;//鍵盤按下時間為0
KeyOld=KeyCurrent;//保存當前按鍵情況
return;
}
else
{
KeyNoChangedTime++;//按下時間累計
if(KeyNoChangedTime>=1)//如果按下時間足夠
{
KeyNoChangedTime=1;
KeyPress=KeyOld;//保存按鍵
KeyDown|=(~KeyLast)&(KeyPress);//求出新按下的鍵
KeyUp|=KeyLast&(~KeyPress);//求出新釋放的鍵
KeyLast=KeyPress;//保存當前按鍵情況
if(KeyDown)BeepOn();//按鍵按下,蜂鳴器響
elseif(KeyUp)BeepOff();
}
}
}
/*******************************************************************/
網(wǎng)友評論:其實都是在消抖上做文章,不要在那里死等,可以間隔調(diào)用掃鍵程序,利用間隔來完成延時,加上們簡單的狀態(tài)機就搞定了,很容易實現(xiàn)短按,長按,連按,組組合等功能。。。
網(wǎng)友評論:voidScanKey()
{
unsignedchartempKeyValue;
if(keyScanState==0)//預先掃描鍵盤
{
if(RA0==0||RA1==0)
keyScanState=1;//啟動消抖延時
}
elseif(keyScanState==2)//延時完畢,確認掃描鍵盤
{
tempKeyValue=0;
TRISA2=0;
TRISA3=1;
RA2=0;
asm("nop");asm("nop");asm("nop");
if(RA0==0)tempKeyValue=4;
elseif(RA1==0){tempKeyValue=1;}
else;
TRISA2=1;
TRISA3=0;
RA3=0;
asm("nop");asm("nop");asm("nop");
if(RA0==0)tempKeyValue=3;
elseif(RA1==0){tempKeyValue=2;}
else;
if(tempKeyValue!=0)//掃描到鍵盤值,存到緩沖隊列
{
if(keyDataLen<MAN_KEYBUF_LEN)
{
keyBuf[keyBufHead]=tempKeyValue;
keyDataLen++;
if(keyBufHead==MAN_KEYBUF_LEN-1)
keyBufHead=0;
else
keyBufHead++;
}
}
TRISA2=0;
TRISA3=0;
RA2=0;
RA3=0;
keyScanState=0;//恢復預先掃描狀態(tài)
}
else
;
}
voidinterruptISR(void)
{
if(T0IE&&T0IF){//判TMR0中斷,8m
if(keyScanState==1)
{
if(KeyDelayTime<4)
KeyDelayTime++;
else
{
keyScanState=2;
KeyDelayTime=0;
}
}
TMR0=5;
T0IF=0;}
}
如果檢測鍵彈起事件,存入緩沖隊列,實現(xiàn)長擊,連擊,也非難事
網(wǎng)友評論:這個問題建議大家,到書店看看這本書《松翰SN8P2700系列單片機原理及應用技術》
網(wǎng)友評論:按鍵處理在300毫秒內(nèi)處理完就可以,有必要放中斷嗎?
還有如果下降沿觸發(fā)進入中斷,硬件電路也要處理。
一般可以不放中斷處理的程序,就不放中斷。
網(wǎng)友評論:我也不贊成放在中斷,按鍵的響應速度要求不高,即使DELAY個幾十MS也沒有問題,在主程序里做掃描,定時中斷只是作為啟動掃描的鑰匙,掃描不存在等待延時,同樣也是零耗時
網(wǎng)友評論://一個單片機系統(tǒng)的設計經(jīng)常會用到多種不同目的和用圖的定時,例如系統(tǒng)需要輸出
//一個指示“心跳正常”的秒閃信號,間隔0.5s;按鍵檢測時臨時需要約20ms的消抖;
//蜂鳴器需要發(fā)聲延時;用戶菜單選擇時可能需要對應的發(fā)光管或LCD點陣(字段)
//閃爍;通訊時需要設定應答超時判別,等等。是不是要抱怨一個單片機上的若干個
//定時器不夠用了?其實,用一個定時器資源就可以搞定所有的這一切定時要求。
//1)首先,選定一個你喜歡的定時器,按所需應用的定時精度要求設定其定時中斷頻
//率。一般人機界面的定時精度為ms級就足夠了,所以可以設定定時中斷時間間隔為
//1ms,5ms或10ms;例如我的選擇:
//==============================================================
//TPM2overflowinterruptserviceroutine
//Interruptatevery1ms
//==============================================================
voidinterrupt14TPM2_Overflow_ISR(void)
{
TPM2SC_TOF=0;//resetinterruptflag
msTimeoutCount++;//1msincrement
}
//變量msTimeoutCount是一個16位word型的靜態(tài)變量,在中斷服務程序中簡單地對它
//遞增,無需考慮溢出。如果你的中斷時間間隔為Nms,則在中斷中對其遞增的方法為
//“msTimeoutCount+=N”。它在程序模塊的前面被聲明,為了提高中斷服務程序的效
//率,其被定位在直接尋址區(qū):
//==============================================================
//Followingdataaredeclaredinthedirectaddressingarea
//forfastaccess(address<0x100)
//==============================================================
#pragmaDATA_SEGSHORTMY_ZEROPAGE//directaddressingdatasegment
volatilewordmsTimeoutCount;
//然后寫一段獨立的定時判別函數(shù)。這個函數(shù)有兩個入口參數(shù):特定定時實例的一個
//定時變量指針和所需的定時時間長度。若定時時間長度為0,則定時過程被復位,實
//際上是當前的定時計數(shù)器值(msTimeoutCount)被復制到定時實例的一個定時變量
//中。返回值為0則表明定時時間未到,0xff則為一次定時時間到并自動開始下一次的
//定時過程。具體代碼如下:
//==============================================================
//Checkfortimeoutoccurance
//Input*timer-pointeroftimercounter
//timeOutVal-timeoutvalue,0=forcedtimerupdate
//Return0-notimeoutyet
//0xff-timeoutoccuredandtimerupdated
//==============================================================
byteTimeOutChk(word*timer,wordtimeOutVal)
{
wordshadow,diff;
TPM2SC_TOIE=0;//針對8位機必須禁止定時中斷,16位機以上則無需如此
shadow=msTimeoutCount;//將當前時間計數(shù)值復制一份以作后需
TPM2SC_TOIE=1;//對應上面的中斷禁止,現(xiàn)在開放中斷
if(timeOutVal==0)
{//復位定時過程
*timer=shadow;
return(0);
}
else
{
diff=shadow-*timer;//
計算定時時間間隔
if(diff>=timeOutVal)
{//定時時間到
*timer+=timeOutVal;//更新定時變量,開始下一次定時過程
return(0xff);//返回時間到標志
}
else
{
return(0);//定時時間未到
}
}
}
//剩下的就看具體應用的需要而開辟特定的定時實例了。每一個實例必須有一個word
//型的變量作為定時跟蹤變量。
//例如產(chǎn)生500ms的定時(msCount變量在模塊前面已經(jīng)定義):
voidmain(void)
{
...
TimeOutChk(&msCount,0);//復位初始化定時實例
...
while(1)
{
Clock();
KeyScan();
...
}
}
//==============================================================
//Keepthesystemclockrunning
//==============================================================
voidClock(void)
{
if(TimeOutChk(&msCount,500)==0)
return;//waitfor0.5secondtimeout
runFlag.halfSec=!runFlag.halfSec;
dispCodeBuff[2]^=0x80;
dispCodeBuff[3]^=0x80;
if(runFlag.halfSec)
{
return;
}
second++;
if(second==30)
{//syncsoftclockwithRTCvalue
RTC_Read();
}
if(second>59)
{
second=0;
minute++;
if(minute>59)
{
minute=0;
hour++;
if(hour>23)
hour=0;
}
}
runFlag.clkDisp=1;
}
//按鍵掃描時的消抖延時實現(xiàn),keyDebounce在模塊前面為局部靜態(tài)變量定義
//==============================================================
//Scaningkeyinput
//==============================================================
voidKeyScan(void)
{
bytekeyInput;
keyInput=(PTFD^0xff)&0b00011111;
switch(keyState)
{
case0://idle
if(keyInput)
{//possiblekeyinputdetected
runFlag.keyCon=0;//continuouskeystrikenotallowedbydefault
TimeOutChk(&keyDebounce,0);//resetdebouncetimer
keyState=1;
}
break;
case1://downdebouncing
if(TimeOutChk(&keyDebounce,50))
{//50msdebouncetimeout
if(keyInput)
{
KeyFifoAdd(keyInput);
TimeOutChk(&keyDebounce,0);//!復位定時準備實現(xiàn)按鍵持續(xù)按下時的連續(xù)激發(fā)功能
keyState=2;//keyisholding
}
else
{
keyState=0;//debouncecheckfailed
}
}
break;
case2://hold
if(keyInput==0)
{//possiblekeyreleasedetected
TimeOutChk(&keyDebounce,0);
keyState=4;
}
else
{
if(runFlag.keyCon)
{//continuouskeystrikeallowed
if(TimeOutChk(&keyDebounce,500))
{//持續(xù)按下時間達0.5s
KeyFifoAdd(keyInput);
TimeOutChk(&keyDebounce,0);//準備后續(xù)每隔0.1s激發(fā)一個按鍵值
keyState=3;//invokekeycontinuousstrike
}
}
}
break;
case3://continuousstrike
if(keyInput==0)
{//possiblekeyreleasedetected
TimeOutChk(&keyDebounce,0);
keyState=4;
}
else
{
if(TimeOutChk(&keyDebounce,100))
{//每隔0.1s激發(fā)一個按鍵值
KeyFifoAdd(keyInput);
TimeOutChk(&keyDebounce,0);
}
}
break;
case4://updebouncing
if(TimeOutChk(&keyDebounce,50))
{//50msdebouncetimeout
if(keyInput)
{
keyState=2;//keyisstillholding
}
else
{
keyState=0;//confirmreleased
}
}
break;
default:
keyState=0;
}
}
//所以理論上只要你有足夠多的
內(nèi)存作為定時跟蹤變量,你就可以實現(xiàn)任意多個定時
//實例,無論什么時間和什么地點。當然上面的定時程序有一個局限,就是一次最大
//的定時時間為65535ms。如果要實現(xiàn)更長時間的定時,可以用一個實例產(chǎn)生1s
//(或更長)的定時基準,然后參照函數(shù)TimeOutChk另外寫一個例如TimeOutChkSec,
//按1s的分辨率最多實現(xiàn)65535s的定時。
//采用狀態(tài)機實現(xiàn)按鍵檢測是最可靠最有效的方法。同時在單片機設計中實現(xiàn)多任務
//的并發(fā)和協(xié)調(diào),狀態(tài)機起著不可或缺的作用。對于按鍵處理,部分代碼如下:
#defineKEY_FIFO_LEN4
bytekeyFifo[KEY_FIFO_LEN],keyPut,keyGet;
//==============================================================
//AddakeyintoFIFO
//==============================================================
voidKeyFifoAdd(bytecode)
{
keyFifo[keyPut++]=code;
keyPut&=(KEY_FIFO_LEN-1);
}
//==============================================================
//FetchakeyfromFIFO
//==============================================================
byteKeyFifoGet(void)
{
bytetmp;
tmp=keyFifo[keyGet++];
keyGet&=(KEY_FIFO_LEN-1);
return(tmp);
}
//==============================================================
//Dokeyfunctionforprimarytask
//==============================================================
voidKeyFuncMain(void)
{
bytekeyCode;
if(keyPut==keyGet)
return;
keyCode=KeyFifoGet();
switch(keyCode)
{
caseKEY_CH1_CTL:
RELAY1_CTL=!RELAY1_CTL;
if(RELAY1_CTL)
dispCodeBuff[4]|=0x10;
elsedispCodeBuff[4]&=(0x10^0xff);
SetBeep(200);
break;
caseKEY_CH2_CTL:
RELAY2_CTL=!RELAY2_CTL;
if(RELAY2_CTL)
dispCodeBuff[4]|=0x08;
elsedispCodeBuff[4]&=(0x08^0xff);
SetBeep(200);
break;
caseKEY_SET:
SetBeep(50);
TimeOutChk(&menuTimeout,0);
menuId=0;
MainTaskEntry=SetupEnable;
MenuTaskEntry=ClockSetup;
dispCodeBuff[4]=0x01;
break;
default:
return;
}
}
網(wǎng)友評論:以后嘗試在中斷中掃描
網(wǎng)友評論:脫離需求環(huán)境討論解決方案是沒有意義的!
網(wǎng)友評論:在中斷函數(shù)中不要太多的執(zhí)行代碼
網(wǎng)友評論:要是放在中斷里掃的話,萬一不停的按著按鍵不就狂進中斷了嗎,那主程序就沒法正常運行了啊!就死了!
不知大俠們怎么處理的!?
網(wǎng)友評論:關鍵是怎么設消抖動關鍵是怎么設消抖動關鍵是怎么設消抖動
網(wǎng)友評論:鍵盤都能把程序整死太危險了
網(wǎng)友評論:我沒什么經(jīng)驗,不過干了20幾年。我從來不在中斷中干這些沒有即時響應要求、沒有流量的事情。可以分析各位的系統(tǒng)中按鍵操作占系統(tǒng)工作多少時間,掃描按鍵中斷又占了系統(tǒng)多少時間?如果領導要求你每天、每小時、每分鐘報到,想一下你的心情、怎么工作。
網(wǎng)友評論:鍵盤不是中斷,而是定時器中斷中掃描鍵盤
我是這樣做的
定時器中檢測到有鍵盤按下置一標志,再次檢測到可以認為它就是真的按下了,如果很重要的功能鍵不妨就對掃描計數(shù),達到一定的次數(shù)后才認為真的有效,每次掃描鍵盤所占的時間微不足道,幾個us,幾ms一次。至于那鍵功能的處理根據(jù)具體情況處理,如果可能涉及到耗時的計算就把鍵的功能放到主循環(huán)里,一般都可以及時處理的,哪怕使用者用手掌亂拍鍵盤也不會死機
網(wǎng)友評論:我明白了。其實如果主程序不是很大的話,鍵盤掃描放主程序里和用定時器感覺沒什么不同的!
網(wǎng)友評論:中斷里做的事情越多哦,全局變量沖突就越多
只有需要極其及其實時性要求的任務才放進去做。
網(wǎng)友評論:一個弱實時,一個強實時而已。
不應有太大爭論。
只是把掃鍵放到主程序后,優(yōu)先級降低,允許其他更需要實時的任務執(zhí)行而已。
網(wǎng)友評論:中斷的話就要多了一個SM口拉!檢測到中斷之后也要掃一下,看是哪個按鍵,呵呵,跟高手的意見不同,等著被人潑水
網(wǎng)友評論:用中斷的話又要多一個單片機引腳,而且中斷之后也一樣要掃描,呵呵,跟樓下的高手意見不同,等著潑水
網(wǎng)友評論:我認為像數(shù)碼管記數(shù)顯示等,應該在定時器中段中處理比較好,可以不用在主函數(shù)中不停的掃描。
像掃描鍵盤,特別是矩陣鍵盤的檢測,我認為不應在中斷中處理,應該獨立寫成一個函數(shù),然后在主函數(shù)中不停的調(diào)用,滿足條件(按下鍵)的話就執(zhí)行處理鍵。
理由,你用一個中斷,相應的就少了一個中斷可用;就算在主函數(shù)中不停的調(diào)用,沒有鍵按下時,才幾個指令,占用不了多少CPU的時間,而且在中斷中,處理的時間不能超過定時的時間;中斷中消抖也不方便。
有人說,在中斷中設置標志位,這樣的話跟在主函數(shù)中不停的判斷也就沒有多大的區(qū)別了。
這個是我寫的,請指教
voidtrach(void)//鍵盤檢測函數(shù)
{
if(temp!=0xf0)//有按鍵(先掃描第一行,主函數(shù)中有移位
{//下一行的)
delay();//消抖
temp=temp&0xf0;
if(temp!=0xf0)//重新檢測
{
temp=P3;//確實有按鍵
switch(temp)//處理鍵,當然,硬件不同,方法是不
{
case0xee:num=0;break;
case0xde:num=1;break;
case0xbe:num=2;break;
case0x7e:num=3;break;
case0xed:num=4;break;
case0xdd:num=5;break;
case0xbd:num=6;break;
case0x7d:num=7;break;
case0xeb:num=8;break;
case0xdb:num=9;break;
case0xbb:num=10;break;
case0x7b:num=11;break;
case0xe7:num=12;break;
case0xd7:num=13;break;
case0xb7:num=14;break;
case0x77:num=15;break;
}
}
}
}
voiddelay(void)//延遲6ms,消抖
{
uchari,j;
for(i=6;i>0;i--)
for(j=100;j>0;j--);
}
網(wǎng)友評論:如果主循環(huán)的最長周期可以控制在一次按鍵掃描的循環(huán)時間內(nèi),則可放在主程序中做,否則放在中斷中,并存儲好按鍵值,以作處理。按鍵如果放在中斷中掃描,程序段必須盡量的短,以減少中斷時間,有時候,中斷時間長是一件很恐怖的事情,非常容易使程序出現(xiàn)莫名其妙的問題。
網(wǎng)友評論:我覺得中斷里做沒什么問題.
一個好的邏輯結(jié)構,有確定性.相對多一點開銷是值得的.這時調(diào)試什么都會方便點.
網(wǎng)友評論:根本就沒入門,還敢充專家說什么“沒有多大的區(qū)別”...