GPIO簡介
GPIO是通用輸入輸出端口的簡稱,也是CKS32可控制的引腳,CKS32芯片的GPIO引腳與外部設備連接起來,從而實現與外部通訊、控制以及數據采集的功能。CKS32芯片的GPIO被分成很多組,每組有16個引腳,如型號為CKS2F107VET6型號的芯片有GPIOA、GPIOB、GPIOC至GPIOE共5組GPIO,芯片一共100個引腳,其中GPIO就占了一大部分,所有的GPIO引腳都有基本的輸入輸出功能。
最基本的輸出功能是由CKS32控制引腳輸出高、低電平,實現開關控制,如把GPIO引腳接入到LED燈,那就可以控制LED燈的亮滅,引腳接入到繼電器或三極管,那就可以通過繼電器或三極管控制外部大功率電路的通斷。最基本的輸入功能是檢測外部輸入電平,如把 GPIO引腳連接到按鍵,通過電平高低區分按鍵是否被按下。
GPIO框圖結構分析
CKS32F107系列MCU的GPIO內部硬件結構如下圖所示,通過GPIO硬件結構框圖,可以從整體上深入了解GPIO外設及它的各種應用模式。該圖從最右端看起,最右端就是代表 MCU引出的 GPIO引腳,其余部件都位于MCU芯片內部。
圖1 GPIO硬件結構框圖
序號①是引腳的兩個保護二級管,可以防止引腳外部過高或過低的電壓輸入,當引腳電壓高于VDD時,上方的二極管導通,當引腳電壓低于VSS時,下方的二極管導通,防止不正常電壓引入芯片導致芯片燒毀。盡管有這樣的保護,并不意味著CKS32的GPIO能直接外接大功率驅動器件,如直接驅動電機,如果強制驅動可能會造成電機不轉或者導致芯片燒壞,必須要在GPIO和電機之間增加大功率及隔離電路驅動。
序號②是GPIO引腳線路經過兩個保護二極管后,下方“輸出模式”電路中的一個由P-MOS和N-MOS管組成的結構單元。這個結構使GPIO具有了“推挽輸出”和“開漏輸出”兩種模式,輸出模式是根據這兩個MOS管的工作方式來命名的。在該結構中輸入高電平時,經過反向后,上方的P-MOS導通,下方的N-MOS關閉,對外輸出高電平;而在該結構中輸入低電平時,經過反向后,N-MOS管導通,P-MOS關閉,對外輸出低電平。當引腳高低電平切換時,兩個管子輪流導通,P管負責灌電流,N管負責拉電流,使其負載能力和開關速度都比普通的方式有很大的提高。推挽輸出的低電平為0伏,高電平為3.3伏,推挽等效電路如下圖(左)。推挽輸出模式一般應用在輸出電平為0和3.3伏而且需要高速切換開關狀態的場合。在實際應用中,除了必須用開漏模式的場合,一般都習慣使用推挽輸出模式。
圖2 GPIO硬件結構框圖
在開漏輸出模式時,上方的P-MOS管完全不工作。如果我們控制輸出為0低電平,則 P-MOS管關閉,N-MOS管導通,使輸出接地,若控制輸出為1 (它無法直接輸出高電平) 時,則P-MOS管和N-MOS管都關閉,所以引腳既不輸出高電平,也不輸出低電平,為高阻態,因此正常使用時必須外部接上拉電阻。開漏等效電路如上圖(右),它具有“線與”特性,若有很多個開漏模式引腳連接到一起時,只有當所有引腳都輸出高阻態,才由上拉電阻提供高電平。若其中一個引腳為低電平,那線路就相當于短路接地,使得整條線路都為低電平0伏。開漏輸出一般應用在I2C、SMBUS通訊等需要“線與”功能的總線電路中。除此之外,還用在電平不匹配的場合,如需要輸出5伏的高電平,就可以在外部接一個上拉電阻,上拉電源為5伏,并且把GPIO設置為開漏模式,當輸出高阻態時,由上拉電阻和電源向外輸出5伏的電平。
序號③是GPIO輸出數據寄存器組,前面提到的雙MOS管結構電路輸入信號,就是由這個寄存器組中的GPIOx_ODR提供的,因此我們通過修改輸出數據寄存器的值就可以修改GPIO引腳的輸出電平。而“置位/復位寄存器GPIOx_BSRR”可以通過修改輸出數據寄存器的值從而影響電路的輸出。
序號④是連接MCU片內外設和GPIO引腳的復用功能輸出模塊,通過此功能可以將GPIO引腳用作指定外設功能的一部分,算是GPIO的第二用途。從其它外設引出來的“復用功能輸出信號”與GPIO本身的數據據寄存器都連接到雙MOS管結構的輸入中,通過內部開關切換選擇。例如我們使用USART串口通訊時,需要用到某個GPIO引腳作為通訊發送引腳,這個時候就可以把該GPIO引腳配置成USART串口復用功能,由串口外設控制該引腳發送數據。
序號⑤是輸入數據寄存器組,位于GPIO結構框圖的上半部分,GPIO引腳經過內部的上、下拉電阻,可以配置成上/下拉輸入,然后再連接到施密特觸發器,信號經過觸發器后,模擬信號轉化為0/1數字信號,然后存儲在“輸入數據寄存器GPIOx_IDR”中,通過讀取該寄存器就可以獲取GPIO引腳的電平狀態。
序號⑥是連接MCU片內外設和GPIO引腳的復用功能輸入模塊,與序號④類似,在“復用功能輸入模式”時,GPIO引腳的信號傳輸到指定片內外設,由該外設讀取引腳狀態。例如我們使用USART串口通訊時,需要用到某個GPIO引腳作為通訊接收引腳,這個時候就可以把該GPIO引腳配置成USART串口復用功能,由串口外設控制該引腳接收外部數據。
序號⑦是用于ADC采集電壓輸入通道的專用“模擬輸入”功能,由于ADC外設要采集到原始的模擬信號,所以輸入信號不經過施密特觸發器,因為經過施密特觸發器后信號只有0/1兩種狀態。類似地,當GPIO引腳作為“模擬輸出”功能用于DAC模擬電壓輸出通道時,模擬信號輸出也不經過雙MOS管結構而直接輸出到GPIO引腳。
GPIO工作模式總結
根據上述結構分析,可以總結出在固件庫中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, //復用開漏輸出
GPIO_Mode_AF_PP = 0x18 //復用推挽輸出
} GPIOMode_TypeDef;
第一類是輸入模式(模擬/浮空/上拉/下拉),在輸入模式時,施密特觸發器打開,輸出被禁止,可通過輸入數據寄存器GPIOx_IDR讀取I/O狀態。其中輸入模式,可設置為上拉、下拉、浮空和模擬輸入四種。上拉和下拉輸入很好理解,默認的電平由上拉或者下拉決定。浮空輸入的電平是不確定的,完全由外部的輸入決定,一般接按鍵的時候用的是這個模式。模擬輸入則專用于ADC采集。
第二類是輸出模式(推挽/開漏),在推挽模式時雙MOS管以輪流方式工作,輸出數據寄存器GPIOx_ODR可控制I/O輸出高低電平。開漏模式時,只有N-MOS管工作,輸出數據寄存器可控制I/O輸出高阻態或低電平。輸出速度可配置,此處的輸出速度即I/O支持的高低電平狀態最高切換頻率,支持的頻率越高,功耗越大。在輸出模式時施密特觸發器是打開的,即輸入可用,通過輸入數據寄存器GPIOx_IDR可讀取I/O的實際狀態。
第三類是復用功能模式(推挽/開漏),復用功能模式中,輸出使能,輸出速度可配置,可工作在開漏及推挽模式,但是輸出信號源于其它外設,輸出數據寄存器GPIOx_ODR無效;輸入可用,通過輸入數據寄存器可獲取I/O實際狀態,但一般直接用外設的寄存器來獲取該數據信號。
以上各類型的GPIO口每一個都可以自由編程,此外,CKS32F107的很多IO口都是5V兼容的,這些IO口在與5V電平的外設連接的時候很有優勢,具體哪些IO口是5V兼容的,可以從該芯片的數據手冊管腳描述章節查到(I/O Level標FT的就是5V電平兼容的)。
GPIO寄存器
CKS32的GPIO口寄存器必須要按32位字被訪問,每個IO端口都有7個寄存器來控制。分別是:配置模式的2個32位的端口配置寄存器CRL和CRH;2個32位的數據寄存器IDR和ODR;1個32位的置位/復位寄存器BSRR;一個16位的復位寄存器BRR;1個32位的鎖存寄存器LCKR。如果想要了解每個寄存器的詳細使用方法,可以參考《CKS32F107參考手冊》。
(1)CRL和CRH控制著每個IO口的模式及輸出速率,本文以CRL為例,看看端口低配置寄存器的描述,如下圖所示。該寄存器的復位值為0x44444444,從圖中可以看到,復位值其實就是配置端口為浮空輸入模式。從下圖還可以得出:CRL控制著每組IO端口的低8位模式。每個IO端口的位占用CRL的4個位,高兩位為CNF,低兩位為MODE。這里我們可以記住幾個常用的配置,比如0x0表示模擬輸入模式(ADC用)、0x3表示推挽輸出模式(做輸出口用,50M速率)、0x8表示上/下拉輸入模式(做輸入口用)、0xB表示復用輸出(使用IO口的第二功能,50M速率)。CRH的作用和CRL完全一樣,只是CRL控制的是低8位輸出口,而CRH控制的是高8位輸出口。這里我們對CRH就不做詳細介紹了。
圖3 GPIOx_CRL寄存器
(2)IDR是一個端口輸入數據寄存器,低16位有效。該寄存器為只讀寄存器,并且只能以16位的形式讀出。該寄存器各位的描述如下圖所示:
圖4 GPIOx_CRL寄存器
(3)ODR是一個端口輸出數據寄存器,也只用了低16位。該寄存器為可讀寫,從該寄存器讀出來的數據可以用于判斷當前IO口的輸出狀態。而向該寄存器寫數據,則可以控制某個IO口的輸出電平。該寄存器的各位描述如下圖所示:
圖5 GPIOx_CRL寄存器
(4)BSRR寄存器是端口位設置/清除寄存器。該寄存器和ODR寄存器具有類似的作用,都可以用來設置GPIO端口的輸出位是1還是0。
圖6 GPIOx_CRL寄存器
通過固件庫操作GPIO
CKS32F107系列GPIO相關的函數和定義分布在固件庫文件cks32f10x_gpio.c和頭文件 cks32f10x_gpio.h文件中。在固件庫開發中,操作寄存器CRH和CRL來配置IO口的模式和速度是通過GPIO初始化函數完成的。
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
這個函數有兩個參數,第一個參數是用來指定GPIO,取值范圍為GPIOA~GPIOG。第二個參數為初始化參數結構體指針,結構體類型為GPIO_InitTypeDef。結構體的定義如下:
typedef struct
{
uint16_t GPIO_Pin;
GPIOSpeed_TypeDef GPIO_Speed;
GPIOMode_TypeDef GPIO_Mode;
}GPIO_InitTypeDef;
下面通過一個GPIO初始化實例來講解這個結構體的成員變量的含義。代碼的意思是設置GPIOB的第5個端口為推挽輸出模式,同時速度為50M。結構體GPIO_InitStructure的第一個成員變量GPIO_Pin用來設置是要初始化哪個或者哪些IO口;第二個成員變量GPIO_Mode是用來設置對應IO端口的輸出輸入模式;第三個參數是IO口速度設置。
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); //根據設定參數配置 GPIO
在固件庫中操作IDR寄存器讀取IO端口數據是通過GPIO_ReadInputDataBit函數實現的。比如我要讀GPIOA5的電平狀態,那么方法是:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);
在固件庫中設置ODR寄存器的值來控制IO口的輸出狀態是通過函數GPIO_Write來實現的,該函數一般用來一次性往一個GPIO的多個端口設值。
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
該寄存器通過舉例子可以很清楚了解它的使用方法。例如你要設置GPIOA的第1個端口值為1,那么你只需要往寄存器BSRR的低16位對應位寫1即可。該寄存器往相應位寫0是無影響的,所以我們要設置某些位,我們不用管其他位的值。
GPIOA->BSRR=1<1;?
在固件庫中,通過BSRR和BRR寄存器設置GPIO端口輸出是通過函數GPIO_SetBits()和函數GPIO_ResetBits()來完成的。在多數情況下,我們都是采用這兩個函數來設置GPIO端口的輸入和輸出狀態。比如我們要設置GPIOB5輸出1和0,那么方法為:
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
GPIO_SetBits(GPIOB, GPIO_Pin_5);
GPIO_ResetBits(GPIOB, GPIO_Pin_5);
GPIO相關的庫函數我們先講解到這里。雖然IO操作步驟很簡單,這里還是做個概括總結,操作步驟為:(1)使能IO口時鐘,調用函數RCC_APB2PeriphClockCmd();(2)初始化IO參數,調用函數GPIO_Init();(3)操作IO。下面我們來講解一個基于CKS32F107VxT6開發板的GPIO按鍵輸入和GPIO輸出實驗軟件例程。
實驗例程
編程要點
(1)使能GPIO端口時鐘;
(2)初始化GPIO按鍵引腳為輸入模式(上拉輸入);
(3)初始化GPIO LED引腳為輸出模式(推挽輸出);
(4)編寫簡單測試程序,檢測按鍵的狀態,實現按鍵控制LED燈。
代碼分析
(1)按鍵檢測引腳相關宏定義
// 引腳定義
#define KEY1_GPIO_CLK RCC_APB2Periph_GPIOC
#define KEY1_GPIO_PORT GPIOC
#define KEY1_GPIO_PIN GPIO_Pin_0
#define LED1_GPIO_PORT GPIOB
#define LED1_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED1_GPIO_PIN GPIO_Pin_15
(2)按鍵GPIO初始化函數
void Key_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*開啟按鍵端口的時鐘*/
RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK,ENABLE);
//選擇按鍵的引腳
GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;
// 設置按鍵的引腳為浮空輸入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
//使用結構體初始化按鍵
GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);
}
(3)LED GPIO初始化函數
void LED_GPIO_Config(void)
{
/*定義一個GPIO_InitTypeDef類型的結構體*/
GPIO_InitTypeDef GPIO_InitStructure;
/*開啟LED相關的GPIO外設時鐘*/
RCC_APB2PeriphClockCmd(LED1_GPIO_CLK, ENABLE);
/*選擇要控制的GPIO引腳*/
GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
/*設置引腳模式為通用推挽輸出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*設置引腳速率為50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*調用庫函數,初始化GPIO*/
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
/* 關閉led燈 */
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
}
(4)主函數
int main(void)
{
/* LED端口初始化 */
LED_GPIO_Config();
/* 按鍵端口初始化 */
Key_GPIO_Config();
/* 輪詢按鍵狀態,若按鍵按下則反轉LED */
while(1)
{
if(GPIO_ReadInputDataBit(KEY1_GPIO_PORT, KEY1_GPIO_PIN) == Bit_RESET)
{
GPIO_WriteBit(LED1_GPIO_PORT, LED1_GPIO_PIN, Bit_SET);
}
else
{
GPIO_WriteBit(LED1_GPIO_PORT, LED1_GPIO_PIN, Bit_RESET);
}
}
}
代碼中先初始化LED燈及按鍵后,在while函數里不斷讀取查詢并判斷按鍵是否按下,若返回值表示按鍵按下,則反轉LED燈的狀態。
-
三極管
+關注
關注
142文章
3611瀏覽量
121923 -
繼電器
+關注
關注
132文章
5334瀏覽量
148894 -
mcu
+關注
關注
146文章
17148瀏覽量
351186 -
GPIO
+關注
關注
16文章
1204瀏覽量
52092
原文標題:MCU微課堂|CKS32F107xx系列GPIO配置
文章出處:【微信號:中科芯MCU,微信公眾號:中科芯MCU】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論