#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)这句话的具体作用是什么啊

2019-08-18 19:04发布

#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)这句话的具体作用是什么啊
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
18条回答
离殇
1楼-- · 2019-08-20 07:56
MARK一下,要好好看看~
archer123
2楼-- · 2019-08-20 11:01
mark一下
yaosongjin
3楼-- · 2019-08-20 14:34
cdshkf 发表于 2014-5-20 11:30
给楼猪看个东西,从阿莫上转过来的,希望会对楼猪有点启发。

我做了个实例,不用ST的库来点LED,解答你的 ...

解释得很详细,刚踏入stm32这块领域,还要多多学习c才行
lihilong
4楼-- · 2019-08-20 19:53
 精彩回答 2  元偷偷看……
lihilong
5楼-- · 2019-08-20 21:17
lihilong 发表于 2016-11-26 08:05
为什么是4个字节?其他都明白

开始没转过来 每个寄存器是32位 懂了
lvshuanghui
6楼-- · 2019-08-20 22:36
一、 GPIO_Init函数解析 1
1、参数GPIO_TypeDef 1
2、参数GPIO_InitStruct 2
3、函数代码详解 4
4、备注 6
一、GPIO_Init函数解析
首先来看一下GPIO_Init函数的原型void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)。这个函数的实现是在Stm32f10x_gpio.c文件中,若要使用该函数在相应的应用程序的前面包含Stm32f10x_gpio.h头文件。
1、参数GPIO_TypeDef
该函数的第一个参数为GPIO_TypeDef,它是一个结构体类型,该类型在Stm32f10x.h中被定义。定义的原型为:
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;
在这个结构体类型当中有7个32(8字节)位的变量,这些变量在存储空间的地址是相邻的。打开STM32数据手册不难看出,每个端口对应有16的引脚,由7个寄存器控制GPIO行为,并且这7个寄存器的顺序也是连续的。各个端口都有相同的结构。STM32的固件库就将这种结构抽象出一个类型GPIO_TypeDef。在操作寄存器之前你一定要有一个寄存器映射的操作,否则无法访问指定的寄存器,在这里我们只需要映射一次而不需要映射7此。这样做是不是很方便,也提高了代码的可读性,使代码规范化。
既然GPIO_Init的第一个参数GPIO_TypeDef的指针变量,这个指针变量存放的就是某一个端口的首地址。某一个程序的调用语句是这样的GPIO_Init(GPIOD,&GPIO_InitStructure); //初始化GPIOD
GPID是固件库中定义的一个宏,在编译的时候会宏展开,先列出与GPIOD端口地址映射有关的宏定义如下:
#define GPIOD               ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOD_BASE            (APB2PERIPH_BASE + 0x1400)
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
#define PERIPH_BASE           ((uint32_t)0x40000000)
看到了0x4000 0000这个数字是不是非常熟悉,它是外设的首地址。在STM32芯片的内部STM32有两个,一个叫APB1,一个叫APB2。每一个APB桥都会管理很多外设。STM32F10x把这两个APB的外设寄存器访问地址放在了不同的存储空间。0x10000就是APB2外设的存储空间首地址相对于整个外设的偏移。而0x1400是GPIOD端口外设首地址相对于APB2外设的存储空间首地址的偏移。这样就找到了GPIOD外设的基地址了!而((GPIO_TypeDef *) GPIOD_BASE)可以同时实现所有控制GPIOD端口的7个寄存器的映射。若访问某一个寄存器只需要通过指向GPIO_TypeDef 变量的指针。
2、参数GPIO_InitStruct
第二个参数的为GPIO_InitTypeDef* GPIO_InitStruct。就是一个指向GPIO _InitTypeDef的地址。第一个参数只找到配置的目标寄存器,第二个参数就是对相应端口如何配置的数据参数。这些参数存储在指向GPIO_InitTypeDef变量的首地址处。先列处该参数由来的一断代码
GPIO_InitTypeDef  GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitTypeDef 是一个结构体的变量,该变量在Stm32f10x_gpio.h头文件中被定义,定义的原型如下:
typedef struct
{
  uint16_t GPIO_Pin;
  GPIOSpeed_TypeDef  GPIO_Speed;  
  GPIOMode_TypeDef  GPIO_Mode;  
}GPIO_InitTypeDef;
GPIO_InitTypeDef的第一个变量为GPIO_Pin是一个16为的无符号数,该数只有16位,每一位代表一个引脚,若要配置某一个端口的某一个引脚只需要把相应的位设置为1就可以了。在STM32的固件库中有如下引脚号定义:
#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_Pin_All 。简单吧!哈哈!
GPIOSpeed_TypeDef是一个枚举变量,它用于存储GPIO速度的参数,它的定义如下:
typedef enum
{
  GPIO_Speed_10MHz = 1,
  GPIO_Speed_2MHz,
  GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
通过定义可以知道,GPIOSpeed_TypeDef的变量有三种取值,那么GPIO的速度有三种,
枚举变量的值
对应的速度
1
10MHZ
2
2MHZ
3
50MHZ
GPIOMode_TypeDef也是一个枚举变量,它用于存储GPIO工作的模式,它的定义如下:
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;
设计这个枚举变量的可取值有一定的意义。在第四位当中只用到了其中的高两位,这两位数据用来存储到某一个引脚的模式控制位MODEx[1:0] ,而高四位用来标志某一些标志。
高四位的取值
意义
0
输入模式
1
输出模式
2
下拉输入
4
上拉输入
3、函数代码详解
上面是GPIO_Init函数参数的解释。我在我们就可以进入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;
  /* 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));  
  
/*---------------------------- GPIO Mode Configuration -----------------------*/
  currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)//若为输出上拉就会配置GPIO的速度
  {
    /* 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 */
  if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)//若对第八个引脚进行配置,GPIO_Pin的值某一位为1就会对该引脚配置
  {
    tmpreg = GPIOx->CRL;//暂存GPIO控制寄存器原来的值
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)//扫描8次决定,查看哪一引脚需要配置,若 //需要配置则进行配置
    {
      pos = ((uint32_t)0x01) << pinpos;//获得要查看的某一个引脚所对应的位为1的值
      /* Get the port pins position */
      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;//currentpin 的值为0或者为pos
      if (currentpin == pos)//若为pos说明该位需要配置
      {
        pos = pinpos << 2;//pinpos 的值乘以4得到某一引脚配置位的最低位号:0,4,8......28
        /* Clear the corresponding low control register bits *///用于屏蔽某一个引脚的配置位, 使这4位为0
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* Write the mode configuration in the corresponding bits */
        tmpreg |= (currentmode << pos);//因为模式所对应的数都存放在第四位,所以需要向左移位到某一个引脚对应的配置位的最低位出,然后对存储到tmpreg 中
        /* Reset the corresponding ODR bit */
        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);
          }
        }
      }
    }
    GPIOx->CRL = tmpreg;//对低8个引脚配置寄存器赋值
  }
/*---------------------------- GPIO CRH Configuration ------------------------*/
  /* Configure the eight high port pins */
  if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  {
    tmpreg = GPIOx->CRH;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = (((uint32_t)0x01) << (pinpos + 0x08));
      /* Get the port pins position */
      currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
      if (currentpin == pos)
      {
        pos = pinpos << 2;
        /* Clear the corresponding high 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)
        {
          GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
        /* Set the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
        {
          GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
      }
    }
    GPIOx->CRH = tmpreg;
  }
}
4、备注
assert_param函数是对参数的检测。参数要么是逻辑0或者1。IS_GPIO_ALL_PERIPH也是一个宏,宏定义为:
#define IS_GPIO_ALL_PERIPH(PERIPH) (((PERIPH) == GPIOA) ||
                                    ((PERIPH) == GPIOB) ||
                                    ((PERIPH) == GPIOC) ||
                                    ((PERIPH) == GPIOD) ||
                                    ((PERIPH) == GPIOE) ||
                                    ((PERIPH) == GPIOF) ||
                                    ((PERIPH) == GPIOG))
其他的参数检测函数当中使用的宏都是相似的,具体可以查看相应的宏定义,在此不一一列出。
对低8位的配置和对高8位的配置原理是一样的。所以在此只对低8引脚配置进行说明。

一周热门 更多>