「STC8A8K64S4A12開發板」—GPIO按鍵實驗講解

前言

做完了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

總結

以上就是今天要講的內容!