一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何進(jìn)行按鍵檢測(cè)和按鍵FIFO的實(shí)現(xiàn)

GReq_mcu168 ? 來(lái)源:果果小師弟 ? 作者:智果芯 ? 2021-11-03 09:13 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

摘要:今年實(shí)驗(yàn)室來(lái)了三個(gè)學(xué)妹,其中一個(gè)學(xué)妹以前是物聯(lián)網(wǎng)專(zhuān)業(yè)的,進(jìn)了實(shí)驗(yàn)室老師二話沒(méi)說(shuō):先把STM32單片機(jī)過(guò)一遍,有啥問(wèn)題就找小師弟。還好單片機(jī)小師弟會(huì)玩一點(diǎn)點(diǎn),玩的也不好,所以一起學(xué)習(xí)吧!

一、如何進(jìn)行按鍵檢測(cè)

檢測(cè)按鍵有中斷方式和GPIO查詢方式兩種。推薦大家用GPIO查詢方式。

1.從裸機(jī)的角度分析

  • 中斷方式:中斷方式可以快速地檢測(cè)到按鍵按下,并執(zhí)行相應(yīng)的按鍵程序,但實(shí)際情況是由于按鍵的機(jī)械抖動(dòng)特性,在程序進(jìn)入中斷后必須進(jìn)行濾波處理才能判定是否有效的按鍵事件。如果每個(gè)按鍵都是獨(dú)立的接一個(gè) IO 引腳,需要我們給每個(gè) IO 都設(shè)置一個(gè)中斷,程序中過(guò)多的中斷會(huì)影響系統(tǒng)的穩(wěn)定性。中斷方式跨平臺(tái)移植困難。

  • 查詢方式:查詢方式有一個(gè)最大的缺點(diǎn)就是需要程序定期的去執(zhí)行查詢,耗費(fèi)一定的系統(tǒng)資源。實(shí)際上耗費(fèi)不了多大的系統(tǒng)資源,因?yàn)檫@種查詢方式也只是查詢按鍵是否按下,按鍵事件的執(zhí)行還是在主程序里面實(shí)現(xiàn)。

2.從OS的角度分析

  • 中斷方式:在 OS 中要盡可能少用中斷方式,因?yàn)樵?a href="http://www.www27dydycom.cn/tags/RTOS/" target="_blank">RTOS中過(guò)多的使用中斷會(huì)影響系統(tǒng)的穩(wěn)定性和可預(yù)見(jiàn)性。只有比較重要的事件處理需要用中斷的方式。
  • 查詢方式:對(duì)于用戶按鍵推薦使用這種查詢方式來(lái)實(shí)現(xiàn),現(xiàn)在的OS基本都帶有CPU利用率的功能,這個(gè)按鍵FIFO占用的還是很小的,基本都在1%以下。

二、最簡(jiǎn)單的按鍵檢測(cè)程序

先給他說(shuō)了一種經(jīng)典的按鍵檢測(cè)代碼,相信大多數(shù)人使用按鍵函數(shù)都見(jiàn)過(guò)它,很簡(jiǎn)單就不過(guò)多介紹了!

#defineKEY0_PRES1//KEY0
#defineKEY1_PRES2//KEY1
#defineWKUP_PRES3//WK_UP

u8KEY_Scan(u8mode)
{
staticu8key_up=1;//按鍵按松開(kāi)標(biāo)志
if(mode)key_up=1;//支持連按
if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))
{
delay_ms(10);//去抖動(dòng)
key_up=0;
if(KEY0==0)returnKEY0_PRES;
elseif(KEY1==0)returnKEY1_PRES;
elseif(WK_UP==1)returnWKUP_PRES;
}elseif(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1;
return0;//無(wú)按鍵按下
}

intmain(void)
{
u8t=0;
delay_init();//延時(shí)函數(shù)初始化
LED_Init();//初始化與LED連接的硬件接口
KEY_Init();//初始化與按鍵連接的硬件接口
LED=0;//點(diǎn)亮LED
while(1)
{
t=KEY_Scan(0);//得到鍵值
switch(t)
{
caseKEY0_PRES://如果KEY0按下
LED=!LED;
break;
default:
delay_ms(10);
}
}
}

如果你在工作中使用這種代碼,有可能會(huì)被同事笑話。當(dāng)然我這里并不是說(shuō)這種代碼不好,不管黑貓白貓,能抓住老鼠就是好貓。只要能滿足項(xiàng)目需求實(shí)現(xiàn)對(duì)應(yīng)的功能就是好代碼。但是如果你使用下面這種個(gè)人感覺(jué)可能會(huì)更好。

其實(shí)也并沒(méi)有什么神秘感,就是使用了FIFO機(jī)制。參考的就是安富萊的按鍵例程,不過(guò)源代碼相對(duì)比較復(fù)雜,對(duì)于初學(xué)者并不友好,所以小小的修改了一下,僅供參考!

