縱觀現(xiàn)在的智能硬件產(chǎn)品,按鍵少,功能多。在保證產(chǎn)品外觀漂亮的同時,如何用最少的按鍵,來實現(xiàn)較多的功能。
所以就去網(wǎng)上搜了,按鍵長按與短按實現(xiàn)的方法。其中精華代碼,是我在其他人的博客【http://www.zg4o1577.cn/bbs/dpj-41706-1.html】學(xué)來的。他的核心代碼寫的很棒,但是在后邊函數(shù)調(diào)用的時候,要根據(jù)自己功能的實現(xiàn)去適當(dāng)?shù)母拇a。
核心算法
unsigned char Trg; unsigned char Cont; void KeyRead( void ) { unsigned char ReadData =GPIO_ReadInputDataBit(GPIO*,GPIO_Pin)^0xff; // 1 Trg = ReadData & (ReadData ^Cont); // 2 Cont=ReadData; // 3 } 作為一個新手來說,還是要具體其分析分析的: 我們就按鍵開始為高電平 ,按下為低電平,進(jìn)行分析。 程序解讀: Trg(triger) 代表的是觸發(fā),Cont(continue)代表的是連續(xù)按下。 1:GPIO_ReadInputDataBit()這個函數(shù),就是讀取按鍵GPIO*按鍵引腳的輸入狀態(tài)。^0xff,和前面讀取到的狀態(tài),取反。并將數(shù)值儲存到ReadData中; 2:用來計算觸發(fā)變量; 3:用來計算連續(xù)變量。 (1)沒有按鍵的時候 端口為0xff,ReadData讀端口并且取反,很顯然,就是 0x00 了。 Trg = ReadData & (ReadData ^ Cont);(初始狀態(tài)下,Cont也是為0的)很簡單的數(shù)學(xué)計算,因為ReadData為0,則它和任何數(shù)“相與”,結(jié)果也是為0的。 Cont = ReadData; 保存Cont 其實就是等于ReadData,為0; 結(jié)果就是: ReadData = 0; Trg = 0; Cont = 0; (2) 第一次PB0按下的情況 端口數(shù)據(jù)為0xfe,ReadData讀端口并且取反,很顯然,就是 0x01 了。 Trg = ReadData & (ReadData ^ Cont);因為這是第一次按下,所以Cont是上次的值,應(yīng)為為0。那么這個式子的值也不難算,也就是 Trg = 0x01 &(0x01^0x00) = 0x01 Cont = ReadData = 0x01; 結(jié)果就是: ReadData = 0x01; Trg = 0x01; (Trg只會在這個時候?qū)?yīng)位的值為1,其它時候都為0) Cont = 0x01; (3) PB0按著不松(長按鍵)的情況 端口數(shù)據(jù)為0xfe,ReadData讀端口并且取反是 0x01 了。 Trg = ReadData & (ReadData ^ Cont);因為這是連續(xù)按下,所以Cont是上次的值,應(yīng)為為0x01。那么這個式子就變成了 Trg = 0x01 &(0x01^0x01) = 0x00 Cont = ReadData = 0x01; 結(jié)果就是: ReadData = 0x01; Trg = 0x00; Cont = 0x01; 因為現(xiàn)在按鍵是長按著,所以MCU會每隔一段時間就會,不斷的執(zhí)行這個函數(shù),那么下次執(zhí)行的時候情況會是怎么樣的呢? ReadData = 0x01;這個不會變,因為按鍵沒有松開 Trg = ReadData & (ReadData ^ Cont) = 0x01 & (0x01 ^ 0x01) =0 ,只要按鍵沒有松開,這個Trg值永遠(yuǎn)為 0 !!! Cont = 0x01;只要按鍵沒有松開,這個值永遠(yuǎn)是0x01!! (4) 按鍵松開的情況 端口數(shù)據(jù)為0xff,ReadData讀端口并且取反是 0x00 了。 Trg = ReadData & (ReadData ^ Cont) = 0x00 & (0x00^0x01) =0x00 Cont = ReadData = 0x00; 結(jié)果就是: ReadData = 0x00; Trg = 0x00; Cont = 0x00; 很顯然,這個回到了初始狀態(tài),也就是沒有按鍵按下的狀態(tài)。 該代碼的精華之處就在于:Trg只有在按一按鍵的時候才為0x01,只要按鍵沒有放開Trg從第二次進(jìn)入KeyRead()后,就一直為0x00;Cout,只要按鍵是按下的,這個值就一直為0x01。 理解基本就是這樣了,下面說一下用到的例子: 1:原文中博主的例子,有一個是這樣的,類似小時候玩過的電子表調(diào)節(jié)時間。 按一個鍵不放手(直到到達(dá)指定的功能在放手),從按下,到放手,這個鍵盤包含了兩個功能——切換模式、累計加數(shù)(針對他的例子說的功能,意會即可)。其實總的意思就是,從按下,到松手,Trg開始的時候(第一下)為0x01,因為沒有放手,故第二次執(zhí)行KeyRead()的時候,Trg就變?yōu)榱?x00;故根據(jù)原博主的if(Trg & 0x01)和if(Cont & 0x01)這個的判斷,從按下,到松手,這個兩個函數(shù)都會執(zhí)行過去。[注:原博主的KEY_PLUS的宏定義應(yīng)該為錯誤的,若為0x02,這個if永遠(yuǎn)都不會執(zhí)行] 上述講述的這個鍵盤的功能,做項目可能用得到。 2:在一個就是,我自己做東西遇到的一個問題。按鍵,短按一個功能,長按一個功能(兩個功能不能同時到來)。顯然例子1的代碼滿足不了我的需求。下面看我的代碼: void Key_Proc(void)
{
if(Trg&0x01) //短按
{
DelayMs(200); //200ms
if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13)==1) //判斷讀取出的鍵盤輸入狀態(tài)是否為高電平 {
運行代碼; b=0;
}
}
if(Cout&0x01) //長按
{
b++;
if(b>20)
{
運行代碼; b=0;
}
}
} int main() { KeyRead(); Key_Proc(); while(1) { DelayMs(10); break; }
} 我改的代碼,除了 DelayMs(200); //200ms
if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13)==1) //判斷讀取出的鍵盤輸入狀態(tài)是否為高電平 其他的基本相同,這段代碼,主要解決長按按鍵,兩種功能同時出現(xiàn)的事件。 分析:按鍵按一下,即按下立馬松開。鍵盤一開始為高電平,Delay,消除鍵盤按下到放開的反應(yīng)時間,200MS足以,然后在檢測按鍵輸入狀態(tài),若為高,說明,已經(jīng)放開了,判斷這是一次短按。 按下不放手,按鍵開始為高電平,不放手,延遲過后,檢測按鍵依然為低,不進(jìn)入if,也就屏蔽掉了這個短,按的功能,然后進(jìn)行后邊的if(Cout&0x01),判斷為真,進(jìn)入,b++,后邊有10MS的延遲。b一會就會加到20,執(zhí)行程序,并清除b的值。 為什么短按里面要加一個b=0呢?如果不加的話,你按鍵盤,若的時間為達(dá)到長按的要求,這時候b也會++,可能會出現(xiàn)這樣的情況,連續(xù)點擊鍵盤,按下松開,按下松開.......雖然你沒有長按,但是連續(xù)的點擊會使b++到20,導(dǎo)致觸發(fā)功能。故在短按里面加一個b=0. 大致就是這個樣子了,還有就是在一個程序中,一個鍵盤,在不同的地方有不同的功能,這個又該如何實現(xiàn)呢?
我想出來的就是用switch() case 語句實現(xiàn)。switch判斷處于程序的哪一個階段,在找對應(yīng)的case去執(zhí)行就好了。今天太累了,具體怎么實現(xiàn)我就不貼代碼了。
|