首先先去了解一下GPIO的Register map
GPIO_Init函式實作如下
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
uint32_t pinpos = 0x00, pos = 0x00 , currentpin = 0x00;
uint32_t tmpreg = 0x00;
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));
assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
assert_param(IS_GPIO_PUPD(GPIO_InitStruct->GPIO_PuPd));
/* -------------------------Configure the port pins---------------- */
/*-- GPIO Mode Configuration --*/
for (pinpos = 0x00; pinpos < 0x10; pinpos++)
{
pos = ((uint32_t)0x01) << pinpos;
/* Get the port pins position */
currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
if (currentpin == pos)
{
/* Use temporary variable to update MODER register configuration, to avoid
unexpected transition in the GPIO pin configuration. */
tmpreg = GPIOx->MODER;
tmpreg &= ~(GPIO_MODER_MODER0 << (pinpos * 2));
tmpreg |= (((uint32_t)GPIO_InitStruct->GPIO_Mode) << (pinpos * 2));
GPIOx->MODER = tmpreg;
if ((GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT) || (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_AF))
{
/* Check Speed mode parameters */
assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
/* Use temporary variable to update OSPEEDR register configuration, to avoid
unexpected transition in the GPIO pin configuration. */
tmpreg = GPIOx->OSPEEDR;
tmpreg &= ~(GPIO_OSPEEDER_OSPEEDR0 << (pinpos * 2));
tmpreg |= ((uint32_t)(GPIO_InitStruct->GPIO_Speed) << (pinpos * 2));
GPIOx->OSPEEDR = tmpreg;
/*Check Output mode parameters */
assert_param(IS_GPIO_OTYPE(GPIO_InitStruct->GPIO_OType));
/* Use temporary variable to update OTYPER register configuration, to avoid
unexpected transition in the GPIO pin configuration. */
tmpreg = GPIOx->OTYPER;
tmpreg &= ~((GPIO_OTYPER_OT_0) << ((uint16_t)pinpos));
tmpreg |= (uint16_t)(((uint16_t)GPIO_InitStruct->GPIO_OType) << ((uint16_t)pinpos));
GPIOx->OTYPER = tmpreg;
}
/* Use temporary variable to update PUPDR register configuration, to avoid
unexpected transition in the GPIO pin configuration. */
tmpreg = GPIOx->PUPDR;
tmpreg &= ~(GPIO_PUPDR_PUPDR0 << ((uint16_t)pinpos * 2));
tmpreg |= (((uint32_t)GPIO_InitStruct->GPIO_PuPd) << (pinpos * 2));
GPIOx->PUPDR = tmpreg;
}
}
}
重點是必須了解為何要宣告兩個指標GPIO_TypeDef* GPIOx及GPIO_InitTypeDef* GPIO_InitStruct做傳入參數來使用,因此找出GPIO_TypeDef及GPIO_InitTypeDef結構定義如下,
typedef struct
{
__IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */
__IO uint16_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */
uint16_t RESERVED0; /*!< Reserved, 0x06 */
__IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */
__IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
__IO uint16_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */
uint16_t RESERVED1; /*!< Reserved, 0x12 */
__IO uint16_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */
uint16_t RESERVED2; /*!< Reserved, 0x16 */
__IO uint16_t BSRRL; /*!< GPIO port bit set/reset low registerBSRR, Address offset: 0x18 */
__IO uint16_t BSRRH; /*!< GPIO port bit set/reset high registerBSRR, Address offset: 0x1A */
__IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */
__IO uint32_t AFR[2]; /*!< GPIO alternate function low register, Address offset: 0x20-0x24 */
#if defined (STM32L1XX_HD) || defined (STM32L1XX_XL)
__IO uint16_t BRR; /*!< GPIO bit reset register, Address offset: 0x28 */
uint16_t RESERVED3; /*!< Reserved, 0x2A */
#endif
} GPIO_TypeDef;
typedef struct
{
uint32_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured.
This parameter can be any value of @ref GPIO_pins_define */
GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.
This parameter can be a value of @ref GPIOMode_TypeDef */
GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.
This parameter can be a value of @ref GPIOSpeed_TypeDef */
GPIOOType_TypeDef GPIO_OType; /*!< Specifies the operating output type for the selected pins.
This parameter can be a value of @ref GPIOOType_TypeDef */
GPIOPuPd_TypeDef GPIO_PuPd; /*!< Specifies the operating Pull-up/Pull down for the selected pins.
This parameter can be a value of @ref GPIOPuPd_TypeDef */
}GPIO_InitTypeDef;
而仔細觀察GPIO_TypeDef及GPIO_InitTypeDef會發現結構內宣告的內容全部都在上述GPIO register map圖片當中,
實際在執行GPIO的規劃程式如下,GPIO_Init(GPIOA, &GPIO_InitStructure);就是最後做GPIO設定的程式,
void GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB |\
RCC_AHBPeriph_GPIOC | RCC_AHBPeriph_GPIOD | RCC_AHBPeriph_GPIOH ,ENABLE);
/* Configure GPIOA pins */
GPIO_InitStructure.GPIO_Pin = BKL_BTN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
返回去看一下void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)這個初始化GPIO的Library,如前述宣告兩個指標GPIO_TypeDef* GPIOx及GPIO_InitTypeDef* GPIO_InitStruct做傳入參數來使用,在
GPIO_Init(GPIOA, &GPIO_InitStructure)執行時GPIO_TypeDef*會指向GPIOA及GPIO_InitTypeDef*會指向GPIO_InitStructure,因此必須繼續挖出GPIOA是什麼,接著找出相關定義如下:
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
由於GPIO屬於AHB Bus,因此找到定義如下,並查看上述圖片table 5.可以了解PERIPH_BASE的address是0x40000000,而GPIOA在AHBPERIPH的offset是0x20000,
#define GPIOA_BASE (AHBPERIPH_BASE + 0x0000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
總結
GPIO_Init(GPIOA, &GPIO_InitStructure)的使用就是GPIO_TypeDef*指標指向GPIOA的位址,然後透過底層的Driver去設定GPIOA的硬體,而GPIO_InitTypeDef*指向GPIO_InitStructure,GPIO_InitStructure則會設定IO相關工作模式,例如輸出入等等,由於GPIO_InitStructure只是一個結構變數,因此需要用取址運算元"&"去指向它,相關細節可能要更加了解硬體底層的工作才能了解,能了解最好,如都不懂的話只要懂得指標相關原理也是可以寫得出來!