在前面分享了使用系統(tǒng)滴答定時(shí)器實(shí)現(xiàn)了多個(gè)軟件定時(shí)器,在按鍵FIFO中也需要使用這個(gè)定時(shí)器。在系統(tǒng)的開(kāi)始我們會(huì)啟動(dòng)一個(gè)10ms的軟件定時(shí)器。在這個(gè)10ms的軟件定時(shí)器中不斷的進(jìn)行按鍵掃描,與其他的任務(wù)互不影響。

三、為什么要了解FIFO

要回答什么是FIFO,先要回答為什么要使用FIFO。只有搞清楚使用FIFO的好處,你才會(huì)有意無(wú)意的使用FIFO。學(xué)習(xí)FIFO機(jī)制和狀態(tài)機(jī)機(jī)制一樣,都是在裸機(jī)編程中非常重要的編程思想。編程思想很重要。初級(jí)coder總是在關(guān)注代碼具體是怎么寫(xiě),高級(jí)coder關(guān)注的是程序的框架邏輯,而不是某個(gè)細(xì)節(jié)。只要你框架邏輯通了,則一通百通。

四、什么是FIFO

FIFO是先入先出的意思,即誰(shuí)先進(jìn)入隊(duì)列,誰(shuí)先出去。比如我們需要串口打印數(shù)據(jù),當(dāng)使用緩存將該數(shù)據(jù)保存的時(shí)候,在輸出數(shù)據(jù)時(shí)必然是先進(jìn)入的數(shù)據(jù)先出去,那么該如何實(shí)現(xiàn)這種機(jī)制呢?首先就是建立一個(gè)緩存空間,這里假設(shè)為10個(gè)字節(jié)空間進(jìn)行說(shuō)明。

從這張圖就知道如果要使用FIFO,就要定義一個(gè)結(jié)構(gòu)體,而這個(gè)結(jié)構(gòu)體至少應(yīng)該有三個(gè)成員。數(shù)組buf、讀指針read、寫(xiě)指針write。

typedefstruct
{
uint8_tBuf[10];/*緩沖區(qū)*/
uint8_tRead;/*緩沖區(qū)讀指針*/
uint8_tWrite;/*緩沖區(qū)寫(xiě)指針*/
}KEY_FIFO_T;

緩存一開(kāi)始沒(méi)有數(shù)據(jù),并且用一個(gè)變量write指示下一個(gè)寫(xiě)入緩存的索引地址,這里下一個(gè)存放的位置就是0,用另一個(gè)變量read 指示下一個(gè)讀出緩存的索引地址,并且下一個(gè)讀出數(shù)據(jù)的索引地址也是0。目前隊(duì)列中是沒(méi)有數(shù)據(jù)的,也就是不能讀出數(shù)據(jù),隊(duì)列為空的判斷條件在這里就是兩個(gè)索引值相同。

現(xiàn)在開(kāi)始存放數(shù)據(jù):

在這里可以看到隊(duì)列中加入了9個(gè)數(shù)據(jù),并且每加入一個(gè)數(shù)據(jù)后隊(duì)尾索引加 1,隊(duì)頭不變,這就是數(shù)據(jù)加入隊(duì)列的過(guò)程。但是緩存空間只有10個(gè),如何判斷隊(duì)列已滿呢?如果只是先一次性加數(shù)據(jù)到隊(duì)列中,然后再讀出數(shù)據(jù),那這里的判斷條件顯然是隊(duì)尾索引為9。

好了這就是FIFO的基本原理,下面來(lái)看一下按鍵FIFO是怎么操作的

我們這里以5個(gè)字節(jié)的FIFO空間進(jìn)行說(shuō)明。Write變量表示寫(xiě)位置,Read 變量表示讀位置。初始狀態(tài)時(shí),Read = Write = 0。

我們依次按下按鍵 K1,K2,那么FIFO中的數(shù)據(jù)變?yōu)椋?/p>

如果 Write!= Read,則我們認(rèn)為有新的按鍵事件。我們通過(guò)函數(shù)KEY_FIFO_Get()讀取一個(gè)按鍵值進(jìn)行處理后,Read 變量變?yōu)?1。Write 變量不變。

繼續(xù)通過(guò)函數(shù)KEY_FIFO_Get()讀取 3 個(gè)按鍵值進(jìn)行處理后,Read 變量變?yōu)?4。此時(shí)Read = Write= 4。兩個(gè)變量已經(jīng)相等,表示已經(jīng)沒(méi)有新的按鍵事件需要處理。

有一點(diǎn)要特別的注意,如果 FIFO 空間寫(xiě)滿了,Write 會(huì)被重新賦值為 0,也就是重新從第一個(gè)字節(jié)空間填數(shù)據(jù)進(jìn)去,如果這個(gè)地址空間的數(shù)據(jù)還沒(méi)有被及時(shí)讀取出來(lái),那么會(huì)被后來(lái)的數(shù)據(jù)覆蓋掉,這點(diǎn)要引起大家的注意。我們的驅(qū)動(dòng)程序開(kāi)辟了 10 個(gè)字節(jié)的 FIFO 緩沖區(qū),對(duì)于一般的應(yīng)用足夠了。

