1 GPIO簡介
GPIO是通用輸入輸出埠的簡稱,簡單來說就是STM32可控制的引腳,STM32芯片的GPIO引腳與外部設備連接起來,從而實作與外部通訊、控制以及資料采集的功能,STM32芯片的GPIO被分成很多組,每組有16個引腳,所有的GPIO引腳都有基本的輸入輸出功能,上電復位后,GPIO默認為浮空狀態,部分特殊功能引腳為特定狀態,
最基本的輸出功能是由STM32控制引腳輸出高、低電平,實作開關控制,如把GPIO引腳接入LED燈,那就可以控制LED燈的亮滅,引腳接入到繼電器或三極管,那就可以通過繼電器或三極管控制外部大功率電路的通斷,最基本的輸入功能是檢測外部電平,如把GPIO引腳連接到按鍵,通過電平高低區分按鍵是否被按下,
STM32的 GPIO模式有以下幾種:
GPIO_Mode_AIN ---------------------- 模擬輸入
GPIO_Mode_IN_FLOATING -------- 輸入浮空
GPIO_Mode_IPD ---------------------- 輸入下拉
GPIO_Mode_IPU ---------------------- 輸入上拉
GPIO_Mode_Out_OD ---------------- 開漏輸出
GPIO_Mode_Out_PP ---------------- 推挽式輸出
GPIO_Mode_AF_OD ---------------- 開漏復用功能
GPIO_Mode_AF_PP ----------------- 推挽式復用功能
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,
GPIO_Mode_AF_PP = 0x18
} GPIOMode_TypeDef;

保護二極管及上、下拉電阻:
引腳的兩個保護二極管可以防止引腳外部過高或過低的電壓輸入,當引腳電壓高于時,上方的二極管導通,當引腳電壓低于Vss時,下方的二極管導通,防止不正常電壓引入芯片導致芯片啥訓,但是盡管如此,還是不能直接外接大功率器件,須加大功率及隔離電路驅動,防止啥訓芯片或者外接器件無法正常作業,

上下拉電阻,從它的結構我們可以看出,通過上、下拉電阻的開關配置,我們可以控制引腳默認狀態的電壓,開啟上拉的時候引腳電壓為高電平,開啟下拉的時候引腳電壓為低電平,也可以設定“既不上拉也不下拉模式”,我們也把這種狀態稱為浮空模式,配置成這個模式時,直接用電壓表測量其引腳電壓為1點幾伏,這是個不確定的值,所以一般來說我們都會選擇給引腳設定“上拉模式”或“下拉模式”使它有默認狀態,STM32的內部上拉時“弱上拉”,即通過上拉輸出的電流時很弱的,如要求大電流還是需要外部上拉,通過“上拉/下拉暫存器GPIOx_CRL和GPIOx_CRH”控制引腳的上、下拉及浮空模式,
TTL施密特觸發器:
基本原理是當輸入電壓高于正向閾值電壓,輸出為高;當輸入電壓低于負向閾值電壓,輸出為低;信號經過觸發器后,模擬信號轉化為0和1的數字信號,但是,當GPIO引腳作為ADC采集電壓的輸入通道時,用其“模擬輸入”功能,此時信號不再經過觸發器進行TTL電平轉換,ADC外設要采集到的原始的模擬信號,IO口信號經過觸發器后,模擬信號轉化為0和1的數字信號 也就是高低電平 并且是TTL電平協議 這也是為什么STM32是TTL電平協議的原因,
P-MOS管和N-MOS管:
GPIO引腳線路經過兩個保護二極管后,向上流向“輸入模式”結構,向下流向“輸出模式”結構,先看輸出模式部分,線路經過一個由P-MOS和N-MOS管組成的單元電路,這個結構使GPIO具有了“推挽輸出”和“開漏輸出”兩種模式,
所謂的推挽輸出模式,是根據這兩個MOS管的作業方式來命名的,在該結構中輸入高電平時,經過反向后,上方的P-MOS導通,下方的N-MOS關閉,對外輸出高電平;而在該結構中輸入低電平時,經過反向后,N-MOS導通,P-MOS關閉,對外輸出低電平,當引腳高低電平切換時,兩個管子輪流導通,P管負責灌電流,N管負責拉電流,使其負載能力和開關速度都比普通的方式由很大的提高,推挽輸出的低電平為0伏,高電平為3.3V,如下圖,它是推挽輸出模式時的等效電路,

而在開漏輸出模式時,上方的P-MOS完全不作業,如果我們控制輸出為0,低電平,則P-MOS管完全關閉,N-MOS管導通,使輸出接地,若控制輸出為1(它無法直接輸出高電平)時,則P-MOS和N-MOS都關閉,所以引腳既不輸出高電平也不輸出低電平,為高阻態,為了正常使用時必須外部接上拉電阻,參考下圖中的等效電路,它具有“線與”特性,也就是說,若有很多個開漏模式引腳連接到一起時,只有當所有引腳都輸出高阻態,才由上拉電阻提供高電平,此電平的電壓為外部上拉電阻所接的電源的電壓,若其中一個引腳為低電平,那線路就相當于短路接地,使得整條線路都為低電平,0伏,

