GPIO寄存器地址與結構體結合


GPIOx寄存器結構體

/** stm32f10x.h頭文件中,使用了很多與寄存器對應的結構體來描述GPIO寄存器的數據結構 **/
typedef struct
{
  __IO uint32_t CRL;		/* Address offset: 0x00 */
  __IO uint32_t CRH;		/* Address offset: 0x04 */
  __IO uint32_t IDR;
  __IO uint32_t ODR;
  __IO uint32_t BSRR;
  __IO uint32_t BRR;
  __IO uint32_t LCKR;
} GPIO_TypeDef;

/** stm32f40xx.h頭文件 **/
typedef struct
{
  __IO uint32_t MODER;    /*!< GPIO port mode register,               Address offset: 0x00      */
  __IO uint32_t OTYPER;   /*!< GPIO port output type register,        Address offset: 0x04      */
  __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 uint32_t IDR;      /*!< GPIO port input data register,         Address offset: 0x10      */
  __IO uint32_t ODR;      /*!< GPIO port output data register,        Address offset: 0x14      */
  __IO uint16_t BSRRL;    /*!< GPIO port bit set/reset low register,  Address offset: 0x18      */
  __IO uint16_t BSRRH;    /*!< GPIO port bit set/reset high register, Address offset: 0x1A      */
  __IO uint32_t LCKR;     /*!< GPIO port configuration lock register, Address offset: 0x1C      */
  __IO uint32_t AFR[2];   /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
} GPIO_TypeDef;

從圖中可以看出,GPIOx的7個寄存器都是32位的,每個寄存器占用4個字節,一共占用28個字節,地址偏移范圍為000H~01BH。這7個寄存器的地址偏移量是相對GPIOx的基地址而言的,每個寄存器的地址偏移量不變。

GPIOx的基地址是怎么算出來的呢?

(1)獲得GPIOA基地址

由於GPIO都是掛載在APB2總線上的,所以GPIOA的基地址是由APB2總線的基地址和GPIOA在APB2總線上的偏移地址決定的。獲得GPIOA基地址的過程如下。

打開stm32f10x.h頭文件,先定位到GPIO_TypeDef結構體定義處,前面已給出了定義GPIO寄存器結構的結構體。然后定位到GPIOA的宏定義:

#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)

通過宏定義,把GPIOA_BASE強制轉化為GPIO_TypeDef類型指針,用定義的宏名GPIOA代替GPIOA_BASE。這樣就可以將GPIOA作為GPIO_TypeDef結構體類型的一個指針,其基地址是GPIOA_BASE。

然后再定位到GPIOA_BASE的宏定義:

/** stm32f10x.h頭文件 **/
#define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00)

/** stm32f40xx.h頭文件 **/
#define GPIOA_BASE            (AHB1PERIPH_BASE + 0x0000)
#define GPIOB_BASE            (AHB1PERIPH_BASE + 0x0400)

以此類推定位到最后兩個位置:

#define APB1PERIPH_BASE       PERIPH_BASE
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)
/***************************************************************************************/
#define PERIPH_BASE           ((uint32_t)0x40000000)  //外設區基地址

由此可以計算出GPIOA的基地址:

GPIOA_BASE = 0x40000000 + 0x10000 + 0x0800 = 0x40010800

(2)GPIOA寄存器地址

在前面我們已經知道如何獲得GPIOA的基地址,那么GPIOA的7個寄存器地址又是如何獲得的呢?GPIOA寄存器地址的計算公式如下:

GPIOA寄存器地址 = GPIOA基地址 + 寄存器相對GPIOA基地址的偏移量

GPIOx端口復用使用

STM32有很多的內置外設,這些內置外設的引腳都是與GPIO引腳復用的。簡單地說,GPIO的引腳可以重新定義為其他功能,這就叫作端口復用。

在使用默認的復用功能前,必須對復用的端口進行初始化。下面以串口2為例,初始化步驟如下如述。

(1)GPIO端口時鍾使能。

由於要使用這個端口,所以要使能這個端口的時鍾,代碼如下:

RCC_APBRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA 時鍾

(2)復用的外設時鍾使能。

在這里要把GPIOA口的PA2和PA3引腳復用為串口2的TX和RX引腳,所以要使能這個串口時鍾,代碼如下:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART2, ENABLE); //使能USART2 時鍾

(3)端口模式配置。

在復用內置外設功能引腳時,必須設置GPIO端口的模式。GPIOA口的PA2和PA3引腳復用為串口2的TX和RX引腳,其初始化代碼如下:

• USART2_TX、PA2復用推挽輸出

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //ISART2_TX PA.2
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.2

• USART2_RX、PA3浮空輸入

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //USART1_RX PA.3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure); 

(4)串口參數初始化

USART_InitStructure.USART_BaudRate = bound; //波特率設置
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字長為 8 位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //一個停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //無奇偶校驗位
USART_InitStructure.USART_HardwareFlowControl
								= USART_HardwareFlowControl_None; //無硬件數據流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收發模式
USART_Init(USART1, &USART_InitStructure); //初始化串口

(5)使能串口

USART_Cmd(USART1, ENABLE); //使能串口

GPIOx端口復用重映射

在STM32中,有很多內置外設的輸入/輸出引腳都具有重映射(remap)的功能,每個內置外設都有若干個輸入/輸出引腳,一般這些引腳的輸出端口都是固定不變的。為了讓設計工程師可以更好地安排引腳的走向和功能,在STM32中引入了外設引腳重映射的概念,即一個外設的引腳除了具有默認的端口外,還可以通過設置重映射寄存器的方式,把它映射到其他端口。

1.寄存器結構體

typedef struct
{
  __IO uint32_t EVCR;
  __IO uint32_t MAPR;
  __IO uint32_t EXTICR[4];
  uint32_t RESERVED0;
  __IO uint32_t MAPR2;  
} AFIO_TypeDef;

2.如何實現端口復用重映射

下面以定時器TIM3為例,介紹如何實現TIM3的重映射。AFIO_MAPR的11:10位可以控制TIM3的通道1至通道4在GPIO端口的重映射。TIM3復用功能重映射引腳一覽表如圖所示。

從圖中可以看到,TIM3復用功能有部分重映射和完全重映射兩種。部分重映射就是一部分引腳和默認是一樣的,另一部分引腳重新映射到其他引腳。完全重映射就是所有引腳都重新映射到其他引腳。在默認情況下(即沒有重映射),TIM3的通道1至通道4復用功能引腳是CH1/PA6、CH2/PA7、CH3/PB0和CH4/PB1。

我們也可以將通道1至通道4完全重映射到引腳CH1/PC6、CH2/PC7、CH3/PC8和CH4/PC9上。若想實現TIM3的完全重映射,要先使能復用功能的兩個時鍾,然后使能AFIO功能時鍾,再調用重映射函數,具體步驟如下所述。

(1)使能GPIOC時鍾。

RCC_APBRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能GPIOC 時鍾

(2)使能TIM3時鍾。

RCC_APBRCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能GPIOC 時鍾

(3)使能AFIO時鍾。

RCC_APBRCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //使能AFIO 時鍾

(4)開啟重映射。

GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);

(5)最后,還需要配置重映射的引腳。

只需配置重映射后的引腳,原來的引腳不需要配置。這里重映射的引腳是PC6、PC7、PC8和PC9,配置為復用推挽輸出,代碼如下:

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9; //ISART2_TX PA.2
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出
GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化 GPIOA.2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM