天天看點

STM32單片機學習3--STM32控制鍵盤

一、前言

單片機型号:STM32F103C8T6

開發環境:Keil5

STM32單片機學習3--STM32控制鍵盤

二、GPIO的8種工作模式

STM32單片機學習3--STM32控制鍵盤

4種輸入模式

  • 上拉輸入模式:在預設狀态下(GPIO引腳無輸入),讀取得的GPIO引腳資料為1,高電平(與Vdd相連的為上拉電阻);
  • 下拉輸入模式:在預設狀态下(GPIO引腳無輸入),讀取得的GPIO引腳資料為0,低電平(與Vss相連的為下拉電阻);
  • 浮空輸入模式:在晶片内部既沒有上拉電阻,也沒有下拉電阻,經由觸發器輸入。配置成這個模式直接用電壓表測量其引腳為1點幾伏,這是個不确定值。由于其輸入阻抗較大,一般把這種模式用于标準的通信協定I2C、USART的接收端;
  • 模拟輸入模式:關閉了斯密特觸發器,不接上、下拉電阻,經由另一線路吧電壓信号傳送到片上外設子產品。如傳送至ADC子產品,由ADC采集電壓信号,是以使用ADC外設的時候,必須設定為模拟輸入模式;

4中輸出模式

  • 推挽輸出模式:線路經過一個由P-MOS管和N-MOS管組成的單元電路。在輸出高電平時,P-MOS管導通;低電平時,N-MOS管導通。兩個管子輪流導通,一個負責灌電流,一個負責拉電流,使其負載能力和開關速度都比普通的方式有很大的提高。普通推挽輸出模式一般應用在輸出電平為0和3.3伏的場合;
  • 開漏輸出模式:如果我們控制輸出為0,低電平,則使N-MOS管導通,使輸出接地;如果我們控制輸出為1(無法直接輸出高電平),則既不輸出高電平,也不輸出低電平,為高阻态;為正常使用時必須在外部接上一個上拉電阻。它具有“線與”特效,即很多個開漏模式引腳連接配接到一起時,隻有當所有引腳都輸出高阻态,才由上拉電阻提供高電平,此高電平的電壓為外部上拉電阻所接電源的電壓。若其中一個引腳為低電平,那線路就相當于短路解讀,使得整條線路都為低電平,0伏;普通開漏輸出模式一般應用在電平不比對的場合,如需要輸出5伏的高電平,就需要在外部接一個上拉電阻,電源為5伏,把GPIO設定為開漏模式,當輸出高阻态時,由上拉電阻和電源向外輸出5伏的電平;
  • 複用推挽輸出模式:
  • 複用開漏輸出模式:

對相應的複用模式,則是根據GPIO的複用功能來選擇的,例如GPIO的引腳用作序列槽的輸出,則使用複用推挽輸出模式;如果用在IC、SMBUS這些需要線與功能的複合場所,就使用複用開漏輸出模式;

注意:在使用任何一種開漏模式時,都需要接上拉電阻;

三、按鍵試驗

1、原理

STM32單片機學習3--STM32控制鍵盤

如上圖,按鈕一端接地,一端接GPIO引腳,這條路其實是一個斷路;控制按鈕時我們通常會将GPIO引腳設定為上拉輸入模式,上拉輸入模式預設為高電平,當按鈕沒有按下的時候,這個引腳讀到的一直是高電平;當按鈕被按下的時候,引腳會被強行拉低,此時引腳讀到的為低電平,那說明按鍵已經被按下;

鍵盤由多行多列按鈕組成,程式設計通常采用逐行逐列進行掃描,4*4的矩陣鍵盤一共需要8個GPIO引腳,将控制行的引腳設定成輸出模式,控制列的引腳設定成上拉輸入模式;

先掃描第一行,那麼就将PD0~PD2輸出高電平,PD3輸出低電平,記為0xF7;控制列的引腳為輸入引腳,将其和0xF7相與,如果哪一位為0,那麼就證明哪一個被按下;

STM32單片機學習3--STM32控制鍵盤

2、代碼

接線:4*4矩陣鍵盤,行從上至下依此接B5、B6、B7、B8;列從左至右依此接A1、A2、A3、A4;

按鍵從左至右,從上至下,依此編号為1、2、3、… 、16

STM32單片機學習3--STM32控制鍵盤

Key.h

#ifndef __KEY_H
#define __KEY_H

