GPIO_SetBits函数解析


一、GPIO_SetBits的作用

GPIO_SetBits函数用于将某位引脚置1,使其输出高电平。

二、详细解析

1.函数原型

代码如下:

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  GPIOx->BSRR = GPIO_Pin;
}

1.1.解析 GPIO_TypeDef* GPIOx

代码如下:

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;

GPIO_TypeDef是一个结构体变量,变量类型时结构体。如同int变量的变量类型是整形变量。

GPIO_TypeDef* GPIOx 类似于 int* p; 表示一个结构体变量的地址,前面定义这个类型的结构体变量,给这个变量的各个成员赋予我们需要用到的值。最后通过它的地址找到这个结构体变量,用这个结构体变量成员的值初始化GPIOx 。

GPIO_TypeDef* GPIOx 类似于 int* p; p=&a; 所以p是一个地址值,那么GPIOx也表示一个地址值。GPIOx为GPIOA时就表示GPIOA的起始地址GPIOx为GPIOB时就表示GPIOA的起始地址。

由于GPIO_TypeDef结构体变量里的成员变量的地址是连续的。所以GPIOx指向谁,地址就从谁开始。


1.2 解析 assert_param(IS_GPIO_ALL_PERIPH(GPIOx))

assert_param函数的作用就是检测传递给函数的参数是否是有效的参数。其中assert_param的传入参数是一个宏。

#define IS_GPIO_ALL_PERIPH(PERIPH) ( ((PERIPH) == GPIOA) || \
                                     ((PERIPH) == GPIOB) || \
                                     ((PERIPH) == GPIOC) || \
                                     ((PERIPH) == GPIOD) || \
                                     ((PERIPH) == GPIOE) || \
                                     ((PERIPH) == GPIOF) || \
                                     ((PERIPH) == GPIOG) )

IS_GPIO_ALL_PERIPH(PERIPH) 中的 PERIPH 和 PERIPH) == GPIOA 的 PERIPH 一样,也就是说IS_GPIO_ALL_PERIPH(PERIPH)中的 PERIPH 只能为 GPIOA~G 这=中的其中一个。

1.3 解析 assert_param(IS_GPIO_PIN(GPIO_Pin))

#define IS_GPIO_PIN(PIN) ((((PIN) & (uint16_t)0x00) == 0x00) && ((PIN) != (uint16_t)0x00))
1、 ((PIN) & (uint16_t)0x00) == 0x00 无论PIN为何值其相与的结果都为0x00;也就是这个等式恒成立成立;
2、 (PIN) !=(uint16_t)0x00) 如果PIN=0x00;就不成立;其他的PIN值都成立;
1&&2 表示两者都成立为真 否则为假;也就是PIN的值不能为0x00;
例如GPIO_SetBits(GPIOB,GPIO_Pin_5); 中的GPIO_Pin_5就不等于0x00,其值为0x20。且GPIO_Pin_0~GPIO_Pin_15的值都不为0x00

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

1.4 解析 GPIOx->BSRR = GPIO_Pin

typedef struct
{
  __IO uint32_t CRL;	/*GPIO 端口配置低寄存器 地址偏移: 0x00 */
  __IO uint32_t CRH;	/*GPIO 端口配置高寄存器 地址偏移: 0x04 */
  __IO uint32_t IDR;	/*GPIO 数据输入寄存器 地址偏移: 0x08 */
  __IO uint32_t ODR;	/*GPIO 数据输出寄存器 地址偏移: 0x0C */
  __IO uint32_t BSRR;	/*GPIO 位设置/清除寄存器 地址偏移: 0x10 */
  __IO uint32_t BRR;	/*GPIO 端口位清除寄存器 地址偏移: 0x14 */
  __IO uint32_t LCKR;	/*GPIO 端口配置锁定寄存器 地址偏移: 0x18 */
} GPIO_TypeDef;

BSRR寄存器被定义在 GPIO_TypeDef 结构体里,GPIOx->BSRR 这一步已经给出了IO口的具体地址,由于BSRR寄存器是32位的,详情在下图。
在这里插入图片描述
由上图可以知道,BSRR寄存器高16位写1->清0,低16位写1->置1,由 #define GPIO_Pin_5 ((uint16_t)0x0020),0x0020化为二进制数是 0000 0000 0010 0000,是低16位(全32位的二进制数为:0000 0000 0000 0000 0000 0000 0010 0000),所以将0x20赋值给BSRR寄存器的话,就是在BSRR寄存器内的第 6(5+1)个位 置1。

对 GPIOx->BSRR = GPIO_Pin 另一个通俗点的理解(和上面的理解有相似之处)就是:
GPIOx->BSRR的意思就是告诉我们,置1或者置0,需要在BSRR寄存器里面设置,但是具体怎么设置呢?
GPIO_Pin 就是具体的设置方法了。因为BSRR寄存器是32位的(里面有32个位置可以置1或者置0),而 GPIO_Pin_5 的置为 0x0020,0x0020化为二进制数是 0000 0000 0010 0000,是低16位(全32位的二进制数为:0000 0000 0000 0000 0000 0000 0010 0000),所以将0x20赋值给BSRR寄存器的话,就是在BSRR寄存器内的第 6(5+1)个位 置1。

GPIOx->BSRR = GPIO_Pin 还有一个理解。在使用例子 GPIO_SetBits(GPIOB,GPIO_Pin_5) 中,GPIOx->BSRR 的 GPIOx 为GPIOB,而:

#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)

#define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00)

#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)

#define PERIPH_BASE           ((uint32_t)0x40000000)

所以,GPIOB的基地址是 0x40000000 + 0x10000 + 0x0C00 = 0x40010C00。整个GPIOB的地址范围是一段内存地址,。下面寄存器的地址分别为: 0x00 、 0x04 、 0x08 、 0x0C 、 0x10 、 0x14 、 0x18,每个相差 0x04 - 0x00 = 4 个内存单元,所以最大的地址为0x18 + 4 = 0x1C,所以 GPIOB 他的地址范围大小是0x00 ~ 0x1C (0x40010C00 ~ 0x40010C1C) 。

typedef struct
{
  __IO uint32_t CRL;	/*GPIO 端口配置低寄存器 地址偏移: 0x00 */
  __IO uint32_t CRH;	/*GPIO 端口配置高寄存器 地址偏移: 0x04 */
  __IO uint32_t IDR;	/*GPIO 数据输入寄存器 地址偏移: 0x08 */
  __IO uint32_t ODR;	/*GPIO 数据输出寄存器 地址偏移: 0x0C */
  __IO uint32_t BSRR;	/*GPIO 位设置/清除寄存器 地址偏移: 0x10 */
  __IO uint32_t BRR;	/*GPIO 端口位清除寄存器 地址偏移: 0x14 */
  __IO uint32_t LCKR;	/*GPIO 端口配置锁定寄存器 地址偏移: 0x18 */
} GPIO_TypeDef;
  • 6
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的示例代码,用于初始化STM32F4的多个IO口。本例子初始化了GPIOA的2,3,4三个输出IO口,将它们设置为推挽输出,输出高电平。 ```c #include "stm32f4xx.h" int main(void) { // 1. 配置GPIOA端口时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // 2. 配置GPIOA的2,3,4三个IO口为输出模式 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); // 3. 设置GPIOA的2,3,4三个IO口输出高电平 GPIO_SetBits(GPIOA, GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4); while(1) { // do something } } ``` 上述代码中,首先通过RCC_AHB1PeriphClockCmd()函数使能GPIOA端口时钟。然后,使用GPIO_InitTypeDef结构体配置GPIOA的2,3,4三个IO口为推挽输出模式,输出速度为50MHz,无上下拉电阻。最后,使用GPIO_SetBits()函数GPIOA的2,3,4三个IO口设置为高电平。 需要注意的是,上述代码中的GPIO_Pin_2、GPIO_Pin_3、GPIO_Pin_4是宏定义,分别代表GPIOA的2、3、4三个IO口。在实际开发中,可以根据需要进行修改。 此外,还需要在main函数之前定义SystemInit()函数,用于初始化系统时钟,具体内容可以参考STM32的启动文件。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值