STM32 GPIO的API函數配置和使用技巧

2023-04-14 11:08:16 來源:明解嵌入式

一、前言


【資料圖】

本篇開始對STM32的GPIO在實際開發設計中的使用配置和技巧進行探討,可以先去回顧下之前介紹的GPIO的相關理論基礎知識包括基本結構,工作模式和寄存器原理。

了解過STM32的GPIO相關的理論知識,這樣在應用GPIO開發過程中,能更好的理解GPIO的特點,應用起來會更加的得心應手。

后續將從以下圖1中所示的幾個方面對GPIO應用設計中的步驟展開介紹。 本篇先介紹GPIO的基本API函數定義,配置初始化的流程,以及使用技巧; 針對將GPIO的引腳用于外部中斷的功能將作為單獨的一篇進行詳細的討論介紹。

圖1 GPIO應用設計

二、API函數

STM32有多種類型的庫,本節所介紹的STM32的GPIO函數接口是STM32標準庫的函數接口,接口總共分為4種類型,如圖2所示。

圖2 GPIO庫函數接口分類

1、關鍵參數

在詳細介紹各個API函數接口功能之前,我們需要對函數接口中使用到的關鍵的幾個參數進行分析。

(1)、GPIO_TypeDefGPIOx*

這個參數是用于指定需要具體的GPIO端口號定義,參數的范圍為GPIOA~GPIOK。

(2)、GPIO_InitTypeDefGPIO_InitStruct*

這個參數是GPIO端口需要初始化的功能參數的結構體指針,下面我們看看這個結構體的定義。

typedef struct{    uint32_t GPIO_Pin;            //GPIO端口的引腳    GPIOMode_TypeDef GPIO_Mode;   //GPIO的端口模式                                             GPIOSpeed_TypeDef GPIO_Speed; //GPIO的輸出速度頻率    GPIOOType_TypeDef GPIO_OType; //GPIO輸出時的類型    GPIOPuPd_TypeDef GPIO_PuPd;   //GPIO上下拉電阻設置                                       }GPIO_InitTypeDef;

(a)、GPIO端口的引腳:可選范圍為GPIO_Pin_0~GPIO_Pin_15,也可以選所有引腳GPIO_Pin_All。

(b)、GPIO的端口模式:用于設置GPIO的端口模式,可選的端口模式如下。

typedef enum{    GPIO_Mode_IN  = 0x00, //普通IO口輸入    GPIO_Mode_OUT = 0x01, //普通IO口輸出    GPIO_Mode_AF  = 0x02, //管腳復用功能    GPIO_Mode_AN  = 0x03  //模擬輸入,用于ADC功能}GPIOMode_TypeDef;

(c)、GPIO的輸出速度頻率:當GPIO引腳用于普通功能輸出或復用功能輸出時,GPIO的輸出速度頻率,可選的輸出速率如下。

typedef enum{    GPIO_Low_Speed     = 0x00, //GPIO_Speed_2MHz    GPIO_Medium_Speed  = 0x01, //GPIO_Speed_25MHz    GPIO_Fast_Speed    = 0x02, //GPIO_Speed_50MHz    GPIO_High_Speed    = 0x03  //GPIO_Speed_100MHz}GPIOSpeed_TypeDef;

速度高的IO耗電大、噪聲也大,速度低的IO耗電小、噪聲也小。 使用合適的速度可以降低功耗和噪聲。 高頻的驅動電路,噪聲也高,當不需要高的輸出頻率時,請選用低頻驅動電路,這樣非常有利于提高系統的EMI性能,也可以降低功耗。 當然如果要輸出較高頻率的信號,但卻選用了較低頻率的速度,很可能會得到失真的輸出信號。 關鍵是GPIO的引腳速度跟應用匹配。

(d)、GPIO輸出時的類型:當GPIO引腳用于普通功能輸出或復用功能輸出時,可選擇設置的GPIO的輸出結構類型有如下。

typedef enum{    GPIO_OType_PP = 0x00, //推挽結構    GPIO_OType_OD = 0x01 //開漏結構}GPIOOType_TypeDef;

推挽輸出時,可以輸出高或者低電平; 開漏輸出時,如果要輸出高電平,則需要在芯片內部配置上拉電阻(弱上拉)或者在芯片IO外部連接上拉電阻。

(e)、GPIO上下拉電阻設置:可以為GPIO端口的引腳選擇設置是否具備帶上拉或下拉電阻功能。

typedef enum{    GPIO_PuPd_NOPULL = 0x00, //無上拉或者下拉    GPIO_PuPd_UP     = 0x01, //帶上拉電阻    GPIO_PuPd_DOWN   = 0x02 //帶下拉電阻}GPIOPuPd_TypeDef;

STM32芯片GPIO的上拉電阻和下拉電阻最小值,典型值和最大值如下:

(3)、uint16_t GPIO_PinSource和uint8_t GPIO_AF

這兩個參數都是GPIO端口引腳需要配置成復用功能引腳用到的參數。

GPIO_PinSource:指需配置的復用功能引腳源,可選范圍GPIO_PinSource0

~GPIO_PinSource15。

GPIO_AF:指該引腳具體需要配置的功能,具體配置功能要看實際應用需求,例如需要配置成SPI1功能的引腳,那么就選GPIO_AF_SPI1。

2、函數接口

下面就對具體的函數接口進行逐個的介紹。 由于使用的是STM32的標準庫,GPIO 相關的函數及配置定義和可以調用的接口放置在官方提供的標準庫文件 stm32fxx_gpio.c和頭文件 stm32fxx_gpio.h 文件中。

(1)、無效GPIO_DeInit(GPIO_TypeDefGPIOx);*

作用:將GPIO端口設置成初始的默認狀態,相當于復位GPIO端口,默認的狀態為輸入浮空的狀態。

舉例:GPIO_DeInit(GPIOA),將GPIOA端口所有引腳復位到默認狀態。

(2)、無效GPIO_Init(GPIO_TypeDefGPIOx, GPIO_InitTypeDefGPIO_InitStruct);**

作用:將GPIO端口引腳進行功能狀態初始化。

舉例:將GPIOA的pin1引腳設為普通輸出功能,IO驅動速率可達50MHz,推挽模式,帶上拉電阻。

gpio_InitStruct.GPIO_Pin = GPIO_Pin_1;gpio_InitStruct. GPIO_Mode = GPIO_Mode_OUT;gpio_InitStruct.GPIO_Speed = GPIO_Fast_Speed;gpio_InitStruct. GPIO_OType = GPIO_OType_PP;gpio_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOA, &gpio_InitStruct);

(3)、無效GPIO_StructInit(GPIO_InitTypeDefGPIO_InitStruct);*

作用:獲取GPIO端口的所有引腳的一個默認狀態,可應用于某個GPIO端口上。 該函數內部默認的引腳默認狀態如下。

GPIO_InitStruct->GPIO_Pin  = GPIO_Pin_All;GPIO_InitStruct->GPIO_Mode = GPIO_Mode_IN;GPIO_InitStruct->GPIO_Speed = GPIO_Speed_2MHz;GPIO_InitStruct->GPIO_OType = GPIO_OType_PP;GPIO_InitStruct->GPIO_PuPd = GPIO_PuPd_NOPULL;

舉例:使用gpio_InitStruct快速獲取到了引腳的默認狀態值。

GPIO_StructInit(&gpio_InitStruct);

(4)、無效GPIO_PinLockConfig(GPIO_TypeDefGPIOx, uint16_t GPIO_Pin);*

作用:將指定GPIO端口引腳當前的配置進行鎖定,鎖定后該引腳配置不能被修改,只有等下次MCU復位鎖定才能釋放。

舉例:鎖定GPIOA的管腳pin1配置不被修改。

GPIO_PinLockConfig(GPIOA, GPIO_Pin_1);

(5)、uint8_t GPIO_ReadInputDataBit(GPIO_TypeDefGPIOx, uint16_t GPIO_Pin);*

作用:為當GPIO的相應管腳配置成輸入時,讀取該GPIO端口下的相應引腳輸入電平值。

舉例:讀取GPIOA的pin1引腳輸入電平值。

status = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1);

(6)、uint16_t GPIO_ReadInputData(GPIO_TypeDefGPIOx);*

作用:為當GPIO配置成輸入時,讀取該GPIO端口下的所有引腳輸入電平值。

舉例:讀取GPIOA端口所有引腳的輸入電平值。

status = GPIO_ReadInputData(GPIOA);

(7)、uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDefGPIOx, uint16_t GPIO_Pin);*

作用:為當GPIO的相應管腳配置成輸出時,讀取該GPIO端口下的相應引腳輸出電平值。

舉例:讀取GPIOA的pin1引腳輸出電平值。

status = GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1);

(8)、uint16_t GPIO_ReadOutputData(GPIO_TypeDefGPIOx);*

作用:為當GPIO配置成輸出時,讀取該GPIO端口下的所有引腳輸出電平值。

舉例:讀取GPIOA端口所有引腳的輸出電平值。

status = GPIO_ReadOutputData(GPIOA);

(9)、無效GPIO_SetBits(GPIO_TypeDefGPIOx, uint16_t GPIO_Pin);*

作用:置位相應GPIO端口引腳的電平值。

舉例:將GPIOA的pin1管腳電平置為1。

GPIO_SetBits(GPIOA, GPIO_Pin_1);

也可以用于多個引腳電平的置位,

GPIO_SetBits(GPIOA, GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);

(10)、無效GPIO_ResetBits(GPIO_TypeDefGPIOx, uint16_t GPIO_Pin);*

作用:清零相應GPIO端口引腳的電平值。

舉例:將GPIOA的pin1管腳電平置為0,

GPIO_ResetBits(GPIOA, GPIO_Pin_1);

也可以用于多個引腳電平的清零。

GPIO_ResetBits(GPIOA, GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);

(11)、無效GPIO_WriteBit(GPIO_TypeDefGPIOx, uint16_t GPIO_Pin, BitAction BitVal);*

作用:將GPIO端口的指定管腳電平置1或置0。

舉例:將GPIOA的pin1管腳電平置為1。

GPIO_WriteBit(GPIOA, GPIO_Pin_1, 1);

也可以用于多個引腳電平操作。

GPIO_WriteBit(GPIOA, GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3, 1)

(12)、無效GPIO_Write(GPIO_TypeDefGPIOx, uint16_t PortVal);*

作用:將GPIO端口的所有管腳電平置1或置0。

舉例:將GPIOA端口的所有管腳電平置為1。

GPIO_Write(GPIOA, 1);

(13)、無效GPIO_ToggleBits(GPIO_TypeDefGPIOx, uint16_t GPIO_Pin);*

作用:翻轉指定GPIO引腳的輸出電平,即0變為1,1變為0。

舉例:翻轉GPIOA的pin1管腳電平值。

GPIO_ToggleBits(GPIOA , GPIO_Pin_1);

(14)、無效GPIO_PinAFConfig(GPIO_TypeDefGPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF);*

作用:將GPIO的指定管腳配置成復用功能管腳。

舉例:將GPIOA的pin9管腳配置成串口USART1的功能管腳。

GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);

三、配置流程

配置流程主要在實際的驅動配置中對GPIO進行初始化的操作,根據實際項目應用開發中的芯片GPIO引腳的定義,進行合理的配置。 圖3所示為GPIO的基本配置操作流程。

圖3 GPIO配置流程

(1)、使能對應GPIO的時鐘

在配置GPIO的開始,首先需要將對應的GPIO模塊的時鐘打開,這樣才能為GPIO工作提供動力源,因此只有先將GPIO的時鐘打開才能使GPIO正常的工作。

關于STM32芯片內部整體的時鐘系統,可以回顧之前明解STM32時鐘系統的文章介紹。 STM32的GPIO模塊是掛載在芯片內部AHB1總線(AHB:高級高性能總線)上的外設,因此就需要打開GPIO在AHB1總線上對應的時鐘。 AHB1總線上的外設時鐘開關在STM32提供的標準庫函數中通過函數 RCC_AHB1PeriphClockCmd ()來實現的。 例如調用:

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

這樣就將GPIOA的時鐘打開,也可以同時打開多個GPIO端口的時鐘:

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB, ENABLE);

(2)、引腳功能配置

接下來對GPIO引腳的配置是需要根據實際的項目應用要求,根據各個芯片管腳的定義來對引腳的功能進行合理的配置,主要是根據引腳是使用成普通IO輸出還是輸入,復用功能還是模擬管腳來調用GPIO_Init()函數進行配置。 舉例說明:

用于普通IO輸出時:

GPIO_WriteBit(GPIOA, GPIO_Pin_1, 1);//向引腳輸出0或1電平,在GPIO_Init前調用GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//設置使用引腳GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通IO輸出GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//根據實際應用配置輸出結構類型GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//根據實際應用配置輸出速度GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//根據實際應用配置上拉或下拉電阻GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化PA1引腳

需要注意的是,初始化輸出電平時,需要先調用寫引腳電平接口,再做初始化操作,這是因為GPIO_WriteBit是將輸出的值寫入寄存器輸出置位/復位寄存器BSRR,BSRR寄存器復位值是0,GPIO_Init相當于將GPIO引腳初始化完打開輸出開關。 如果需要輸出的是高電平,GPIO_WriteBit在前,GPIO_Init在后相當于在沒打開開關之前就將1在BSRR中放置好,GPIO_Init將開關一打開就可以輸出高電平; 如果GPIO_Init在前,GPIO_WriteBit在后,GPIO_Init完會將BSRR中的0先輸出,過了一個函數指令周期后調用GPIO_WriteBit才輸出高電平,因此若驅動時序對函數指令周期敏感的外圍器件時,可能帶來驅動時序問題!

