一、前言
單片機型号:STM32F103C8T6
開發環境:Keil5

二、GPIO的8種工作模式
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、原理
如上圖,按鈕一端接地,一端接GPIO引腳,這條路其實是一個斷路;控制按鈕時我們通常會将GPIO引腳設定為上拉輸入模式,上拉輸入模式預設為高電平,當按鈕沒有按下的時候,這個引腳讀到的一直是高電平;當按鈕被按下的時候,引腳會被強行拉低,此時引腳讀到的為低電平,那說明按鍵已經被按下;
鍵盤由多行多列按鈕組成,程式設計通常采用逐行逐列進行掃描,4*4的矩陣鍵盤一共需要8個GPIO引腳,将控制行的引腳設定成輸出模式,控制列的引腳設定成上拉輸入模式;
先掃描第一行,那麼就将PD0~PD2輸出高電平,PD3輸出低電平,記為0xF7;控制列的引腳為輸入引腳,将其和0xF7相與,如果哪一位為0,那麼就證明哪一個被按下;
2、代碼
接線:4*4矩陣鍵盤,行從上至下依此接B5、B6、B7、B8;列從左至右依此接A1、A2、A3、A4;
按鍵從左至右,從上至下,依此編号為1、2、3、… 、16
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引腳的電平(配置為輸入模式);GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
- 一般檢測到低電平後延時一會再次檢測,是常見的消抖手段,防止誤觸;
#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;
}
}
}