[STM32]从零开始的STM32 BSRR、BRR、ODR寄存器讲解

news2025/5/12 8:50:47

一、前言

        学习STM32一阵子以后,相信大家对STM32 GPIO的控制也有一定的了解了。之前在STM32 LED的教程中也教了大家如何使用寄存器以及库函数控制STM32的引脚从而点亮一个LED,之前的寄存器只是作为一个引入,并没有深层次的讲解,在教程中,也没让大家有太多的了解。既然学习STM32有一段时间了,那我们能不能从现象推导到本质,去了解一下STM32 引脚控制的本质原理。那么本次教程,就来为大家讲解一下STM32引脚驱动的三个关键寄存器BSRR、BRR、ODR,相信看了本次教程以后,你对STM32会有更深层次的理解。如果你准备好了,那就让我们开始吧!

二、谁适合本次教程

        因为已经涉及到寄存器的操作与讲解了,所以请学习本次教程的小伙伴需要具备一定的STM32基础以及对十六进制转二进制比较熟悉。

三、资料的准备

        本次使用STM32F103C8T6进行演示,所以请大家自己准备一块STM32F103的开发板。本次教程中,我们需要去翻阅STM32F103的手册,所以大家准备一份STM32F103的数据手册,相信学习过一阵子STM32的小伙伴应该都有这个手册吧,这里我就不放了。

四、BSRR、BRR、ODR寄存器讲解

        在我们日常的代码或者一些模块的代码中,可能经常会看到下面这样的写法:

GPIOC->BSRR=0x00002000;

又或者是这种写法:

GPIOC->ODR=0x00002000;

我们可以很明显的看到,这些写法都是在控制GPIO的电平,那么这些写法之间都有什么区别呢?哪种写法更好呢?

1.BSRR

        这里我们首先来看BSRR,这里我们打开STM32F10的中文手册,找到BSRR寄存器处:

下面我们就来解释一下这个寄存器的作用,很明显的看到,这里寄存器的位下面只有一个W,表示这个寄存器只是可写的:

这里大家只需要记住这个寄存器只能写就可以了,后面会为大家讲解为什么。

我们再往下看,下面的寄存器详细描述中,将寄存器分为了两个部分来讲,分别是0-15位,以及16-31位,总的来说就是将这个寄存器分为了高十六位和低十六位:

这里我们先来看位16-31,这些位都被叫做BR位,这里的R即Reset,手册中的描述是这些位是用于清除端口0-15:

简单来说,16-31位就是用于将GPIO口拉低的。假如这里我想控制GPIOC,那么我就可以使用BSRR中的16-31位将GPIPC的0-16引脚全部拉低。

在下面也提到了,这些位只能写入并且只能一十六位的形式写入:

我们继续往下看,如果BSRR的16-31位写0不会对ODR产生影响,如果写1,则ODR的对应位就会为0,从而引脚电平被拉低。至于这里为什么我们写ODR的位就可以直接控制引脚我们后面讲解ODR寄存器的时候会讲。

然后我们来看BSRR的0-15位,这些位从手册中可以看到,是用于置位GPIO口的某一位的,0-15位被叫做BS位,这里的S即Set:

同样的,假如我还是控制GPIOC,那我就可以通过BSRR下的0-15位将GPIOC的0-15引脚都置高。同样的,下面也提到了,如果给0-15位的某一位置0,则ODR对应的位不产生影响。如果给0-15位置1那么对应的ODR位就会置1从而对应的引脚置1:

这里大家就可能有疑惑了,如果我将一个引脚对应BS位和BR位都置位,会怎么样?其实在手册中已经提到了,如果这样做的话,只有BR位会起作用:

看了上面的内容,相信大家对BSRR寄存器有一定的了解了,简单来说,它就是一个控制ODR寄存器对应位高低电平从而控制引脚的一个寄存器,在官方的库函数中,也使用到了BSRR寄存器来控制引脚电平:

这里大家可能又有疑问了,为什么我这里要通过BSRR来控制ODR从而来控制引脚的电平,我不能直接控制ODR吗?这个问题,同样也留到我们讲解ODR的时候再做讲解。

2.BRR

        BRR的功能和BSRR非常接近,以至于现在一些高端的芯片已经阉割了BRR寄存器,不过我们现在还是可以来看看,我们在手册中找到BRR对应的描述:

我们可以看到BRR寄存器的高十六位是没有作用的:

并且低十六位和BSRR一样,只能写:

这里我们直接看寄存器描述,这里提到了,0-15位主要用于清除端口的位,也就是为指定端口拉低。这里的用法其实和BSRR的高十六位一样,都是通过给对应的位置1从而给ORD对应的位置0从而控制引脚电平。如果你理解BSRR的话,理解BRR也不是什么难题,这里就不多说了:

3.ODR

        现在我们来讲解ODR,前面已经为大家留了许多问题在ODR这里了,现在我们一一来解决。

我们同样先在手册中找到ODR所在的位置:

