娇小w搡bbbb搡bbb,《第一次の人妻》,中国成熟妇女毛茸茸,边啃奶头边躁狠狠躁视频免费观看

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

發(fā)布者:Radiant777最新更新時(shí)間:2024-07-10 來源: elecfans關(guān)鍵字:按鍵檢測(cè)  按鍵FIFO  STM32 手機(jī)看文章 掃描二維碼
隨時(shí)隨地手機(jī)看文章

一、如何進(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è)中斷,程序中過多的中斷會(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)樵赗TOS中過多的使用中斷會(huì)影響系統(tǒng)的穩(wěn)定性和可預(yù)見性。只有比較重要的事件處理需要用中斷的方式。

查詢方式:對(duì)于用戶按鍵推薦使用這種查詢方式來實(shí)現(xiàn),現(xiàn)在的OS基本都帶有CPU利用率的功能,這個(gè)按鍵FIFO占用的還是很小的,基本都在1%以下。


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

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


#defineKEY0_PRES1//KEY0

#defineKEY1_PRES2//KEY1

#defineWKUP_PRES3//WK_UP


u8KEY_Scan(u8mode)

{

staticu8key_up=1;//按鍵按松開標(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;//無按鍵按下

}


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)然我這里并不是說這種代碼不好,不管黑貓白貓,能抓住老鼠就是好貓。只要能滿足項(xiàng)目需求實(shí)現(xiàn)對(duì)應(yīng)的功能就是好代碼。但是如果你使用下面這種個(gè)人感覺可能會(huì)更好。


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


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


三、為什么要了解FIFO

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


四、什么是FIFO

FIFO是先入先出的意思,即誰先進(jìn)入隊(duì)列,誰先出去。比如我們需要串口打印數(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)行說明。


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


typedefstruct

{

uint8_tBuf[10];/*緩沖區(qū)*/

uint8_tRead;/*緩沖區(qū)讀指針*/

uint8_tWrite;/*緩沖區(qū)寫指針*/

}KEY_FIFO_T;

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


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


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


好了這就是FIFO的基本原理,下面來看一下按鍵FIFO是怎么操作的。


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


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

如果 Write!= Read,則我們認(rèn)為有新的按鍵事件。我們通過函數(shù)

KEY_FIFO_Get()

讀取一個(gè)按鍵值進(jìn)行處理后,Read 變量變?yōu)?1。Write 變量不變。


繼續(xù)通過函數(shù)

KEY_FIFO_Get()

讀取 3 個(gè)按鍵值進(jìn)行處理后,Read 變量變?yōu)?4。此時(shí)Read = Write= 4。兩個(gè)變量已經(jīng)相等,表示已經(jīng)沒有新的按鍵事件需要處理。


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


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

可靠地記錄每一個(gè)按鍵事件,避免遺漏按鍵事件。特別是需要實(shí)現(xiàn)按鍵的按下、長(zhǎng)按、自動(dòng)連發(fā)、彈起等事件時(shí)。

讀取按鍵的函數(shù)可以設(shè)計(jì)為非阻塞的,不需要等待按鍵抖動(dòng)濾波處理完畢。

按鍵 FIFO 程序在嘀嗒定時(shí)器中定期的執(zhí)行檢測(cè),不需要在主程序中一直做檢測(cè),這樣可以有效地降低系統(tǒng)資源消耗。

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

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

在我們的key.h文件中定義一個(gè)結(jié)構(gòu)體類型為

KEY_FIFO_T

的結(jié)構(gòu)體。就是前面說的那個(gè)結(jié)構(gòu)體。這只是類型聲明,并沒有分配變量空間。


typedefstruct

{

uint8_tBuf[10];/*緩沖區(qū)*/

uint8_tRead;/*緩沖區(qū)讀指針*/

uint8_tWrite;/*緩沖區(qū)寫指針*/

}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ù)類型就定義完了,很簡(jiǎn)單吧!


2.將鍵值寫入FIFO

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


/*

**********************************************************

*函數(shù)名:KEY_FIFO_Put

*功能說明:將1個(gè)鍵值壓入按鍵FIFO緩沖區(qū)。可用于模擬一個(gè)按鍵。

*形參:_KeyCode:按鍵代碼

*返回值:無

**********************************************************

*/

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

寫入到FIFO中,而這個(gè)FIFO就是我們定義結(jié)構(gòu)體的這個(gè)數(shù)組成員,每寫一次,就是每調(diào)用一次

KEY_FIFO_Put()

函數(shù),寫指針write就

++

