前言
做完了GPIO點燈實驗,開始做下一個GPIO按鍵檢測的實驗。
一、硬體電路設計
1。開發板使用者按鍵硬體電路
輕觸按鍵又稱輕觸開關(下文簡稱按鍵),是電路中常用的一種開關元器件,也是一種常用的人機介面。廣泛用於家電、數碼產品、便攜儀產品、電腦產品等電子裝置中。
STC8A8K64S4A12開發板上設計了4個使用者按鍵KEY1、KEY2、KEY3、KEY4,當使用KEY1和KEY2時,可短接J26端子的P37||KEY1、P36||KEY2。程式中透過讀取這些按鍵對應的GPIO的狀態(高電平或低電平)可判斷該按鍵是否按下,這種電路的形式稱為高低電平接法,這種檢測按鍵的方法稱為按鍵高低電平檢測。
圖1:開發板按鍵檢測電路及實物圖
☆1個使用者按鍵佔用的微控制器的引腳如下表:
表1:使用者按鍵引腳分配
KEY 引腳 功能描述 說明 備註
KEY1 P3。7 使用者按鍵 非獨立GPIO 黃色輕觸按鍵
KEY2 P3。6 使用者按鍵 非獨立GPIO 黃色輕觸按鍵
KEY3 P0。7 使用者按鍵 獨立GPIO 黃色輕觸按鍵
KEY4 P0。5 使用者按鍵 獨立GPIO 黃色輕觸按鍵
☆注:獨立GPIO表示開發板沒有其他的電路使用這個GPIO,非獨立GPIO說明開發板有其他電路用到了該GPIO。STC8A8K64S4A12開發板上W5500模組介面用到了P3。6和P3。7口,故在將W5500模組接插到開發板時,不可再將P3。6和P3。7用於按鍵檢測。
輕觸按鍵,顧名思義我們只需要施加很小的力量即可改變開關連線的狀態。輕觸按鍵在所需外力作用下(按下按鍵)觸點導通,無外力作用時(釋放按鍵)觸點斷開,如下圖所示:
圖2:輕觸按鍵原理
2。按鍵檢測接法
高低電平式接法是最常見的按鍵檢測的接法,顧名思義,該接法就是需要微控制器引腳具有高低電平的檢測能力,也就是常見的GPIO引腳即可。高低電平式接法又可分為兩種:獨立式接法和行列式接法。
行列式接法是利用微控制器的 GPIO口組成行與列,在行與列的每一個交點處連線按鍵。 故也稱為矩陣式按鍵,該接線方法最大的優勢是可以使用較少的GPIO口實現較多按鍵的檢測,這個在矩陣按鍵掃描實驗中會詳細介紹。
獨立式接法的含義就是使用微控制器的一個GPIO引腳檢測一個按鍵的狀態,有多少按鍵需檢測就需要多少個GPIO引腳,對每個按鍵的檢測相互獨立。
獨立式按鍵接法一般會用低電平有效的方式,即按鍵按下是GPIO輸入為低電平,如下圖所示。
圖3:獨立式接法
上圖中的電阻R的作用是將GPIO輸入埠不確定的訊號透過該電阻鉗位在高電平狀態。我們知道數位電路有三種狀態:高電平、低電平和高阻狀態,有些應用場合並不希望出現高阻狀態,這時加上拉電阻即可讓GPIO輸入埠保持確定的狀態。
按鍵釋放時,因為上拉電阻R的關係,GPIO輸入檢測是高電平,按鍵閉合時,GPIO短接到GND,輸入檢測是低電平。這樣,微控制器根據GPIO的輸入狀態即可確定按鍵是否按下。
■ 按鍵檢測知識擴充套件:ADC透過電阻分壓檢測多個按鍵
按鍵檢測除了高低電平檢測的方法之外,還有一種方法是使用ADC透過電阻分壓檢測多個按鍵,這種按鍵檢測的電路形式稱為分壓式接法。
分壓式接法,使用的微控制器引腳必須具有ADC功能,根據檢測口測得的不同的電壓值來識別是哪個按鍵被按下。如下圖所示,是分壓式接法的原理示意圖。這種方法最大的好處是節省IO資源,它只需一個具有ADC功能的IO即可實現多個按鍵的檢測,它適用於IO資源緊張的場合,如一些電磁爐的按鍵使用的就是這種方法。
相對於高低電平檢測,這種方法在程式設計上要複雜一些,需要事先計算好分壓的電壓值,儲存於“表”中,程式執行時,取樣到電壓值後查表即可獲知是哪個按鍵按下。
圖4:分壓式接法原理
3。按鍵檢測電路考慮因素
按鍵檢測電路設計的時候,需要我們考慮兩個方面:按鍵釋放時GPIO口狀態的確定和按鍵消抖。
1)按鍵釋放時GPIO口狀態的確定
按鍵檢測電路中,當按鍵釋放後要能保證GPIO口電平是確定的,即按鍵釋放時GPIO口固定為高電平或低電平。開發板RN1排阻就是滿足使用者按鍵釋放時,在微控制器GPIO口上保持高電平。
2)按鍵消抖
對於按鍵硬體上的消抖,一般常用的方式是在按鍵上並接一個容值約0。1uF左右電容,利用電容兩端的電壓不能突變的特性,消除抖動時產生的毛刺電壓。雖然電容可以起到消除抖動的作用,但是在考慮按鍵靈敏度的情況下,電容時無法完全消除抖動的,消除抖動還需要軟體的配合。
開發板上按鍵電路沒有增加硬體消抖,開發板使用的是軟體消抖,這對於一般的按鍵檢測已經完全足夠。
3)GPIO口保護
開發板按鍵檢測電路中還串有排阻RN12,該排阻阻值100歐姆,串在微控制器GPIO口和按鍵引腳中,起到保護GPIO口的目的。
分析:如果GPIO口不小心誤配置為輸出模式,並且輸出高電平,則分析下電路可知,此時如果沒有排阻RN12,若按下使用者按鍵,則微控制器GPIO口(控制輸出高電平)直接和GND相連,會損壞GPIO口。
二、軟體設計
1。暫存器解析
1。1。埠資料暫存器
下圖是對埠資料暫存器P0、P1、P2、P3、P4、P5、P6、P7的描述,埠資料暫存器各位代表對應埠的GPIO口,在完成對配置暫存器設定後,可直接讀取埠引腳電平。
圖5:埠資料暫存器
1。2。埠上拉電阻控制暫存器
下圖是對埠上拉電阻控制暫存器P0PU、P1PU、P2PU、P3PU、P4PU、P5PU、P6PU、P7PU的描述,這些特殊功能暫存器不支援位定址。埠上拉電阻控制暫存器各位代表對應埠的GPIO口是否使能其上拉電阻,在完成對該暫存器設定後,相應GPIO埠便在微控制器內部有了上拉電阻。該功能使得GPIO口在硬體電路設計時具有了更多的靈活性。但務必知曉埠上拉電阻控制暫存器為擴充套件SFR,邏輯地址位於XDATA區域,訪問前需先將P_SW2暫存器的最高位(EAXFR)置1。
圖6:埠資料暫存器
☆注:如果GPIO外部加了上拉電阻,而軟體又使能內部上拉電阻有效,那實際上拉電阻阻值是微控制器GPIO口片內和片外上拉電阻並連後的值。
2。GPIO輸入按鍵檢測實驗(單個c檔案)
☆注:本節的實驗原始碼是在“實驗2-1-3:流水燈(單個c檔案)”的基礎上修改。本節對應的實驗原始碼是:“實驗2-2-1: GPIO輸入按鍵檢測(單個c檔案)”。
2。1。標頭檔案引用和路徑設定
■ 需要宏定義部分及引用的標頭檔案
因為在“main。c”檔案中使用了STC8的標頭檔案“STC8。h”,所以需要引用下面的標頭檔案。在標頭檔案“STC8。h”中需要確定主時鐘取值,所以宏定義主時鐘值。
#define MAIN_Fosc 11059200L //定義主時鐘
#include “STC8。H”
1
2
在程式設計中會用到定義變數的型別,為了定義變數方便,將較為複雜的“unsigned int”和“unsigned char ”進行了宏定義。
#define uint16 unsigned int
#define uint8 unsigned char
1
2
3
這樣,再定義變數時可直接使用“uint16”和“uint8”來取代“unsigned int”和“unsigned char ”即可。
■ 需要包含的標頭檔案路徑
本例需要包含的標頭檔案路徑如下表:
表2:標頭檔案包含路徑
序號 路徑 描述
1 …\User STC8。H標頭檔案在該路徑,所以要包含
MDK中點選魔術棒,開啟工程配置視窗,按照下圖所示新增標頭檔案包含路徑。
圖7:新增標頭檔案包含路徑
2。2。編寫程式碼
首先根據開發板按鍵及指示燈GPIO分配,定義暫存器位變數,程式碼如下。
/**********************
引腳別名定義
***********************/
sbit KEY=P0^7; //使用者按鍵KEY3用IO口P07
sbit LED_D3=P7^2; //使用者指示燈D3用IO口P72
1
2
3
4
5
6
然後,在主函式中先對P7。2和P0。7口進行模式配置,後主迴圈中檢測按鍵狀態,確認按鍵按下控制藍色指示燈D3亮。
int main(void)
{
P7M1 &= 0xFB; P7M0 &= 0xFB; //設定P7。2為準雙向口
P0M1 &= 0x7F; P0M0 &= 0x7F; //設定P0。7為準雙向口
// P0M1 |= 0x80; P0M0 &= 0x7F; //設定P0。7為高阻輸入
while(1)
{
if(KEY == 0) //檢測使用者按鍵KEY3對應引腳P0。7是否是低電平 (按鍵按下,引腳為低電平)
{
delay_ms(10); //軟體延時10ms,如果延時後按鍵KEY3的電平依然沒有變化,說明按鍵確實被有效操作,簡稱按鍵消抖
if(KEY== 0) //檢測使用者按鍵KEY3對應引腳P0。7是否依然是低電平
{
LED_D3=0; //點亮使用者指示燈D3
while(KEY == 0) //等待按鍵KEY3釋放,即如果P0。7一直為低電平,會一直執行空命令
{
; //條件KEY == 0成立,會執行這個空命令
}
LED_D3=1; //按鍵KEY3釋放,熄滅使用者指示燈D3
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
3。流水燈實驗(多個c檔案)
☆注:本節的實驗原始碼是在“實驗2-2-1: GPIO輸入按鍵檢測(單個c檔案)”的基礎上修改。本節對應的實驗原始碼是:“實驗2-2-2: GPIO輸入按鍵檢測(多個c檔案)”。
3。1。工程需要用到的c檔案
本例需要用到的c檔案如下表所示,工程需要新增下表中的c檔案。
表3:實驗需要用到的c檔案
序號 檔名 字尾 功能描述
1 led 。c 包含與使用者led控制有關的使用者自定義函式
2 key 。c 包含與使用者按鍵檢測有關的使用者自定義函式
3 delay 。c 包含使用者自定義延時函式
3。2。標頭檔案引用和路徑設定
■ 需要引用的標頭檔案
因為在“main。c”檔案中使用了控制led的函式和延時函式(延時函式沒有在main。c中定義),所以需要引用下面的標頭檔案。
#include “led。h”
#include “delay。h”
#include “key。h”
1
2
3
■ 需要包含的標頭檔案路徑
本例需要包含的標頭檔案路徑如下表:
表4:標頭檔案包含路徑
序號 路徑 描述
1 …\ Source led。h、key。h和delay。h標頭檔案在該路徑,所以要包含
2 …\User STC8。h標頭檔案在該路徑,所以要包含
MDK中點選魔術棒,開啟工程配置視窗,按照下圖所示新增標頭檔案包含路徑。
圖8:新增標頭檔案包含路徑
3。3。編寫程式碼
首先在key。h中,宏定義4個使用者按鍵,引用延時函式的標頭檔案,宣告按鍵檢測函式供外部呼叫。程式碼如下。
#include “delay。h”
#define KEY_ON 0
#define KEY_OFF 1
#define KEYS_OFF 0 //沒有按鍵按下
#define KEY1_ON 1 //按鍵KEY1按下
#define KEY2_ON 2 //按鍵KEY2按下
#define KEY3_ON 3 //按鍵KEY3按下
#define KEY4_ON 4 //按鍵KEY4按下
/********************************************
引腳別名定義
*********************************************/
sbit KEY_S1=P3^7; //使用者按鍵KEY1用IO口P37
sbit KEY_S2=P3^6; //使用者按鍵KEY2用IO口P36
sbit KEY_S3=P0^7; //使用者按鍵KEY3用IO口P07
sbit KEY_S4=P0^5; //使用者按鍵KEY4用IO口P05
extern uint8 Keys_Scan(uint8 mode);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
然後,在key。c檔案中編寫一個按鍵檢測函式Keys_Scan,程式碼如下。
程式清單:延時函式
/**************************************************************************
功能描述:檢測開發板上的4個使用者按鍵(KEY1、KEY2、KEY3、KEY4)
入口引數:uint8 mode 是否支援連按
返回值:按鍵編號
*************************************************************************/
uint8 Keys_Scan(uint8 mode)
{
static uint8 Key_up=1; //標誌變數
if(mode==1) //支援連按
{
Key_up=1; //變數 Key_up會被重新賦值為1
}
//檢測按鍵KEY1、按鍵KEY2、按鍵KEY3、按鍵KEY4用IO口電平是否為低電平
if(Key_up&&((KEY_S1 == KEY_ON ) || (KEY_S2 == KEY_ON ) || (KEY_S3 == KEY_ON ) || (KEY_S4 == KEY_ON )))
{
delay_ms(10); //軟體延時10ms,去抖
Key_up=0; //變數 Key_up會被賦值為0
if(KEY_S1 == KEY_ON )
{
return KEY1_ON;
}
else if(KEY_S2 == KEY_ON )
{
return KEY2_ON;
}
else if(KEY_S3 == KEY_ON )
{
return KEY3_ON;
}
else if(KEY_S4 == KEY_ON )
{
return KEY4_ON;
}
}
else if((KEY_S1 == KEY_OFF )&&(KEY_S2 == KEY_OFF ) &&(KEY_S3 == KEY_OFF )&&(KEY_S4 == KEY_OFF ) )
{
Key_up=1; //變數 Key_up會被賦值為1
}
return KEYS_OFF;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
因為按鍵檢測函式Keys_Scan中使用了控制led的函式、有關KEY的宏定義和延時函式,所以需要在key。c檔案中引用下面的標頭檔案。
#include “led。h”
#include “key。h”
1
2
最後,在主函式中先對4個使用者指示燈和4個使用者按鍵用到的GPIO口進行模式配置,後主迴圈中呼叫按鍵檢測函式,可觀察各使用者按鍵按下後用戶指示燈變化情況。
程式碼清單:主函式
int main(void)
{
uint8 temp;
P2M1 &= 0x3F; P2M0 &= 0x3F; //設定P2。6~P2。7為準雙向口
P7M1 &= 0xF9; P7M0 &= 0xF9; //設定P7。1~P7。2為準雙向口
P0M1 &= 0x5F; P0M0 &= 0x5F; //設定P0。5,P0。7為準雙向口
// P0M1 |= 0xA0; P0M0 &= 0x5F; //設定P0。5,P0。7為高阻輸入
P3M1 &= 0x3F; P3M0 &= 0x3F; //設定P3。6~P3。7為準雙向口
// P3M1 |= 0xC0; P3M0 &= 0x3F; //設定P3。6~P3。7為高阻輸入
// P_SW2 |= 0x80; //將EAXFR位置1,以訪問在XDATA區域的擴充套件SFR
// P0PU |= 0xA0; //設定P0。5,P0。7口有上拉電阻
// P3PU |= 0xC0; //設定P3。6~P3。7口有上拉電阻
// P_SW2 &= 0x7F; //將EAXFR位置0,恢復訪問XRAM
while(1)
{
temp=Keys_Scan(0); //獲取開發板使用者按鍵檢測值,不支援連按
if(temp == KEY1_ON) //如果按鍵KEY1被操作
{
led_toggle(LED_1); //控制使用者指示燈D1翻轉
}
else if(temp == KEY2_ON) //如果按鍵KEY2被操作
{
led_toggle(LED_2); //控制使用者指示燈D2翻轉
}
else if(temp == KEY3_ON) //如果按鍵KEY3被操作
{
led_toggle(LED_3); //控制使用者指示燈D3翻轉
}
else if(temp == KEY4_ON) //如果按鍵KEY4被操作
{
led_toggle(LED_4); //控制使用者指示燈D4翻轉
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
總結
以上就是今天要講的內容!