五、按鍵FIFO的優(yōu)點(diǎn)

  1. 可靠地記錄每一個(gè)按鍵事件,避免遺漏按鍵事件。特別是需要實(shí)現(xiàn)按鍵的按下、長(zhǎng)按、自動(dòng)連發(fā)、彈起等事件時(shí)。
  2. 讀取按鍵的函數(shù)可以設(shè)計(jì)為非阻塞的,不需要等待按鍵抖動(dòng)濾波處理完畢。
  3. 按鍵 FIFO 程序在嘀嗒定時(shí)器中定期的執(zhí)行檢測(cè),不需要在主程序中一直做檢測(cè),這樣可以有效地降低系統(tǒng)資源消耗。

六、按鍵 FIFO 的實(shí)現(xiàn)

1.定義結(jié)構(gòu)體

在我們的key.h文件中定義一個(gè)結(jié)構(gòu)體類(lèi)型為KEY_FIFO_T的結(jié)構(gòu)體。就是前面說(shuō)的那個(gè)結(jié)構(gòu)體。這只是類(lèi)型聲明,并沒(méi)有分配變量空間。

typedefstruct
{
uint8_tBuf[10];/*緩沖區(qū)*/
uint8_tRead;/*緩沖區(qū)讀指針*/
uint8_tWrite;/*緩沖區(qū)寫(xiě)指針*/
}KEY_FIFO_T;

接著在key.c 中定義 s_tKey 結(jié)構(gòu)變量, 此時(shí)編譯器會(huì)分配一組變量空間。

staticKEY_FIFO_Ts_tKey;/*按鍵FIFO變量,結(jié)構(gòu)體*/

好了按鍵FIFO的結(jié)構(gòu)體數(shù)據(jù)類(lèi)型就定義完了,很簡(jiǎn)單吧!

2.將鍵值寫(xiě)入FIFO

既然結(jié)構(gòu)體都定義好了,接著就是往這個(gè)FIFO的數(shù)組中寫(xiě)入數(shù)據(jù),也就是按鍵的鍵值,用來(lái)模擬按鍵的動(dòng)作了。

/*
**********************************************************
*函數(shù)名:KEY_FIFO_Put
*功能說(shuō)明:將1個(gè)鍵值壓入按鍵FIFO緩沖區(qū)??捎糜谀M一個(gè)按鍵。
*形參:_KeyCode:按鍵代碼
*返回值:無(wú)
**********************************************************
*/
voidKEY_FIFO_Put(uint8_t_KeyCode)
{
s_tKey.Buf[s_tKey.Write]=_KeyCode;

if(++s_tKey.Write>=KEY_FIFO_SIZE)
{
s_tKey.Write=0;
}
}

函數(shù)的主要功能就是將按鍵代碼_KeyCode寫(xiě)入到FIFO中,而這個(gè)FIFO就是我們定義結(jié)構(gòu)體的這個(gè)數(shù)組成員,每寫(xiě)一次,就是每調(diào)用一次KEY_FIFO_Put()函數(shù),寫(xiě)指針write就++一次,也就是向后移動(dòng)一個(gè)空間,如果FIFO空間寫(xiě)滿了,也就是s_tKey.Write >= KEY_FIFO_SIZE,Write會(huì)被重新賦值為 0。

3.從FIFO讀出鍵值

有寫(xiě)入鍵值當(dāng)然就有讀出鍵值。

/*
***********************************************************
*函數(shù)名:KEY_FIFO_Get
*功能說(shuō)明:從按鍵FIFO緩沖區(qū)讀取一個(gè)鍵值。
*形參:無(wú)
*返回值:按鍵代碼
************************************************************
*/
uint8_tKEY_FIFO_Get(void)
{
uint8_tret;

if(s_tKey.Read==s_tKey.Write)
{
returnKEY_NONE;
}
else
{
ret=s_tKey.Buf[s_tKey.Read];

if(++s_tKey.Read>=KEY_FIFO_SIZE)
{
s_tKey.Read=0;
}
returnret;
}
}

如果寫(xiě)指針和讀出的指針相等,那么返回值就為0,表示按鍵緩沖區(qū)為空,所有的按鍵時(shí)間已經(jīng)處理完畢。如果不相等就說(shuō)明FIFO的緩沖區(qū)不為空,將Buf中的數(shù)讀出給ret變量。同樣,如果FIFO空間讀完了,沒(méi)有緩存了,也就是s_tKey.Read >= KEY_FIFO_SIZE,Read也會(huì)被重新賦值為 0。按鍵的鍵值定義在key.h 文件,下面是具體內(nèi)容:

typedefenum
{
KEY_NONE=0,/*0表示按鍵事件*/

KEY_1_DOWN,/*1鍵按下*/
KEY_1_UP,/*1鍵彈起*/
KEY_1_LONG,/*1鍵長(zhǎng)按*/

KEY_2_DOWN,/*2鍵按下*/
KEY_2_UP,/*2鍵彈起*/
KEY_2_LONG,/*2鍵長(zhǎng)按*/

KEY_3_DOWN,/*3鍵按下*/
KEY_3_UP,/*3鍵彈起*/
KEY_3_LONG,/*3鍵長(zhǎng)按*/
}KEY_ENUM;

必須按次序定義每個(gè)鍵的按下、彈起和長(zhǎng)按事件,即每個(gè)按鍵對(duì)象占用 3 個(gè)數(shù)值。推薦使用枚舉enum, 不用#define的原因是便于新增鍵值,方便調(diào)整順序。使用{ } 將一組相關(guān)的定義封裝起來(lái)便于理解。編譯器也可幫我們避免鍵值重復(fù)。

4.按鍵檢測(cè)程序

上面說(shuō)了如何將按鍵的鍵值存入和讀出FIFO,但是既然是按鍵操作,就肯定涉及到按鍵消抖處理,還有按鍵的狀態(tài)是按下還是彈起,是長(zhǎng)按還是短按。所以為了以示區(qū)分,我們用還需要給每一個(gè)按鍵設(shè)置很多參數(shù),就需要再定義一個(gè)結(jié)構(gòu)體KEY_T,讓每個(gè)按鍵對(duì)應(yīng)1個(gè)全局的結(jié)構(gòu)體變量。

typedefstruct
{
/*下面是一個(gè)函數(shù)指針,指向判斷按鍵手否按下的函數(shù)*/
uint8_t(*IsKeyDownFunc)(void);/*按鍵按下的判斷函數(shù),1表示按下*/

uint8_tCount;/*濾波器計(jì)數(shù)器*/
uint16_tLongCount;/*長(zhǎng)按計(jì)數(shù)器*/
uint16_tLongTime;/*按鍵按下持續(xù)時(shí)間,0表示不檢測(cè)長(zhǎng)按*/
uint8_tState;/*按鍵當(dāng)前狀態(tài)(按下還是彈起)*/
uint8_tRepeatSpeed;/*連續(xù)按鍵周期*/
uint8_tRepeatCount;/*連續(xù)按鍵計(jì)數(shù)器*/
}KEY_T;

在key.c 中定義s_tBtn結(jié)構(gòu)體數(shù)組變量。

staticKEY_Ts_tBtn[3]={0};

每個(gè)按鍵對(duì)象都分配一個(gè)結(jié)構(gòu)體變量,這些結(jié)構(gòu)體變量以數(shù)組的形式存在將便于我們簡(jiǎn)化程序代碼行數(shù)。因?yàn)槲业挠布?個(gè)按鍵,所以這里的數(shù)組元素為3。使用函數(shù)指針IsKeyDownFunc可以將每個(gè)按鍵的檢測(cè)以及組合鍵的檢測(cè)代碼進(jìn)行統(tǒng)一管理。

因?yàn)楹瘮?shù)指針必須先賦值,才能被作為函數(shù)執(zhí)行。因此在定時(shí)掃描按鍵之前,必須先執(zhí)行一段初始化函數(shù)來(lái)設(shè)置每個(gè)按鍵的函數(shù)指針和參數(shù)。這個(gè)函數(shù)是void KEY_Init(void)。

voidKEY_Init(void)
{
KEY_FIFO_Init();/*初始化按鍵變量*/
KEY_GPIO_Config();/*初始化按鍵硬件*/
}

下面是KEY_FIFO_Init函數(shù)的定義:

staticvoidKEY_FIFO_Init(void)
{
uint8_ti;

/*對(duì)按鍵FIFO讀寫(xiě)指針清零*/
s_tKey.Read=0;
s_tKey.Write=0;

/*給每個(gè)按鍵結(jié)構(gòu)體成員變量賦一組缺省值*/
for(i=0;i100;/*長(zhǎng)按時(shí)間0表示不檢測(cè)長(zhǎng)按鍵事件*/
s_tBtn[i].Count=5/2;/*計(jì)數(shù)器設(shè)置為濾波時(shí)間的一半*/
s_tBtn[i].State=0;/*按鍵缺省狀態(tài),0為未按下*/
s_tBtn[i].RepeatSpeed=0;/*按鍵連發(fā)的速度,0表示不支持連發(fā)*/
s_tBtn[i].RepeatCount=0;/*連發(fā)計(jì)數(shù)器*/
}
/*判斷按鍵按下的函數(shù)*/
s_tBtn[0].IsKeyDownFunc=IsKey1Down;
s_tBtn[1].IsKeyDownFunc=IsKey2Down;
s_tBtn[2].IsKeyDownFunc=IsKey3Down;
}

