详细的学习一下GPIO_Init()函数,比如下面的一段程序:

GPIO_InitTypeDef GPIO_InitStructure;
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); 
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; 
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
 GPIO_Init(GPIOB, &GPIO_InitStructure);

第一行为:

GPIO_InitTypeDef GPIO_InitStructure;

定义一个GPIO_InitTypeDef数据类型的数,取名叫GPIO_InitStructure,所以需要知道  GPIO_InitTypeDef 是什么数据类型, 其定义如下:

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_InitTypeDef是一个结构体类型,里面有三个成员

第一个是无符号16位的数如下:

uint16_t GPIO_Pin;

GPIO_Pin可以在0000 0000 0000 0000到1111 1111 1111 1111 之间随意取值。

第二个成员如下,需要知道GPIOSpeed_TypeDef是什么类型,GPIOSpeed_TypeDef定义如下:

GPIOSpeed_TypeDef GPIO_Speed;//  成员
//###################################下面是它的类型定义################################
typedef enum
{ 
  GPIO_Speed_10MHz = 1,
  GPIO_Speed_2MHz, 
  GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;

第二个成员GPIO_Speed是一个枚举型数据,枚举型是一个集合,第一个成员如果没有取值就默认取值为1,后续成员没有取值就默认取值为前一成员值+1,比如上面GPIO_Speed_2MHz的值为2,GPIO_Speed_50MHz的值为3。

第三个成员是GPIO_Mode,它的类型是GPIOMode_TypeDef,也是一个枚举型数据

GPIOMode_TypeDef GPIO_Mode; //成员
//###################################下面是它的类型定义################################
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;

GPIO_Mode有8个成员,将其取值展开成2进制如下,可以看到第5位是“0”就为输入,第5位是“1”就为输出。

GPIO_Mode_AIN         = 0000 0000      //模拟输入
  GPIO_Mode_IN_FLOATING = 0000 0100      //浮空输入
  GPIO_Mode_IPD         = 0010 1000      //下拉输入       
  GPIO_Mode_IPU         = 0100 1000      //上拉输入
  GPIO_Mode_Out_OD      = 0001 0100      //开漏输出
  GPIO_Mode_Out_PP      = 0001 0000      //推挽输出
  GPIO_Mode_AF_OD       = 0001 1100      //复用开漏
  GPIO_Mode_AF_PP       = 0001 1000      //服用推挽


然后看下面的程序

GPIO_Init(GPIOB, &GPIO_InitStructure);

其中GPIO_InitStructure我们已经知道了它是一个结构体数据,它都有三个成员,上面都有详细描述,但我们还需要知道它是用在哪的,给谁用的,这个在GPIO_Init()函数里面写着给GPIOB用,我们先来看GPIOB这个东西它是什么。

#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)

GPIOB是将 GPIOB_BASE这个东西强制转换成(GPIO_TypeDef *)指针类型,然后取名叫GPIOB,所以需要知道GPIOB_BASE是什么东西、GPIO_TypeDef是什么类型,如下:

#define PERIPH_BASE           ((uint32_t)0x40000000)
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
#define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00)

所以GPIOB_BASE是一个指向0x4001 0c00地址的指针,然后这个指针的结构是GPIO_TypeDef,所以需要知道GPIO_TypeDef是什么数据结构,如下:

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;

这是一个结构体,成员都是无符号32位的数,所以GPIOB实际上是一个结构体指针,指向首地址为0x4001 0c00的一大块区间,这块区间就是这个结构体GPIO_TypeDef,里面7个成员全都是32位,所以区间大小为7*(32/8)=28层=0x1c层。即区间0x4001 0c00 到 0x4001 0c1c。

这里把STM32单片机看成是一栋高楼,高楼一共有0xFFFF FFFF层,每层有8个房间,就是8位,所以一个32位的数要占4层楼。

现在我们就知道 GPIO_Init(GPIOB, &GPIO_InitStructure)函数中这两个参数具体是什么了,GPIOB是指向首地址为0x4001 0c00的一个结构体指针,&GPIO_InitStructure是取GPIO_InitStructure这个结构体的地址,取这个结构体地址有什么用呢,往下看

我们再来看初始化函数如下:

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)

这里将&GPIO_InitStructure定义成了指针,所以第二个参数还是这个结构体地址里面的数据。下面看这个初始化函数的内容:

uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  uint32_t tmpreg = 0x00, pinmask = 0x00;
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
  assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));

首先是一些参数定义,和assert_param函数,这个函数的意思是检查参数是否有效,如  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));就是检查GPIOx这个参数是否有效,假如我输入一个GPIOK进去,那就是无效参数,会报错。

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_Mode选择如下#################################
 GPIO_Mode_Out_OD      = 0001 0100      //开漏输出

前面详细说了GPIO_Mode有8个成员,接下来是把GPIO_InitStruct->GPIO_Mode设置为其中一个成员(用来设置输入输出模式的),假设为  GPIO_Mode_Out_OD,和(uint32_t)0x0F相与,就是前四位清零、后四位保留,然后给currentmode这个参数,所以currentmode=0000 0100 ;

接着用if语句判断GPIO_Mode的第五位是0还是1(是输入还是输出),是输出的话就执行{}内容

然后currentmode和GPIO_InitStruct->GPIO_Speed相或,假设GPIO_InitStruct->GPIO_Speed取值为(设置为2MHZ)GPIO_Speed_2MHz,即GPIO_InitStruct->GPIO_Speed=0000 0010,和currentmode相或后,currentmode=0000 0110,此时currentmode后四位就携带了输入输出模式信息和输出速度信息。

if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  {
    tmpreg = GPIOx->CRL;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = ((uint32_t)0x01) << pinpos;
      /* Get the port pins position */
      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);
        GPIOx->CRL = tmpreg;
     //###############

接下来判断引脚是高八位还是第八位,为什么是8位呢,因为一个引脚的输入输出模式速度是由CNF[1:0]和MODE[1:0]四位决定的,CNF决定输入输出模式MODE决定速度,而这4位在GPIOx->CRL;这个地址里,CRL之前说过,是32位的,所以CRL能设置8个IO的模式和速度。

所以,上面程序功能就是,将携带着模式和速度信息的currentmode赋值给对应的需要进行设置的IO口。

上面程序的整个过程是:判断是否位低八位,如果是,将32位的CRL值给tmpreg,然后进入for循环去找引脚,这里对32位的0.....0000 0000 0000 0001 每次左移pinpos位(pinpos在0-7之间)然后赋值给pos,(假如我要设置GPIOB的Pin3,则GPIO_Pin(前面有讲过,他是GPIO_InitTypeDef的第一个成员)的取值为0000 0000 0000 1000),假如某次循环pinpos为3,那么pos为0.....0000 1000,GPIO_Pin的值相同,所以currentpin == pos,进入if语句,将pinpos左移2位(乘以4)赋值给pos,此时pos为12,接着将0......0000 0000 0000 1111向左移pos位(12位)赋值给pinmask,此时pinmask第15-12位为1,取反后和tmpreg相与(将15-12位清零,其余位保留),最后把currentmode左移pos位(12位)后把4位(15-12位)的模式速度信息,填入tmpreg刚才清零的地方,最最后把tmpreg赋值给CRL,完成GPIOB的Pin3的模式速度配置。

android 打开 gpio编号 gpio_initstructure.gpio_android 打开 gpio编号

 如果设置成输入上拉或者下拉的话还有一段程序如下:

if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
          GPIOx->BRR = (((uint32_t)0x01) << pinpos);
        }
        else
        {
          /* Set the corresponding ODR bit */
          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
          {
            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
          }
        }

 这个很容易了,就不讲了。