GPIO全稱爲General Purpose Input Output,中文理解爲通用輸入輸出端口。它指的是編程可控制的引腳,便可以控制引腳是做爲輸入來用,仍是輸出功能,又或者是交給片上外設使用(複用)。編程
理解GPIO硬件電路的實現,有助於編程的理解。下圖是官方手冊給出的GPIO框圖:ui
此框圖中只有最右端的I/O pin是板上外露可見的,其餘部分均在芯片內部。該框圖結構被分爲兩個部分,其一是輸出結構,主要由引腳(I/O pin)、保護二極管(protection diode)、雙MOS管(P-MOS+N-MOS)、輸出控制(output control)、輸出數據寄存器(output data register)、位置位/復位寄存器(bit set/reset register)組成 ;其二是輸入結構,由引腳、保護二極管、雙開關(on/off)、TTL肖特基觸發器(TTL Schmitt trigger)、輸入數據寄存器(input data register)組成。spa
兩個二極管構成了雙向限幅電路。當外部輸入電壓大於VDD時,上方二極管導通(全文皆忽略二極管和MOS管導通壓降),使得電壓限制在VDD左右;當外部輸入電壓低於VSS,下方二極管導通,電壓限制在VSS左右。然而這樣的保護並不能使引腳直接驅動大功率器件,例如電機,就須要外加驅動電路。3d
這個雙開關電路與上拉、下拉和浮空模式有關,當處於上拉模式時上方開關閉合,下方斷開,除非引腳的外部輸入爲低電平,不然輸入到肖特基觸發器的電平都是高電平;當處於下拉模式時上方開關斷開,下方閉合,除非引腳的外部輸入爲高電平,不然輸入到肖特基觸發器的電平都是低電平;當處於浮空模式時開關所有斷開,電平默認爲高阻態,輸入的電平徹底取決於外部輸入。上下拉多用於按鍵檢測,而浮空則經常使用於ADC檢測。code
該觸發器主要是將模擬電壓轉爲0/1數字信號,上圖中模擬輸入(Analog input)就取自觸發器以前,而輸入數據寄存器則取自觸發器以後的數字信號。blog
這個寄存器用來存儲各個引腳電平狀態,可經過讀取該寄存器內容,來獲取任意引腳狀態。input
經過讀寫該寄存器,能夠間接地更改輸出數據寄存器的值。it
能夠直接讀寫該寄存器,來更改各個引腳的輸出狀態,當寄存器值保持不變時,每一個引腳的電平也就不發生變化。io
雙MOS管組成一個推輓輸出(push-pull)電路,當輸出寄存器中值爲1時,通過反相器,兩管的共柵極輸入低電平,則PMOS導通,NMOS截止,引腳輸出高電平;當輸出寄存器中值爲0時,通過反相器,兩管的共柵極輸入高電平,則NMOS導通,PMOS截止,引腳輸出低電平。當PMOS被禁用時,電路變爲開漏輸出,引腳仍可輸出低電平,但沒法正常輸出高電平,取而代之的是高阻態。若是此時想要輸出高電平,則必須接上拉電阻。開漏輸出僅在一些特殊場合下使用,更多的是推輓輸出。class
RCC_APB2ENR並不屬於GPIO的寄存器組,可是它控制着GPIO的引腳時鐘使能,芯片上電後,全部外設時鐘默認是關閉的,因此在使用GPIO前要先打開端口時鐘使能。該寄存器內容以下,咱們只關心IOPxEN位,置1則開啓時鐘。
該寄存器可配置低8個GPIO引腳的工做模式:模擬輸入、浮空輸入、上拉\下拉輸入、通用推輓輸出、通用開漏輸出、複用推輓輸出、複用開漏輸出。複用輸入模式沒有單獨的配置,只要將引腳配置爲浮空/上拉/下拉輸入模式,由片上外設驅動便可。CRL還能夠在輸出模式下配置工做頻率,頻率越大,功耗越高。注意每一個引腳模式配置佔4bit。對於上拉\下拉輸入來講,是上拉仍是下拉取決於ODR寄存器配置的值。
該寄存器同GPIOx_CRL,只是它控制的是高8個GPIO引腳的工做模式。
該寄存器存儲了當前引腳的狀態,不管GPIO在哪一種工做模式下,均可以讀取該寄存器,但在模擬輸入模式下,寄存器的值一直爲0。
在通用輸出模式下,該寄存器可用來控制電平高低;在複用推輓輸出模式下,可讀取ODR獲取引腳狀態(在複用開漏輸出模式下,可讀取IDR獲取引腳狀態)。在上拉或下拉輸入模式下,ODR可決定是上拉仍是下拉。
經過該寄存器,能夠單獨控制某個引腳的電平變化。由於向這個寄存器寫0的結果是無操做,寫1是置位或者復位,因此訪問該寄存器能夠直接用=號;而ODR寄存器寫0則表示給低電平,寫1表示給高電平,爲了避免影響其餘引腳,寫入必須用&=和|=號。另外還有個寄存器叫GPIOx_BRR,同該寄存器差很少,只不過BRR只能復位,高16位是保留的。
GPIO最經常使用的就是通用推輓輸出和上拉/下拉輸入這兩種模式。一下程序實現功能爲:
PA0爲按鍵輸入,按下爲低電平。PC13是led控制引腳,低電平點亮LED。當按下按鍵時,LED亮;鬆開按鍵時,LED滅。
程序代碼以下,根據GPIO原理,編程順序通常是:先開啓時鐘使能,配置引腳工做模式,最後給引腳電平信號。
int main(void) { uint32_t key_value; //開啓GPIO外設時鐘使能 //PORTA,第三位置1 RCC->APB2ENR |= (1<<2); //PORTC,第五位置1 RCC->APB2ENR |= (1<<4); //GPIO輸入輸出模式配置 //清零PA0控制位 GPIOA->CRL &= ~((uint32_t)(0x0F)<<(4*0)); //設置PA0爲上拉輸入 GPIOA->CRL |= (uint32_t)(0x08)<<(4*0); GPIOA->ODR |= (uint32_t)(0x01)<<0; //清零PC13控制位 GPIOC->CRH &= ~((uint32_t)(0x0F)<<(4*5)); //設置PC13爲推免輸出 GPIOC->CRH |= (uint32_t)(0x01)<<(4*5); while(1){ //獲取PA0電平狀態,key_value==1表示按鍵沒有按下 key_value = (GPIOA->IDR & (uint32_t)(0x01)) == (uint32_t)(0x01); //改變PC13電平 if (key_value==0) //PC13低電平輸出 GPIOC->ODR &= ~((uint32_t)(0x01)<<13); else //PC13高電平輸出 GPIOC->ODR |= (uint32_t)(0x01)<<13; } }