我們知道按鍵會(huì)有機(jī)械抖動(dòng),你以為按鍵按下就是低電平,其實(shí)在按下的一瞬間會(huì)存在機(jī)械抖動(dòng),如果不做延時(shí)處理,可能會(huì)出錯(cuò),一般如果按鍵檢測(cè)到按下后再延時(shí)50ms檢測(cè)一次,如果還是檢測(cè)低電平,才能說(shuō)明按鍵真正的被按下了。反之按鍵彈起時(shí)也是一樣的。所以我們程序設(shè)置按鍵濾波時(shí)間50ms, 因?yàn)榇a每10ms掃描一次按鍵,所以按鍵的單位我們可以理解為10ms,濾波的次數(shù)就為5次。這樣只有連續(xù)檢測(cè)到50ms狀態(tài)不變才認(rèn)為有效,包括彈起和按下兩種事件,即使按鍵電路不做硬件濾波(沒(méi)有電容濾波),該濾波機(jī)制也可以保證可靠地檢測(cè)到按鍵事件。

判斷按鍵是否按下,用一個(gè)HAL_GPIO_ReadPin就可以搞定。

staticuint8_tIsKey1Down(void)
{
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==GPIO_PIN_RESET)
return1;
else
return0;
}
staticuint8_tIsKey2Down(void)
{
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3)==GPIO_PIN_RESET)
return1;
else
return0;
}

staticuint8_tIsKey3Down(void)
{
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_2)==GPIO_PIN_RESET)
return1;
else
return0;
}

下面是KEY_GPIO_Config函數(shù)的定義,這個(gè)函數(shù)就是配置具體的按鍵GPIO的,就不需要過(guò)多的解釋了。

staticvoidKEY_GPIO_Config(void)
{
GPIO_InitTypeDefGPIO_InitStructure;

/*第1步:打開(kāi)GPIO時(shí)鐘*/
__HAL_RCC_GPIOE_CLK_ENABLE();

/*第2步:配置所有的按鍵GPIO為浮動(dòng)輸入模式(實(shí)際上CPU復(fù)位后就是輸入狀態(tài))*/
GPIO_InitStructure.Mode=GPIO_MODE_INPUT;/*設(shè)置輸入*/
GPIO_InitStructure.Pull=GPIO_NOPULL;/*上下拉電阻不使能*/
GPIO_InitStructure.Speed=GPIO_SPEED_FREQ_VERY_HIGH;/*GPIO速度等級(jí)*/

GPIO_InitStructure.Pin=GPIO_PIN_4;
HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);

GPIO_InitStructure.Pin=GPIO_PIN_3;
HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);

GPIO_InitStructure.Pin=GPIO_PIN_2;
HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);
}

5.按鍵掃描

按鍵掃描函數(shù)KEY_Scan()每隔 10ms 被執(zhí)行一次。RunPer10ms函數(shù)在 systick中斷服務(wù)程序中執(zhí)行。

voidRunPer10ms(void)
{
KEY_Scan();
}

voidKEY_Scan(void)
{
uint8_ti;
for(i=0;i/*

每隔10ms所有的按鍵GPIO均會(huì)被掃描檢測(cè)一次。KEY_Detect函數(shù)實(shí)現(xiàn)如下:

staticvoidKEY_Detect(uint8_ti)
{
KEY_T*pBtn;

pBtn=&s_tBtn[i];
if(pBtn->IsKeyDownFunc())
{//這個(gè)里面執(zhí)行的是按鍵按下的處理
if(pBtn->Count//按鍵濾波前給Count設(shè)置一個(gè)初值
pBtn->Count=KEY_FILTER_TIME;
}
elseif(pBtn->Count2*KEY_FILTER_TIME)
{//實(shí)現(xiàn)KEY_FILTER_TIME時(shí)間長(zhǎng)度的延遲
pBtn->Count++;
}
else
{
if(pBtn->State==0)
{
pBtn->State=1;

/*發(fā)送按鈕按下的消息*/
KEY_FIFO_Put((uint8_t)(3*i+1));
}

if(pBtn->LongTime>0)
{
if(pBtn->LongCountLongTime)
{
/*發(fā)送按鈕持續(xù)按下的消息*/
if(++pBtn->LongCount==pBtn->LongTime)
{
/*鍵值放入按鍵FIFO*/
KEY_FIFO_Put((uint8_t)(3*i+3));
}
}
else
{
if(pBtn->RepeatSpeed>0)
{
if(++pBtn->RepeatCount>=pBtn->RepeatSpeed)
{
pBtn->RepeatCount=0;
/*長(zhǎng)按鍵后,每隔10ms發(fā)送1個(gè)按鍵*/
KEY_FIFO_Put((uint8_t)(3*i+1));
}
}
}
}
}
}
else
{//這個(gè)里面執(zhí)行的是按鍵松手的處理或者按鍵沒(méi)有按下的處理
if(pBtn->Count>KEY_FILTER_TIME)
{
pBtn->Count=KEY_FILTER_TIME;
}
elseif(pBtn->Count!=0)
{
pBtn->Count--;
}
else
{
if(pBtn->State==1)
{
pBtn->State=0;

/*發(fā)送按鈕彈起的消息*/
KEY_FIFO_Put((uint8_t)(3*i+2));
}
}
pBtn->LongCount=0;
pBtn->RepeatCount=0;
}
}

