一、什么是GPIO
GPIO(英語:General-purpose input/output),通用型之輸入輸出的簡稱,可以用來輸入高低電平或者輸出高低電平。這里的高電平指的是3.3V,低電平指的是0V。通常稱GPIO為IO口,或者引腳。
STM32F103ZET6有GPIOx_0~GPIOx_15,其中x = A,B,C,D,E,F,G。
二、GPIO的輸入/輸出模式
GPIO有多種輸入輸出模式,輸入模式有
- ? 輸入浮空 輸入浮空指的是GPIO與外設(shè)之間既不接高電平,也不接低電平,呈高阻態(tài)。除了類似于在數(shù)據(jù)傳輸時將GPIO配置為輸入浮空外,一般不配置為該模式。因為輸入浮空狀態(tài)的GPIO電壓具有不確定性,可能是0V,也可能是VCC,或者是介于0V和VCC之間的某一個值。
- ? 輸入上拉 輸入上拉是通過一個上拉電阻,將GPIO拉至高電平狀態(tài),不受外接電路的影響。
- ? 輸入下拉 輸入下拉是通過一個下拉電阻,將GPIO拉至低電平,不受外接電路的影響。
- ? 模擬輸入 模擬輸入模式通常用在ADC采集,采集模擬信號。
輸出模式有
- ? 開漏輸出
- ? 推挽式輸出
- ? 推挽式復(fù)用功能
- ? 開漏復(fù)用功能
對于一些輸出模式這里就不再做詳細(xì)介紹了,貼一篇大佬的文章深刻理解GPIO。這些模式在接下來的學(xué)習(xí)過程中會慢慢的介紹這些模式需要在什么時候使用,這里只需要知道就夠了。
三、GPIO初始化配置
本專欄介紹的是使用庫函數(shù)進(jìn)行開發(fā),很多內(nèi)容都是庫函數(shù)提供的,相對來講非常方便。在初始化GPIO時有一個結(jié)構(gòu)體,只需要對這個結(jié)構(gòu)體進(jìn)行配置即可。結(jié)構(gòu)體中包括想要配置的GPIO引腳,GPIO速度,GPIO工作模式。
初始化GPIO的步驟主要有
- ? 定義GPIO結(jié)構(gòu)體
- ? 開啟時鐘 GPIO工作需要提供時鐘信號,在初始化結(jié)構(gòu)體之前需要將時鐘打開
- ? 配置結(jié)構(gòu)體成員 GPIO_Pin是想要配置的IO,GPIO_Speed,通常寫GPIO_Speed_50MHz,GPIO_Mode是IO的工作模式
- ? 寫入配置
GPIO的工作模式在程序中有定義
typedef enum
{ GPIO_Mode_AIN = 0x0, // 模擬輸入
GPIO_Mode_IN_FLOATING = 0x04, // 輸入浮空
GPIO_Mode_IPD = 0x28, // 輸入下拉
GPIO_Mode_IPU = 0x48, // 輸入上拉
GPIO_Mode_Out_OD = 0x14, // 開漏輸出
GPIO_Mode_Out_PP = 0x10, // 推挽式輸出
GPIO_Mode_AF_OD = 0x1C, // 開漏復(fù)用功能
GPIO_Mode_AF_PP = 0x18 // 推挽式復(fù)用功能
}GPIOMode_TypeDef;
初始化GPIO的例程如下
void Drv_Gpio_Init (void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定義結(jié)構(gòu)體
// 開啟時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB,ENABLE);
// 配置結(jié)構(gòu)體
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置結(jié)構(gòu)體
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
這里初始化的是PA0,PA8和PB1,PB2。在開啟時鐘不必要寫兩遍相同的代碼用來初始化GPIOA和GPIOB的時鐘。寫一句,用“ | ”同時開啟兩個GPIO的時鐘。寫Pin時也同理。
四、Boot引腳
這里簡單介紹一下Boot引腳的配置,對于只是利用核心板編寫程序的小伙伴來說Boot引腳的存在感較低,但是當(dāng)我們需要繪制硬件電路圖時,Boot引腳怎么連接就顯得很重要。中文參考手冊中介紹如下
中文參考手冊Boot引腳介紹
這里簡單說一下本人經(jīng)歷得來的經(jīng)驗。如果想使用USB轉(zhuǎn)TTL通過串口下載程序,Boot0需要通過KΩ級電阻接VCC,Boot1接地。下載完程序后再將Boot0接地。需要注意的是這個KΩ級別的電阻最好用10KΩ左右電阻,如果電阻太大會導(dǎo)致下載失敗。
由于本人在使用自制板時只使用過串口下載,所以對于用調(diào)試器下載需要怎么配置Boot引腳,未測試。
五、一些特殊的GPIO
在使用GPIO時需要注意一些特殊的GPIO,否則你會疑惑,為什么一些引腳的高低電平無法控制。其實有些GPIO在上電后就有自己的默認(rèn)設(shè)置,會穩(wěn)定在高電平或者低電平。比如用作JTAG的幾個GPIO——PB3,PB4,PA13,PA14和PA15。這幾個GPIO在上電后就已經(jīng)默認(rèn)用作JTAG,即使用上面的GPIO初始化程序?qū)⑦@幾個引腳初始化后,依舊無法控制(無法正常用程序拉高拉低)。
這個時候我們需要對這幾個GPIO進(jìn)行復(fù)用重映射,關(guān)掉其默認(rèn)的功能,才能作為普通的GPIO使用。針對JTAG/SWD復(fù)用功能重映射,中文參考手冊描述如下
中文參考手冊關(guān)于JTAG/SWD引腳的描述
圖中說明這幾個GPIO有幾種模式,完全SWJ,表中的五個IO均不可作為普通IO使用。完全SWJ但是沒有JNTRST,PB3和PB4可用,依此解讀。上圖是針對寄存器開發(fā)描述的內(nèi)容,使用庫函數(shù)開發(fā)時有封裝好的函數(shù),但是需要注意的是需要提前開啟AFIO時鐘(對于AFIO是指什么,大家可以自行了解)。使用庫函數(shù)開發(fā)時針對特殊引腳進(jìn)行重映射的操作步驟為
- ? 定義GPIO結(jié)構(gòu)體
- ? 開啟GPIO時鐘和AFIO時鐘
- ? 重映射引腳(根據(jù)所需情況設(shè)定引腳模式)
- ? 配置GPIO結(jié)構(gòu)體初始化GPIO
庫函數(shù)中提供了可以選擇的三種引腳模式
GPIO_Remap_SWJ_NoJTRST // 完全SWJ(恢復(fù)引腳的默認(rèn)功能)
GPIO_Remap_SWJ_JTAGDisable // 關(guān)閉JTAG,啟用SW-DP
GPIO_Remap_SWJ_Disable // 關(guān)閉JTAG-DP,關(guān)閉SW-DP
提供了一個函數(shù),可以進(jìn)行重映射操作
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
當(dāng)需要使用到上述的五個GPIO時,初始化程序需要修改,舉例如下
void Drv_Gpio_Init (void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定義結(jié)構(gòu)體
// 開啟時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
RCC_APB2Periph_AFIO,ENABLE);
// 關(guān)閉JTAG-DP,關(guān)閉SW-DP
GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);
// 配置結(jié)構(gòu)體
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置結(jié)構(gòu)體
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
STM32F103ZET6芯片GPIO資源較為豐富,平時開發(fā)時盡量不要使用這幾個特殊引腳。
六、點亮LED
學(xué)習(xí)完GPIO后我們就可以利用GPIO進(jìn)行一些簡單的操作,比如點亮LED,使用蜂鳴器,用IO驅(qū)動小黃電機(jī),檢測按鍵等。
點亮LED比較簡單,但是也有一些需要注意的點
- ? 根據(jù)硬件電路確定LED是高電平點亮還是低電平點亮
- ? 初始化GPIO后要先將所有的LED
6.1 硬件電路
下圖所示電路中,LED右側(cè)接3.3V,左側(cè)接IO口。此時如果想點亮LED,需要將對應(yīng)的IO電平拉低。相反如果右側(cè)接的是地,如果想要點亮LED,需要將對應(yīng)IO電平拉高。
LED電路
6.2 拉高/拉低GPIO
如何將GPIO電平拉高拉低?庫函數(shù)提供了封裝好的函數(shù)
// 設(shè)置為高電平
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
// 設(shè)置為低電平
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
6.3 程序設(shè)計
點亮LED很簡單,只需要初始化相應(yīng)的GPIO,輸入模式設(shè)置為推挽式輸出,然后設(shè)定電平即可。
這里給出點亮LED的例程,LED電路為一側(cè)接3.3V,另一側(cè)接GPIO。
這里給出的只是一些必要的函數(shù),如果需要工程模板可私信聯(lián)系。
需要注意的是,初始化GPIO時需要開啟時鐘,開啟時鐘之前需要確認(rèn)GPIO掛載的總線。
/*
*==============================================================================
*函數(shù)名稱:Drv_Led_Init
*函數(shù)功能:初始化LED的GPIO
*輸入參數(shù):無
*返回值:無
*==============================================================================
*/
void Drv_Led_Init (void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定義結(jié)構(gòu)體
// 開啟時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// 配置結(jié)構(gòu)體
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_1); // 熄滅LED
}
/*
*==============================================================================
*函數(shù)名稱:Med_Led_On
*函數(shù)功能:點亮LED
*輸入?yún)?shù):無
*返回值:無
備注:如果有多個LED可以定義一個結(jié)構(gòu)體或者使用swtich,增加一個輸入變量
來確定開啟哪個LED
*==============================================================================
*/
void Med_Led_On (void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1); // 熄滅LED
}
七、GPIO的位帶操作
對于什么是“位帶”這里就不做解釋了,大家可以自行搜索。這里只介紹如何使用。位帶操作可以使操作GPIO變得更加簡單。在模板程序的sys.h文件中已經(jīng)定義好了各個IO的位帶操作所需內(nèi)容
//IO口操作,只對單一的IO口!
//確保n的值小于16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //輸出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //輸入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //輸出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //輸入
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //輸出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //輸入
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //輸出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //輸入
#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //輸出
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //輸入
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //輸出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //輸入
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //輸出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //輸入
有了位帶操作后,就不需要再使用前面介紹的拉高拉低函數(shù)來操作GPIO。比如需要拉d低PA1
PAout(1) = 0; // 拉低PA1
有了位帶操作后也可以對GPIO進(jìn)行宏定義,用自己想要的名字來操作它。還是拿上面的拉低PA1舉例
#define LED PAout(1) // 將PA1宏定義為LED
LED = 1; // 拉高PA1
LED = 0; // 拉低PA1
-
上拉電阻
+關(guān)注
關(guān)注
5文章
360瀏覽量
30660 -
GPIO
+關(guān)注
關(guān)注
16文章
1215瀏覽量
52231 -
調(diào)試器
+關(guān)注
關(guān)注
1文章
306瀏覽量
23784 -
開漏輸出
+關(guān)注
關(guān)注
0文章
34瀏覽量
7338 -
STM32F103ZET6
+關(guān)注
關(guān)注
9文章
67瀏覽量
21167
發(fā)布評論請先 登錄
相關(guān)推薦
評論