stm32 F103C8T6 4x4矩陣鍵盤使用

我辈年轻發表於2024-08-05

首先感謝 江科大 的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);
    }
}

相關文章