推挽輸出模式一般應用在輸出電平0和3.3伏而且需要高速切換開關狀態的場合,在STM32的應用中,除了必須用開漏模式的場合,我們都習慣使用推挽輸出模式,開漏輸出一般應用在I2C、SMBUS通訊等需要“線與”功能的總線電路中,除此之外,還用在電平不匹配的場合,如需要輸出5伏的高電平,就可以在外部接一個上拉電阻,上拉電源為5伏,并且把GPIO設定為開漏模式,當輸出高阻態時,由上拉電阻和電源向外輸出5伏電平,如下圖所示,

這里需要注意的是,在查看《STM32中文參考手冊V10》中的GPIO的表格時,會看到有“FT”一列,這代表著這個GPIO口時兼容3.3V和5V的;如果沒有標注“FT”,就代表著不兼容5V,
2 GPIO設定模式
根據上面的幾種模式,可以大致分為 4種模式:輸入、輸出、復用、模擬,先來了解一下M3和M4 I/O口總的功能結構:


2.1 輸入配置
當I/O埠配置為輸入時:
● 輸出緩沖器被禁止
● 施密特觸發輸入被激活
● 根據輸入配置(上拉,下拉或浮動)的不同,弱上拉和下拉電阻被連接
● 出現在I/O腳上的資料在每個APB2時鐘被采樣到輸入資料暫存器
● 對輸入資料暫存器的讀訪問可得到I/O狀態
下圖給出了I/O埠位的輸入配置:

浮空輸入模式:GPIO_Mode_IN_FLOATING
浮空輸入模式下,I/O埠的電平信號直接進入輸入資料暫存器,電平進入后,不經過上下拉,在觸發施密特觸發器后,進入輸入資料暫存器,最后由CPU讀取,MCU直接讀取I/O口電平,I/O的電平狀態是不確定的,完全由外部輸入決定;如果在該引腳懸空(在無信號輸入)的情況下,讀取該埠的電平是不確定的, (接用電壓表測量其引腳電壓為1點幾伏,這是個不確定值) 以用來做KEY識別,

輸入上拉模式:GPIO_Mode_IPU
IO內部接上拉電阻,此時如果IO口外部沒有信號輸入或者引腳懸空,IO口默認為高電平,如果I/O口輸入低電平,那么引腳就為低電平,MCU讀取到的就是低電平,STM32的內部上拉是"弱上拉",即通過此上拉輸出的電流是很弱的,如要求大電流還是需要外部上拉, 觸發時需要低電平觸發,有上拉電阻存在,使得埠為高電平,達到抗干擾作用,只接受低電平!

輸入下拉模式:GPIO_Mode_IPD
IO內部接下拉電阻,此時如果IO口外部沒有信號輸入或者引腳懸空,IO口默認為低電平,如果I/O口輸入高電平,那么引腳就為高電平,MCU讀取到的就是高電平,需要高電平觸發,有下拉電阻存在,使得埠為低電平,達到抗干擾作用,只接受高電平!

GPIO輸入模式總結:
在輸入模式時,施密特觸發器打開,輸出被禁止,資料暫存器每隔1個AHB1時鐘周期更新一次,可通過資料暫存器GPIOx_IDR讀取I/O狀態,其中F1的AHB1的時鐘如按默認配置一般為72MHz,F4的AHB1的時鐘如按默認配置一般為180MHz,
2.2 輸出配置
當I/O埠被配置為輸出時:
● 輸出緩沖器被激活
─ 開漏模式:輸出暫存器上的’0’激活N-MOS,而輸出暫存器上的’1’將埠置于高阻狀態(P-MOS從不被激活),
─ 推挽模式:輸出暫存器上的’0’激活N-MOS,而輸出暫存器上的’1’將激活P-MOS,
● 施密特觸發輸入被激活
● 弱上拉和下拉電阻被禁止
● 出現在I/O腳上的資料在每個APB2時鐘被采樣到輸入資料暫存器
● 在開漏模式時,對輸入資料暫存器的讀訪問可得到I/O狀態
● 在推挽式模式時,對輸出資料暫存器的讀訪問得到最后一次寫的值,
下圖給出了I/O埠位的輸出配置:

開漏輸出模式:GPIO_Mode_Out_OD
CPU先寫入輸出資料暫存器中,再經過輸出控制電路,最終到達埠(在此模式下,IO口也可以讀取IO口電壓),在開漏輸出模式時,只有N-MOS管作業,如果我們控制輸出為0,低電平,則P-MOS管關閉,N-MOS管導通,使輸出低電平,I/O埠的電平就是低電平,若控制輸出為1時,高電平,則P-MOS管和N-MOS管都關閉,輸出指令就不會起到作用,此時I/O埠的電平就不會由輸出的高電平決定,而是由I/O埠外部的上拉或者下拉決定 如果沒有上拉或者下拉 IO口就處于懸空狀態,并且此時施密特觸發器是打開的,即輸入可用,通過輸入資料暫存器GPIOx_IDR可讀取I/O的實際狀態,,I/O口的電平不一定是輸出的電平,
優點:
1)輸出端相當于三極管的集電極,要得到高電平需要上拉電阻才行,適合做電流型的驅動,其吸收電流的能力較強(20ma以內)
2)開漏是用來連接不同電平的器件,匹配電平用的,因為開漏引腳不連接外部的上拉電阻時,只能輸出低電平,如果需要同時具備輸出高電平的功能,則需要接上拉電阻,很好的一個優點是通過改變上拉電源的電壓,便可以改變傳輸電平,比如加上上拉電阻就可以提供TTL/CMOS電平輸出等,但其也帶來上升沿的延時!
3)可以將多個開漏輸出的Pin,連接到一條線上,通過一只上拉電阻,在不增加任何器件的情況下,形成“與邏輯”關系,這也是I2C,SMBus等總線判斷總線占用狀態的原理,
(補充:什么是“線與”?: 在一個結點(線)上, 連接一個上拉電阻到電源 VCC 或 VDD 和 n 個 NPN 或 NMOS 晶體管的集電極 C 或漏極 D, 這些晶體管的發射極 E 或源極 S 都接到地線上, 只要有一個晶體管飽和, 這個結點(線)就被拉到地線電平上. 因為這些晶體管的基極注入電流(NPN)或柵極加上高電平(NMOS),晶體管就會飽和, 所以這些基極或柵極對這個結點(線)的關系是或非NOR 邏輯. 如果這個結點后面加一個反相器, 就是或 OR 邏輯.)

推挽輸出模式:GPIO_Mode_Out_PP
CPU先寫入輸出資料暫存器中,再經過輸出控制電路,最終到達埠(在此模式下,IO口也可以讀取IO口電壓),與開漏輸出在于其在經過輸出控制電路后面的MOS管不同!在推挽輸出模式時,N-MOS管和P-MOS管都作業,如果我們控制輸出為0,低電平,則P-MOS管關閉,N-MOS管導通,使輸出低電平,I/O埠的電平就是低電平,若控制輸出為1 高電平,則P-MOS管導通N-MOS管關閉,使輸出高電平,I/O埠的電平就是高電平, 外部上拉和下拉的作用是控制在沒有輸出時IO口電平,此時施密特觸發器是打開的,即輸入可用,通過輸入資料暫存器GPIOx_IDR可讀取I/O的實際狀態,I/O口的電平一定是輸出的電平,
優點:
推挽電路是兩個引數相同的三極管或MOSFET,以推挽方式存在于電路中,各負責正負半周的波形放大任務,電路作業時,兩只對稱的功率開關管每次只有一個導通,所以導通損耗小、效率高,輸出既可以向負載灌電流,也可以從負載抽取電流,推拉式輸出級既提高電路的負載能力,又提高開關速度,

GPIO輸出模式總結:
在輸出模式中,輸出使能,推挽模式時以雙MOS管的方式作業,輸出資料暫存器GPIOx_ODR可控制I/O輸出高低點評,開漏模式時,只有N-MOS作業,輸出資料暫存器可控制I/O輸出高阻態或低電平,輸出速度可配置,有2MHz\25MHz\50MHz的選項,此處的輸出速度即I/O支持的高低電平狀態最高切換頻率,支持的頻率越高,功耗越大,如果功耗要求不嚴格,把速度設定成最大即可,
此時施密特觸發器時打開的,即輸入可用,通過輸入資料暫存器GPIOx_IDR可讀取I/O的實際狀態,
用于輸出模式時,可使用上拉、下拉或懸空模式,但此時由于輸出模式時引腳電平會收到ODR暫存器的影響,而ODR暫存器對應引腳的位為0,即引腳初始化后默認輸出低電平,所以在這種情況下,上拉只能起到小幅提高輸出電流能力,但不會影響引腳的默認狀態,
2.3 復用功能配置
當I/O埠被配置為復用功能時:
● 在開漏或推挽式配置中,輸出緩沖器被打開
● 內置外設的信號驅動輸出緩沖器(復用功能輸出)
● 施密特觸發輸入被激活
● 弱上拉和下拉電阻被禁止
● 在每個APB2時鐘周期,出現在I/O腳上的資料被采樣到輸入資料暫存器
● 開漏模式時,讀輸入資料暫存器時可得到I/O口狀態
● 在推挽模式時,讀輸出資料暫存器時可得到最后一次寫的值
下圖示出了I/O埠位的復用功能配置,一組復用功能I/O暫存器允許用戶把一些復用功能重新映象到不同的引腳,

開漏復用輸出模式:GPIO_Mode_AF_OD
復用與不是復用的區別在于———非復用輸出是cpu控制,復用功能則是從復用功能輸出口上接受輸出信號,可以是從外設接受輸入信號輸出,GPIO復用為其他外設,輸出資料暫存器GPIOx_ODR無效; 輸出的高低電平的來源于其它外設,施密特觸發器打開,輸入可用,通過輸入資料暫存器可獲取I/O實際狀態 除了輸出信號的來源改變 其他與開漏輸出功能相同,

推挽復用輸出模式:GPIO_Mode_AF_PP
復用功能與之前類似,輸出信號來源來自復用功能輸出口,與開漏復用輸出在于其在經過輸出控制電路后面的MOS管不同!