這個(gè)函數(shù)還是比較難以理解的,主要是結(jié)構(gòu)體的操作。所以好好學(xué)習(xí)結(jié)構(gòu)體,不要見(jiàn)了結(jié)構(gòu)體就跑。

分析:首先讀取相應(yīng)按鍵的結(jié)構(gòu)體地址賦值給結(jié)構(gòu)體指針變量pBtn ,因?yàn)槌绦蚶锩婷總€(gè)按鍵都有自己的結(jié)構(gòu)體,只有通過(guò)這個(gè)方式才能對(duì)具體的按鍵進(jìn)行操作。(在前面我們使用軟件定時(shí)器時(shí)也使用了這中操作,在滴答定時(shí)器的中斷服務(wù)函數(shù)中)。

staticKEY_Ts_tBtn[3];//程序里面每個(gè)按鍵都有自己的結(jié)構(gòu)體,有三個(gè)按鍵
KEY_T*pBtn;//定義一個(gè)結(jié)構(gòu)體指針變量pBtn
pBtn=&s_tBtn[i];//將按鍵的結(jié)構(gòu)體地址賦值給結(jié)構(gòu)體指針變量pBtn

然后接著就是給按鍵濾波前給Count設(shè)置一個(gè)初值,前面說(shuō)按鍵初始化的時(shí)候已經(jīng)設(shè)置了Count =5/2。然后判斷是否按下的標(biāo)志位,如果按鍵按下了,這里就將其設(shè)置為 1,如果沒(méi)有按下這個(gè)變量的值就會(huì)一直是 0。這里可能不理解是就是按鍵按下發(fā)送的鍵值是3 * i + 1。按鍵彈起發(fā)送的鍵值是3 * i + 2,按鍵長(zhǎng)按發(fā)送的鍵值是3 * i + 3。也就是說(shuō)按鍵按下發(fā)送的鍵值是1和4和7。按鍵彈起發(fā)送的鍵值是2和5和8,按鍵長(zhǎng)按發(fā)送的鍵值是3和6和9。看下面這個(gè)枚舉enum你就明白了。

typedefenum
{
KEY_NONE=0,/*0表示按鍵事件*/

KEY_1_DOWN,/*1鍵按下*/
KEY_1_UP,/*1鍵彈起*/
KEY_1_LONG,/*1鍵長(zhǎng)按*/

KEY_2_DOWN,/*2鍵按下*/
KEY_2_UP,/*2鍵彈起*/
KEY_2_LONG,/*2鍵長(zhǎng)按*/

KEY_3_DOWN,/*3鍵按下*/
KEY_3_UP,/*3鍵彈起*/
KEY_3_LONG,/*3鍵長(zhǎng)按*/


}KEY_ENUM;

7.試驗(yàn)演示

