STM32-GPIO的配置和使用

1.寄存器介绍

STM32 的每个 IO 端口都有 7 个寄存器来控制。他们分别是:配置模式的 2 个 32 位的端口配置寄存器CRL 和 CRH;2 个 32 位的数据寄存器IDR 和 ODR;1 个 32 位的置位/复位寄存器BSRR;一个 16 位的复位寄存器 BRR;1 个 32 位的锁存寄存器LCKR;这里我们仅介绍常用 的几个寄存器,我们常用的 IO 端口寄存器只有 4 个:CRL、CRH、IDR、ODR,即端口配置寄存器和数据寄存器。

1.1端口配置寄存器(CRL和CRH)

CRL:每个 IO 端口的位占用 CRL 的 4 个位,高两位为 CNF,用来配置端口的模式,低两位为 MODE,用来配置端口的最大输出速度(CRH 的作用和 CRL 完全一样,只是 CRL 控制的是低 8 位输出口,而 CRH 控制的是高 8位输出口)。

比如我们要配置端口n为"推挽输出、最大输出速度为10M",那就是CNFn[1:0]=00,MODEn[1:0]=01。

关于IO模式(转自不文东

(1) 浮空输入_IN_FLOATING ——浮空输入,可以做KEY识别,RX1

(2)带上拉输入_IPU——IO内部上拉电阻输入

(3)带下拉输入_IPD—— IO内部下拉电阻输入

(4) 模拟输入_AIN ——应用ADC模拟输入,或者低功耗下省电

(5)开漏输出_OUT_OD ——IO输出0接GND,IO输出1,悬空,需要外接上拉电阻,才能实现输出高电平。当输出为1时,IO口的状态由上拉电阻拉高电平,但由于是开漏输出模式,这样IO口也就可以由外部电路改变为低电平或不变。可以读IO输入电平变化,实现C51的IO双向功能

(6)推挽输出_OUT_PP ——IO输出0-接GND, IO输出1 -接VCC,读输入值是未知的

(7)复用功能的推挽输出_AF_PP ——片内外设功能(I2C的SCL,SDA)

(8)复用功能的开漏输出_AF_OD——片内外设功能(TX1,MOSI,MISO.SCK.SS)

关于输出速度(转自wuyuzun

I/O口输出模式下有三种输出速度可选(2MHz,10MHz,50MHz),这个速度是指I/O口驱动电路的响应速度;I/O管脚内部有多个响应不同的驱动电路,用户可以根据自己的需要选择合适的驱动电路。

高低频比较
高频驱动电路:输出频率高,噪音大,功耗高,电磁干扰强;
低频驱动电路:输出频率低,噪音小,功耗低,电磁干扰弱;提高系统EMI(电磁干扰)性能;

总结:通过选择速度来选择不同的输出驱动模块,达到最佳的噪音控制和降低功耗的目的如果需要选择较高频率信号,但是却选择了低频驱动模块,很有可能会失真的输出信号;所以GPIO的引脚速度应与应用匹配。

举几个栗子:
1. 对于串口来说,加入最大波特率为115200,这样只需要用2M的GPIO的引脚速度就可以了,省电噪音又小;
2. 对于I2C接口,假如使用400 000波特率,若想把余量留大一些,2M的GPIO引脚速度或许是不够,这时可以选用10M的GPIO引脚速度;
3. 对于SPI接口,假如使用18M或9M的波特率,用10M的GPIO口也不够用了,需要选择呢50M的GPIO引脚速度
4. GPIO口设置为输入时,输出驱动电路与端口是断开的,所以这时配置输出速度是无意义的;
5. 在复位期间和刚复位后,复位功能未开启,I/O端口被配置成浮空输入模式;
6. 所有端口都有外部中断能力,当使用外部中断功能时,端口必须设置成输入模式;
7. GPIO的配置具有上锁的功能,当配置好GPIO后,可以通过程序锁住配置组合,知道下次芯片复位才能解开;

1.2.数据寄存器(IDR和ODR)

IDR 是一个端口输入数据寄存器,只用了低 16 位。该寄存器为只读寄存器,并且只能以16 位的形式读出. 要想知道某个 IO 口的状态,只要读这个寄存器,再看某个位的状态就可以了。

 

ODR 是一个端口输出数据寄存器,也只用了低 16 位。该寄存器为可读写,从该寄存器读出来的数据可以用于判断当前 IO 口的输出状态。而向该寄存器写数据,则可以控制某个 IO 口的输出电平。

1.3.BSRR和BRR寄存器

BSRR寄存器是端口位设置/清除寄存器,它和ODR寄存器类似。

BRR寄存器是端口位清除寄存器,与BSRR的高16位相同。

 

2.GPIO的代码实现(库函数)

GPIO配置函数主要在stm32f10x_gpio.h/c文件中,此外在使用GPIO之前要使能端口的时钟,相关函数在stm32f10x_rcc.h/c文件中。一般的端口配置中需要用到以下两个函数。

RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);//stm32f10x_rcc.h/c中

GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);//stm32f10x_gpio.h/c中

这里需要了解一些STM32的系统时钟。我们大多数情况下用到的是APB1和APB2的时钟,其中APB1上连接的是低速外设,包括电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3、等等。APB2上连接的是高速外设,包括UART1、SPI1、Timer1、ADC1、ADC2、GPIO、第二功能IO口等。当我们使用外设的时候,一定要知道这个外设使用的是哪个时钟,并且使能该时钟,才能使用该外设。

2.1 RCC_APB2PeriphClockCmd( )函数

RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);函数的可选参数

//RCC_APB2Periph可选参数(以APB2为时钟源的外设):
#define RCC_APB2Periph_AFIO              ((uint32_t)0x00000001)
/*与AFIO(alternate function io)时钟相关的寄存器
1、 事件控制寄存器(AFIO_EVCR)
2、 复用重映射和du调试I/O 配置寄存器(AFIO_MAPR)
3、 外部中zhi断配置寄存器1(AFIO_EXTICR1)
4、 外部中断配置寄存器2(AFIO_EXTICR2)
5、 外部中断配置寄存器3(AFIO_EXTICR3)
6、 外部中断配置寄存器4(AFIO_EXTICR4)
对这些寄存器进行操作前要使能该时钟*/
#define RCC_APB2Periph_GPIOA             ((uint32_t)0x00000004)
#define RCC_APB2Periph_GPIOB             ((uint32_t)0x00000008)
#define RCC_APB2Periph_GPIOC             ((uint32_t)0x00000010)
#define RCC_APB2Periph_GPIOD             ((uint32_t)0x00000020)
#define RCC_APB2Periph_GPIOE             ((uint32_t)0x00000040)
#define RCC_APB2Periph_GPIOF             ((uint32_t)0x00000080)
#define RCC_APB2Periph_GPIOG             ((uint32_t)0x00000100)
#define RCC_APB2Periph_ADC1              ((uint32_t)0x00000200)
#define RCC_APB2Periph_ADC2              ((uint32_t)0x00000400)
#define RCC_APB2Periph_TIM1              ((uint32_t)0x00000800)
#define RCC_APB2Periph_SPI1              ((uint32_t)0x00001000)
#define RCC_APB2Periph_TIM8              ((uint32_t)0x00002000)
#define RCC_APB2Periph_USART1            ((uint32_t)0x00004000)
#define RCC_APB2Periph_ADC3              ((uint32_t)0x00008000)
#define RCC_APB2Periph_TIM15             ((uint32_t)0x00010000)
#define RCC_APB2Periph_TIM16             ((uint32_t)0x00020000)
#define RCC_APB2Periph_TIM17             ((uint32_t)0x00040000)
#define RCC_APB2Periph_TIM9              ((uint32_t)0x00080000)
#define RCC_APB2Periph_TIM10             ((uint32_t)0x00100000)
#define RCC_APB2Periph_TIM11             ((uint32_t)0x00200000)

//NewState参数
#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE))

2.2 GPIO_Init( )函数

GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

GPIO_TypeDef是一个GPIO结构体,里边定义了GPIO的寄存器(可以理解为GPIO的属性,有了这些属性,它就是了GPIO),库文件使用该结构体来定义GPIO外设,这里做一个了解。在使用的时候很简单,其参数为GPIOA~G。这个时候,我们就创建了一个GPIO的空壳子,有了属性,接下来还要对其属性进行配置。

typedef struct
{
  __IO uint32_t CRL;
  __IO uint32_t CRH;
  __IO uint32_t IDR;
  __IO uint32_t ODR;
  __IO uint32_t BSRR;
  __IO uint32_t BRR;
  __IO uint32_t LCKR;
} GPIO_TypeDef;

#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)

选择了GPIO之后,接下来就要对其参数进行配置了。GPIO_InitTypeDef是一个配置GPIO(寄存器)的结构体(配置CRL、CRH寄存器参数),这些参数包括具体的端口GPIO_Pin、端口速度GPIO_Speed、端口模式GPIO_Mode(GPIO端口的模式和速度见上文)。

typedef struct
{
  uint16_t GPIO_Pin;             /*!< Specifies the GPIO pins to be configured.
                                      This parameter can be any value of @ref GPIO_pins_define */

  GPIOSpeed_TypeDef GPIO_Speed;  /*!< Specifies the speed for the selected pins.
                                      This parameter can be a value of @ref GPIOSpeed_TypeDef */

  GPIOMode_TypeDef GPIO_Mode;    /*!< Specifies the operating mode for the selected pins.
                                      This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;

GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);函数的可选参数:

//GPIO_Pin
#define GPIO_Pin_0                 ((uint16_t)0x0001)  /*!< Pin 0 selected */
#define GPIO_Pin_1                 ((uint16_t)0x0002)  /*!< Pin 1 selected */
#define GPIO_Pin_2                 ((uint16_t)0x0004)  /*!< Pin 2 selected */
#define GPIO_Pin_3                 ((uint16_t)0x0008)  /*!< Pin 3 selected */
#define GPIO_Pin_4                 ((uint16_t)0x0010)  /*!< Pin 4 selected */
#define GPIO_Pin_5                 ((uint16_t)0x0020)  /*!< Pin 5 selected */
#define GPIO_Pin_6                 ((uint16_t)0x0040)  /*!< Pin 6 selected */
#define GPIO_Pin_7                 ((uint16_t)0x0080)  /*!< Pin 7 selected */
#define GPIO_Pin_8                 ((uint16_t)0x0100)  /*!< Pin 8 selected */
#define GPIO_Pin_9                 ((uint16_t)0x0200)  /*!< Pin 9 selected */
#define GPIO_Pin_10                ((uint16_t)0x0400)  /*!< Pin 10 selected */
#define GPIO_Pin_11                ((uint16_t)0x0800)  /*!< Pin 11 selected */
#define GPIO_Pin_12                ((uint16_t)0x1000)  /*!< Pin 12 selected */
#define GPIO_Pin_13                ((uint16_t)0x2000)  /*!< Pin 13 selected */
#define GPIO_Pin_14                ((uint16_t)0x4000)  /*!< Pin 14 selected */
#define GPIO_Pin_15                ((uint16_t)0x8000)  /*!< Pin 15 selected */
#define GPIO_Pin_All               ((uint16_t)0xFFFF)  /*!< All pins selected */

//GPIO_Speed
typedef enum
{ 
  GPIO_Speed_10MHz = 1,
  GPIO_Speed_2MHz, 
  GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;

//GPIO_MODE
#define IS_GPIO_MODE(MODE) (((MODE) == GPIO_Mode_AIN) || ((MODE) == GPIO_Mode_IN_FLOATING) || \
                            ((MODE) == GPIO_Mode_IPD) || ((MODE) == GPIO_Mode_IPU) || \
                            ((MODE) == GPIO_Mode_Out_OD) || ((MODE) == GPIO_Mode_Out_PP) || \
                            ((MODE) == GPIO_Mode_AF_OD) || ((MODE) == GPIO_Mode_AF_PP))

2.3 GPIO配置实例

IO初始化实例:

void GPIO_Init(void)
{
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);//使能PC端口时钟
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;				 //PC.13 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
 GPIO_Init(GPIOC, &GPIO_InitStructure);					 //根据设定参数初始化GPIOC.13
}

2.4 几个常用的操作GPIO的函数

另外还有几个常用的函数,主要用来操作几个关于端口数据的寄存器(BSRR、BRR、ODR、IDR)。从下列函数的名字就能看出来,前四个是写IO数据、后四个是读IO数据。需要说明的是,要清楚操作对象是"port "还是"pin",即清楚“写寄存器”和写“寄存器的某一位的区别”——下边几个函数凡是带bits的都是操作寄存器的某一位,对应的就是一个IO引脚(pin),否则就是操作寄存器(32的寄存器,但是只用了16位【一组GPIO有16个端口】,所以定义的参数是uint16_t 型),即对应的是一组GPIO(port )。

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//端口置1
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//端口清0
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);//向端口写BitVal
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);//向GPIOx写PortVal

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//读端口值(bit)
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);//读GPIO组值(16位)
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);


/**
  * @brief  Sets the selected data port bits.
  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
  * @param  GPIO_Pin: specifies the port bits to be written.
  *   This parameter can be any combination of GPIO_Pin_x where x can be (0..15).
  * @retval None
  */
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  
  GPIOx->BSRR = GPIO_Pin;
}

/**
  * @brief  Clears the selected data port bits.
  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
  * @param  GPIO_Pin: specifies the port bits to be written.
  *   This parameter can be any combination of GPIO_Pin_x where x can be (0..15).
  * @retval None
  */
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  
  GPIOx->BRR = GPIO_Pin;
}

/**
  * @brief  Sets or clears the selected data port bit.
  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
  * @param  GPIO_Pin: specifies the port bit to be written.
  *   This parameter can be one of GPIO_Pin_x where x can be (0..15).
  * @param  BitVal: specifies the value to be written to the selected bit.
  *   This parameter can be one of the BitAction enum values:
  *     @arg Bit_RESET: to clear the port pin
  *     @arg Bit_SET: to set the port pin
  * @retval None
  */
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
{
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
  assert_param(IS_GPIO_BIT_ACTION(BitVal)); 
  
  if (BitVal != Bit_RESET)
  {
    GPIOx->BSRR = GPIO_Pin;
  }
  else
  {
    GPIOx->BRR = GPIO_Pin;
  }
}

/**
  * @brief  Writes data to the specified GPIO data port.
  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
  * @param  PortVal: specifies the value to be written to the port output data register.
  * @retval None
  */
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)
{
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  
  GPIOx->ODR = PortVal;
}



/**
  * @brief  Reads the specified input port pin.
  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
  * @param  GPIO_Pin:  specifies the port bit to read.
  *   This parameter can be GPIO_Pin_x where x can be (0..15).
  * @retval The input port pin value.
  */
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  uint8_t bitstatus = 0x00;
  
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GET_GPIO_PIN(GPIO_Pin)); 
  
  if ((GPIOx->IDR & GPIO_Pin) != (uint32_t)Bit_RESET)
  {
    bitstatus = (uint8_t)Bit_SET;
  }
  else
  {
    bitstatus = (uint8_t)Bit_RESET;
  }
  return bitstatus;
}

/**
  * @brief  Reads the specified GPIO input data port.
  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
  * @retval GPIO input data port value.
  */
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx)
{
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  
  return ((uint16_t)GPIOx->IDR);
}

/**
  * @brief  Reads the specified output data port bit.
  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
  * @param  GPIO_Pin:  specifies the port bit to read.
  *   This parameter can be GPIO_Pin_x where x can be (0..15).
  * @retval The output port pin value.
  */
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  uint8_t bitstatus = 0x00;
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GET_GPIO_PIN(GPIO_Pin)); 
  
  if ((GPIOx->ODR & GPIO_Pin) != (uint32_t)Bit_RESET)
  {
    bitstatus = (uint8_t)Bit_SET;
  }
  else
  {
    bitstatus = (uint8_t)Bit_RESET;
  }
  return bitstatus;
}

/**
  * @brief  Reads the specified GPIO output data port.
  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
  * @retval GPIO output data port value.
  */
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)
{
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
    
  return ((uint16_t)GPIOx->ODR);
}

3.小结

3.1 理论总结

1.GPIO在APB2时钟下,使用前要使能该时钟。

2.GPIO的基本使用过程中的几个常用的寄存器:

端口配置寄存器(CRL和CRH)用来配置IO引脚(pin)的模式、速度。

数据寄存器(IDR和ODR)和端口置位/清除位寄存器(BSRR和BRR)用来设置pin、port的值和读取pin、port的值。

3.2 使用总结

1.调用RCC_APB2PeriphClockCmd( )函数使能IO时钟。

2.定义一个 GPIO_InitTypeDef 型结构体变量,并配置要初始化的IO参数。

3.通过GPIO_Init( )函数使用配置好的GPIO_InitTypeDef 类型的变量初始化IO。

4.使用2.4节的函数操作IO。

猜你喜欢

转载自blog.csdn.net/wei348144881/article/details/108680336