stm32深入浅出——由GPIO谈谈寄存器配置

相信大家对GPIO的配置并不陌生,只需简单的几个库函数就能完成。而本菜今天要讲的不是怎么用这些库函数,而是要讲讲这些库函数是怎么工作的。本菜留意了下,无论是网上还是书籍,涉及这方面的知识很少,直接抄了使用手册就上了。那么本菜在这里就详细讲一讲,做些补充,希望能帮助到大家。

 

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

GPIO_Init(GPIOD, &GPIO_InitStructure);

 

 这几行代码是司空见惯了。我们先从它们入手。

GPIO_InitStructure  看名字,就知道是个结构体,在主文件里是这样声明的:

 

GPIO_InitTypeDef  GPIO_InitStructure;

 

于是乎,我们再看看GPIO_InitTypeDef 是什么东西吧。

 

typedef struct

{

  u16 GPIO_Pin;

  GPIOSpeed_TypeDef GPIO_Speed;

  GPIOMode_TypeDef GPIO_Mode;

}GPIO_InitTypeDef;

 

其中各有声明为:

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;

    我们可以发现,这些配置信息都有它们的固定数值。这些数值,代表什么意义呢?我们接下去看看GPIO_Init(GPIOD, &GPIO_InitStructure)这个函数吧。

先看参数的类型GPIO_TypeDef* (GPIO_InitTypeDef之前已经有讲解) :

GPIO_TypeDef  :

typedef struct

{

  vu32 CRL;

  vu32 CRH;

  vu32 IDR;

  vu32 ODR;

  vu32 BSRR;

  vu32 BRR;

  vu32 LCKR;

} GPIO_TypeDef;

 

    这里有几个寄存器需要简单讲解的:CRL是低位配置寄存器,是用来存储低位数据(低8位)的配置情况。CRH是高位寄存器。ODR是写入输出寄存器,在配置为输出时,该寄存器的值就输出到I/O引脚。IDR是输入数据寄存器,在每个APB2时钟周期读取并捕获对应I/O口数据。BSRR寄存器是32位置/复位寄存器,高16位可以对2字节(16位)的ODR上对应位进行位操作,低16位则进行复位操作,一般都用这个寄存器对I/O口的输出进行操作,当然也可以通过修改ODR寄存器来实现,像在这个库函数里就是通过BSRR来实现的。BRR是16位复位寄存器,只能进行复位操作。LCKR是端口锁定寄存器,开锁定以后,对该端口的某位的配置修改不可行。具体请参照STM32F10X使用手册。

 

然后另一个实参(举例):GPIOD  查找其声明,就可以发现:  #define GPIOD         ((GPIO_TypeDef *) GPIOD_BASE)

就和之前的例子一样,是以GPIOD_BASE为基地址的结构体。那么当你再查找GPIOD_BASE的定义时,就会发现其值为0x40011800,也即:

 

这是什么意思呢?就是指管理GPIOD的寄存器的初始地址就是0x40011800。那么我们再来看看第一个寄存器CRL的信息:

 

 

我们发现CRL的偏移地址是00h,也即其是基地址上的第一个寄存器组。仔细一看便知,这个寄存器组包含32个寄存器,也即占了32位空间。我们再看CRL在结构体的类型是vu32,就能明白,这些空间分配,是遵循着线性规律的。当我们再看CRH,也就是第二个寄存器组时,不难发现其偏移地址是04h,也即4*8=32位的偏移地址。这和我们之前的分析是完全符合的。

    再回到程序中,可以发现I/O口的配置是分高低8位的,原因在于GPIO的配置寄存器分为高位配置寄存器CRH和低位配置CRL。在STM32中通常一个寄存器有32位,在这里却用了2个寄存器来配置GPIO,说明一个I/O口需要4个位来配置。就仅仅以CRL为例,来看继续看上图。

    我们再看GPIO_PIN_x的定义:

 

从中不难发现一些规律。看官心中有些明了之后,我们再回到代码中,只看这几句:

if (((u32)GPIO_InitStruct->GPIO_Pin & ((u32)0x00FF)) != 0x00)

  {

    tmpreg = GPIOx->CRL;

    for (pinpos = 0x00; pinpos < 0x08; pinpos++)

    {

      pos = ((u32)0x01) << pinpos;

      

      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;

    我们大致揣摩,就能得出这几句语句是用来选择当前的引脚的。这也就是为何上面GPIO_PIN_x的定义是以2的倍数递增。当然,CRL这个寄存器的位的排列也是遵循这个规律的:

 

 这是PIN_0脚上的4个配置位,可见这是与它们名称的尾号是一致的,MODEx和CNFx控制PIN_x。掌握这个规律,后面几条语句自然容易推敲出来是什么意思。

    不过,有些不同寻常的是:

// Reset the corresponding ODR bit 

        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)

        {

          GPIOx->BRR = (((u32)0x01) << (pinpos + 0x08));

        }

        //Set the corresponding ODR bit 

        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)

        {

          GPIOx->BSRR = (((u32)0x01) << (pinpos + 0x08));

        }

 

很匪夷所思,根据判断的条件,显然是要选择下拉输入还是上拉输入。但它居然需要对输出数据复位置位寄存器BRR和BSRR来操作。这是为何?不管那么多,先看手册:

 

在CNFX[1:0]中,显然不能选定上拉和下拉(MODEX[1:0]同样也没有),再看输入口配置电路图:

 

 

 

 

    必须有一个控制器控制着这个ON/OFF。那么在下图中,我们就可以找到答案:

 

 

 

 

这下了然了吧!ODR寄存器是参与选择上拉和下拉模式的。

    现在大家可以通过写寄存器来做一些像点LED灯的简单操作了。虽说直接使用库函数绝对是很快捷的方法,但是了解了寄存器的功能,目的是为了未来要使用这些库函数时能得心应手。我们学习这些寄存器不是为了摆脱库函数,而是为了更好地利用库函数。

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
您好!对于STM32GPIO输出实验,点亮三色LED,您可以按照以下步骤进行操作: 1. 首先,您需要在STM32的开发环境中创建一个新的工程。 2. 在工程中,您需要包含相应的头文件,如"stm32f10x.h"。 3. 接下来,配置相应的引脚为输出模式。假设红色LED连接到GPIOA的Pin0引脚,绿色LED连接到GPIOA的Pin1引脚,蓝色LED连接到GPIOA的Pin2引脚,您可以使用以下代码进行配置: ``` GPIO_InitTypeDef GPIO_InitStructure; // 使能GPIOA的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 配置红色LED引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置绿色LED引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置蓝色LED引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); ``` 4. 现在,您可以使用相应的寄存器来控制LED的点亮和熄灭。例如,要点亮红色LED,可以使用以下代码: ``` GPIO_SetBits(GPIOA, GPIO_Pin_0); // 将Pin0引脚置高,点亮红色LED ``` 要熄灭红色LED,可以使用以下代码: ``` GPIO_ResetBits(GPIOA, GPIO_Pin_0); // 将Pin0引脚置低,熄灭红色LED ``` 同样的方法,您可以控制绿色和蓝色LED的点亮和熄灭。 这就是点亮三色LED的基本步骤。希望对您有所帮助!如果您有任何其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值