intmain(void)
{
uint8_tKeyCode;/*按鍵代碼*/
KEY_Init();
while(1)
{
/*按鍵濾波和檢測(cè)由后臺(tái)systick中斷服務(wù)程序?qū)崿F(xiàn),我們只需要調(diào)用KEY_FIFO_Get讀取鍵值即可。*/
KeyCode=KEY_FIFO_Get();/*讀取鍵值,無(wú)鍵按下時(shí)返回KEY_NONE=0*/
if(KeyCode!=KEY_NONE)
{
switch(KeyCode)
{
caseKEY_DOWN_K1:/*K1鍵按下*/
printf("K1鍵按下
");
break;
caseKEY_UP_K1:/*K1鍵彈起*/
printf("K1鍵彈起
");
break;
caseKEY_DOWN_K2:/*K2鍵按下*/
printf("K2鍵按下
");
break;
caseKEY_UP_K2:/*K2鍵彈起*/
printf("K2鍵彈起
");
break;
caseKEY_DOWN_K3:/*K3鍵按下*/
printf("K3鍵按下
");
break;
caseKEY_UP_K3:/*K3鍵彈起*/
printf("K3鍵彈起
");
break;
default:
/*其它的鍵值不處理*/
break;
}

}
}
}

不知道學(xué)妹看懂沒(méi),沒(méi)看懂就多看幾遍。代碼例程已上傳至Gitee。

https://gitee.com/zhiguoxin/Wechat-Data.git

責(zé)任編輯:haq


聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 單片機(jī)
    +關(guān)注

    關(guān)注

    6067

    文章

    44969

    瀏覽量

    649180
  • STM32
    +關(guān)注

    關(guān)注

    2291

    文章

    11026

    瀏覽量

    363775
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4378

    瀏覽量

    64610

原文標(biāo)題:這個(gè)學(xué)妹寫(xiě)的按鍵檢測(cè)函數(shù)把我秀翻了!

文章出處:【微信號(hào):mcu168,微信公眾號(hào):硬件攻城獅】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    汽車(chē)物理按鍵力學(xué)檢測(cè)要點(diǎn)

    汽車(chē)人機(jī)交互系統(tǒng)中,物理按鍵和旋鈕是駕駛員與車(chē)輛進(jìn)行交互的重要界面,其力學(xué)感知特性對(duì)于駕駛體驗(yàn)和操作安全性具有重要影響。對(duì)這些部件的按壓、推拉以及旋鈕的力學(xué)感知類(lèi)檢測(cè)是確保汽車(chē)人機(jī)交互系統(tǒng)性能的關(guān)鍵環(huán)節(jié)。
    的頭像 發(fā)表于 05-29 11:22 ?1108次閱讀
    汽車(chē)物理<b class='flag-5'>按鍵</b>力學(xué)<b class='flag-5'>檢測(cè)</b>要點(diǎn)

    AS32X601驅(qū)動(dòng)系列教程 GPIO_按鍵檢測(cè)詳解

    在嵌入式系統(tǒng)開(kāi)發(fā)中,GPIO(通用輸入輸出端口)是實(shí)現(xiàn)硬件與軟件交互的關(guān)鍵組件。本節(jié)主要利用的時(shí)GPIO的輸入采集功能,本節(jié)的主要功能為,讀取板載按鍵,當(dāng)按鍵按下時(shí),對(duì)應(yīng)led亮起,抬起按鍵
    的頭像 發(fā)表于 05-23 16:44 ?231次閱讀
    AS32X601驅(qū)動(dòng)系列教程 GPIO_<b class='flag-5'>按鍵</b><b class='flag-5'>檢測(cè)</b>詳解

    第六章 GPIO輸入——按鍵檢測(cè)

    本篇文章講述了如何在W55MH32上實(shí)現(xiàn)按鍵輸入檢測(cè),以及按鍵輸入檢測(cè)的注意事項(xiàng)、實(shí)現(xiàn)方式和代碼
    的頭像 發(fā)表于 05-22 16:41 ?263次閱讀
    第六章 GPIO輸入——<b class='flag-5'>按鍵</b><b class='flag-5'>檢測(cè)</b>

    SDRAM控制器設(shè)計(jì)之按鍵的處理

    邊沿檢測(cè)經(jīng)常用于按鍵輸入檢測(cè)電路中,按鍵按下時(shí)輸入信號(hào) key 變?yōu)榈碗娖剑?b class='flag-5'>按鍵抬起變?yōu)楦唠娖健.?dāng)輸入的信號(hào)為理想的高低電平時(shí)(不考慮毛刺和
    的頭像 發(fā)表于 03-19 11:47 ?689次閱讀
    SDRAM控制器設(shè)計(jì)之<b class='flag-5'>按鍵</b>的處理

    遠(yuǎn)心鏡頭應(yīng)用手機(jī)按鍵檢測(cè)

    遠(yuǎn)心鏡頭的手機(jī)按鍵檢測(cè)方案具有高效、準(zhǔn)確、穩(wěn)定等優(yōu)點(diǎn)。該方案能夠實(shí)現(xiàn)對(duì)手機(jī)按鍵的全方位檢測(cè),包括按鍵
    的頭像 發(fā)表于 01-20 10:18 ?432次閱讀
    遠(yuǎn)心鏡頭應(yīng)用手機(jī)<b class='flag-5'>按鍵</b><b class='flag-5'>檢測(cè)</b>

    開(kāi)發(fā)第1個(gè)LVGL程序與實(shí)現(xiàn)按鍵操作

    開(kāi)發(fā)第1個(gè)LVGL程序與實(shí)現(xiàn)按鍵操作
    的頭像 發(fā)表于 01-07 13:49 ?2754次閱讀
    開(kāi)發(fā)第1個(gè)LVGL程序與<b class='flag-5'>實(shí)現(xiàn)</b><b class='flag-5'>按鍵</b>操作

    基于狀態(tài)機(jī)和面向?qū)ο蟮乃枷朐O(shè)計(jì)按鍵檢測(cè)模塊

    物理按鍵,在很多嵌入式產(chǎn)品里面應(yīng)用得非常廣泛,很多嵌入式軟件工程師在剛剛開(kāi)始入門(mén)的時(shí)候,點(diǎn)完燈之后就開(kāi)始學(xué)習(xí)按鍵輸入檢測(cè)。按鍵輸入可以說(shuō)是繼點(diǎn)燈之后,又一經(jīng)典的嵌入式入門(mén)必學(xué)內(nèi)容之一。
    的頭像 發(fā)表于 11-14 11:44 ?934次閱讀
    基于狀態(tài)機(jī)和面向?qū)ο蟮乃枷朐O(shè)計(jì)<b class='flag-5'>按鍵</b><b class='flag-5'>檢測(cè)</b>模塊

    基于FPGA實(shí)現(xiàn)按鍵消抖處理

    引言: 按鍵在電子產(chǎn)品中經(jīng)常用到,由于按鍵的機(jī)械特性,按鍵在閉合或松開(kāi)的瞬間伴隨著一連串的抖動(dòng),這樣的抖動(dòng)將直接影響設(shè)計(jì)系統(tǒng)的穩(wěn)定性。因此,必須對(duì)抖動(dòng)進(jìn)行處理。本文介紹如何在FPGA中
    的頭像 發(fā)表于 10-24 14:54 ?1108次閱讀
    基于FPGA<b class='flag-5'>實(shí)現(xiàn)</b><b class='flag-5'>按鍵</b>消抖處理

    AD快捷按鍵

    電子發(fā)燒友網(wǎng)站提供《AD快捷按鍵.docx》資料免費(fèi)下載
    發(fā)表于 10-24 10:45 ?3次下載

    Keithley吉時(shí)利2400源表按鍵無(wú)反應(yīng)維修案例

    近期西安某院校送修一臺(tái)Keithley吉時(shí)利2400源表,報(bào)修故障是前面板屏幕顯示正常,但是所有按鍵均無(wú)反應(yīng),對(duì)儀器進(jìn)行初步檢測(cè),確定故障與客戶報(bào)修一致。 下面是 吉時(shí)利2400源表 維修情況
    的頭像 發(fā)表于 10-14 18:33 ?850次閱讀
    Keithley吉時(shí)利2400源表<b class='flag-5'>按鍵</b>無(wú)反應(yīng)維修案例

    電容式觸摸按鍵按鍵擴(kuò)展方法

    電子發(fā)燒友網(wǎng)站提供《電容式觸摸按鍵按鍵擴(kuò)展方法.pdf》資料免費(fèi)下載
    發(fā)表于 09-27 11:43 ?0次下載
    電容式觸摸<b class='flag-5'>按鍵</b>的<b class='flag-5'>按鍵</b>擴(kuò)展方法

    為什么按鍵消抖那么重要

    和錯(cuò)誤操作。因此,進(jìn)行按鍵消抖處理顯得尤為重要。 按鍵抖動(dòng)的產(chǎn)生是由于機(jī)械觸點(diǎn)的彈性特性所決定的。當(dāng)按鍵被按下或釋放時(shí),觸點(diǎn)并不會(huì)立即穩(wěn)定地接通或斷開(kāi),而是會(huì)在閉合和斷開(kāi)的瞬間產(chǎn)生一連
    的頭像 發(fā)表于 09-25 16:50 ?1260次閱讀
    為什么<b class='flag-5'>按鍵</b>消抖那么重要

    基于MSP430實(shí)現(xiàn)電視的電容式觸摸按鍵

    電子發(fā)燒友網(wǎng)站提供《基于MSP430實(shí)現(xiàn)電視的電容式觸摸按鍵.pdf》資料免費(fèi)下載
    發(fā)表于 08-28 11:56 ?0次下載
    基于MSP430<b class='flag-5'>實(shí)現(xiàn)</b>電視的電容式觸摸<b class='flag-5'>按鍵</b>

    如何在FPGA中實(shí)現(xiàn)按鍵消抖

    在FPGA(現(xiàn)場(chǎng)可編程門(mén)陣列)中實(shí)現(xiàn)按鍵消抖是一個(gè)重要的設(shè)計(jì)環(huán)節(jié),特別是在處理用戶輸入時(shí),由于物理按鍵的機(jī)械特性和電氣特性,按鍵在按下和釋放的瞬間會(huì)產(chǎn)生抖動(dòng)現(xiàn)象,這種抖動(dòng)可能導(dǎo)致系統(tǒng)錯(cuò)
    的頭像 發(fā)表于 08-19 18:15 ?3455次閱讀

    何進(jìn)行IP檢測(cè)

    排查網(wǎng)絡(luò)連接問(wèn)題,并及時(shí)的防范潛在的網(wǎng)絡(luò)攻擊。 那么,如何進(jìn)行 IP 地址檢測(cè)呢?接下來(lái)我將進(jìn)行圖示哦~ 使用操作系統(tǒng)自帶的工具 ① Windows 系統(tǒng)中,按win+R,輸入“ipconfig”命令。 ② Mac 系統(tǒng)中,則可
    的頭像 發(fā)表于 07-26 14:09 ?1214次閱讀
    如<b class='flag-5'>何進(jìn)行</b>IP<b class='flag-5'>檢測(cè)</b>