CKS32F107系列MCU的GPIO內(nèi)部結(jié)果分析
GPIO簡介
GPIO是通用輸入輸出端口的簡稱,也是CKS32可控制的引腳,CKS32芯片的GPIO引腳與外部設(shè)備連接起來,從而實(shí)現(xiàn)與外部通訊、控制以及數(shù)據(jù)采集的功能。CKS32芯片的GPIO被分成很多組,每組有16個(gè)引腳,如型號(hào)為CKS2F107VET6型號(hào)的芯片有GPIOA、GPIOB、GPIOC至GPIOE共5組GPIO,芯片一共100個(gè)引腳,其中GPIO就占了一大部分,所有的GPIO引腳都有基本的輸入輸出功能。
最基本的輸出功能是由CKS32控制引腳輸出高、低電平,實(shí)現(xiàn)開關(guān)控制,如把GPIO引腳接入到LED燈,那就可以控制LED燈的亮滅,引腳接入到繼電器或三極管,那就可以通過繼電器或三極管控制外部大功率電路的通斷。最基本的輸入功能是檢測(cè)外部輸入電平,如把 GPIO引腳連接到按鍵,通過電平高低區(qū)分按鍵是否被按下。
GPIO框圖結(jié)構(gòu)分析
CKS32F107系列MCU的GPIO內(nèi)部硬件結(jié)構(gòu)如下圖所示,通過GPIO硬件結(jié)構(gòu)框圖,可以從整體上深入了解GPIO外設(shè)及它的各種應(yīng)用模式。該圖從最右端看起,最右端就是代表 MCU引出的 GPIO引腳,其余部件都位于MCU芯片內(nèi)部。
圖1 GPIO硬件結(jié)構(gòu)框圖
序號(hào)①是引腳的兩個(gè)保護(hù)二級(jí)管,可以防止引腳外部過高或過低的電壓輸入,當(dāng)引腳電壓高于VDD時(shí),上方的二極管導(dǎo)通,當(dāng)引腳電壓低于VSS時(shí),下方的二極管導(dǎo)通,防止不正常電壓引入芯片導(dǎo)致芯片燒毀。盡管有這樣的保護(hù),并不意味著CKS32的GPIO能直接外接大功率驅(qū)動(dòng)器件,如直接驅(qū)動(dòng)電機(jī),如果強(qiáng)制驅(qū)動(dòng)可能會(huì)造成電機(jī)不轉(zhuǎn)或者導(dǎo)致芯片燒壞,必須要在GPIO和電機(jī)之間增加大功率及隔離電路驅(qū)動(dòng)。
序號(hào)②是GPIO引腳線路經(jīng)過兩個(gè)保護(hù)二極管后,下方“輸出模式”電路中的一個(gè)由P-MOS和N-MOS管組成的結(jié)構(gòu)單元。這個(gè)結(jié)構(gòu)使GPIO具有了“推挽輸出”和“開漏輸出”兩種模式,輸出模式是根據(jù)這兩個(gè)MOS管的工作方式來命名的。在該結(jié)構(gòu)中輸入高電平時(shí),經(jīng)過反向后,上方的P-MOS導(dǎo)通,下方的N-MOS關(guān)閉,對(duì)外輸出高電平;而在該結(jié)構(gòu)中輸入低電平時(shí),經(jīng)過反向后,N-MOS管導(dǎo)通,P-MOS關(guān)閉,對(duì)外輸出低電平。當(dāng)引腳高低電平切換時(shí),兩個(gè)管子輪流導(dǎo)通,P管負(fù)責(zé)灌電流,N管負(fù)責(zé)拉電流,使其負(fù)載能力和開關(guān)速度都比普通的方式有很大的提高。推挽輸出的低電平為0伏,高電平為3.3伏,推挽等效電路如下圖(左)。推挽輸出模式一般應(yīng)用在輸出電平為0和3.3伏而且需要高速切換開關(guān)狀態(tài)的場合。在實(shí)際應(yīng)用中,除了必須用開漏模式的場合,一般都習(xí)慣使用推挽輸出模式。
圖2 GPIO硬件結(jié)構(gòu)框圖
在開漏輸出模式時(shí),上方的P-MOS管完全不工作。如果我們控制輸出為0低電平,則 P-MOS管關(guān)閉,N-MOS管導(dǎo)通,使輸出接地,若控制輸出為1 (它無法直接輸出高電平) 時(shí),則P-MOS管和N-MOS管都關(guān)閉,所以引腳既不輸出高電平,也不輸出低電平,為高阻態(tài),因此正常使用時(shí)必須外部接上拉電阻。開漏等效電路如上圖(右),它具有“線與”特性,若有很多個(gè)開漏模式引腳連接到一起時(shí),只有當(dāng)所有引腳都輸出高阻態(tài),才由上拉電阻提供高電平。若其中一個(gè)引腳為低電平,那線路就相當(dāng)于短路接地,使得整條線路都為低電平0伏。開漏輸出一般應(yīng)用在I2C、SMBUS通訊等需要“線與”功能的總線電路中。除此之外,還用在電平不匹配的場合,如需要輸出5伏的高電平,就可以在外部接一個(gè)上拉電阻,上拉電源為5伏,并且把GPIO設(shè)置為開漏模式,當(dāng)輸出高阻態(tài)時(shí),由上拉電阻和電源向外輸出5伏的電平。
序號(hào)③是GPIO輸出數(shù)據(jù)寄存器組,前面提到的雙MOS管結(jié)構(gòu)電路輸入信號(hào),就是由這個(gè)寄存器組中的GPIOx_ODR提供的,因此我們通過修改輸出數(shù)據(jù)寄存器的值就可以修改GPIO引腳的輸出電平。而“置位/復(fù)位寄存器GPIOx_BSRR”可以通過修改輸出數(shù)據(jù)寄存器的值從而影響電路的輸出。
序號(hào)④是連接MCU片內(nèi)外設(shè)和GPIO引腳的復(fù)用功能輸出模塊,通過此功能可以將GPIO引腳用作指定外設(shè)功能的一部分,算是GPIO的第二用途。從其它外設(shè)引出來的“復(fù)用功能輸出信號(hào)”與GPIO本身的數(shù)據(jù)據(jù)寄存器都連接到雙MOS管結(jié)構(gòu)的輸入中,通過內(nèi)部開關(guān)切換選擇。例如我們使用USART串口通訊時(shí),需要用到某個(gè)GPIO引腳作為通訊發(fā)送引腳,這個(gè)時(shí)候就可以把該GPIO引腳配置成USART串口復(fù)用功能,由串口外設(shè)控制該引腳發(fā)送數(shù)據(jù)。
序號(hào)⑤是輸入數(shù)據(jù)寄存器組,位于GPIO結(jié)構(gòu)框圖的上半部分,GPIO引腳經(jīng)過內(nèi)部的上、下拉電阻,可以配置成上/下拉輸入,然后再連接到施密特觸發(fā)器,信號(hào)經(jīng)過觸發(fā)器后,模擬信號(hào)轉(zhuǎn)化為0/1數(shù)字信號(hào),然后存儲(chǔ)在“輸入數(shù)據(jù)寄存器GPIOx_IDR”中,通過讀取該寄存器就可以獲取GPIO引腳的電平狀態(tài)。
序號(hào)⑥是連接MCU片內(nèi)外設(shè)和GPIO引腳的復(fù)用功能輸入模塊,與序號(hào)④類似,在“復(fù)用功能輸入模式”時(shí),GPIO引腳的信號(hào)傳輸?shù)街付ㄆ瑑?nèi)外設(shè),由該外設(shè)讀取引腳狀態(tài)。例如我們使用USART串口通訊時(shí),需要用到某個(gè)GPIO引腳作為通訊接收引腳,這個(gè)時(shí)候就可以把該GPIO引腳配置成USART串口復(fù)用功能,由串口外設(shè)控制該引腳接收外部數(shù)據(jù)。
序號(hào)⑦是用于ADC采集電壓輸入通道的專用“模擬輸入”功能,由于ADC外設(shè)要采集到原始的模擬信號(hào),所以輸入信號(hào)不經(jīng)過施密特觸發(fā)器,因?yàn)榻?jīng)過施密特觸發(fā)器后信號(hào)只有0/1兩種狀態(tài)。類似地,當(dāng)GPIO引腳作為“模擬輸出”功能用于DAC模擬電壓輸出通道時(shí),模擬信號(hào)輸出也不經(jīng)過雙MOS管結(jié)構(gòu)而直接輸出到GPIO引腳。
GPIO工作模式總結(jié)
根據(jù)上述結(jié)構(gòu)分析,可以總結(jié)出在固件庫中GPIO可以配置成如下8種工作模式,且大致歸為三類。
//Configuration Mode enumeration
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;
第一類是輸入模式(模擬/浮空/上拉/下拉),在輸入模式時(shí),施密特觸發(fā)器打開,輸出被禁止,可通過輸入數(shù)據(jù)寄存器GPIOx_IDR讀取I/O狀態(tài)。其中輸入模式,可設(shè)置為上拉、下拉、浮空和模擬輸入四種。上拉和下拉輸入很好理解,默認(rèn)的電平由上拉或者下拉決定。浮空輸入的電平是不確定的,完全由外部的輸入決定,一般接按鍵的時(shí)候用的是這個(gè)模式。模擬輸入則專用于ADC采集。
第二類是輸出模式(推挽/開漏),在推挽模式時(shí)雙MOS管以輪流方式工作,輸出數(shù)據(jù)寄存器GPIOx_ODR可控制I/O輸出高低電平。開漏模式時(shí),只有N-MOS管工作,輸出數(shù)據(jù)寄存器可控制I/O輸出高阻態(tài)或低電平。輸出速度可配置,此處的輸出速度即I/O支持的高低電平狀態(tài)最高切換頻率,支持的頻率越高,功耗越大。在輸出模式時(shí)施密特觸發(fā)器是打開的,即輸入可用,通過輸入數(shù)據(jù)寄存器GPIOx_IDR可讀取I/O的實(shí)際狀態(tài)。
第三類是復(fù)用功能模式(推挽/開漏),復(fù)用功能模式中,輸出使能,輸出速度可配置,可工作在開漏及推挽模式,但是輸出信號(hào)源于其它外設(shè),輸出數(shù)據(jù)寄存器GPIOx_ODR無效;輸入可用,通過輸入數(shù)據(jù)寄存器可獲取I/O實(shí)際狀態(tài),但一般直接用外設(shè)的寄存器來獲取該數(shù)據(jù)信號(hào)。
以上各類型的GPIO口每一個(gè)都可以自由編程,此外,CKS32F107的很多IO口都是5V兼容的,這些IO口在與5V電平的外設(shè)連接的時(shí)候很有優(yōu)勢(shì),具體哪些IO口是5V兼容的,可以從該芯片的數(shù)據(jù)手冊(cè)管腳描述章節(jié)查到(I/O Level標(biāo)FT的就是5V電平兼容的)。
GPIO寄存器
CKS32的GPIO口寄存器必須要按32位字被訪問,每個(gè)IO端口都有7個(gè)寄存器來控制。分別是:配置模式的2個(gè)32位的端口配置寄存器CRL和CRH;2個(gè)32位的數(shù)據(jù)寄存器IDR和ODR;1個(gè)32位的置位/復(fù)位寄存器BSRR;一個(gè)16位的復(fù)位寄存器BRR;1個(gè)32位的鎖存寄存器LCKR。如果想要了解每個(gè)寄存器的詳細(xì)使用方法,可以參考《CKS32F107參考手冊(cè)》。
(1)CRL和CRH控制著每個(gè)IO口的模式及輸出速率,本文以CRL為例,看看端口低配置寄存器的描述,如下圖所示。該寄存器的復(fù)位值為0x44444444,從圖中可以看到,復(fù)位值其實(shí)就是配置端口為浮空輸入模式。從下圖還可以得出:CRL控制著每組IO端口的低8位模式。每個(gè)IO端口的位占用CRL的4個(gè)位,高兩位為CNF,低兩位為MODE。這里我們可以記住幾個(gè)常用的配置,比如0x0表示模擬輸入模式(ADC用)、0x3表示推挽輸出模式(做輸出口用,50M速率)、0x8表示上/下拉輸入模式(做輸入口用)、0xB表示復(fù)用輸出(使用IO口的第二功能,50M速率)。CRH的作用和CRL完全一樣,只是CRL控制的是低8位輸出口,而CRH控制的是高8位輸出口。這里我們對(duì)CRH就不做詳細(xì)介紹了。
圖3 GPIOx_CRL寄存器
(2)IDR是一個(gè)端口輸入數(shù)據(jù)寄存器,低16位有效。該寄存器為只讀寄存器,并且只能以16位的形式讀出。該寄存器各位的描述如下圖所示:
圖4 GPIOx_CRL寄存器
(3)ODR是一個(gè)端口輸出數(shù)據(jù)寄存器,也只用了低16位。該寄存器為可讀寫,從該寄存器讀出來的數(shù)據(jù)可以用于判斷當(dāng)前IO口的輸出狀態(tài)。而向該寄存器寫數(shù)據(jù),則可以控制某個(gè)IO口的輸出電平。該寄存器的各位描述如下圖所示:
圖5 GPIOx_CRL寄存器
(4)BSRR寄存器是端口位設(shè)置/清除寄存器。該寄存器和ODR寄存器具有類似的作用,都可以用來設(shè)置GPIO端口的輸出位是1還是0。
圖6 GPIOx_CRL寄存器
通過固件庫操作GPIO
CKS32F107系列GPIO相關(guān)的函數(shù)和定義分布在固件庫文件cks32f10x_gpio.c和頭文件 cks32f10x_gpio.h文件中。在固件庫開發(fā)中,操作寄存器CRH和CRL來配置IO口的模式和速度是通過GPIO初始化函數(shù)完成的。
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
這個(gè)函數(shù)有兩個(gè)參數(shù),第一個(gè)參數(shù)是用來指定GPIO,取值范圍為GPIOA~GPIOG。第二個(gè)參數(shù)為初始化參數(shù)結(jié)構(gòu)體指針,結(jié)構(gòu)體類型為GPIO_InitTypeDef。結(jié)構(gòu)體的定義如下:
typedef struct
uint16_t GPIO_Pin;
GPIOSpeed_TypeDef GPIO_Speed;
GPIOMode_TypeDef GPIO_Mode;
}GPIO_InitTypeDef;
下面通過一個(gè)GPIO初始化實(shí)例來講解這個(gè)結(jié)構(gòu)體的成員變量的含義。代碼的意思是設(shè)置GPIOB的第5個(gè)端口為推挽輸出模式,同時(shí)速度為50M。結(jié)構(gòu)體GPIO_InitStructure的第一個(gè)成員變量GPIO_Pin用來設(shè)置是要初始化哪個(gè)或者哪些IO口;第二個(gè)成員變量GPIO_Mode是用來設(shè)置對(duì)應(yīng)IO端口的輸出輸入模式;第三個(gè)參數(shù)是IO口速度設(shè)置。
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //PB5端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根據(jù)設(shè)定參數(shù)配置 GPIO
在固件庫中操作IDR寄存器讀取IO端口數(shù)據(jù)是通過GPIO_ReadInputDataBit函數(shù)實(shí)現(xiàn)的。比如我要讀GPIOA5的電平狀態(tài),那么方法是:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);
在固件庫中設(shè)置ODR寄存器的值來控制IO口的輸出狀態(tài)是通過函數(shù)GPIO_Write來實(shí)現(xiàn)的,該函數(shù)一般用來一次性往一個(gè)GPIO的多個(gè)端口設(shè)值。
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
該寄存器通過舉例子可以很清楚了解它的使用方法。例如你要設(shè)置GPIOA的第1個(gè)端口值為1,那么你只需要往寄存器BSRR的低16位對(duì)應(yīng)位寫1即可。該寄存器往相應(yīng)位寫0是無影響的,所以我們要設(shè)置某些位,我們不用管其他位的值。
GPIOA->BSRR=1<< 1;
在固件庫中,通過BSRR和BRR寄存器設(shè)置GPIO端口輸出是通過函數(shù)GPIO_SetBits()和函數(shù)GPIO_ResetBits()來完成的。在多數(shù)情況下,我們都是采用這兩個(gè)函數(shù)來設(shè)置GPIO端口的輸入和輸出狀態(tài)。