这里我们可以看到,ODR的高十六位同样作为保留位。然后就是低十六位,这里的低十六位我们简单来说就是,对应了GPIO的十六个引脚。我们给ODR对应的位置高或者置低,那么对应的GPIO引脚就会被置对应的电平。假如我么就将ODR的第12位置1,那么对应的GPIO12就会被置1。在手册中也提到了,我们的BSRR寄存器可以对每个ODR位进行独立的设置和清除:

这就是为什么我们要通过BSRR来操作ODR从而来操作GPIO。因为BSRR可以对ODR的每一位进行操作并且不影响别的位。如果我们直接操作ODR的话,要实现不影响别的位的效果就需要将ODR的值先读出来然后再写入对应的值最后整体写入ODR,这也是我们常用的读-改-写的操作流程。那么大家可能又有疑问了,为什么我们的BSRR可以直接操作ODR。

这里我们在手册中,找到“8.1.8输出配置”,这里我们主要是需要下面的图:

我们将下面的图单独拿出来:

这里我们可以看到,我们的写入可以写到“位设置/清除寄存器”,这个“位设置/清除寄存器”是什么?这不就是BSRR吗?:

然后,我们写入BSRR以后,BSRR直接就写入了一个名为“输出数据寄存器”的地方,这个输出数据寄存器,不就是我们的ODR吗?:

相当于,BSRR一旦收到数据,就会发送给ODR,从而对ODR的位进行操作。BSRR发送完数据以后,就直接将内部存储的值扔掉了,本身就不保存值,所以读取BSRR本很就没有意义。这样也印证了为什么之前我们说BSRR不能读取。

最后,我们可以看到,我们的ODR上有一条单独的线,是用于读写ODR的,这些表示ODR可以被直接读写:

其实,总的来说,为什么我们不直接操作ODR呢?因为我们操作BSRR可以直接操作ODR的值从而不影响别的引脚。为什么我们操作BSRR可以直接操作ODR呢?因为它们在物理总线层面被链接在了一起,并且操作BSRR可以直接操作ODR的位。为什么BSRR不能读取呢?因为BSRR有了值以后直接就拿给ODR了,本身不存储值,没有读取的必要。

假如我们执行下面两段代码,在实际效果上应该是一样的:

#include "stm32f10x.h"                  
int main(void)
{
	RCC->APB2ENR=0x00000010;
	GPIOC->CRH=0x00300000;
	GPIOC->BSRR=0x00002000;
	while(1)
	{
	}
}
#include "stm32f10x.h"                  
int main(void)
{
	RCC->APB2ENR=0x00000010;
	GPIOC->CRH=0x00300000;
	GPIOC->ODR=0x00002000;
	while(1)
	{
	}
}

但是,最后的最后,仍然建议大家在操作引脚时不直接操作ODR,虽然我们可以通过读-改-写的方法保留原本的值并且写入新值,但是,这样会增加我们的代码量并且增加出错的概率。

五、结语

        以上就是我对GPIO相关的寄存器的一些见解,如果有讲得不对的地方,还请大家指正,最后感谢大家的观看!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2308651.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DeepSeek-V3关键技术之一:DeepSeekMoE