GPIO復用功能總結:
復用功能模式中,輸出使能,輸出速度可配置,可作業在開漏及推挽模式,但是輸出信號源于其它外設,輸出資料暫存器GPIOx_ODR無效;輸入可用,通過輸入資料暫存器可獲取I/O實際狀態,但一般直接用外設的暫存器來獲取該資料信號,
用于復用功能時,可使用上拉、下拉或者浮空模式,同輸出模式,在這種情況下,初始化后引腳默認輸出低電平,上拉只起到小幅提高輸出電流能力,但不會影響引腳的默認狀態,
2.4 模擬輸入配置
當I/O埠被配置為模擬輸入配置時:
● 輸出緩沖器被禁止
● 禁止施密特觸發輸入,實作了每個模擬I/O引腳上的零消耗,施密特觸發輸出值被強置為’0’
● 弱上拉和下拉電阻被禁止
● 讀取輸入資料暫存器時數值為’0’
下圖示出了I/O埠位的高阻抗模擬輸入配置:

模擬輸入通道:GPIO_Mode_AIN
當GPIO用于模擬功能時,引腳的上、下拉電阻是不起作用的,輸入沒有上拉下拉電阻,這個時候即使配置了上拉或下拉模式,也不會影響到模擬信號的輸入輸出,且輸入的是電壓而非電平(電平只有高低之分,電壓則是一個連續值),該輸入方式可用于AD轉換接受模擬信號,當GPIO引腳用于ADC采集電壓的輸入通道時,用作"模擬輸入"功能,此時信號不經過施密特觸發器,直接直接進入ADC模塊,并且輸入資料暫存器為空 ,CPU不能在輸入資料暫存器上讀到引腳狀態,
除了 ADC 和 DAC 要將 IO 配置為模擬通道之外其他外設功能一律 要配置為復用功能模式,

GPIO模擬輸入功能總結:
模擬輸入輸出模式中,雙MOS管結構被關閉,施密特觸發器停用,上/下拉也被禁止,其他外設通過模擬通道進行輸入輸出,
IO口總結:
在STM32中選用IO模式總結:
(1) 浮空輸入_IN_FLOATING ——浮空輸入,可以做KEY識別
(2)帶上拉輸入_IPU——IO內部上拉電阻輸入
(3)帶下拉輸入_IPD—— IO內部下拉電阻輸入
(4) 模擬輸入_AIN ——應用ADC模擬輸入,或者低功耗下省電
(5)開漏輸出_OUT_OD ——IO輸出0接GND,IO輸出1,懸空,需要外接上拉電阻,才能實作輸出高電平,當輸出為1時,IO口的狀態由上拉電阻拉高電平,但由于是開漏輸出模式,這樣IO口也就可以由外部電路改變為低電平或不變,可以讀IO輸入電平變化,實作STM32的IO雙向功能
(6)推挽輸出_OUT_PP ——IO輸出0-接GND, IO輸出1 -接VCC,讀輸入值是未知的
(7)復用功能的推挽輸出_AF_PP ——片內外設功能(I2C的SCL,SDA)
(8)復用功能的開漏輸出_AF_OD——片內外設功能(TX1,MOSI,MISO.SCK.SS)
正點原子對常用輸入輸出方式的作用的總結:
(1)作為普通GPIO 輸入:根據需要配置該引腳為浮空輸入、帶弱上拉輸入或帶弱下拉輸入,同時不要使能該引腳對應的所有復用功能模塊,
(2)作為普通GPIO 輸出:根據需要配置該引腳為推挽輸出或開漏輸出,同時不要使能該引腳對應的所有復用功能模塊,
(3)作為普通模擬輸入:配置該引腳為模擬輸入模式,同時不要使能該引腳對應的所有復用功能模塊,
(4)作為內置外設的輸入:根據需要配置該引腳為浮空輸入、帶弱上拉輸入或帶弱下拉輸入,同時使能該引腳對應的某個復用功能模塊,
(5)作為內置外設的輸出:根據需要配置該引腳為復用推挽輸出或復用開漏輸出,同時使能該引腳對應的所有復用功能模塊,
3 外設 I/O配置模式選擇
高級定時器TIM1/TIM8:

通用定時器TIM2/3/4/5:

USART:

SPI:

I2S:

I2C介面:

BxCAN:

USB:

全速USB OTG引腳配置:

SDIO:

ADC/DAC:

FSMC:

其它I/O功能:

4 F1暫存器配置
埠配置低暫存器(GPIOx_CRL) (x=A..E)

埠配置高暫存器(GPIOx_CRH) (x=A..E)

埠輸入資料暫存器(GPIOx_IDR) (x=A..E)
看GPIO結構框圖的上半部分,它時GPIO引腳經過上、下拉電阻后引入的,它連接到施密特觸發器,信號經過觸發器后,模擬信號轉化為0、1的數字信號,然后存盤再“輸出資料暫存器GPIOx_IDR”中,通過讀取該暫存器就可以了解GPIO引腳的電平狀態,

埠輸出資料暫存器(GPIOx_ODR) (x=A..E)
前面提到的雙MOS管結構電路的輸入信號,是由GPIO“埠輸出資料暫存器“GPIOx_ODR”提供的,因此我們通過修改輸出資料暫存器的值就可以修改GPIO引腳的輸出電平,

埠位設定/清除暫存器(GPIOx_BSRR) (x=A..E)
”埠位設定/清除暫存器GPIOx_BSRR“可以通過修改輸出資料暫存器的值從而影響電路的輸出,

埠位清除暫存器(GPIOx_BRR) (x=A..E)
”埠位清除暫存器GPIOx_BRR“可以通過修改輸出資料暫存器的值從而影響電路的輸出,

埠配置鎖定暫存器(GPIOx_LCKR) (x=A..E)

