细说HAL_GPIO_TogglePin()函数写BSSR寄存器的原理

2024-04-27 23:12

本文主要是介绍细说HAL_GPIO_TogglePin()函数写BSSR寄存器的原理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1.GPIO端口寄存器

2.HAL_GPIO_TogglePin()函数的定义

(1) 详细解读odr=GPIOx->ODR

(2)详细解读给BSRR寄存器赋值

(3)给BSRR寄存器的相应位赋值就可以改变PB3的引脚状态

(4)BRR寄存器

(5)ODR寄存器


1.GPIO端口寄存器

        在结构体GPIO_TypeDef中,11个GPIO端口寄存器都是32位。其中,有4个配置寄存器(GPIOx_MODER,GPIOx_OTYPER,GPIOx_OSPEEDR和GPIOx_PUPDR)、2个数据寄存器(GPIOx_IDR和GPIOx_ODR)、1个置位/复位寄存器(GPIOx_BSRR)、1个锁定(locking)配置寄存器(GPIOx_LCKR)、2个功能选择寄存器(alternate function selection registers,GPIOx_AFRH和GPIOx_AFRL)和1个按位复位寄存器GPIO_BRR。因为STM32G4xx中有7个GPIO端口,所以上述寄存器中的x是指A、B、C、D、E、F、G。

        譬如GPIO端口B的输出寄存器,就是GPIOB_ODR。

2.HAL_GPIO_TogglePin()函数的定义

        重写函数HAL_GPIO_TogglePin():

void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
{uint32_t odr;assert_param(IS_GPIO_PIN(GPIO_Pin));odr = GPIOx->ODR;GPIOx->BSRR = ((odr & GPIO_Pin)<<GPIO_NUMBER)|(~odr & GPIO_Pin);
}

        第一个参数GPIOx的类型为GPIO_TypeDef,也就是说,GPIOx只能是结构体GPIO_TypeDef的或员,即GPIO端口的寄存器。注意,在函数HAL_GPIO_TogglePin()的参数义中,参数GPIOx的前面有个“*”,表示GPIOx这个变量是一个指针型变量。因此,在访问结构体成员时,就要采用指针的形式。

(1) 详细解读odr=GPIOx->ODR

        首先,语句odr=GPIOx->ODR,表示用指计形式访间GPIO端口的输出数据寄有(ODR),输出寄存器ODR的值也就是该寄存器所对应端口的状态。前面调用函数HAL_GPIO_TogglePin时,用的参数是LED3_GPIO_Port,而LEDB_GPIO_Port在main.h文件中定义为GPIOB,所以程序运行到此处,odr = GPIOx→ODR就是取出GPIOB的输出数寄存器的值,赋给变量odr。

(2)详细解读给BSRR寄存器赋值

        其次,函数的最后一条语句,是一个给BSRR寄存器赋值的语句:

GPIOx->BSRR = ((odr & GPIO_Pin)<<GPIO_NUMBER)|(~odr & GPIO_Pin);

        语句中,等号右侧将odr“与”GPIO_Pin后左移GPIO_NUMBER(在stm32g4xx_hal_gpio.c中有定义,为16)位,然后与~odr &.GPIO_Pin的结果相“或”,结果赋值给BSRR寄存器。

        GPIO_Pin是HAL_GPIO_TogglePin函数的第2个参数,它的值为16位无符号数(uint16_t),本例对应的是PB3,所以该值为0x0008(从右侧数第3位为1,其余均为0)。

        将odr“与”GPIO_Pin后即可得到当前PB3引脚的状态:如果PB3引脚的状态为1(高电平),“与”后的结果为0x0008,将此结果左移16位(GPIO_NUMBER),也就是移位到高16位(odr被定义为32位无符号数),移位后第19位为1;如果PB3引脚的状态为0(低电平),相“与”后的结果为0x0,移位后第19位同样也为0。

        odr取“反”后与GPIO_Pin相“与”的含义。仍然以PB3为例,如果PB3引脚当前状态为1,取反(~)后为0,则与GPIO_Pin相“与”后的结果为0x0;如果PB3引脚当前状态0,则相“与”后的结果为0x0008。

        这样,这条赋值语句执行后的结果就比较清楚了:当I/O引脚状态为1时,等号右侧“或”之前括号内的值为1,之后的为0;当I/O引脚状态为0时,之前的值为0,之后的值为1。但“或”之前的值会移位到高16位,“或”之后的值则没有移位。也就是说,当I/O引脚状态为1时,给BSRR寄存器的高16位赋值;当I/O引脚状态为0时,给低16位赋值。

(3)给BSRR寄存器的相应位赋值就可以改变PB3的引脚状态

        在STM32G4系列MCU的参考手册中,查到BSRR寄存器的结构:

31

30

29

28

27

26

25

24

23

22

21

20

19

18

17

16

BR15

BR14

BR13

BR12

BR11

BR10

BR9

BR8

BR7

BR6

BR5

BR4

BR3

BR2

BR1

BR0

w

w

w

w

w

w

w

w

w

w

w

w

w

w

w

w

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

BS15

BS14

BS13

BS12

B811

BS10

BS9

BS8

BS7

BS6

BS5

BS4

BS3

BS2

BS1

BS0

w

w

w

w

w

w

w

w

w

w

w

w

w

w

w

w

        BSRR寄存器的高16位为BR[15:0],其中BR是指bit reset,按位复位;低16位为BS[15:0],其中BS是指bit set,按位置位。这些位都是只写(write-only,字母w表示只写)的,读它是没有意义的只是返回0而已。BR[15;0]中的某位为1,会复位相应的输出数据位(ODx,即输出数据寄存器中的某位);SR[15:0]中的某位为1,会置位相应的输出数据位(ODx)。当然,要在硬件上实现端口或引脚输出状态的变化,是需要相应电路来实现的;不过,对使用者来说,需要的只是配置相应的的寄存器。

       于是,库函数HAL_GPIO_TogglePin()通过操作GPIO的BSRR寄理了翻转I/0引脚状态的目的。

(4)BRR寄存器

        在STM32G4系列MCU的参考手册中,查到BRR寄存器的结构:

31

30

29

28

27

26

25

24

23

22

21

20

19

18

17

16

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

BR15

BR14

BR13

BR12

BR11

BR10

BR9

BR8

BR7

BR6

BR5

BR4

BR3

BR2

BR1

BR0

w

w

w

w

w

w

w

w

w

w

w

w

w

w

w

w

        BRR寄存器虽然是32位的,但实际用到的只是低16位,分别对应该GPIO端口的16个I/O。BRR寄存器的低16位与BSRR寄存器的高16都是按位复位寄存器(BR[15:0]),所起的作用是一样的。

(5)ODR寄存器

        GPIO的BRR和BSRR对输出引脚状态的改变,是与ODR相一致的。也就是说,PB3通过BRR复位后,GPIOB的ODR寄存器的相应位(第3位)的值也会变为0。ODR也只是用了低16位,分别对应GPIO端口的16个引脚,OD[15:0]就是相应引脚的状态数据,rw表示该位可读可写。

         在STM32G4系列MCU的参考手册中,查到ODR寄存器的结构:

31

30

29

28

27

26

25

24

23

22

21

20

19

18

17

16

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

OD15

OD14

OD13

OD12

OD11

OD10

OD9

OD8

OD7

OD6

OD5

OD4

OD3

OD2

OD1

OD0

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

这篇关于细说HAL_GPIO_TogglePin()函数写BSSR寄存器的原理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/941747

相关文章

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

Kotlin 作用域函数apply、let、run、with、also使用指南

《Kotlin作用域函数apply、let、run、with、also使用指南》在Kotlin开发中,作用域函数(ScopeFunctions)是一组能让代码更简洁、更函数式的高阶函数,本文将... 目录一、引言:为什么需要作用域函数?二、作用域函China编程数详解1. apply:对象配置的 “流式构建器”最

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

C++中::SHCreateDirectoryEx函数使用方法

《C++中::SHCreateDirectoryEx函数使用方法》::SHCreateDirectoryEx用于创建多级目录,类似于mkdir-p命令,本文主要介绍了C++中::SHCreateDir... 目录1. 函数原型与依赖项2. 基本使用示例示例 1:创建单层目录示例 2:创建多级目录3. 关键注

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程

kotlin的函数forEach示例详解

《kotlin的函数forEach示例详解》在Kotlin中,forEach是一个高阶函数,用于遍历集合中的每个元素并对其执行指定的操作,它的核心特点是简洁、函数式,适用于需要遍历集合且无需返回值的场... 目录一、基本用法1️⃣ 遍历集合2️⃣ 遍历数组3️⃣ 遍历 Map二、与 for 循环的区别三、高

C语言字符函数和字符串函数示例详解

《C语言字符函数和字符串函数示例详解》本文详细介绍了C语言中字符分类函数、字符转换函数及字符串操作函数的使用方法,并通过示例代码展示了如何实现这些功能,通过这些内容,读者可以深入理解并掌握C语言中的字... 目录一、字符分类函数二、字符转换函数三、strlen的使用和模拟实现3.1strlen函数3.2st