GPIO

一、GPIO介绍

GPIO(General -purpose input/output):通用型输入输出,功能类似与8051的P0-P3,接脚可以供使用者由程序自由控制。可以通过一些暂存器来确定引脚电位的高低,对于out可以让这个引脚输出高电位或低电位。
GPIO可以作为一组的输入输出,通过拉高/拉低可以将每个引脚可以设置为不同的逻辑电平,可以用作模拟信号IO、计数器/定时器、串口,用户可以通过GPIO口和硬件进行数据交互(如UART)、控制硬件工作(如LED、蜂鸣器等)、读取硬件的工作状态信号(如中断信号)等。此外,在一些IC(Integrated circuit )中,GPIO可能是复用的,所以需要配置引脚的行为。

图片来自百度图片

使用GPIO需要完成:

  1. GPIO引脚时钟使能
  2. GPIO引脚需要被配置为输入或者输出
  3. 要给GPIO引脚相应的逻辑值

1.1 GPIO功能描述

输入模式:输入浮空、输入上拉、输入下拉、模拟输入
输出模式:开漏输出、推挽式输出、推挽式复用功能、开漏复用功能
推挽输出 : 负责灌电流和拉电流,使其负载能力和开关速度有很大的提高。
开漏输出: 具有线与能力,若多个开漏模式引脚连接到一起,只有所有为高时才有上拉电阻提供高电平。
上拉/下拉输入:
上拉就是把电位拉高,如通过一个电阻连接到Vcc,默认状态下引脚数据位1。
下拉就是把电压拉低,拉到GND,默认为0。
浮空输入:既不拉高也不拉低。端口呈高阻态,引脚电压是不确定的。如果想要减少上下拉电阻对结果的影响,可以选择此模式。
模拟输入:既不拉高也不拉低,输入数字信号即01的二进制数字信号,通过数模转换,转换成模拟信号。如使用ADC外设

1.2 寄存器

  • 两个32位配置寄存器(GPIOx_CRL, GPIOx_CRH)
  • 两个32位数据寄存器(GPIOx_IDR和GPIOx_ODR),
  • 一个32位置位/复位寄存器(GPIOx_BSRR),
  • 一个16位复位寄存器(GPIOx_BRR)
  • 一个32位锁定寄存器(GPIOx_LCKR)

/* 端口输入数据寄存器GPIOx_IDR 地址偏移:0x08
31:16 保留
这些位为只读并只能以字(16位)的形式读出。读出的值为对应I/O口的状态 */

/* 端口输出数据寄存器GPIOx_ODR 地址偏移: 0x0C
31:16 保留
这些位为读写,只能以字(16位)的形式读出。读出的值为对应I/O口的状态
对GPIOx_BSRR(x = A…E),可以分别地对各个ODR位进行独立的设置/清除*/

/* 端口位设置/清除寄存器(GPIOx_BSRR) 地址偏移: 0x10
位31:16:BRy: 清除端口x的位y (y = 0…15)
0:对对应的ODRy位不产生影响
1:清除对应的ODRy位为0
位15:0 BSy: 设置端口x的位y (y = 0…15)
0:对对应的ODRy位不产生影响
1:设置对应的ODRy位为1 */

/* 端口位清除寄存器(GPIOx_BRR) 地址偏移: 0x14
位15:0 BRy: 清除端口x的位y (y = 0…15) (Port x Reset bit y)
这些位只能写入并只能以字(16位)的形式操作。
0:对对应的ODRy位不产生影响
1:清除对应的ODRy位为0 */

/* 端口配置锁定寄存器 地址偏移: 0x18
当执行正确的写序列设置了位16(LCKK)时,该寄存器用来锁定端口位的配置。位[15:0]用于锁
定GPIO端口的配置。在规定的写入操作期间,不能改变LCKP[15:0]。当对相应的端口位执行了
LOCK序列后,在下次系统复位之前将不能再更改端口位的配置。

位16 LCKK:锁键 (Lock key)
该位可随时读出,它只可通过锁键写入序列修改。
0:端口配置锁键位激活
1:端口配置锁键位被激活,下次系统复位前GPIOx_LCKR寄存器被锁住。
锁键的写入序列:
写1 -> 写0 -> 写1 -> 读0 -> 读1
最后一个读可省略,但可以用来确认锁键已被激活。
注:在操作锁键的写入序列时,不能改变LCK[15:0]的值。
操作锁键写入序列中的任何错误将不能激活锁键 */

二、GPIO控制LED灯亮灭

参考STM32使用手册

2.1 LED初始化函数

 void LED_Init(void)
{
 
 GPIO_InitTypeDef  GPIO_InitStructure;
    
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);  //使能PB,PE端口时钟
    
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;               //LED0-->PB.5 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;        //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       //IO口速度为50MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);                  //根据设定参数初始化GPIOB.5
 GPIO_SetBits(GPIOB,GPIO_Pin_5);                         //PB.5 输出高

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;               //LED1-->PE.5 端口配置, 推挽输出
 GPIO_Init(GPIOE, &GPIO_InitStructure);                  //推挽输出 ,IO口速度为50MHz
 GPIO_SetBits(GPIOE,GPIO_Pin_5);                         //PE.5 输出高 
}

从这里看出,LED初始化函数有重要的四个部分:

  1. GPIO_InitTypeDef 结构体
  2. RCC_APB2PeriphClockCmd函数
  3. GPIO_Init初始化函数,传入参数为GPIOgroup和GPIO_InitTypeDef 类型的结构体
  4. GPIO_SetBits()函数

2.2 GPIO_InitTypeDef 结构体

typedef struct
{
  uint16_t GPIO_Pin;           
  GPIOSpeed_TypeDef GPIO_Speed; 
  GPIOMode_TypeDef GPIO_Mode;   
}GPIO_InitTypeDef;

#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 */

typedef enum
{ 
  GPIO_Speed_10MHz = 1,
  GPIO_Speed_2MHz, 
  GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
                              
typedef enum
{ GPIO_Mode_AIN = 0x0,
  GPIO_Mode_IN_FLOATING = 0x04,
  GPIO_Mode_IPD = 0x28,
  GPIO_Mode_IPU = 0x48,
  GPIO_Mode_Out_OD = 0x14,
  GPIO_Mode_Out_PP = 0x10,
  GPIO_Mode_AF_OD = 0x1C,
  GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;

2.3 RCC_APB2PeriphClockCmd结构体

GPIO 口时钟配置函数

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  if (NewState != DISABLE)
  {
      /* #define RCC    ((RCC_TypeDef *) (( ((uint32_t)0x40000000) + 0x20000) + 0x1000))
         #define RCC_APB2Periph_GPIOB             ((uint32_t)0x00000008)      */
    RCC->APB2ENR |= RCC_APB2Periph;
  }
  else
  {
    RCC->APB2ENR &= ~RCC_APB2Periph;
  }
}

时钟使能就一句,现在来分析RCC(Reset Clock Controller, 复位与时钟控制)、AP2ENR:


AHB地址
#define RCC                 ((RCC_TypeDef *) RCC_BASE)

#define RCC_BASE              (AHBPERIPH_BASE + 0x1000)
#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)
#define PERIPH_BASE           ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */

/* #define RCC  ((RCC_TypeDef *) (( ((uint32_t)0x40000000) + 0x20000) + 0x1000)) 8/
typedef struct
{
  __IO uint32_t CR;
  __IO uint32_t CFGR;
  __IO uint32_t CIR;
  __IO uint32_t APB2RSTR;
  __IO uint32_t APB1RSTR;
  __IO uint32_t AHBENR;
  __IO uint32_t APB2ENR;
  __IO uint32_t APB1ENR;
  __IO uint32_t BDCR;
  __IO uint32_t CSR;

} RCC_TypeDef;
RCC寄存器

RCC所定义的类型为什么这样定义?根据手册中的上图。

此时我们想使能GPIOB和GPIOE的时钟,在上图的RCC_APB2ENR寄存器中有使能配置,如下图,赋值相应的位就可以使能外设的时钟。

#define RCC_APB2Periph_GPIOB             ((uint32_t)0x00000008) /* 0000 1000*/
#define RCC_APB2Periph_GPIOE             ((uint32_t)0x00000040) /* 0100 0000*/
RCC_APB2ENR,时钟开启

2.4 GPIO_Init初始化函数

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
  uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  uint32_t tmpreg = 0x00, pinmask = 0x00;
  
/*---------------------------- GPIO Mode Configuration -----------------------*/
  currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
  {   
    /* Check the parameters */
    assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
    /* Output mode */
    currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
  }
 
  
/*---------------------------- GPIO CRL Configuration ------------------------*/
  /* Configure the eight low port pins */
  /* 判断是否是低8引脚 */
  if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  {
    tmpreg = GPIOx->CRL;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
        // 复位值应该是0x4444 4444,0100浮空输入
      pos = ((uint32_t)0x01) << pinpos;
      /* Get the port pins position */
      /* ???GPIO_InitStruct->GPIO_Pin,这里是确定是哪个引脚,还是引脚的状态值 */
      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
      
      if (currentpin == pos)
      {
        pos = pinpos << 2;
        /* Clear the corresponding low control register bits */
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* Write the mode configuration in the corresponding bits */
        tmpreg |= (currentmode << pos);
        /* Reset the corresponding ODR bit */   
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
            /* BRR:端口位清除寄存器 1:清楚对应的ODR y位为0 */
          GPIOx->BRR = (((uint32_t)0x01) << pinpos);
        }
        else
        {
          /* Set the corresponding ODR bit */
          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
          {
              /* 端口位设置/清除寄存器
              0:对对应的ODRy位不产生影 1:清除对应的ODRy位为0(31:16)
              1:设置对应的ODRy为1,0:不产生影响 (15:0)*/
            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
          }
        }
      }
    }
    GPIOx->CRL = tmpreg;
  }
/*---------------------------- GPIO CRH Configuration ------------------------*/
/* 与CRL类似的 */
}

2.3.1 GPIO Mode Configuration

在初始化的代码中,
端口配置寄存器有两个(一共64位): GPIOx_CRL:端口配置低寄存器; GPIOx_CRH:端口配置高寄存器
一共可配置16个IO口,每个IO口由4位来控制(CNF1 CNF0 MODE1 MODE0)。
CNF1 CNF0:配置输入输出的具体模式;
MODE1 MODE0:配置输入还是输出,此外,配置输出时,还配置了输出速度。

CRL寄存器

输出:(mode > 00)
00:推挽输出 : 可以输出高,低电平,连接数字器件;
01:开漏输出: 输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行,适合于做电流型的驱动,其吸收电流的能力相对强。
特点:1. 利用外部电路的驱动能力,减少IC内部的驱动。 2. 开漏是用来连接不同电平的器件,匹配电平用的,因为开漏引脚不连接外部的上拉电阻时,只能输出低电平,如果需要同时具备输出高电平的功能,则需要接上拉电阻,很好的一个优点是通过改变上拉电源的电压,便可以改变传输电平。
10: 复用推挽输出
11: 复用开漏输出

输入:(mode = 00)
00:模拟输入:模拟输入是指传统方式的输入,数字输入是输入数字信号,即0,1的二进制数字信号,通过数模转换,转换成模拟信号,
01:浮空输入: 浮空就是逻辑器件与引脚即不接高电平,也不接低电平。由于逻辑器件的内部结构,当它输入引脚悬空时,相当于该引脚接了高电平。
10:上拉/下拉输入
上拉就是把点位拉高,比如拉到Vcc。上拉就是将不确定的信号通过一个电阻嵌位在高电平。
下拉就是把电压拉低,拉到GND。与上拉原理相似
11:保留

速度(max):00:保留 01:10MHz;10:2MHz; 11:50MHz

typedef enum
{ GPIO_Mode_AIN = 0x0,
  GPIO_Mode_IN_FLOATING = 0x04,
  GPIO_Mode_IPD = 0x28,
  GPIO_Mode_IPU = 0x48,
  GPIO_Mode_Out_OD = 0x14,
  GPIO_Mode_Out_PP = 0x10,
  GPIO_Mode_AF_OD = 0x1C,
  GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;

端口配置不是4位吗?为什么定义时用到了8位呢?其实,在上图中可以看出输入模式和输出模式的CNF是一样的配置,不同之处在于Mode不同从而选择不同的方式。所以,配置了第4位,根据第4位的值判断输入还是输出。

2.3.2 GPIO配置

#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)

#define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00)
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
#define PERIPH_BASE           ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
#define GPIOB   ((GPIO_TypeDef *) ( (((uint32_t)0x40000000) + 0x10000) + 0x0C00))
APB2地址

2.4 GPIO引脚赋值

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;//1:清除对应的ODRy位为1 */
/*  GPIOE->BRR=GPIO_Pin;  //1:清除对应的ODRy位为0 */
/*  PS:点LED灯的时候,不仅考虑IO口的电平,还有考虑LED是低电平点亮还是高电平点亮 */
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容