DeepSeekMoE 是一种创新的大规模语言模型架构,旨在通过高效的计算流程和优化设计,在保持高性能的同时显著降低计算成本。 1. 架构设计 DeepSeekMoE 基于 Transformer 架构,融合了以下核心技术: 专家混合系统(Mixture…

Android Activity启动流程详解

目录 Activity 启动流程详细解析 1. 应用层发起启动请求 1.1 调用 startActivity() 1.2 通过 Instrumentation 转发请求 2. 系统服务处理(AMS 阶段) 2.1 Binder IPC 通信 2.2 AMS 处理流程 2.3 跨进程回调 ApplicationThread 3. 目标进程初始化…

夜天之书 #106 Apache 软件基金会如何投票选举?

近期若干开源组织进行换届选举。在此期间,拥有投票权的成员往往会热烈讨论,提名新成员候选人和治理团队的候选人。虽然讨论是容易进行的,但是实际的投票流程和运作方式,在一个成员众多的组织中,可能会有不少成员并不清…

保姆级教程:用Chart.js实现柱状图与折线图联动

保姆级教程:用Chart.js实现柱状图与折线图联动 ▲ 最终实现的交互式组合图表效果 一、技术原理剖析 1.1 Chart.js渲染机制 Chart.js基于HTML5 Canvas实现图表绘制,其核心原理包括: 数据绑定:将数据对象映射为图形元素分层渲染:通过order属性控制图层叠加顺序坐标系计算:…

初阶MySQL(两万字全面解析)

文章目录 1.初识MySQL1.1数据库1.2查看数据库1.3创建数据库1.4字符集编码和排序规则1.5修改数据库1.6删除数据库 2.MySQL常用数据类型和表的操作2.(一)常用数据类型1.数值类2.字符串类型3.二进制类型4.日期类型 2.(二)表的操作1查看指定库中所有表2.创建表 3.查看表结构和查看表…

4.3MISC流量分析练习-wireshark-https

流量分析题目的例题 1.了解wireshark的过滤方式 2.了解tls跟ssl协议基本还原 3.了解xor基本变换方式,获取flag 附件是一个流量包,打开之后有各种流量,但是分析无果,然后丢到kali中使用binwalk进行分析,发现有一个r…

STM32CubeMx DRV8833驱动

一、DRV8833驱动原理 ​ STBY口接单片机的IO口,STBY置0电机全部停止,置1才能工作。STBY置1后通过AIN1、AIN2、BIN1、BIN2 来控制正反转。 AIN1AIN2电机状态00停止1speed反转speed1正转11停止 其中A端(AIN1与AIN2)只能控制AO1与…

【Qt】ffmpeg照片提取、视频播放▲

目录 一、图像的成像原理: RGB成像原理: YUV成像原理: 二、多线程 三、ffmpeg解码(照片提取) 1.准备工作 (1)在工程文件夹里面新建三个文件夹 (2)在main函数中加…

Vue+Elementui 全局配置el-table表格列宽可拖拽

1、需求分析 如何让表格列宽可以拖动 elementui的el-table如果想要列宽可以拖动的话 有一个属性叫 border 在模板里添加这个属性即可实现 但是系统里面的表格我不可能一个一个去添加border太麻烦 如果能够全局配置岂不是非常省时间吗 我们在main.js里面通过全局混入的方式来…

SQL命令详解之增删改数据

目录 简介 1 添加数据 1.1 基础语法 1.2 SQL 练习 2 修改数据 2.1 基础语法 2.2 SQL 练习 ​3 删除数据 3.1 基础语法 3.2 SQL 练习 总结 简介 在数据库操作中,增、删、改是最基础的操作,它们通常对应着SQL中的INSERT、DELETE和UPDATE命令。…

鸿蒙开发第4篇__关于在鸿蒙应用中使用Java语言进行设计

本博文对于鸿蒙APP程序员来说,很重要 HarmonyOS从 API8 开始不再支持使用Java作为开发语言,未来的新功能将在ArkTS中实现. API 8对应的是HarmonyOS 3.0.0版本, 2022年7月27日, 华为发布了HarmonyOS 3.0。 请看下图: 因此&#…

Linux三种网络方式

前言 发现运维啥都得会,这周就遇到了网络问题自己无法解决,因此痛定思痛学一下。 参考文献 你管这破玩意叫网络? 桥接模式、NAT模式、仅主机模式,原来是这样工作的 交换机 构成局域网,实现所有设备之间的通信。 …

Spring DIIoC

一.IoC 1.简介 什么是IoC?IoC,全称 Inversion of Control,控制反转。IoC是Spring的核心思想,Spring是⼀个“控制反转”的容器。 如果我们需要一个对象,正常来说我们是通过new一个对象,这个时候我们依赖的…

【前端基础】Day 2 CSS层叠样式表

目录 1.CSS简历 2.CSS 基础选择器 2.1标签选择器 2.2类选择器 2.3 id选择器 2.4通配符选择器 2.5总结 3.CSS字体属性 字体属性总结 4.CSS文本属性 4.1颜色 4.2对齐文本 4.3装饰文本 4.4文本缩进 4.5行间距 4.6文本属性总结 5.CSS的引入方式 5.1内部样式表 …

计算机毕业设计SpringBoot+Vue.js汽车资讯网站(源码+文档+PPT+讲解)

温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…

nuxt常用组件库html-validator、@nuxtjs/i18n、@nuxt/image、@unocss/nuxt使用解析

html-validator 主要用于自动验证nuxt服务器呈现的HTML(SSR和SSG),以检测可能导致水合错误的HTML常见问题,有助于减少水合错误,检测常见的可访问性错误。 安装 npx nuxilatest module add html-validator配置 若自动更新nuxt.config.ts配置文…

Leetcode-最大矩形(单调栈)

一、题目描述 给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。 输入:matrix [["1","0","1","0","0"],["1","0&…

Vue核心知识:动态路由实现完整方案

在Vue中实现动态路由,并结合后端接口和数据库表设计,是一个复杂的项目,需要多个技术栈和步骤的配合。以下将详细描述整个实现过程,包括数据库设计、后端接口设计、前端路由配置以及如何实现动态路由的功能。 目录 一、需求分析二…

【Docker】使用Docker搭建-MySQL数据库服务

零、更换Docker镜像源 因为国内现在封锁了Docker默认拉取镜像的站点(DockerHub),而且国内大部分Docker镜像站已全部下线,导致现在很多朋友在拉取镜像的时候会出现无法拉取的现象,这时候就需要进行更换Docker镜像源。 可…

DHCP配置和地址

DHCP:动态主机配置协议 DHCP系统组成 DHCP报文结构 DHCP报文类型 DHCP工作流程 DHCP租期更新 DHCP重绑定 自动保留IP 租期设置建议 IP地址释放 DHCP地址池 DHCP配置 DHCP接口地址池配置 DHCP全局地址池配置