一、GPIO簡介
- GPIO(General Purpose Input Output)通用輸入輸出口
- GPIO引腳電平:0V(低電平)~3.3V(高電平),部分引腳可容忍5V
- 容忍5V,即部分引腳輸入5V的電壓,也認為是高電平;引腳定義中有FT(Five Tolerate)即表示可容忍5V;
- 對于輸出而言,最大就只能輸出3.3V,因為供電就只有3.3V;
- 可配置為8種輸入輸出模式;
- 輸出模式下可控制端口輸出高低電平,用以驅動LED、控制蜂鳴器、模擬通信協議輸出時序等;
- 輸入模式下可讀取端口的高低電平或電壓,用于讀取按鍵輸入、外接模塊電平信號輸入、ADC電壓采集、模擬通信協議接收數據等;
二、GPIO基本結構
1.GPIO整體結構
- STM32上,所有的GPIO都是掛載在APB2時鐘總線上;
- GPIO外設的名稱按照GPIOA、B、C等等來命名的;
- 每個GPIO外設有16個引腳,編號0~15;
- 每個GPIO模塊內,主要包含了寄存器和驅動器;
- 寄存器就是一段特殊的存儲器,內核可以通過APB2總線對寄存器進行讀寫,從而實現電平的輸出和讀取功能;
- 寄存器的每一位對應一個引腳;
- 輸出寄存器寫1,對應引腳就會輸出高電平;寫0,就會輸出低電平;
- 輸入寄存器讀取為1,對應引腳目前為高電平;讀取為0,為低電平;
- STM32為32位的單片機,所以STM32內部的寄存器都是32位,但每個GPIO只有16個引腳,所以寄存器只有低16位有對應的引腳,高16位是沒有用到的;
- 驅動器是用來增加信號的驅動能力,寄存器只負責存儲數據;當進行點燈這樣的操作,還是需要驅動器增大驅動能力;
- 寄存器就是一段特殊的存儲器,內核可以通過APB2總線對寄存器進行讀寫,從而實現電平的輸出和讀取功能;
2.GPIO位結構
- 保護二極管:用于限制引腳的輸入電壓;
- 上面保護二極管接VDD(3.3V),下面二極管接VSS(0V);
- 當輸入電壓高于3.3V時,上方二極管導通,輸入電壓產生的電流就會直接流入VDD而不會流入內部電路,避免過高的電壓對內部電路產生傷害;
- 當輸入電壓低于0V時,下方二極管導通,電流會直接從IO引腳流出去,而不會從內部電路汲取電流,從而保護內部電路;
- 當輸入電壓位于0~3.3V之間,上下方二極管均不會導通;
- 上下拉電阻
- 上下拉電阻的作用是給輸入提供一個默認的輸入電平,上下拉電阻均斷開時,引腳處于浮空狀態,此時引腳的輸入電平極易受到外界干擾而改變;
- 上拉電阻連接VDD(3.3V);下方電阻連接VSS(0V);連接開關可通過程序進行配置;
- 上拉電阻打開,下拉電阻斷開,即上拉輸入模式,又稱作默認為高電平的輸入模式;
- 上拉電阻斷開,下拉電阻打開,即下拉輸入模式,又稱作默認為低電平的輸入模式;
- 上拉電阻和下拉電阻均斷開,即浮空輸入模式;
- 上下拉電阻的阻值都是比較大的,是一種弱上拉和弱下拉,目的是盡量不影響正常的輸入操作;
- 施密特觸發器:對輸入電壓進行整形
- 當施密特觸發器的輸入電壓大于某一閾值時,輸出就會瞬間升為高電平;小于某一閾值,輸出就會瞬間降為低電平;
- 例如:引腳的波形為外界輸入,雖然是數字信號,但實際情況下可能產生各種失真,一下面波形舉例:
- 數據選擇器:選擇是輸出數據寄存器還是片上外設(復用功能輸出)的信號輸入到輸出控制;
- 位設置/清除寄存器:用來單獨操作輸出數據寄存器的某一位,而不影響其他位(即單獨操作GPIO中某一引腳的電平高低,輸出寄存器同時控制GPIO的16個端口,且只能整體讀寫 )
- MOS管:MOS管可以理解成電子開關,輸出控制的信號控制開關的導通和關閉,進而控制IO口連接到VDD(3.3V)或VSS(0V);可選擇推挽、開漏、關閉三種輸出模式
- 推挽輸出模式:P-MOS和N-MOS均有效;這種模式下,高低電平均有較強的驅動能力,所以推挽輸出模式也稱為強推輸出模式;在推挽輸出模式下,STM32對IO口具有絕對的控制權,高低電平都有STM32控制;
- 輸出控制輸出1時,P-MOS導通,N-MOS斷開,輸出直接接到VDD(3.3V),輸出高電平;
- 輸出控制輸出0時,P-MOS斷開,N-MOS導通,輸出直接接到VSS(0V),輸出低電平;
- 開漏輸出模式:P-MOS無效,N-MOS有效;這種模式下,只有低電平有驅動能力,高電平沒有驅動能力;開漏模式常用作通信協議的輸出方式,比如I2C通信的引腳;在多機通信的情況下,開漏模式可以避免各個設備的相互干擾;
- 輸出控制輸出1時,P-MOS斷開,N-MOS斷開,輸出相當于斷開,即高阻模式;
- 輸出控制輸出0時,P-MOS斷開,N-MOS導通,輸出直接接到VSS(0V),輸出低電平;
- 開漏模式還可以用于輸出5V的電平信號:在IO口外接一個上拉電阻到5V的電源,輸出低電平時,由內部的N-MOS直接接VSS(0V);輸出高電平時,由外部上拉電阻拉高至5V;這樣就可以輸出5V信號,用于兼容一些5V的電平設備;
- 關閉模式:當引腳配置為輸入模式時,P-MOS和N-MOS均無效,端口的電平由外部信號來控制;
- 推挽輸出模式:P-MOS和N-MOS均有效;這種模式下,高低電平均有較強的驅動能力,所以推挽輸出模式也稱為強推輸出模式;在推挽輸出模式下,STM32對IO口具有絕對的控制權,高低電平都有STM32控制;
三、GPIO的8種工作模式
- 上拉/下拉/浮空輸入配置
- 模擬輸入配置
- 推挽/開漏輸出配置
- 復用推挽/開漏輸出配置
- 在輸出模式下,輸入都是有效的;在輸入模式下,輸出都是無效的;(因為一個端口只能有一個輸出,但可以有多個輸入);
- 在GPIO的8種模式下,除了模擬輸入模式會關閉數字輸入功能,其他7種模式下,數字輸入都是有效的;
四、參考手冊_GPIO部分介紹
-
STM32F103數據手冊和參考手冊 藍奏云下載鏈接,密碼:2nkx
-
端口配置寄存器
- 每一個端口的模式由4位進行配置, 16個端口就需要64位,每個寄存器32位,所以配置寄存器有2個;
- GPIO的輸出速度可以限制輸出引腳的最大翻轉速度,該設計是為了低功耗和穩定性,一般要求不高時,配置成50MHz即可;
-
端口輸入寄存器
- 低16位對應16個引腳,高16位沒有使用;
-
端口輸出寄存器
- 低16位對應16個引腳,高16位沒有使用;
-
端口位設置/清除寄存器
- 高16位用于位清除,低16位用于位設置; 寫1用于設置或清除,寫0不產生影響;
-
端口位清除寄存器
- 低16位效果和端口位設置/清除寄存器的高16位功能一樣;
- 當只需要單一的進行位設置或位清除,位設置時,用端口位設置/清除寄存器;位清除時,用端口位清除寄存器;(此時進行位設置和位清除時,使用的都是低16位的數據,比較方便);
- 當需要對多個端口同時進行位設置和位清除,可以使用端口設置/清除寄存器,這樣可以保證位設置和位清除的同步性;
五、GPIO輸出實驗_外圍設備介紹
1.LED和蜂鳴器
(1)LED和蜂鳴器簡介
- LED,發光二極管,正向通電點亮,反向通電不亮;
- 蜂鳴器:分為有源蜂鳴器和無源蜂鳴器
-
有源蜂鳴器:內部自帶震蕩源,正負極接上直流電壓即可持續發聲,頻率固定;
-
無源蜂鳴器:內部不帶震蕩源,需要控制器提供震蕩脈沖才可發聲;調整提供的震蕩脈沖的頻率,可發出不同頻率的聲音;
-
(2)LED和蜂鳴器硬件電路
- STM32的GPIO在推挽輸出模式下,高低電平具有比較強的驅動能力,選用高電平驅動或者低電平驅動均可;
- 單片機電路種,一般傾向于低電平驅動,因為很多單片機或芯片,都是用了高電平弱驅動,低電平強驅動的規則(這樣可以一定程度上避免高低電平沖突);
2.面包板
六、GPIO輸出實驗
1.LED閃爍
-
接線圖
-
操作STM32的GPIO外設一共需要3個步驟:
- 使用RCC(Reset Clock Control,復位時鐘控制),開啟GPIO的時鐘;
- 使用GPIO_Init()函數初始化GPIO;
- 使用輸出或輸入函數控制GPIO口;
-
RCC外設常用的3個庫函數
-
GPIO常用的庫函數:GPIO_Init、GPIO的8個讀寫函數
- GPIO的4個輸出函數
- GPIO的4個輸出函數
-
Delay延時函數
- Delay.c
#include "stm32f10x.h"/*** @brief 微秒級延時* @param xus 延時時長,范圍:0~233015* @retval 無*/ void Delay_us(uint32_t xus) {SysTick->LOAD = 72 * xus; //設置定時器重裝值SysTick->VAL = 0x00; //清空當前計數值SysTick->CTRL = 0x00000005; //設置時鐘源為HCLK,啟動定時器while(!(SysTick->CTRL & 0x00010000)); //等待計數到0SysTick->CTRL = 0x00000004; //關閉定時器 }/*** @brief 毫秒級延時* @param xms 延時時長,范圍:0~4294967295* @retval 無*/ void Delay_ms(uint32_t xms) {while(xms--){Delay_us(1000);} }/*** @brief 秒級延時* @param xs 延時時長,范圍:0~4294967295* @retval 無*/ void Delay_s(uint32_t xs) {while(xs--){Delay_ms(1000);} }
- Delay.h
#ifndef __DELAY_H #define __DELAY_Hvoid Delay_us(uint32_t us); void Delay_ms(uint32_t ms); void Delay_s(uint32_t s);#endif
-
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"int main(void)
{//1.打開GPIOA時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //2.配置PA0為推挽輸出GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);while(1){GPIO_ResetBits(GPIOA, GPIO_Pin_0); // 打開LEDDelay_ms(500);GPIO_SetBits(GPIOA, GPIO_Pin_0); // 關閉LEDDelay_ms(500);}}
- LED閃爍實驗,工程下載地址,密碼:gso9
2.LED流水燈
- 接線圖
- main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"int main(void)
{//1.打開GPIOA時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //2.配置PA0-PA7為推挽輸出GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);while(1){GPIO_Write(GPIOA, ~0x0001);Delay_ms(500);GPIO_Write(GPIOA, ~0x0002);Delay_ms(500);GPIO_Write(GPIOA, ~0x0004);Delay_ms(500);GPIO_Write(GPIOA, ~0x0008);Delay_ms(500);GPIO_Write(GPIOA, ~0x0010);Delay_ms(500);GPIO_Write(GPIOA, ~0x0020);Delay_ms(500);GPIO_Write(GPIOA, ~0x0040);Delay_ms(500);GPIO_Write(GPIOA, ~0x0080);Delay_ms(500);}}
- LED流水燈實驗,工程下載地址,密碼:1vmb
3.蜂鳴器
- 接線圖
- main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"int main(void)
{//1.打開GPIOB時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //2.配置PA0-PA7為推挽輸出GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);while(1){GPIO_ResetBits(GPIOB, GPIO_Pin_12);Delay_ms(100);GPIO_SetBits(GPIOB, GPIO_Pin_12);Delay_ms(100);GPIO_ResetBits(GPIOB, GPIO_Pin_12);Delay_ms(100);GPIO_SetBits(GPIOB, GPIO_Pin_12);Delay_ms(700);}}
- 蜂鳴器實驗,工程下載地址,密碼:a1zp
4.庫函數的使用方法
- 打開對應外設庫函數種的.h文件,查看有哪些函數------>轉到對應函數的定義,查看函數功能和參數的用法;
- 庫函數使用手冊(老版本,部分用法有出入,整體差異不大)下載鏈接
- 網上搜索,參考別人的代碼;
七、GPIO輸入實驗_外圍設備介紹
1.按鍵
- 常用的輸入設備,按下導通,松開斷開;
- 按鍵抖動:按鍵內部使用的是機械式彈簧片來進行通斷,在按下和松手的瞬間會伴有一連串的抖動;
- 按鍵的抖動時間比較短,通常在5~10ms,人眼是分辨不出來的,但是對于高速運行的單片機而言,5 ~ 10ms還是很漫長的,所以需要對抖動進行過濾,即按鍵消抖;
- 最簡單的過濾辦法就是加一段延時,把抖動時間耗過去;
- 按鍵的電路
2.傳感器模塊
- 課程中有4個傳感器模塊:光敏電阻傳感器、熱敏電阻傳感器、對射式紅外傳感器、反射式紅外傳感器;
- 這些傳感器模塊都是利用傳感器元件(光敏電阻/熱敏電阻/紅外接收管)的電阻會隨著外界的模擬量變化而變化(光線越強,光敏電阻阻值越小;溫度越高,熱敏電阻阻值越小;紅外光線越強,紅外接收管的阻值越小;)
- 電阻的變化不容易采集到,通常將傳感器元件與定值電阻進行串聯分壓,這樣就可以得到模擬電壓的輸出。對于電路來說,檢測電壓就比較容易;
- 還可以通過電壓比較器,對輸出的模擬電壓進行二值化,這樣就可以得到數字電壓輸出;
- 傳感器的電路
八、GPIO輸入實驗
1. 按鍵控制LED
- 接線圖
- 讀取引腳和端口的庫函數
- LED.c
#include "stm32f10x.h" // Device headervoid LED_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2);
}void LED1_ON(void)
{GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}void LED1_OFF(void)
{GPIO_SetBits(GPIOA, GPIO_Pin_1);
}void LED1_Turn(void)
{if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1) == 0){GPIO_SetBits(GPIOA, GPIO_Pin_1);}else{GPIO_ResetBits(GPIOA, GPIO_Pin_1);}
}void LED2_ON(void)
{GPIO_ResetBits(GPIOA, GPIO_Pin_2);
}void LED2_OFF(void)
{GPIO_SetBits(GPIOA, GPIO_Pin_2);
}void LED2_Turn(void)
{if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_2) == 0){GPIO_SetBits(GPIOA, GPIO_Pin_2);}else{GPIO_ResetBits(GPIOA, GPIO_Pin_2);}
}
- LED.h
#ifndef __LED_H
#define __LED_Hvoid LED_Init(void);void LED1_ON(void);
void LED1_OFF(void);
void LED1_Turn(void);
void LED2_ON(void);
void LED2_OFF(void);
void LED2_Turn(void);#endif
- Key.c
#include "stm32f10x.h" // Device header
#include "Delay.h"void Key_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);
}uint8_t Ket_GetNum(void)
{uint8_t KeyNum = 0;if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) // 按鍵1按下{ Delay_ms(20); // 消抖while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0); // 等待抬起Delay_ms(20); // 消抖KeyNum = 1; // 按鍵1按下后松開}if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0) // 按鍵2按下{ Delay_ms(20); // 消抖while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0); // 等待抬起Delay_ms(20); // 消抖KeyNum = 2; // 按鍵2按下后松開}return KeyNum;
}
- Key.h
#ifndef __KEY_H
#define __KEY_H
#include "stm32f10x.h" // Device headervoid Key_Init(void);
uint8_t Ket_GetNum(void); #endif
- 按鍵控制LED實驗,工程下載地址,密碼:2pzm
2.光敏傳感器控制蜂鳴器
- 接線圖
- Buzzer.c
#include "stm32f10x.h" // Device headervoid Buzzer_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_SetBits(GPIOB, GPIO_Pin_12);
}void Buzzer_ON(void)
{GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}void Buzzer_OFF(void)
{GPIO_SetBits(GPIOB, GPIO_Pin_12);
}void Buzzer_Turn(void)
{if(GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_12) == 0){GPIO_SetBits(GPIOB, GPIO_Pin_12);}else{GPIO_ResetBits(GPIOB, GPIO_Pin_12);}
}
- Buzzer.h
#ifndef __BUZZER_H
#define __BUZZER_Hvoid Buzzer_Init(void);
void Buzzer_ON(void);
void Buzzer_OFF(void);
void Buzzer_Turn(void);#endif
- LightSensor.c
#include "stm32f10x.h" // Device headervoid LightSeneorInit(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 光敏傳感器,光照強時,輸出低電平,輸出指示燈亮;光照弱時,輸出高電平,輸出指示燈滅;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_SetBits(GPIOB, GPIO_Pin_13);
}uint8_t LightSensor_Get(void)
{return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13);
}
- LightSensor.h
#ifndef __LightSensor_H
#define __LightSensor_H
#include "stm32f10x.h" // Device headervoid LightSeneorInit(void);
uint8_t LightSensor_Get(void);#endif
- main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Buzzer.h"
#include "LightSensor.h"int main(void)
{Buzzer_Init();LightSeneorInit();while(1){if(LightSensor_Get() == 1){Buzzer_ON();}else{Buzzer_OFF();}}}
- 光敏傳感器控制蜂鳴器,工程下載地址,密碼:bh7h