把KeyScan.h, KeyScan.c, KeyScanConfig.h放到工程目錄下。
在主函數(shù)中引入頭文件
- #include "main.h"
- #include "Uart.h"
- #include "KeyScan.h"
復(fù)制代碼main.h包含基本數(shù)據(jù)類型的typedef定義,一些C庫(kù)的頭文件引入和系統(tǒng)時(shí)鐘設(shè)置;
Uart.h是單片機(jī)串口模塊頭文件;
KeyScan.h是按鍵掃描模塊頭文件。
假如現(xiàn)在有4個(gè)按鍵A,B,C,D,給按鍵編號(hào),放到枚舉類型里。
- enum EnumUserKey{ //按鍵編號(hào) 從0開始 不得超過(MAX_KEY_NUMBER-1)
- EnumKey_A = 0,
- EnumKey_B = 1,
- EnumKey_C = 2,
- EnumKey_D = 3
- };
復(fù)制代碼
定義按鍵相關(guān)的兩個(gè)結(jié)構(gòu)體,作為全局變量。GPIO_KEY_NUM是第3步中按鍵的數(shù)量,這里是4;FUNC_KEY_NUM是第5步中事件函數(shù)的數(shù)量,這里是3,功能與按鍵是獨(dú)立的,數(shù)量可以不相等。
- #define GPIO_KEY_NUM 4 // 按鍵總數(shù),即enum EnumUserKey定義的按鍵數(shù)量
- xdata KeyIO_t SingleKey[GPIO_KEY_NUM]; // 按鍵IO數(shù)組
- #define FUNC_KEY_NUM 3 // 用戶自定義的功能總數(shù)
- xdata KeyFunc_t KeyFuncs[FUNC_KEY_NUM]; // 按鍵功能數(shù)組
復(fù)制代碼
定義按鍵功能函數(shù)。比如,需要按鍵A,B單擊分別觸發(fā),按鍵C,D同時(shí)按下觸發(fā),功能函數(shù)可以定義成下面這樣,函數(shù)名字隨意。
- void KeyAPressEvent(void){
- P40 = ~P40;
- }
- void KeyBPressEvent(void){
- Delay100ms();
- }
- void KeyCDPressEvent(void){
- P41 = ~P41;
- // printf發(fā)送長(zhǎng)串被中斷打斷會(huì)死機(jī),使用UartSendString
- // 如果很短可以使用printf
- UartSendString("testtesttesttesttesttesttesttesttesttesttesttesttest\r\n");
- Delay100ms(); // 長(zhǎng)延時(shí)也不會(huì)死機(jī)了,哈哈
- UartSendString("testtesttesttesttesttesttesttesttesttesttesttesttest\r\n");
- Delay100ms();
- }
復(fù)制代碼
好的,現(xiàn)在按鍵有了,功能也有了,但是還沒聯(lián)系到一起。下面是按鍵掃描初始化函數(shù),把它們聯(lián)系起來。
- //按鍵掃描初始化
- void KeyInit(void){
- u8 i;
- // 函數(shù)指針必須全部初始化為NULL
- for(i=0; i<FUNC_KEY_NUM; i++){
- KeyFuncs.fp_singleClick = NULL;
- KeyFuncs.fp_comboClick = NULL;
- KeyFuncs.fp_longPress = NULL;
- KeyFuncs.fp_multiPress = NULL;
- }
-
- // 注冊(cè)按鍵 Port1必須是IO口 Port2是IO口或"GND"
- SingleKey[EnumKey_A].IOPort1 = "P36"; SingleKey[EnumKey_A].IOPort2 = "GND";
- SingleKey[EnumKey_B].IOPort1 = "P52"; SingleKey[EnumKey_B].IOPort2 = "GND";
- SingleKey[EnumKey_C].IOPort1 = "P54"; SingleKey[EnumKey_C].IOPort2 = "GND";
- SingleKey[EnumKey_D].IOPort1 = "P53"; SingleKey[EnumKey_D].IOPort2 = "GND";
-
- // 需要響應(yīng)的鍵值 注意是鍵值! 不是鍵編號(hào)! 組合按鍵用或
- KeyFuncs[0].triggerValue = TRIGGER_VALUE(EnumKey_A);
- // 注冊(cè)回調(diào)函數(shù)為單擊功能
- KeyFuncs[0].fp_singleClick = KeyAPressEvent;
-
- // 需要響應(yīng)的鍵值 注意是鍵值! 不是鍵編號(hào)! 組合按鍵用或
- KeyFuncs[1].triggerValue = TRIGGER_VALUE(EnumKey_B);
- // 注冊(cè)回調(diào)函數(shù)為單擊功能
- KeyFuncs[1].fp_singleClick = KeyBPressEvent;
-
- // 需要響應(yīng)的鍵值 注意是鍵值! 不是鍵編號(hào)! 組合按鍵用或
- KeyFuncs[2].triggerValue = TRIGGER_VALUE(EnumKey_C) | TRIGGER_VALUE(EnumKey_D);
- // 注冊(cè)回調(diào)函數(shù)為組合鍵功能
- KeyFuncs[2].fp_multiPress = KeyCDPressEvent;
-
- KeyScanInit((KeyIO_t*)&SingleKey, GPIO_KEY_NUM, (KeyFunc_t*)&KeyFuncs, FUNC_KEY_NUM);
- }
復(fù)制代碼初始化過程可以分為4個(gè)步驟:
初始化函數(shù)指針為NULL,這里的函數(shù)指針變量來自第4步定義的xdata KeyFunc_t KeyFuncs[FUNC_KEY_NUM]
告訴單片機(jī)按鍵的硬件連線位置,假如按鍵A,B,C,D的一端分別連到單片機(jī)的P36,P52,P54,P53上,另一端接地,就按照上面的程序設(shè)置。如果按鍵是矩陣的,沒有接地,就把按鍵兩端的IO都對(duì)應(yīng)寫成字符串。
(這么做的好處就是可以把按鍵隨便亂接,畢竟有的封裝,比如SOP-16是沒有完整的一組8bit IO引出的,假如在這個(gè)單片機(jī)上用傳統(tǒng)的方式應(yīng)用4×4矩陣鍵盤,位處理是不是特別難受?)
這一步把按鍵和一個(gè)事件函數(shù)聯(lián)系起來。
需要用到TRIGGER_VALUE宏,把按鍵編號(hào)轉(zhuǎn)換成觸發(fā)值,如果用到組合鍵,把各個(gè)按鍵的觸發(fā)值用|連接即可。
還需要注意的就是按鍵的功能是靠結(jié)構(gòu)體成員的名字來區(qū)分的,有fp_singleClick, fp_comboClick, fp_longPress, fp_multiPress共4種,給哪個(gè)賦值,對(duì)應(yīng)的事件函數(shù)就是什么功能。
把剛才設(shè)置好的結(jié)構(gòu)體給到按鍵掃描程序,開始按鍵掃描。
編寫主函數(shù),把KeyInit()放到初始化里,把KeyEventProcess()放到while(1)里。
- void main(){
- EA = 1;
- UartInit();
- KeyInit(); //按鍵掃描初始化
- // printf發(fā)送長(zhǎng)串被中斷打斷會(huì)死機(jī),使用UartSendString
- // printf("testtesttesttesttesttesttesttesttesttesttesttesttest\r\n");
- UartSendString("testtesttesttesttesttesttesttesttesttesttesttesttest\r\n");
- while(1){
- KeyEventProcess();
- }
- }
復(fù)制代碼
KeyEventProcess()檢查事件隊(duì)列,執(zhí)行隊(duì)列里所有函數(shù)。如果隊(duì)列滿了,那么再有按鍵事件的話,就會(huì)被忽略。所以,慢點(diǎn)按,哈哈。(其實(shí)單片機(jī)速度夠,快點(diǎn)按也沒事,而且隊(duì)列長(zhǎng)度能改,在KeyScanConfig.h里)