细说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

相关文章

Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别对比分析

《SpringBootInterceptor的原理、配置、顺序控制及与Filter的关键区别对比分析》本文主要介绍了SpringBoot中的拦截器(Interceptor)及其与过滤器(Filt... 目录前言一、核心功能二、拦截器的实现2.1 定义自定义拦截器2.2 注册拦截器三、多拦截器的执行顺序四、过

Java 队列Queue从原理到实战指南

《Java队列Queue从原理到实战指南》本文介绍了Java中队列(Queue)的底层实现、常见方法及其区别,通过LinkedList和ArrayDeque的实现,以及循环队列的概念,展示了如何高效... 目录一、队列的认识队列的底层与集合框架常见的队列方法插入元素方法对比(add和offer)移除元素方法

SQL 注入攻击(SQL Injection)原理、利用方式与防御策略深度解析

《SQL注入攻击(SQLInjection)原理、利用方式与防御策略深度解析》本文将从SQL注入的基本原理、攻击方式、常见利用手法,到企业级防御方案进行全面讲解,以帮助开发者和安全人员更系统地理解... 目录一、前言二、SQL 注入攻击的基本概念三、SQL 注入常见类型分析1. 基于错误回显的注入(Erro

Spring IOC核心原理详解与运用实战教程

《SpringIOC核心原理详解与运用实战教程》本文详细解析了SpringIOC容器的核心原理,包括BeanFactory体系、依赖注入机制、循环依赖解决和三级缓存机制,同时,介绍了SpringBo... 目录1. Spring IOC核心原理深度解析1.1 BeanFactory体系与内部结构1.1.1

pandas使用apply函数给表格同时添加多列

《pandas使用apply函数给表格同时添加多列》本文介绍了利用Pandas的apply函数在DataFrame中同时添加多列,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录一、Pandas使用apply函数给表格同时添加多列二、应用示例一、Pandas使用apply函

Python中Namespace()函数详解

《Python中Namespace()函数详解》Namespace是argparse模块提供的一个类,用于创建命名空间对象,它允许通过点操作符访问数据,比字典更易读,在深度学习项目中常用于加载配置、命... 目录1. 为什么使用 Namespace?2. Namespace 的本质是什么?3. Namesp

MySQL 批量插入的原理和实战方法(快速提升大数据导入效率)

《MySQL批量插入的原理和实战方法(快速提升大数据导入效率)》在日常开发中,我们经常需要将大量数据批量插入到MySQL数据库中,本文将介绍批量插入的原理、实现方法,并结合Python和PyMySQ... 目录一、批量插入的优势二、mysql 表的创建示例三、python 实现批量插入1. 安装 PyMyS

MySQL中如何求平均值常见实例(AVG函数详解)

《MySQL中如何求平均值常见实例(AVG函数详解)》MySQLavg()是一个聚合函数,用于返回各种记录中表达式的平均值,:本文主要介绍MySQL中用AVG函数如何求平均值的相关资料,文中通过代... 目录前言一、基本语法二、示例讲解1. 计算全表平均分2. 计算某门课程的平均分(例如:Math)三、结合

深入理解Redis线程模型的原理及使用

《深入理解Redis线程模型的原理及使用》Redis的线程模型整体还是多线程的,只是后台执行指令的核心线程是单线程的,整个线程模型可以理解为还是以单线程为主,基于这种单线程为主的线程模型,不同客户端的... 目录1 Redis是单线程www.chinasem.cn还是多线程2 Redis如何保证指令原子性2.

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS