完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980
第17章 STM32H7之GPIO的HAL庫API
本章教程為大家講解GPIO(General-purpose I/Os)的API使用和注意事項。GPIO是所有外設里面較容易掌握的,但也是用到最多的。
配合第15章講解的各種IO模式再學習本章,更容易理解透徹。
17.1 初學者重要提示
17.2 GPIO涉及到的寄存器
17.3 源文件stm32h7xx_hal_gpio.c
17.4 如何使用HAL庫的GPIO驅動
17.5 總結
17.1 初學者重要提示
1、 如何閱讀HAL庫源碼的問題
HAL庫實現的函數有復雜的,也有簡單的,簡單的可以直接閱讀代碼。復雜的代碼閱讀起來比較耗時間,如果再配合參考手冊摳每個寄存器的配置,那就更消耗時間了。所以對於這種函數,用戶僅需了解每個部分實行的功能即可,而且HAL庫都做了關鍵注釋,以說明這部分實現的功能。所以用戶沒有必要去摳每個配置是如何實現的,僅需知道實現了什么功能。以后工程項目有需要了解具體配置時,再看即可。
2、 學習本章節前,務必保證已經學習了第15章。
17.2 GPIO涉及到的寄存器
GPIO外設涉及到的寄存器比較少,也容易理解,推薦大家閱讀GPIO源碼的時候將參考手冊中對應的寄存器功能做一個了解。
很多時候,我們會直接調用GPIO的寄存器進行配置,而不使用HAL進行調用,以提高執行效率,特別是中斷里面執行時。
17.3 源文件stm32h7xx_hal_gpio.c
這個文件主要是實現GPIO的引腳配置,學習這個文件注意事項:
- 系統上電后,引腳默認狀態是模擬模式。
- 所有的引腳有弱上拉和弱下拉電阻,阻值范圍30-50KΩ。其中配置為模擬模式時,上拉和下拉被硬件禁止,其它的輸入、輸出和復用都可以配置上拉和下拉。
- 在輸出或者復用模式,每個引腳可以配置成推挽或者開漏,且有GPIO速度等級可配置。另外注意,不同的供電范圍,實際速度等級是有些區別的。
- 每個GPIO都可以配置成外部中斷/事件模式,但要特別注意,引腳要配置成輸入模式,在芯片的內部有個多路選擇器,選擇引腳與16個外部中斷/事件EXTI0 - EXTI15中的那個導通。這就決定了,每個外部中斷/事件只能與一個引腳導通,如果用戶配置了多個引腳PA0,PB0,PC0等,那么只有一個能夠與EXTI0導通。
17.3.1 函數HAL_GPIO_Init
函數原型:
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) { /* 部分省略未寫 */ /* 配置GPIO引腳,這些采用16個引腳的循環檢測模式 */ for(position = 0; position < GPIO_NUMBER; position++) { /* 部分省略未寫 */ if(iocurrent == ioposition) { /*--------------------- GPIO模式配置 ------------------------*/ /*--------------------- EXTI模式配置 ------------------------*/ } } }
函數描述:
此函數用於初始化GPIO,此函數主要實現如下功能:
- GPIO功能配置。
- 設置EXTI功能。
函數參數:
- 第1個參數用於填寫使用的端口號,可以是:
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) #define GPIOH ((GPIO_TypeDef *) GPIOH_BASE) #define GPIOI ((GPIO_TypeDef *) GPIOI_BASE) #define GPIOJ ((GPIO_TypeDef *) GPIOJ_BASE) #define GPIOK ((GPIO_TypeDef *) GPIOK_BASE)
- 第2個形參是GPIO_InitTypeDef類型的結構體變量,這個變量比較重要,要熟練掌握,定義如下:
typedef struct { uint32_t Pin; uint32_t Mode; uint32_t Pull; uint32_t Speed; uint32_t Alternate; }GPIO_InitTypeDef;
下面將結構體每個成員做個說明:
- 成員Pin用於配置選擇的引腳,范圍GPIO_PIN_0到GPIO_PIN_15,額外還可以選擇GPIO_PIN_All和GPIO_PIN_MASK。
- 成員Mode可以選擇:
GPIO_MODE_INPUT /* 輸入模式 */ GPIO_MODE_OUTPUT_PP /* 推挽輸出 */ GPIO_MODE_OUTPUT_OD /* 開漏輸出 */ GPIO_MODE_AF_PP /* 復用推挽 */ GPIO_MODE_AF_OD /* 復用開漏 */ GPIO_MODE_ANALOG /* 模擬模式 */ GPIO_MODE_IT_RISING /* 外部中斷,上升沿觸發檢測 */ GPIO_MODE_IT_FALLING /* 外部中斷,下降沿觸發檢測 */ GPIO_MODE_IT_RISING_FALLING /* 外部中斷,雙沿觸發檢測 */ GPIO_MODE_EVT_RISING /* 外部事件模式,上升沿觸發檢測 */ GPIO_MODE_EVT_FALLING /* 外部事件模式,下降沿觸發檢測 */ GPIO_MODE_EVT_RISING_FALLING /* 外部事件模式,雙沿觸發檢測 */
- 成員Pull用於配置上拉下拉電阻:
GPIO_NOPULL /* 無上拉和下拉電阻 */ GPIO_PULLUP /* 帶上拉電阻 */ GPIO_PULLDOWN /* 帶下拉電阻 */
- 成員Speed用於配置GPIO速度等級,有下面四種可選:
GPIO_SPEED_FREQ_LOW /* 低速 */ GPIO_SPEED_FREQ_MEDIUM /* 中等速度 */ GPIO_SPEED_FREQ_HIGH /* 快速 */ GPIO_SPEED_FREQ_VERY_HIGH /* 高速 */
- 成員Alternate用於配置引腳復用,可選擇的復用方式在文件stm32h7xx_hal_gpio_ex.h里面進行了定義,比如串口復用:
GPIO_AF7_USART1
GPIO_AF7_USART2
GPIO_AF7_USART3
GPIO_AF7_USART6
GPIO_AF7_UART7
注意事項:
- 與F1,F4系列的標准庫不同,H7的HAL庫已經沒有單獨的EXTI外部中斷設置文件,是將其整合到此函數里面了。
- 函數HAL_GPIO_Init對引腳的初始化是把同組16個引腳for循環檢測了一遍,效率稍低。所以不推薦下面這種初始化:
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Pin = GPIO_PIN_0; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //這里會執行16次for查詢 GPIO_InitStruct.Pin = GPIO_PIN_1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //這里會執行16次for查詢 GPIO_InitStruct.Pin = GPIO_PIN_2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //這里會執行16次for查詢
如果是程序運行期間的引腳狀態切換,最好采用下面的方式或者直接寄存器操作:
GPIO_InitStruct.Pin = GPIO_PIN_0 |GPIO_PIN_1 | GPIO_PIN_2 ; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //這里會執行16次for查詢
使用舉例:
GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽輸出 */ GPIO_InitStruct.Pull = GPIO_NOPULL; /* 無上拉和下拉電阻 */ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* GPIO速度等級最高 */ HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
17.3.2 函數HAL_GPIO_DeInit
函數原型:
void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin) { for(position = 0; position < GPIO_NUMBER; position++) { /* 部分省略未寫 */ if(iocurrent == ioposition) { /*------------------------- GPIO Mode Configuration --------------------*/ /* 配置為模擬模式 */ GPIOx->MODER |= (GPIO_MODER_MODER0 << (position * 2)); /* 配置復用模式為AF0,即作為通用IO */ GPIOx->AFR[position >> 3] &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ; /* 配置到最低速度 */ GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2)); /* 輸出類型是推挽,如果IO模式被設置為模擬,此選項對其沒有影響 */ GPIOx->OTYPER &= ~(GPIO_OTYPER_OT_0 << position) ; /* 無上拉和下拉電阻 */ GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << (position * 2)); /*------------------------- EXTI模式配置 --------------------*/ } } }
函數描述:
此函數用於復位IO到初始化狀態,具體狀態看函數原型中的注釋即可。
函數參數:
- 第1個參數用於填寫使用的端口號,可以是:
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) #define GPIOH ((GPIO_TypeDef *) GPIOH_BASE) #define GPIOI ((GPIO_TypeDef *) GPIOI_BASE) #define GPIOJ ((GPIO_TypeDef *) GPIOJ_BASE) #define GPIOK ((GPIO_TypeDef *) GPIOK_BASE)
- 第2個參數是配置選擇的引腳,范圍GPIO_PIN_0到GPIO_PIN_15。
使用舉例:
此函數的使用比較簡單,需要調用的時候直接調用即可。
17.3.3 函數HAL_GPIO_ReadPin
函數原型:
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { GPIO_PinState bitstatus; /* Check the parameters */ assert_param(IS_GPIO_PIN(GPIO_Pin)); if((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET) { bitstatus = GPIO_PIN_SET; } else { bitstatus = GPIO_PIN_RESET; } return bitstatus; }
函數描述:
此函數用於讀取引腳狀態,通過GPIO的IDR寄存器讀取。
函數參數:
- 第1個參數用於填寫使用的端口號,從GPIOA到GPIAK。
- 第2個參數是配置選擇的引腳,范圍GPIO_PIN_0到GPIO_PIN_15。
使用舉例:
此函數的使用比較簡單,需要調用的時候直接調用即可。
17.3.4 函數HAL_GPIO_WritePin
函數原型:
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState) { /* Check the parameters */ assert_param(IS_GPIO_PIN(GPIO_Pin)); assert_param(IS_GPIO_PIN_ACTION(PinState)); if(PinState != GPIO_PIN_RESET) { GPIOx->BSRRL = GPIO_Pin; } else { GPIOx->BSRRH = GPIO_Pin ; } }
函數描述:
此函數用於設置引腳輸出高電平或者低電平。使用GPIO的BSRR寄存器進行設置,使用這個寄存器的好處是支持原子操作,由硬件支持的。原子操作的含義是操作過程不會被中斷打斷,而我們使用GPIO中另一個設置輸出的寄存ODR是會被中斷打斷的。大家看下寄存器賦值操作對應的反匯編,是由多條匯編指令組成的。
函數參數:
- 第1個參數用於填寫使用的端口號,從GPIOA到GPIAK。
- 第2個參數是配置選擇的引腳,范圍GPIO_PIN_0到GPIO_PIN_15。
- 第3個參數用於設置引腳輸出高電平還是低電平,GPIO_PIN_RESET表示低電平,GPIO_PIN_SET表示高電平。
使用舉例:
此函數的使用比較簡單,需要調用的時候直接調用即可。
17.3.5 函數HAL_GPIO_TogglePin
函數原型:
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { /* Check the parameters */ assert_param(IS_GPIO_PIN(GPIO_Pin)); GPIOx->ODR ^= GPIO_Pin; }
函數描述:
此函數用於設置引腳的電平翻轉,使用GPIO的ODR寄存器進行設置。
函數參數:
- 第1個參數用於填寫使用的端口號,從GPIOA到GPIAK。
- 第2個參數是配置選擇的引腳,范圍GPIO_PIN_0到GPIO_PIN_15。
使用舉例:
此函數的使用比較簡單,需要調用的時候直接調用即可。
17.3.6 函數HAL_GPIO_LockPin
函數原型:
HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { __IO uint32_t tmp = GPIO_LCKR_LCKK; assert_param(IS_GPIO_LOCK_INSTANCE(GPIOx)); assert_param(IS_GPIO_PIN(GPIO_Pin)); /* 應用IO鎖的寫入順序 */ tmp |= GPIO_Pin; /* 設置 LCKx bit(s): LCKK='1' + LCK[15-0] */ GPIOx->LCKR = tmp; /* 復位 LCKx bit(s): LCKK='0' + LCK[15-0] */ GPIOx->LCKR = GPIO_Pin; /* 設置 LCKx bit(s): LCKK='1' + LCK[15-0] */ GPIOx->LCKR = tmp; /* 復位 LCKK bit*/ tmp = GPIOx->LCKR; if((GPIOx->LCKR & GPIO_LCKR_LCKK) != RESET) { return HAL_OK; } else { return HAL_ERROR; } }
函數描述:
此函數用於鎖住GPIO引腳所涉及到的寄存器,這些寄存器包括GPIOx_MODER,GPIOx_OTYPER,GPIOx_OSPEEDR,GPIOx_PUPDR,GPIOx_AFRL 和 GPIOx_AFRH。
函數參數:
- 第1個參數用於填寫使用的端口號,從GPIOA到GPIAK。
- 第2個參數是配置選擇的引腳,范圍GPIO_PIN_0到GPIO_PIN_15。
注意事項:
- 此函數是鎖住用戶設置的引腳所對應的寄存器某些位,並不是把整個寄存器都鎖住了。
- 一旦鎖住后,就不能再修改,只有復位后才可以重新配置。
使用舉例:
此函數的使用比較簡單,需要調用的時候直接調用即可。
17.4 如何使用HAL庫的GPIO驅動
使用方法由HAL庫提供(本章17.3.1小節提供的例子就是這種方式):
第1步:使能GPIO所在總線的AHB時鍾,__HAL_RCC_GPIOx_CLK_ENABLE()。
第2步:通過函數HAL_GPIO_Init()配置GPIO。
(1) 通過結構體GPIO_InitTypeDef的成員Mode配置輸入、輸出、模擬等模式。
(2) 通過結構體GPIO_InitTypeDef的成員Pull配置上拉、下拉電阻。
(3) 通過結構體GPIO_InitTypeDef的成員Speed配置GPIO速度等級。
(4) 如果選擇了復用模式,那么就需要配置結構體GPIO_InitTypeDef的成員Alternate。
(5) 如果引腳功能用於ADC、DAC的話,需要配置引腳為模擬模式。
(6) 如果是用於外部中斷/事件,結構體GPIO_InitTypeDef的成員Mode可以配置相應模式,相應的上升沿、下降沿或者雙沿觸發也可以選擇。
第3步:如果配置了外部中斷/事件,可以通過函數HAL_NVIC_SetPriority設置優先級,然后調用函數HAL_NVIC_EnableIRQ使能此中斷。
第4步:輸入模式讀取引腳狀態可以使用函數HAL_GPIO_ReadPin。
第5步:輸出模式設置引腳狀態可以調用函數HAL_GPIO_WritePin()和HAL_GPIO_TogglePin。
另外注意下面三個問題:
- 系統上電復位后,GPIO默認是模擬模式,除了JTAG相關引腳。
- 關閉LSE的話,用到的兩個引腳OSC32_IN和OSC32_OUT(分別是PC14,PC15)可以用在通用IO,如果開啟了,就不能再做GPIO。
- 關閉HSE的話,用到的兩個引腳OSC_IN和OSC_OUT(分別是PH0,PH1)可以用在通用IO,如果開啟了,就不能再做GPIO。
17.5 總結
本章節就為大家講解這么多,建議大家將GPIO的驅動源碼結合參考手冊中的寄存器通讀一遍,對於我們后面章節的學習大有裨益。