用于普通IO輸入時:

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//設置使用引腳GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通IO輸入GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//根據實際應用配置輸出速度GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//根據實際應用配置上拉或下拉電阻GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化PA1引腳

用于復用功能時:

在管腳應用于復用功能時,需要調用GPIO_PinAFConfig()接口來將管腳配置成具體的外設管腳。

GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //PA9 復用為 USART1GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //PA10復用為USART1GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //設置使用引腳GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//復用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //根據實際應用配置輸出速度GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //根據實際應用配置輸出結構類型GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //根據實際應用配置上拉或下拉電阻GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9和PA10引腳

用于模擬管腳時:

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PA5 通道 5GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模擬輸入GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//不帶上下拉GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化PA5引腳

當STM32需要進行 AD( 模數 ) 轉換采樣時,需要把引腳設置為模擬輸入模式,模擬輸入模式下,不需要連接上拉和下拉電阻,因為GPIO用于模擬功能時,引腳的上、下拉電阻是不起作用的。 這個時候即使在配置了上拉或下拉電阻,也不會影響到模擬信號的輸入。

(3)、對GPIO引腳進行操作

在初始化完GPIO引腳的具體配置后,就可以對GPIO引腳進行具體的操作使用了。

用于普通IO輸出時:

可以調用相關GPIO相關寫操作接口對引腳進行輸出0或者1的操作:GPIO_SetBits、GPIO_ResetBits、GPIO_WriteBit、GPIO_ToggleBits。

也可以調用相關GPIO讀接口對輸出類型的GPIO進行讀取引腳電平的操作GPIO_ReadOutputDataBit。

用于普通IO輸入時:

可以調用相關GPIO讀接口對輸入類型的GPIO進行讀取引腳電平的操作:GPIO_ReadInputDataBit。

用于復用功能時:

需要根據實際使用時的具體外設配置,接著初始化相應的片上外設后,調用具體的外設信號讀或者寫接口進行信號的讀寫操作。

用于模擬管腳時:

由于模擬管腳功能是用芯片上的ADC對芯片外部的模擬信號進行采樣,因此還需要初始化完ADC外設后,調用ADC外設采樣的接口進行信號讀取。

四、使用技巧

在日常程序開發調試的過程中,可以簡單有效的利用GPIO驅動輸出高低電平來進行輔助的測試及驗證工作。 下面介紹幾個較為常用的使用場景,如果有其它可以利用GPIO的方法和技巧,也請大家積極留言,我們一起探討。

(1)、在boot程序階段使用IO翻轉輸出信號的頻率可以和APP程序階段使用IO翻轉輸出信號的頻率相異,通過使用示波器測量波形,用于區分程序是運行在boot程序階段還是APP程序階段,即不同程序階段。

(2)、在使用定時器中斷的時候,為了確保定時器時基設置的正確性,測試是可以定時器中斷中增加IO口信號翻轉邏輯,通過使用示波器測量翻轉的頻率來測試驗證定時器中斷的周期。

void TIM1_IRQHandler(void) //定時器 1 中斷服務函數{    if(TIM_GetITStatus(TIM1,TIM_IT_Update)==SET) //溢出中斷    {        GPIO_ToggleBits(); //IO口信號翻轉操作邏輯,用于驗證定時器中斷頻率    }    TIM_ClearITPendingBit(TIM1,TIM_IT_Update); //清除中斷標志位}

(3)、在不同的程序段中使用多個IO,輸出高電平,通過示波器測量IO口之間輸出高電平的間隔,可以確定兩個程序段之間運行的準確時間。

(4)、在板卡上沒有LED進行閃爍指示的情況或沒有使用外部看門狗芯片的情況下,為了確認程序是否仍然在正常運行,需要留出一個IO口,用于翻轉高低電平輸出,后續就可以用示波器測量該信號的有無來判斷程序是否死機。

(5)、在沒有調試打印程序信息的串口時,查找死機問題的時候,放置不同的IO輸出高電平的在不同的程序段,這樣類似的進行插樁驅動測試,通過示波器測量信號,可以大體的定位在程序運行的哪一塊發生了死機的問題。

(6)、在測試驗證階段,可以將某個IO引腳配置成輸入模式,利用外部給的激勵信號,在程序中判斷讀到的信號電平的高低狀態,去作為邏輯判斷條件進行一些代碼段的驗證測試。

五、總結

本篇主要主要是對STM32的GPIO在日常基本應用開發中的具體的操作配置和使用方法進行了說明,包括API功能函數的定義,驅動初始化的配置流程以及一些利用GPIO操作的相關技巧,后續將對GPIO使用成外部中斷時進行詳細的介紹。

標簽:

上一篇:
下一篇: