首先感謝 江科大 的stm32入門課程 受益匪淺。推薦有興趣的朋友去看看。
先看看我用的矩陣鍵盤是啥樣的(很常見的一種)
接線如圖(其他型號根據自己需求接上GPIO口)
程式碼基於stm大善人的程式碼修改而來,講的很詳細,非常感謝。
直接上程式碼:
標頭檔案Key4x4.h
#ifndef __KEY4x4_H #define __KEY4x4_H void KEY_4x4_Init(void); void KEY_Scan(void); u16 Key_Read(void); #endif
主體檔案Key4x4.c
#include "stm32f10x.h" #include "Delay.h" u8 anxia = 0; u8 key = 1; u16 line[4] = {0x00fe , 0x00fd , 0x00fb ,0x00f7}; u16 off = 0x00ff; // 全部引腳置為1 u16 keys[16] = { 49, 50, 51, 65, 52, 53, 54, 66, 55, 56, 57, 67, 42, 48, 35, 68, }; void KEY_4x4_Init(void){ GPIO_InitTypeDef GPIO_InitStructre; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); // 使能 GPIOA 的時鐘 // 第一組 GPIO_InitStructre.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; GPIO_InitStructre.GPIO_Mode = GPIO_Mode_Out_PP; // 推輓輸出 GPIO_InitStructre.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA , &GPIO_InitStructre); GPIO_SetBits(GPIOA , GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3); // 第二組資料 GPIO_InitStructre.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStructre.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOA , &GPIO_InitStructre); GPIO_SetBits(GPIOA , GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7); } void Do_Click(uint16_t gpio_pin_x , u8 num){ anxia = 1; key = num; while(!GPIO_ReadInputDataBit(GPIOA , gpio_pin_x)); } void KEY_Click_Listener(u8 num){ if((GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_4)==0)||(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_5)==0)||(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_6)==0)||(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_7)==0)){ Delay_ms(10); if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_4)){ Do_Click(GPIO_Pin_4 , num+0); }else if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_5)){ Do_Click(GPIO_Pin_5 , num+1); }else if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_6)){ Do_Click(GPIO_Pin_6 , num+2); }else if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_7)){ Do_Click(GPIO_Pin_7 , num+3); }else { anxia = 0; GPIO_Write(GPIOA , off); } } } void KEY_Scan(){ // 第一行 1111 1110 GPIO_Write(GPIOA , line[0]); KEY_Click_Listener(1); // 第二行 GPIO_Write(GPIOA , line[1]); KEY_Click_Listener(5); // 第三行 GPIO_Write(GPIOA , line[2]); KEY_Click_Listener(9); // 第四行 GPIO_Write(GPIOA , line[3]); KEY_Click_Listener(13); } u16 Key_Read(){ return keys[key-1]; }
主要程式碼說明:
初始化配置 (KEY_4x4_Init):
- 使能GPIOA模組的時鐘。
- 配置GPIOA的前四個引腳(GPIO_Pin_0至GPIO_Pin_3)為推輓輸出模式,用於鍵盤行線的掃描。
- 設定GPIOA的後四個引腳(GPIO_Pin_4至GPIO_Pin_7)為上拉輸入模式,用於檢測鍵盤列線的狀態。
// 第一組 GPIO_InitStructre.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; GPIO_InitStructre.GPIO_Mode = GPIO_Mode_Out_PP; // 推輓輸出 GPIO_InitStructre.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA , &GPIO_InitStructre); GPIO_SetBits(GPIOA , GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3); // 第二組資料 GPIO_InitStructre.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStructre.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOA , &GPIO_InitStructre); GPIO_SetBits(GPIOA , GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
按鍵檢測 (KEY_Click_Listener):
- 檢測列線狀態,如果有鍵按下,則呼叫
Do_Click
函式記錄按鍵資訊並等待按鍵釋放。 - 使用延時函式
Delay_ms
來消除抖動。
void KEY_Click_Listener(u8 num){ if((GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_4)==0)||(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_5)==0)
||(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_6)==0)||(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_7)==0)){ Delay_ms(10); if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_4)){ Do_Click(GPIO_Pin_4 , num+0); }else if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_5)){ Do_Click(GPIO_Pin_5 , num+1); }else if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_6)){ Do_Click(GPIO_Pin_6 , num+2); }else if(0 == GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_7)){ Do_Click(GPIO_Pin_7 , num+3); }else { anxia = 0; GPIO_Write(GPIOA , off); } } }
void Do_Click(uint16_t gpio_pin_x , u8 num){ anxia = 1; key = num; while(!GPIO_ReadInputDataBit(GPIOA , gpio_pin_x)); }
掃描過程 (KEY_Scan):
- 迴圈掃描每一行,透過改變行線的狀態來檢測是否有鍵按下。
- 呼叫
KEY_Click_Listener
函式來處理每一行的按鍵檢測。
讀取按鍵值 (Key_Read):
- 返回當前按下的鍵對應的數值。
補充說明:
u16 line[4] = {0x00fe , 0x00fd , 0x00fb ,0x00f7}; u16 off = 0x00ff; // 全部引腳置為1 u16 keys[16] = { 49, 50, 51, 65, 52, 53, 54, 66, 55, 56, 57, 67, 42, 48, 35, 68, };
line 定義 了4個16進位制的數值分別轉成二進位制
0000 0000 1111 1110 0x00fe
0000 0000 1111 1101 0x00fd
0000 0000 1111 1011 0x00fb
0000 0000 1111 0111 0x00f7
低4位為行 高四位為列
設定0就是給對應行設定低電平
這樣我們在scan的程式碼就能看出來 掃描的做法就是先給傳入的行line[x] 設定低電平
所有列都是高電平當掃描到某一列為低電平時就說明這一列被點選了。
迴圈從第一列到第四列設定低電平直到檢測到某一列也變成低電平
假設1被點選 則1這一列也是低電平
就變成第一行 第一列低電平
這樣就能確定1被點選。給對應引數賦值1即可
再根據定義的keys (askII 碼錶對應數值 )
主要程式碼就是上面這些,其他程式碼只要複製 江科大OLED的課件原始碼即可
使用方式 man.c
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Key4x4.h" int main(void) { /*模組初始化*/ OLED_Init(); //OLED初始化 KEY_4x4_Init(); /*OLED顯示*/ OLED_ShowString(1, 1, "in put:"); //1行1列顯示字元A u8 num = 0; while (1) { KEY_Scan(); num = Key_Read(); OLED_ShowChar(1 ,8 ,num); } }