DIY一把屬於自己的機械鍵盤(四)

軟件篇(一)—— 按鍵掃描


  回顧一下硬件篇矩陣鍵盤部分,爲了方便軟件掃描、提高掃描效率,我在每一行增加了一個上拉電阻,如圖:
在這裏插入圖片描述
  首先分析一下原理,經過電阻上拉,每一行的初始狀態爲高電平,那麼要檢測到按鍵事件對應行 K_ROWx 就應該出現低電平或下降沿才能檢測。從圖中可以看出產生低電平或下降沿由 COLx 控制,對應的只有軟件逐列拉低 COLx 即可。這樣一來只需要循環拉低 COLx,獲取 K_ROWx 狀態,掃描一次即可實現鍵值讀取。例如,拉低 COL3,此時 K_ROW4 檢測到下降沿,說明座標 (4,3) 的按鍵被按下。
  爲了進一步提高掃描速率,我把 K_ROWx 和 COLx 分別接入一組 IO 口,這樣就可以通過寄存器移位實現按鍵掃描,提升效率。弄清原理之後,寫代碼就輕鬆多了。下面看一下代碼:

  1. 相關定義
#define         COL_NUM         	14
#define         ROW_NUM        	 	5

#define         KEY_PRESSED         0
#define         KEY_UNPRESSED       1

#define         KEY_ROW_GPIO_CLK                RCC_APB2Periph_GPIOA
#define         KEY_ROW_GPIO_APBxClkCmd         RCC_APB2PeriphClockCmd
#define         KEY_ROW_PORT                    GPIOA
#define         KEY_ROW_PIN                     0x001F

#define         KEY_COL_GPIO_CLK                RCC_APB2Periph_GPIOB
#define         KEY_COL_GPIO_APBxClkCmd         RCC_APB2PeriphClockCmd
#define         KEY_COL_PORT                    GPIOB
#define         KEY_COL_PIN                     0x3FFF

#define         KEY_ROW_READ                    GPIOA->IDR & 0x001F       // 取低5位
#define         KEY_COL_OUT                     GPIOB->ODR

const u16 COL_Scan_Table[COL_NUM] = {
0x3FFE, 0x3FFD, 0x3FFB, 0x3FF7,0x3FEF, 0x3FDF, 0x3FBF, 
0x3F7F, 0x3EFF, 0x3DFF, 0x3BFF, 0x37FF,0x2FFF, 0x1FFF,
};

u8 KeyBoard_KeyStatus_New[5][14] = {KEY_UNPRESSED};
  1. IO初始化
void Key_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    KEY_ROW_GPIO_APBxClkCmd(KEY_ROW_GPIO_CLK, ENABLE);
    KEY_COL_GPIO_APBxClkCmd(KEY_COL_GPIO_CLK, ENABLE);
    
    /* 這裏用到了JTAG接口的IO,需要重定義 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
    
    /* 行輸入,外部上拉,低電平有效 */
    GPIO_InitStructure.GPIO_Pin = KEY_ROW_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(KEY_ROW_PORT, &GPIO_InitStructure); 
    
    /* 列輸出,低電平有效 */
    GPIO_InitStructure.GPIO_Pin = KEY_COL_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(KEY_COL_PORT, &GPIO_InitStructure); 
    
    KEY_COL_OUT = 0x3FFF;   // 全部列拉高
}

  這裏重點說明一下列輸出 IO 的配置。首先討論一下推輓輸出的情況:當按下某一行的一個按鍵,此時該行電平被拉低,能夠正常檢測。但當這一行同時按下兩個及以上按鍵時,由於列掃描是逐列移位拉低(即同一時刻有且只有一列爲低電平),那麼其他列如果也按下去的話,該行就會被多次上拉,導致無法完全拉低該行電平,進而導致無法識別同一行的多個按鍵。
  該如何解決這個問題,從上面的分析可以得出同一行多個按鍵無法識別的根本原因是其他列的高電平導致了電平無法完全拉低。那麼如果其他列也保持低電平呢? 顯然是不行的,這樣就無法檢測到這一行到底是哪個按鍵被按下。因此,可以明確一點,同一時刻有且只能有一列爲低電平。
  那麼除了高、低電平兩個狀態還有什麼? 就是高阻態。將 IO 配置爲開漏輸出,就能保證同一時刻有且只有一列爲低電平,同時其他列爲高阻態,不會影響按鍵行電平。

  1. 按鍵掃描
void Key_Scan(void)
{
    u8 i,j;
    u8 row_data;
    
    for(i=0;i<COL_NUM;i++)
    {
        KEY_COL_OUT = COL_Scan_Table[i];  // 列輸出
        Delay_us(10);
        
        row_data = KEY_ROW_READ; // 獲取行按鍵狀態
        for(j=0;j<ROW_NUM;j++)
        {
            KeyBoard_KeyStatus_New[j][i] = row_data & 0x01; // 獲取按鍵狀態
            row_data = row_data >> 1; // 行切換
        }
    }
}

  程序中 i 的值爲當前列,j 爲當前行。每切換一次列輸出讀一組行,再將讀出的行值按位更新到數組中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章