#include "stm32f10x.h"

void delay_us(uint32_t delay_us);
void delay_ms(uint16_t delay_ms);

void KEY_GPIO_Config(void);
int scan(void);

#endif      

Key.c

#include "Key.h"

void KEY_GPIO_Config(void)
{
  //定義一個GPIO_InitTypeDef類型的結構體
  GPIO_InitTypeDef GPIO_InitStructure;
  
  //開啟GPIOA、GPIOB的外設時鐘
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB,ENABLE);
  
  ///控制行
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOB,&GPIO_InitStructure);
  
  ///讀取列
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  GPIO_Init(GPIOA,&GPIO_InitStructure);
}

int scan(void)
{
  uint8_t flag = 1;
  
  //掃描第一行
  GPIO_ResetBits(GPIOB,GPIO_Pin_5);
  GPIO_SetBits(GPIOB,GPIO_Pin_6);
  GPIO_SetBits(GPIOB,GPIO_Pin_7);
  GPIO_SetBits(GPIOB,GPIO_Pin_8);
  
  //掃描第一列
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
    if(flag == 0) {
      flag = 1;
      return 1;
    }
  }
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
    if(flag == 0) {
      flag = 1;
      return 2;
    }
  }
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
    if(flag == 0) {
      flag = 1;
      return 3;
    }
  }
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4);
    if(flag == 0) {
      flag = 1;
      return 4;
    }
  }
  
  //掃描第二行
  GPIO_SetBits(GPIOB,GPIO_Pin_5);
  GPIO_ResetBits(GPIOB,GPIO_Pin_6);
  GPIO_SetBits(GPIOB,GPIO_Pin_7);
  GPIO_SetBits(GPIOB,GPIO_Pin_8);
  
  //掃描第二列
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
    if(flag == 0) {
      flag = 1;
      return 5;
    }
  }
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
    if(flag == 0) {
      flag = 1;
      return 6;
    }
  }
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
    if(flag == 0) {
      flag = 1;
      return 7;
    }
  }
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4);
    if(flag == 0) {
      flag = 1;
      return 8;
    }
  }
  
  //掃描第三行
  GPIO_SetBits(GPIOB,GPIO_Pin_5);
  GPIO_SetBits(GPIOB,GPIO_Pin_6);
  GPIO_ResetBits(GPIOB,GPIO_Pin_7);
  GPIO_SetBits(GPIOB,GPIO_Pin_8);
  
  //掃描第三列
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
    if(flag == 0) {
      flag = 1;
      return 9;
    }
  }
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
    if(flag == 0) {
      flag = 1;
      return 10;
    }
  }
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
    if(flag == 0) {
      flag = 1;
      return 11;
    }
  }
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4);
    if(flag == 0) {
      flag = 1;
      return 12;
    }
  }
  
  //掃描第四行
  GPIO_SetBits(GPIOB,GPIO_Pin_5);
  GPIO_SetBits(GPIOB,GPIO_Pin_6);
  GPIO_ResetBits(GPIOB,GPIO_Pin_7);
  GPIO_SetBits(GPIOB,GPIO_Pin_8);
  
  //掃描第四列
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
    if(flag == 0) {
      flag = 1;
      return 13;
    }
  }
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
    if(flag == 0) {
      flag = 1;
      return 14;
    }
  }
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
    if(flag == 0) {
      flag = 1;
      return 15;
    }
  }
  flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4);
  if(flag == 0) {
    delay_ms(200);
    flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4);
    if(flag == 0) {
      flag = 1;
      return 16;
    }
  }
  
  return -1;
}      
  • STM32用​

    ​GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)​

    ​讀取GPIO引腳的電平(配置為輸入模式);
  • 一般檢測到低電平後延時一會再次檢測,是常見的消抖手段,防止誤觸;
#include "stm32f10x.h"
#include "Key.h"

int main(void)
{
  KEY_GPIO_Config();
  while(1)
  {
    switch(scan())
    {
      case 1:
        break;
      case 2:
        break;
      case 3:
        break;
      case 4:
        break;
      case 5:
        break;
      case 6:
        break;
      case 7:
        break;
      case 8:
        break;
      case 9:
        break;
      case 10:
        break;
      case 11:
        break;
      case 12:
        break;
      case 13:
        break;
      case 14:
        break;
      case 15:
        break;
      case 16:
        break;
    }
    
  }
  
}