5 F4暫存器配置
埠模式暫存器(GPIOx_MODER)

埠輸出型別暫存器(GPIOx_OTYPER)

埠輸出速度暫存器(GPIOx_OSPEEDR)

埠上拉/下拉暫存器(GPIOx_PUPDR)

埠輸入資料暫存器(GPIOx_IDR)

埠輸出資料暫存器(GPIOx_ODR)

埠置位/復位暫存器(GPIOx_BSRR)

埠配置鎖存暫存器(GPIOx_LCKR )

復位功能暫存器(低位GPIOx_AFRL & GPIOx_AFRH)


6 GPIO的初始化(F1)
我們以初始化LED為例
6.1 定義一個 GPIO_InitTypeDef 型別的結構體
GPIO_InitTypeDef GPIO_InitStructure; /*定義一個 GPIO_InitTypeDef 型別的結構體*/
一共有3個引數
typedef struct
{
uint16_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured.
This parameter can be any value of @ref GPIO_pins_define */
GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.
This parameter can be a value of @ref GPIOSpeed_TypeDef */
GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.
This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;
6.2 開啟 LED 相關的 GPIO 外設時鐘
RCC_AHB1PeriphClockCmd ( RCC_AHB1Periph_GPIOB, ENABLE); /*開啟 AHB1時鐘*/
相對應的外設功能所使用的時鐘在stm32f10x_rcc.h 中即可查看到,下面是部分時鐘:
//APB2_peripheral
#define RCC_APB2Periph_AFIO ((uint32_t)0x00000001)
#define RCC_APB2Periph_GPIOA ((uint32_t)0x00000004)
#define RCC_APB2Periph_GPIOB ((uint32_t)0x00000008)
#define RCC_APB2Periph_GPIOC ((uint32_t)0x00000010)
#define RCC_APB2Periph_GPIOD ((uint32_t)0x00000020)
#define RCC_APB2Periph_GPIOE ((uint32_t)0x00000040)
#define RCC_APB2Periph_GPIOF ((uint32_t)0x00000080)
#define RCC_APB2Periph_GPIOG ((uint32_t)0x00000100)
#define RCC_APB2Periph_ADC1 ((uint32_t)0x00000200)
#define RCC_APB2Periph_ADC2 ((uint32_t)0x00000400)
#define RCC_APB2Periph_TIM1 ((uint32_t)0x00000800)
#define RCC_APB2Periph_SPI1 ((uint32_t)0x00001000)
#define RCC_APB2Periph_TIM8 ((uint32_t)0x00002000)
#define RCC_APB2Periph_USART1 ((uint32_t)0x00004000)
#define RCC_APB2Periph_ADC3 ((uint32_t)0x00008000)
#define RCC_APB2Periph_TIM15 ((uint32_t)0x00010000)
#define RCC_APB2Periph_TIM16 ((uint32_t)0x00020000)
#define RCC_APB2Periph_TIM17 ((uint32_t)0x00040000)
#define RCC_APB2Periph_TIM9 ((uint32_t)0x00080000)
#define RCC_APB2Periph_TIM10 ((uint32_t)0x00100000)
#define RCC_APB2Periph_TIM11 ((uint32_t)0x00200000)
6.3 選擇要控制的 GPIO 引腳
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 ; /*選擇Pin9引腳*/
可選引腳為0-15 一組IO口有16個引腳
#define GPIO_Pin_0 ((uint16_t)0x0001) /* Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /* Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /* Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /* Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /* Pin 4 selected */
#define GPIO_Pin_5 ((uint16_t)0x0020) /* Pin 5 selected */
#define GPIO_Pin_6 ((uint16_t)0x0040) /* Pin 6 selected */
#define GPIO_Pin_7 ((uint16_t)0x0080) /* Pin 7 selected */
#define GPIO_Pin_8 ((uint16_t)0x0100) /* Pin 8 selected */
#define GPIO_Pin_9 ((uint16_t)0x0200) /* Pin 9 selected */
#define GPIO_Pin_10 ((uint16_t)0x0400) /* Pin 10 selected */
#define GPIO_Pin_11 ((uint16_t)0x0800) /* Pin 11 selected */
#define GPIO_Pin_12 ((uint16_t)0x1000) /* Pin 12 selected */
#define GPIO_Pin_13 ((uint16_t)0x2000) /* Pin 13 selected */
#define GPIO_Pin_14 ((uint16_t)0x4000) /* Pin 14 selected */
#define GPIO_Pin_15 ((uint16_t)0x8000) /* Pin 15 selected */
6.4 設定所選引腳的模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*設定為推挽輸出模式*/
引腳的模式共有八種,分別為模擬輸入,輸入浮空,輸入下拉,輸入上拉,開漏輸出,推挽式輸出,開漏復用功能,推挽式復用功能
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,
GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;
6.5 設定所選管腳的速度
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*設定速度為50MHz 高速模式*/
typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
6.6 初始化GPIO
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化所設定的引腳
GPIO_Init() 是官方配置的初始化函式 第一個引數是GPIOX 第二個引數是結構體所對應GPIO各種引數的配置
7 GPIO的初始化(F4)
我們以初始化LED為例
7.1 定義一個 GPIO_InitTypeDef 型別的結構體
GPIO_InitTypeDef GPIO_InitStructure; /*定義一個 GPIO_InitTypeDef 型別的結構體*/
一共有5個引數
typedef struct
{
uint32_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured.
This parameter can be any value of @ref GPIO_pins_define */
GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.
This parameter can be a value of @ref GPIOMode_TypeDef */
GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.
This parameter can be a value of @ref GPIOSpeed_TypeDef */
GPIOOType_TypeDef GPIO_OType; /*!< Specifies the operating output type for the selected pins.
This parameter can be a value of @ref GPIOOType_TypeDef */
GPIOPuPd_TypeDef GPIO_PuPd; /*!< Specifies the operating Pull-up/Pull down for the selected pins.
This parameter can be a value of @ref GPIOPuPd_TypeDef */
}GPIO_InitTypeDef;
7.2 開啟 LED 相關的 GPIO 外設時鐘
RCC_AHB1PeriphClockCmd ( RCC_AHB1Periph_GPIOB, ENABLE); /*開啟 AHB1時鐘*/
Q:為什么要設定時鐘?
任何外設都需要時鐘,51單片機,stm32,430等等,因為暫存器是由D觸發器組成的,往觸發器里面寫東西,前提條件是有時鐘輸入,stm32是低功耗,他將所有的門都默認設定為disable(不使能),在你需要用哪個門的時候,開哪個門就可以,也就是說用到什么外設,只要打開對應外設的時鐘就可以, 其他的沒用到的可以還是disable(不使能),這樣耗能就會減少,
Q:為什么 STM32 要有多個時鐘源呢?
因為首先STM32本身非常復雜,外設非常的多,但是并不是所有外設都需要系統時鐘這么高的頻率, 比如看門狗以及 RTC 只需要幾十k的時鐘即可,同一個電路,時鐘越快功耗越大,同時抗電磁干擾能力也會越弱,所以對于較為復雜的 MCU 一般都是采取多時鐘源的方法來解決這些問題,
相對應的外設功能所使用的時鐘在stm32f4xx_rcc.h 中即可查看到,下面是部分時鐘:
//RCC_AHB1_Peripherals
#define RCC_AHB1Periph_GPIOA ((uint32_t)0x00000001)
#define RCC_AHB1Periph_GPIOB ((uint32_t)0x00000002)
#define RCC_AHB1Periph_GPIOC ((uint32_t)0x00000004)
#define RCC_AHB1Periph_GPIOD ((uint32_t)0x00000008)
#define RCC_AHB1Periph_GPIOE ((uint32_t)0x00000010)
#define RCC_AHB1Periph_GPIOF ((uint32_t)0x00000020)
#define RCC_AHB1Periph_GPIOG ((uint32_t)0x00000040)
#define RCC_AHB1Periph_GPIOH ((uint32_t)0x00000080)
#define RCC_AHB1Periph_GPIOI ((uint32_t)0x00000100)
#define RCC_AHB1Periph_GPIOJ ((uint32_t)0x00000200)
#define RCC_AHB1Periph_GPIOK ((uint32_t)0x00000400)
#define RCC_AHB1Periph_CRC ((uint32_t)0x00001000)
#define RCC_AHB1Periph_FLITF ((uint32_t)0x00008000)
#define RCC_AHB1Periph_SRAM1 ((uint32_t)0x00010000)
#define RCC_AHB1Periph_SRAM2 ((uint32_t)0x00020000)
#define RCC_AHB1Periph_BKPSRAM ((uint32_t)0x00040000)
#define RCC_AHB1Periph_SRAM3 ((uint32_t)0x00080000)
#define RCC_AHB1Periph_CCMDATARAMEN ((uint32_t)0x00100000)
#define RCC_AHB1Periph_DMA1 ((uint32_t)0x00200000)
#define RCC_AHB1Periph_DMA2 ((uint32_t)0x00400000)
#define RCC_AHB1Periph_DMA2D ((uint32_t)0x00800000)
#define RCC_AHB1Periph_ETH_MAC ((uint32_t)0x02000000)
#define RCC_AHB1Periph_ETH_MAC_Tx ((uint32_t)0x04000000)
#define RCC_AHB1Periph_ETH_MAC_Rx ((uint32_t)0x08000000)
#define RCC_AHB1Periph_ETH_MAC_PTP ((uint32_t)0x10000000)
#define RCC_AHB1Periph_OTG_HS ((uint32_t)0x20000000)
#define RCC_AHB1Periph_OTG_HS_ULPI ((uint32_t)0x40000000)
#define IS_RCC_AHB1_CLOCK_PERIPH(PERIPH) ((((PERIPH) & 0x810BE800) == 0x00) && ((PERIPH) != 0x00))
#define IS_RCC_AHB1_RESET_PERIPH(PERIPH) ((((PERIPH) & 0xDD1FE800) == 0x00) && ((PERIPH) != 0x00))
#define IS_RCC_AHB1_LPMODE_PERIPH(PERIPH) ((((PERIPH) & 0x81106800) == 0x00) && ((PERIPH) != 0x00))
7.3 選擇要控制的 GPIO 引腳
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; /*選擇Pin9引腳*/
可選引腳為0-15 一組IO口有16個引腳
#define GPIO_Pin_0 ((uint16_t)0x0001) /* Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /* Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /* Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /* Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /* Pin 4 selected */
#define GPIO_Pin_5 ((uint16_t)0x0020) /* Pin 5 selected */
#define GPIO_Pin_6 ((uint16_t)0x0040) /* Pin 6 selected */
#define GPIO_Pin_7 ((uint16_t)0x0080) /* Pin 7 selected */
#define GPIO_Pin_8 ((uint16_t)0x0100) /* Pin 8 selected */
#define GPIO_Pin_9 ((uint16_t)0x0200) /* Pin 9 selected */
#define GPIO_Pin_10 ((uint16_t)0x0400) /* Pin 10 selected */
#define GPIO_Pin_11 ((uint16_t)0x0800) /* Pin 11 selected */
#define GPIO_Pin_12 ((uint16_t)0x1000) /* Pin 12 selected */
#define GPIO_Pin_13 ((uint16_t)0x2000) /* Pin 13 selected */
#define GPIO_Pin_14 ((uint16_t)0x4000) /* Pin 14 selected */
#define GPIO_Pin_15 ((uint16_t)0x8000) /* Pin 15 selected */
#define GPIO_Pin_All ((uint16_t)0xFFFF) /* All pins selected */
7.4 設定所選引腳的模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; /*設定為輸出模式*/
引腳的模式共有四種,分別為輸入,輸出,復用,和模擬模式
typedef enum
{
GPIO_Mode_IN = 0x00, /*!< GPIO Input Mode */
GPIO_Mode_OUT = 0x01, /*!< GPIO Output Mode */
GPIO_Mode_AF = 0x02, /*!< GPIO Alternate function Mode */
GPIO_Mode_AN = 0x03 /*!< GPIO Analog Mode */
}GPIOMode_TypeDef;
7.5 設定所選引腳的輸出型別
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; /*設定引腳的輸出型別為推挽輸出*/
輸出模式有兩種:推挽輸出和開漏輸出
typedef enum
{
GPIO_OType_PP = 0x00,
GPIO_OType_OD = 0x01
}GPIOOType_TypeDef;
只有輸出模式才需要配置,輸入模式下不需要配置
7.6 設定所選管腳的速度
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//設定速度為100MHz 高速模式
typedef enum
{
GPIO_Low_Speed = 0x00, /*!< Low speed */
GPIO_Medium_Speed = 0x01, /*!< Medium speed */
GPIO_Fast_Speed = 0x02, /*!< Fast speed */
GPIO_High_Speed = 0x03 /*!< High speed */
}GPIOSpeed_TypeDef;
/* Add legacy definition */
#define GPIO_Speed_2MHz GPIO_Low_Speed
#define GPIO_Speed_25MHz GPIO_Medium_Speed
#define GPIO_Speed_50MHz GPIO_Fast_Speed
#define GPIO_Speed_100MHz GPIO_High_Speed
7.7 設定所選管腳的上拉與下拉
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; /*設定引腳為上拉模式*/
可設定為:上拉,下拉,與浮空
typedef enum
{
GPIO_PuPd_NOPULL = 0x00,
GPIO_PuPd_UP = 0x01,
GPIO_PuPd_DOWN = 0x02
}GPIOPuPd_TypeDef;
7.8 初始化GPIO
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化所設定的引腳
GPIO_Init() 是官方配置的初始化函式 第一個引數是GPIOX 第二個引數是結構體所對應GPIO各種引數的配置
8 stm32 GPIO速率
8.1 GPIO 引腳速度
GPIO 引腳輸出速度有:GPIO_Speed_2MHz(10MHz,50MHz,100MHz)
官方一點的解釋:GPIO口的驅動電路回應速度,不是輸出信號的速度,輸出信號的速度與程式有關,通過選擇速度來選擇不同的驅動電路,降低功耗控制噪聲,又稱輸出驅動電路的回應速度:(芯片內部在I/O口的輸出部分安排了多個回應速度不同的輸出驅動電路,用戶可以根據自己的需要選擇合適的驅動電路,通過選擇速度來選擇不同的輸出驅動模塊,達到最佳的噪聲控制和降低功耗的目的,)
可理解為: 輸出驅動電路的帶寬:即一個驅動電路可以不失真地通過信號的最大頻率,(如果一個信號的頻率超過了驅動電路的回應速度,就有可能信號失真,) 例如如果信號頻率為10MHz,而你配置了2MHz的帶寬,則10MHz的方波很可能就變成了正弦波,就好比是公路的設計時速,汽車速度低于設計時速時,可以平穩地運行,如果超過設計時速就會顛簸,甚至翻車,
關鍵是:GPIO的引腳速度跟應用相匹配,速度配置越高,輸出驅動電路的帶寬越大,噪聲也越大,功耗越大,速度配置越低,輸出驅動電路的帶寬越小,噪聲也越小,功耗越小,使用合適的驅動器可以降低功耗和噪聲,我們選擇的只是不同的輸出驅動電路,而電路在設計好了后它本身的帶寬也就確定了,也就是說這個速率(帶寬)與系統時鐘無關,
比如:高頻的驅動電路,噪聲也高,當不需要高的輸出頻率時,請選用低頻驅動電路,這樣非常有利于提高系統的EMI性能,當然如果要輸出較高頻率的信號,但卻選用了較低頻率的驅動模塊,很可能會得到失真的輸出信號,關鍵是GPIO的引腳速度跟應用匹配(推薦10倍以上),
比如:
1)USART串口,若最大波特率只需115.2k,那用2M的速度就夠了,既省電也噪聲小,
2)I2C介面,若使用400k波特率,若想把余量留大些,可以選用10M的GPIO引腳速度,
3)SPI介面,若使用18M或9M波特率,需要選用50M的GPIO的引腳速度,
8.2 GPIO的翻轉速度
GPIO的翻轉速度指:輸入/輸出暫存器的0 ,1 值反映到外部引腳(APB2上)高低電平的速度,F1手冊上指出GPIO最大翻轉速度可達18MHz,
通過簡單的程式測驗,用示波器觀察到的翻轉時間: 是綜合的時間,包括取指令的時間、指令執行的時間、指令執行后信號傳遞到暫存器的時間(這其中可能經過很多環節,比如AHB、APB、總線仲裁等),最后才是信號從暫存器傳輸到引腳所經歷的時間,
如:有上拉電阻,其阻值越大,RC延時越大,即邏輯電平轉換的速度越慢,功耗越大,
8.3 GPIO的輸出速度
GPIO 輸出速度:與程式有關,(程式中寫的多久輸出一個信號),
9 stm32埠重映射
重映射:為了使不同器件封裝的外設 IO 功能數量達到最優,可以把一些復用功能重新映射到其他一些引腳上,目的為了讓設計工程師可以更好地安排引腳的走向和功能,在 STM32 中引入了外設引腳重映射的概念,即一個外設的引腳除了具有默認的埠外,還可以通過設定重映射暫存器的方式,把這個外設的引腳映射到其它的埠,簡單的講就是把管腳的外設功能映射到另一個管腳去使用,但是不是可以隨便映射的,根據手冊是否可以映射,
重映射技術的需求背景:
1)I/O的復用:GPIO和內置外設共用引出管腳
2)I/O的重映射:復用功能(AFIO)從不同的GPIO管腳引出
3)方便了PCB的設計,減少了信號交叉干擾
4)分時復用某些外設,虛擬地增加了埠數目
5)為了使不同器件封裝的外設IO功能數量達到最優,可以把一些復用功能重新映射到其他一些引腳上,STM32中有很多內置外設的輸入輸出引腳都具有重映射(remap)的功能,
以串口1為例:

上圖中的,Remap對應的I/O就是可以重映射到的I/O,Default就是該I/O默認可復用的功能,從上圖中可以看出 串口1 可以重映射到 PB6和PB7引腳,也就是說如果PA9和PA10引腳不好用的時候,或者已經被占用了;那么可以用PB6和PB7來實作串口1的功能,
9.1 AFIO重映射的操作步驟
1)使能被重新映射到的I/O埠時鐘
2)使能被重新映射的外設時鐘
3)使能AFIO功能的時鐘(勿忘)
4)進行重映射
注意第3步,使能AFIO功能時鐘,為什么需要使能這個時鐘 & 什么時候需要使能這個時鐘, 可以參見下面這個回答
那么,問題來了!AFIO 是什么?AFIO 時鐘什么時候需要開啟?
我們從《STM32中文參考手冊_V10》中找到:對暫存器 AFIO_EVCR、AFIO_MAPR 和 AFIO_EXTICRX 進行讀寫操作前,應當首先打開 AFIO 的時鐘(設定 APB2 外設時鐘使能暫存器 RCC_APB2ENR),
也就是說:當你需要配置 AFIO 這些暫存器的時候,就需要把 RCC_APB2ENR 暫存器的 AFIO 位置‘1’打開 AFIO 時鐘,
跟 AFIO 相關的暫存器有:
1、 事件控制暫存器(AFIO_EVCR)
2、 復用重映射和除錯I/O 配置暫存器(AFIO_MAPR)
3、 外部中斷配置暫存器1(AFIO_EXTICR1)
4、 外部中斷配置暫存器2(AFIO_EXTICR2)
5、 外部中斷配置暫存器3(AFIO_EXTICR3)
6、 外部中斷配置暫存器4(AFIO_EXTICR4)
看看這些暫存器的定義,我們就明白,這些暫存器是用于“事件控制”、“重映射”、“除錯IO配置”、“外部中斷”的,例如 AFIO_EXTICRX 用于選擇 EXTIx 外部中斷的輸入源,
總結:當我們需要配置這些 AFIO 暫存器的時候,就需要打開 RCC_APB2ENR 暫存器的 AFIO 時鐘,而不是用到引腳復用功能的時候打開,
9.2 部分重映射 & 完全重映射
部分重映射:功能外設的部分引腳重新映射,還有一部分引腳是原來的默認引腳,
完全重映射:功能外設的所有引腳都重新映射,

引腳重映射配置程序(串口3為例):
1)使能GPIO時鐘(重映射后的IO);
2)使能功能外設時鐘(例如串口3);
3)使能AFIO時鐘,重映射必須使能AFIO時鐘:
4)開啟重映射,
GPIO_PinRemapConfig(GPIO_FullRemap_USART3, ENABLE);
//根據第一個引數,來確定是部分重映射還是全部重映射
以串口3為例,如果想部分映射:GPIO_PinRemapConfig()的第一個引數取值應該是: GPIO_PartialRemap_USART3,如果是完全映射就是:GPIO_FullRemap_USART3
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/301113.html
標籤:其他