一次,也就是向后移動(dòng)一個(gè)空間,如果FIFO空間寫滿了,也就是

s_tKey.Write >= KEY_FIFO_SIZE

,Write會(huì)被重新賦值為 0。


3.從FIFO讀出鍵值

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


/*

***********************************************************

*函數(shù)名:KEY_FIFO_Get

*功能說明:從按鍵FIFO緩沖區(qū)讀取一個(gè)鍵值。

*形參:無

*返回值:按鍵代碼

************************************************************

*/

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;

}

}

如果寫指針和讀出的指針相等,那么返回值就為0,表示按鍵緩沖區(qū)為空,所有的按鍵時(shí)間已經(jīng)處理完畢。如果不相等就說明FIFO的緩沖區(qū)不為空,將Buf中的數(shù)讀出給

ret

變量。同樣,如果FIFO空間讀完了,沒有緩存了,也就是

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)的定義封裝起來便于理解。編譯器也可幫我們避免鍵值重復(fù)。


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

上面說了如何將按鍵的鍵值存入和讀出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ù)來設(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讀寫指針清零*/

s_tKey.Read=0;

s_tKey.Write=0;


/*給每個(gè)按鍵結(jié)構(gòu)體成員變量賦一組缺省值*/

for(i=0;i< HARD_KEY_NUM; i++)

 {

  s_tBtn[i].LongTime = 100;/*長(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è)低電平,才能說明按鍵真正的被按下了。反之按鍵彈起時(shí)也是一樣的。所以我們程序設(shè)置按鍵濾波時(shí)間50ms, 因?yàn)榇a每10ms掃描一次按鍵,所以按鍵的單位我們可以理解為10ms,濾波的次數(shù)就為5次。這樣只有連續(xù)檢測(cè)到50ms狀態(tài)不變才認(rèn)為有效,包括彈起和按下兩種事件,即使按鍵電路不做硬件濾波(沒有電容濾波),該濾波機(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的,就不需要過多的解釋了。


staticvoidKEY_GPIO_Config(void)

{

GPIO_InitTypeDefGPIO_InitStructure;


/*第1步:打開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< HARD_KEY_NUM; i++)

 {

  KEY_Detect(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< KEY_FILTER_TIME)

  {//按鍵濾波前給Count設(shè)置一個(gè)初值

pBtn->Count=KEY_FILTER_TIME;

}

elseif(pBtn->Count< 2*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->LongCount< pBtn->LongTime)

{

/*發(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í)行的是按鍵松手的處理或者按鍵沒有按下的處理

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é)構(gòu)體就跑。


分析:首先讀取相應(yīng)按鍵的結(jié)構(gòu)體地址賦值給結(jié)構(gòu)體指針變量pBtn ,因?yàn)槌绦蚶锩婷總€(gè)按鍵都有自己的結(jié)構(gòu)體,只有通過這個(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è)初值,前面說按鍵初始化的時(shí)候已經(jīng)設(shè)置了Count =5/2。然后判斷是否按下的標(biāo)志位,如果按鍵按下了,這里就將其設(shè)置為 1,如果沒有按下這個(gè)變量的值就會(huì)一直是 0。這里可能不理解是就是按鍵按下發(fā)送的鍵值是

3 * i + 1

。按鍵彈起發(fā)送的鍵值是

3 * i + 2

,按鍵長(zhǎng)按發(fā)送的鍵值是

3 * i + 3

。也就是說按鍵按下發(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)演示

復(fù)制

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();/*讀取鍵值,無鍵按下時(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é)妹看懂沒,沒看懂就多看幾遍。代碼例程已上傳至Gitee。


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


關(guān)鍵字:按鍵檢測(cè)  按鍵FIFO  STM32 引用地址:如何進(jìn)行按鍵檢測(cè)和按鍵FIFO的實(shí)現(xiàn)

上一篇:教室智能照明控制系統(tǒng)的設(shè)計(jì)說明
下一篇:如何在Keil MDK環(huán)境移植ThreadX操作系統(tǒng)?

推薦閱讀最新更新時(shí)間:2025-04-23 19:06

玩轉(zhuǎn) ESP32 + Arduino (四) 電容按鍵 霍爾傳感器 外部中斷 延時(shí) 脈沖檢測(cè)
一. 電容輸入 touchRead(pin) 及電容輸入中斷touchAttachInterrupt(pin, TSR , threshold) ESP32專門提供了電容觸摸傳感器的功能, 共有T0,T2~T9 共 9個(gè)touch傳感器可用.分別對(duì)應(yīng)引腳4、2、15、13、12、14、27、33、32. 無需設(shè)置PinMode touchRead(pin) 返回值 0~255. 觸摸強(qiáng)度 注意: 摸得越瓷實(shí),數(shù)值越小 void setup(){ Serial.begin(9600);}void loop(){ Serial.printf( touch:%dn ,touchRead(4));} touchAttachInter
[單片機(jī)]
MSP430入門教程專題(六)——IO中斷方式按鍵檢測(cè)
//MSP430F14-利用IO中斷方式實(shí)現(xiàn)按鍵檢測(cè)程序 //以下是結(jié)合MC430F14開發(fā)板來實(shí)現(xiàn)的按鍵檢處理程序?qū)嶒?yàn). //分別使用了采個(gè)三個(gè)按鍵接到MSP430的通用IO口,按任意一個(gè)按鍵可以使板上的LED反轉(zhuǎn). //例程中,單片機(jī)一直處于最低功耗狀態(tài),用戶可以通過按下按鍵后喚醒單片機(jī).單片機(jī)喚醒 //后再進(jìn)行去抖動(dòng)動(dòng)作.同時(shí)執(zhí)行鍵處理程序.處理完后再次進(jìn)入LPM4低功耗模式. //在本程序中用戶可以靈活地修改程序來實(shí)現(xiàn)你相關(guān)的功能. //本程序適用在手持設(shè)備或電池供電的設(shè)計(jì)中.此程序結(jié)構(gòu)比較通用,級(jí)用戶可參與或套用修改. //應(yīng)用目標(biāo)板:MC430F14x開發(fā)板 #include msp430x14x.h #defin
[單片機(jī)]
IAR STM32 BootLoader
STM32 Bootloader與APP IROM中可以分成兩個(gè)區(qū)域,起始代碼運(yùn)行地址為0x08000000,這是基本固定的,可以將IROM的0x08000000 ~ 0x08002000這8KB的空間存放Bootloader代碼,而0x08002000 ~ 上限的位置存放APP代碼。 需要實(shí)現(xiàn)兩個(gè)工程,分別為bootloader工程與app工程。 1.1 先說bootloader工程,以IAR為例 建立基本stm32工程需要的工程文件等,工程的建立這里不去說明,有了工程后,需要將該工程代碼的鏈接地址配置為0x08000000 ~ 0x08002000,對(duì)于IAR可以在icf文件中進(jìn)行修改參數(shù)達(dá)到效果,也可在工程配置中修
[單片機(jī)]
stm32型號(hào)命名規(guī)則
stm32型號(hào)命名規(guī)則示例: STM32 | F | 100 | C | 6 | T | 6 | B | XXX 1 2 3 4 5 6 7 8 9 從上面的料號(hào)可以看出以下信息: ST品牌ARM Cortex-Mx系列內(nèi)核32位超值型MCU,LQFP -48封裝 閃存容量32KB 溫度范圍-40℃-85℃; 產(chǎn)品系列:STM32代表ST品牌Cortex-Mx系列內(nèi)核(ARM)的32位MCU; 產(chǎn)品類型: F:通用快閃(Flash Memory);L:低電壓(1.65~3.6V); F類型中F0xx和 F1xx系列為2.0~3.6V; F2xx和F4xx系列為1.8~3.6V; W:無線系統(tǒng)芯片,開發(fā)版.產(chǎn)品
[單片機(jī)]
STM32中斷系統(tǒng)
中斷與異常 中斷與異常這個(gè)概念其實(shí)可以說是一回事。最早出現(xiàn)中斷概念的時(shí)候就管它就叫中斷。外設(shè)某些時(shí)候需要中斷(Interrupt)CPU的運(yùn)行,來處理自己的請(qǐng)求。此時(shí)中斷用于外設(shè)響應(yīng)。但是系統(tǒng)內(nèi)部也會(huì)出現(xiàn)錯(cuò)誤,必須要中止CPU的運(yùn)行。為了區(qū)分這兩者所引發(fā)的中斷,便有了內(nèi)中斷與外中斷概念。內(nèi)中斷指的是系統(tǒng)內(nèi)部的錯(cuò)誤,外中斷是外設(shè)的請(qǐng)求。為了區(qū)分兩者,內(nèi)中斷就被稱為異常(Exception)了。從此有了中斷和異常的概念,外設(shè)請(qǐng)求叫中斷,內(nèi)部錯(cuò)誤叫異常。外中斷這個(gè)概念就有了新的含義,就是芯片外部發(fā)來的中斷請(qǐng)求。在STM32芯片中,由GPIO管腳產(chǎn)生的中斷,稱為EXTI(External Interrupts 外部中斷)。 NVIC NV
[單片機(jī)]
STM32的備份電池壽命
請(qǐng)教諸位使用STM32的兄弟: 如果用50mah的備份電池給STM32的RTC供電,能堅(jiān)持多長(zhǎng)時(shí)間?規(guī)格書上IDD_VBAT是。而VBAT的范圍是1.8~3.6V。 ST是否有相應(yīng)針對(duì)特定電池的數(shù)據(jù)。 -------------------------------------------------------------------- 半導(dǎo)體公司不會(huì)給出一個(gè)準(zhǔn)確的數(shù)據(jù),一般自己計(jì)算一下就好了。 比如50mAh的電池按照1.4uA放電,可以使用的時(shí)間為:50mAh / 1.4uA = 35714小時(shí) CR2025電池 電壓3.0V,容量150,尺寸20.0X2.5mm, 重量2.4g 150mAh / 1.4uA =
[單片機(jī)]
stm32 can總線程序的介紹
bxCAN是基本擴(kuò)展CAN的縮寫,它支持CAN協(xié)議2.0A和2.0B;它的設(shè)計(jì)目標(biāo)是以最小的CPU負(fù)載來高效處理大量的報(bào)文。它也支持報(bào)文發(fā)送的優(yōu)先級(jí)要求。 bxCAN模塊可以完全自動(dòng)地接收和發(fā)送CAN報(bào)文,且完全支持標(biāo)準(zhǔn)標(biāo)識(shí)符和擴(kuò)展標(biāo)識(shí)符。 bxCAN主要特征: 支持CAN協(xié)議2.0A和2.0B主動(dòng)模式; 波特率最高可達(dá)1兆位/秒; 支持時(shí)間觸發(fā)通信功能; bxCAN工作模式: bxCAN有3個(gè)主要的工作模式:初始化、正常和睡眠模式; 還包括:測(cè)試模式、靜默模式、環(huán)回模式、環(huán)回靜默模式 初始化模式主要是進(jìn)行配置,配置完就進(jìn)入正常模式,然后進(jìn)入睡眠模式(低功耗模式)。
[單片機(jī)]
基于stm32單片機(jī)的通用定時(shí)器配置
stm32單片機(jī)的定時(shí)器資源相當(dāng)豐富,它的定時(shí)器分為高級(jí)控制定時(shí)器、通用定時(shí)器和基本定時(shí)器,具體這些定時(shí)器資源在哪個(gè)系列的片子有就得看不同的片子的手冊(cè)了。他們具體有什么區(qū)別,我也是剛接觸這個(gè),看他的數(shù)據(jù)手冊(cè)介紹也是茫然,主要是剛開始摸,那些功能都沒用到,反正用做定時(shí)作用的話哪種定時(shí)器都行。在這我就把我自己配置通用定時(shí)器的方法及心得簡(jiǎn)短做個(gè)總結(jié),以防以后忘記了。我配置的是定時(shí)器2(TIM2)。 通用定時(shí)器的時(shí)鐘可來自于外部或內(nèi)部,選用默認(rèn)即是采用內(nèi)部的。通用定時(shí)器的時(shí)鐘來源為APB1總線,所以首先,得將APB1外設(shè)時(shí)鐘打開。 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE)
[單片機(jī)]
小廣播
設(shè)計(jì)資源 培訓(xùn) 開發(fā)板 精華推薦

最新單片機(jī)文章

 
EEWorld訂閱號(hào)

 
EEWorld服務(wù)號(hào)

 
汽車開發(fā)圈

 
機(jī)器人開發(fā)圈

電子工程世界版權(quán)所有 京ICP證060456號(hào) 京ICP備10001474號(hào)-1 電信業(yè)務(wù)審批[2006]字第258號(hào)函 京公網(wǎng)安備 11010802033920號(hào) Copyright ? 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
主站蜘蛛池模板: 临城县| 崇仁县| 额尔古纳市| 应用必备| 皮山县| 贵定县| 安塞县| 武功县| 六安市| 丽水市| 安阳市| 梨树县| 苏尼特右旗| 鹤岗市| 无极县| 泸西县| 巴青县| 韶关市| 虞城县| 闵行区| 体育| 宜川县| 平和县| 河北区| 新龙县| 广德县| 田阳县| 虹口区| 封丘县| 定襄县| 衡阳县| 霍林郭勒市| 天全县| 界首市| 分宜县| 南投市| 武城县| 抚顺县| 江安县| 枞阳县| 汝州市|