• STM32 BSRR BRR ODR 寄存器解析(F4系列已经去掉BRR寄存器了)


    STM32 BSRR BRR ODR 寄存器解析(F4系列已经去掉BRR寄存器了)


    G0x0系列GPIO寄存器
    在这里插入图片描述

    F4系列GPIO寄存器(没有BRR寄存器了)
    在这里插入图片描述

    一、用法

    经常会看到类似如下的宏定义语句,用于对已经初始化后的 IO 口输出高、低电平。

    #define SET_BL_HIGH()           GPIOA->BSRR=GPIO_Pin_0 
    #define SET_BL_LOW()			GPIOA->BRR=GPIO_Pin_0
    
    • 1
    • 2

    其作用类似于如下两个库函数,

    void GPIO_SetBits(GPIO_Typedef* GPIOx, uint16_t GPIO_Pin)
    void GPIO_ResetBits(GPIO_Typedef* GPIOx, uint16_t GPIO_Pin)  
    
    • 1
    • 2

    而且实际上这两个库函数就是通过修改BSRR,BRR寄存器的值来实现对 IO 口设置的。如下便是输出高电平的函数体:

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    因此,使用宏或者库函数本质上都是一样的。区别在于使用宏更快,而使用函数更灵活。

    注意:在要求速度/时序的更快更严格的场合(模拟I2C,模拟SPI,Σ-∆ADC中DOUT/RDRY同一引脚等情况的),建议使用宏定义操作IO口对应的引脚。
    在这里插入图片描述

    #define IN1()  {GPIOA->MODER&=0XFF3FFFFF;} //PA11
    #define OUT1() {GPIOA->MODER&=0XFF3FFFFF;GPIOA->MODER|=0X00400000;}
    
    #define IN2()  {GPIOA->MODER&=0XFFF3FFFF;}//PA9
    #define OUT2() {GPIOA->MODER&=0XFFF3FFFF;GPIOA->MODER|=0X00040000;}
    
    
    #define	DRDY1   (GPIOA->IDR)&(uint16_t)0x0800
    #define	DRDY2   (GPIOA->IDR)&(uint16_t)0x0200
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    二、解释

    BSRR 和 BRR 都是 STM32 系列 MCU 中 GPIO 的寄存器。 BSRR 称为端口位设置/清楚寄存器,BRR称为端口位清除寄存器。

    BSRR 低 16 位用于设置 GPIO 口对应位输出高电平,高 16 位用于设置 GPIO 口对应位输出低电平。

    BRR 低 16 位用于设置 GPIO 口对应位输出低电平。高 16 位为保留地址,读写无效。

    所以理论上来讲,BRR 寄存器的功能和 BSRR 寄存器高 16 位的功能是一样的。也就是说,输出低电平的宏语句,可以有如下两种写法。

    #define SET_BL_LOW()			GPIOA->BRR=GPIO_Pin_0
    等价于
    #define SET_BL_LOW()            GPIOA->BSRR=GPIO_Pin_0 << 16 
    
    • 1
    • 2
    • 3

    这么来看的话,其实 BRR 寄存器是比较多余的。而实际上,在最新的 STM32F4 系列 MCU 的 GPIO 寄存器中,已经找不到 BRR 寄存器了,仅保留了 BSRR 寄存器用于实现端口输出高低电平。因此,在 STM32F4 系列 MCU 的 HAL 库函数中,对 GPIO 口输出高低电平的函数为如下形式:

    void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    {
      /* Check the parameters */
      assert_param(IS_GPIO_PIN(GPIO_Pin));
      assert_param(IS_GPIO_PIN_ACTION(PinState));
    
      if(PinState != GPIO_PIN_RESET)
      {
        GPIOx->BSRR = GPIO_Pin;
      }
      else
      {
        GPIOx->BSRR = (uint32_t)GPIO_Pin << 16U;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    而早期 ST 的标准库 std 中,关于 GPIO 口输出高低电平的函数为如下形式,里面通过两个16位的指针分别指向 BSRR 的高16位和低16位。

    typedef struct
    {
      __IO uint32_t MODER;    /*!< GPIO port mode register,               Address offset: 0x00      */
      __IO uint32_t OTYPER;   /*!< GPIO port output type register,        Address offset: 0x04      */
      __IO uint32_t OSPEEDR;  /*!< GPIO port output speed register,       Address offset: 0x08      */
      __IO uint32_t PUPDR;    /*!< GPIO port pull-up/pull-down register,  Address offset: 0x0C      */
      __IO uint32_t IDR;      /*!< GPIO port input data register,         Address offset: 0x10      */
      __IO uint32_t ODR;      /*!< GPIO port output data register,        Address offset: 0x14      */
      __IO uint16_t BSRRL;    /*!< GPIO port bit set/reset low register,  Address offset: 0x18      */
      __IO uint16_t BSRRH;    /*!< GPIO port bit set/reset high register, Address offset: 0x1A      */
      __IO uint32_t LCKR;     /*!< GPIO port configuration lock register, Address offset: 0x1C      */
      __IO uint32_t AFR[2];   /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
    } GPIO_TypeDef;
    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->BSRRL = GPIO_Pin;
    }
    void GPIO_ResetBits(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->BSRRH = GPIO_Pin;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    可见,不管是输出高还是输出低,都是对 BSRR 寄存器的操作。

    三、BSRR、BRR、 ODR 之间的关系

    配置 BSRR , BRR 是为了对端口输出进行配置,而 ODR 寄存器也是用于输出数据的寄存器,一个 ODR 寄存器控制了一组(16位)的 GPIO 输出。因此,对 ODR 进行修改也可以到达对 IO 口输出进行配置。

    但是,由于对 ODR 寄存器的读写操作必须以 16 位的形式进行。因此,如果使用 ODR 改写数据以控制输出时,须采用“读-改-写”的形式进行。

    假设需要对 GPIOA_Pin_6 输出高电平。采用改写 ODR 寄存器的方式时,使用“读-改-写”操作,代码如下:

    uint32_t temp;
    temp = GPIOA->ODR;
    temp = temp | GPIO_Pin_6;
    GPIOA->ODR = temp;
    
    • 1
    • 2
    • 3
    • 4

    这是因为在修改 ODR 时,为了确保对端口 6 的修改不会影响到其他端口的输出,需要对端口的原始数据进行保存,之后再对端口 6 的值进行修改,最后再写入寄存器。而对 BSRR 的操作,是写 1 有效,写 0 不改变原状态,因此可以对端口 6 置 1,其他位保持为 0。BSRR 为 1 的位,会修改相应的 ODR 位,从而控制输出电平。

    对 BSRR 的操作可以实现原子操作。因此在设置单个 IO 口输出时,使用 BSRR 进行操作会更加方便。

    但也有例外的时候,在需要对单个IO口进行 Toggle 操作时(即对当前输出取反输出,当前输出为高则输出低,当前输出低则输出高),官方的库函数就是直接对 ODR 寄存器进行操作的。代码如下:

    void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
    {
      /* Check the parameters */
      assert_param(IS_GPIO_PIN(GPIO_Pin));
    
      GPIOx->ODR ^= GPIO_Pin;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    原文链接:https://blog.csdn.net/u011303443/article/details/76514537

  • 相关阅读:
    nginx 命令
    golang 中的go指针
    【mysql】1000w数据量的分页查询SQL,如何优化提升性能?
    Improved Baselines with Momentum Contrastive Learning
    java虚拟机栈
    Redis_配置文件详解
    代码随想录训练营第35天|LeetCode 860.柠檬水找零、406.根据身高重建队列、452. 用最少数量的箭引爆气球
    【Python】Python 网络编程 ( Socket 套接字简介 | Socket 套接字使用步骤 | Socket 套接字服务端与客户端开发 )
    Python得到字符的阿斯克码值 chr ord
    植物单细胞核转录组测序——单细胞研究高能新爆发点
  • 原文地址:https://blog.csdn.net/m0_46577050/article/details/127897059