STM32标准库编程与51单片机直接写寄存器的区别和联系

简介:

        在学完51单片机之后,我们去学习32的时候,会发现编程的方法有很大的区别,让人非常的不适应,但是通过不断的调用相应外设的库函数之后,你也可以去编程STM32,来实现功能,但是你真的了解标准库吗?不少人只会局限在调用标准库,不论你是学完标准库还是初学标准库,都很有必要了解以下标准库的原理。

 寄存器:

我们首先要明确我们编程到底在编些什么?

单片机寄存器在单片机编程中起着至关重要的作用,它们是与单片机硬件紧密关联的特殊存储单元,用于控制各种硬件功能和状态。单片机寄存器与编程之间的联系主要体现在以下几个方面:

1. 硬件控制:单片机寄存器直接控制着单片机的各种硬件功能,如输入/输出端口、定时器、串口通信等。通过编程操作这些寄存器,可以实现对硬件的控制和配置。

2. 状态监测:单片机寄存器中存储着各种硬件的状态信息,如中断标志、定时器计数值、输入端口状态等。通过编程读取这些寄存器的值,可以实时监测硬件的状态,并根据需要进行相应的处理。

3. 中断处理:单片机中断是一种重要的事件处理机制,通过编程配置中断寄存器,可以实现对各种中断事件的响应和处理。例如,可以通过设置中断使能位和中断优先级来控制中断的触发和处理顺序。

4. 外设通信:单片机通常需要与外围设备进行通信,如传感器、执行器、显示器等。编程时需要操作相应的寄存器来配置通信接口和协议,以实现与外设的数据交换和控制。

5. 优化性能:直接操作寄存器可以提高程序的执行效率和性能,因为与使用高级函数库相比,直接操作寄存器可以减少额外的开销和延迟,从而更好地满足实时性要求。

因此,单片机编程中的许多操作都是通过操作寄存器来实现的,程序员需要深入了解单片机寄存器的功能和用法,以充分利用单片机的硬件资源,并编写出高效可靠的程序。

标准库的本质:

        别看标准库调用一大堆函数,其实你不断的通过拆开函数的一层层嵌套,最后还是发现,他还是通过写单片机的寄存器,来实现功能。

寄存器映射:

        既然写寄存器,那就要知道寄存器的地址,这就是寄存器映射。存储器本身不具有地址信息,它的地址是由芯片厂商或用户分配,给存储器分配地址的过程就称为存储器映射,具体见图存储器映射。如果给存储器再分配一个地址就叫存储器重映射

我们在编程的时候,可以通过他们的地址找到他们,然后来操作他们(通过 C 语言对它们进行数据的读和写)。

        储存器区域功能划分:

        在这 4GB 的地址空间中, ARM 已经粗线条的平均分成了 8 个块,每块 512MB ,每个块也都规定了用途,具体分类见表格存储器功能分类 。每个块的大小都有 512MB ,显然这是非常大的,芯片厂商在每个块的范围内设计各具特色的外设时并不一定都用得完,都是只用了其中的一部分而 已。

        在这 8 Block 里面,有 3 个块非常重要,也是我们最关心的三个块。 Block0用来设计成内部 FLASH,Block1 用来设计成内部 RAM,Block2 用来设计成片上的外设, 下面我们简单的介绍下这三个 Block 里面的具体区域的功能划分。 其中Block2我们需要特别的去留意。
        Block2 用于设计片内的外设,根据外设的总线速度不同, Block 被分成了 APB 和 AHB 两部分, 其中 APB 又被分为 APB1 和 APB2, 所以为什么我们写调用库的时候,会有APB1 和APB2以及AHB,因为他们外设所在的地方不同。

        在存储器 Block2 这块区域,设计的是片上外设,它们以四个字节为一个单元,共 32bit ,每一个 单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。 我们可以找到每个单元的起 始地址,然后通过 C 语言指针的操作方式来访问这些单元.如果每次都是通过这种地址的方式 来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。
        比如,我们找到 GPIOB 端口的输出数据寄存器 ODR 的地址是 0x40010C0C (至于这个地址如何 找到可以先跳过,后面我们会有详细的讲解),ODR 寄存器是 32bit ,低 16bit 有效,对应着 16 个 外部 IO ,写 0/1 对应的的 IO 则输出低 / 高电平。现在我们通过 C 语言指针的操作方式,让 GPIOB的16 IO 都输出高电平。
        
*(unsigned int*)(0x4001 0C0C) = 0xFFFF;
        0x4001 0C0C 在我们看来是 GPIOB 端口 ODR 的地址,但是在编译器看来,这只是一个普通的变 量,是一个立即数,要想让编译器也认为是指针,我们得进行强制类型转换,把它转换成指针, 即 (unsigned int *)0x4001 0C0C ,然后再对这个指针进行 * 操作。
        刚刚我们说了,通过绝对地址访问内存单元不好记忆且容易出错,我们可以通过寄存器的方式来操作。
// GPIOB 端口全部输出 高电平
#define GPIOB_ODR *(unsigned int*)(GPIOB_BASE+0x0C)
GPIOB_ODR = 0xFF;

        记住绝对地址或许有点难,但是记住个寄存器的名字, 似乎简单不少,我们这里直接通过宏定义,实现直接往寄存器赋值。本质还是通过寄存器的地址,通过指针来对该寄存器进行读写。

         stm32外设地址映射

        片上外设区分为三条总线,根据外设速度的不同,不同总线挂载着不同的外设,APB1 挂载低速外设,APB2 AHB 挂载高速外设。相应总线的最低地址我们称为该总线的基地址,总线基地
址也是挂载在该总线上的首个外设的地址。 其中 APB1 总线的地址最低,片上外设从这里开始, 也叫外设基地址。

        总线基地址  :

表格 总线基地址 的“相对外设基地址偏移”即该总线地址与“片上外设”基地址 0x4000 0000 的差值。

       外设基地址:

这里面我们以 GPIO 这个外设来讲解外设的基地址,GPIO 属于高速的外设,挂载到APB2总线 上,具体见表格外设 GPIO 基地址

         外设寄存器:

        在 XX 外设的地址范围内,分布着的就是该外设的寄存器。以 GPIO 外设为例, GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,基本功能是控制引脚输出高电平或者低电平。
        GPIO 有很多个寄存器,每一个都有特定的功能。每个寄存器为 32bit ,占四个字节,在该外设的基地址上按照顺序排列,寄存器的位置都以相对该外设基地址的偏移地址来描述。这里我们以GPIOB 端口为例,来说明 GPIO 都有哪些寄存器,具体见表格 GPIOB 端口的寄存器地址列表

c语言对寄存器的封装:

        可以说,只要你看懂这个,那么你几乎就能知道标准库大体是怎么写出来的。

封装总线和外设基地址:

        在编程上为了方便理解和记忆,我们把总线基地址和外设基地址都以相应的宏定义起来,总线或者外设都以他们的名字作为宏名。

 

        首先定义了“片上外设”基地址 PERIPH_BASE ,接着在 PERIPH_BASE 上加入各个总线的地址偏移,得到 APB1APB2 总线的地址 APB1PERIPH_BASEAPB2PERIPH_BASE, 在其之上加入外设地址的偏移,得到 GPIOA-G 的外设地址,最后在外设地址上加入各寄存器的地址偏移,得到特定寄存器的地址。一旦有了具体地址,就可以用指针读写。

 

封装寄存器列表:

        用上面的方法去定义地址,还是稍显繁琐,例如 GPIOA-GPIOE 都各有一组功能相同的寄存器, 如 GPIOA_ODR/GPIOB_ODR/GPIOC_ODR 等等,它们只是地址不一样,但却要为每个寄存器都定义它的地址。为了更方便地访问寄存器,我们引入 C 语言中的 结构体 语法对寄存器进行封装这就是为什么,标准库要定义这么多结构体。
 /* GPIO 寄存器列表 */typedef struct 
{uint32_t CRH; /*GPIO 端口配置高寄存器 地址偏移: 0x04 */uint32_t IDR; /*GPIO 数据输入寄存器 地址偏移: 0x08 */uint32_t ODR; /*GPIO 数据输出寄存器 地址偏移: 0x0C */uint32_t BSRR; /*GPIO 位设置/清除寄存器 地址偏移: 0x10 */uint32_t BRR; /*GPIO 端口位清除寄存器 地址偏移: 0x14 */uint16_t LCKR; /*GPIO 端口配置锁定寄存器 地址偏移: 0x18 */} GPIO_TypeDef; uint32_t CRL; /*GPIO 端口配置低寄存器 地址偏移: 0x00 */
这段代码用 typedef 关键字声明了名为 GPIO_TypeDef 的结构体类型,结构体内有 7 个成员变量, 变量名正好对应寄存器的名字。C 语言的语法规定,结构体内变量的存储空间是连续的,其中 32位的变量占用 4 个字节, 16 位的变量占用 2 个字节。
        也就是说,我们定义的这个GPIO_TypeDef ,假如这个结构体的首地址为 0x4001 0C00(这也是第一个成员变量 CRL 的地址),那么结构体中第二个成员变量 CRH 的地址即为 0x4001 0C00 +0x04,加上的这个 0x04,正是代表 CRL 所占用的 4 个字节地址的偏移量,其它成员变量相对于结构体首地址的偏移,在上述代码右侧注释已给。这样的地址偏移与 STM32 GPIO 外设定义的寄存器地址偏移一一对应,只要给结构体设置好首地址,就能把结构体内成员的地址确定下来,然后就能以结构体的形式访问寄存器,

 

这 段 代 码 先 用 GPIO_TypeDef 类 型 定 义 一 个 结 构 体 指 针 GPIOx , 并让指针指向地址
GPIOB_BASE(0x4001 0C00) ,使用地址确定下来,然后根据 C 语言访问结构体的语法,用 GPIOx- >ODR 及 GPIOx->IDR 等方式读写寄存器。

 

最后,我们更进一步,直接使用宏定义好 GPIO_TypeDef 类型的指针,而且指针指向各个 GPIO
端口的首地址,使用时我们直接用该宏访问寄存器即可.
 /* 使用 GPIO_TypeDef 把地址强制转换成指针 */#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)#define GPIOH ((GPIO_TypeDef *) GPIOH_BASE)/* 使用定义好的宏直接访问 *//* 访问 GPIOB 端口的寄存器 */GPIOB->BSRR = 0xFFFF; //通过指针访问并修改 GPIOB_BSRR 寄存器GPIOB->CRL = 0xFFFF; //修改 GPIOB_CRL 寄存器GPIOB->ODR =0xFFFF; //修改 GPIOB_ODR 寄存器uint32_t temp;temp = GPIOB->IDR; //读取 GPIOB_IDR 寄存器的值到变量 temp 中/* 访问 GPIOA 端口的寄存器 */GPIOA->BSRR = 0xFFFF;GPIOA->CRL = 0xFFFF;GPIOA->ODR =0xFFFF;uint32_t temp;temp = GPIOA->IDR; //读取 GPIOA_IDR 寄存器的值到变量 temp 中

总结:

因为32的寄存器太多也太复杂,如果说像51单片机来直接对寄存器编程,会非常的麻烦,需要不断的去查询地址,和各个寄存器和各个位的功能,虽然说,他为我们提供了便利,但是,真正的学会这个单片机,还是需要去查阅芯片手册,看看各个寄存器的功能以及单片机整体的架构。

这篇只是大概告诉读者标准库是如何封装寄存器来对stm32中的寄存器进行读写,真正的去看懂标准库和标准库的函数,还是需要读者仔细的去研究标准库的每行代码。

最后,十分感谢野火科技,这些知识点都是在他那里学来的,也很推荐大家去他那里进行学习。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何高效协作?

前言 上一篇文章我们分享了《如何高效沟通》,本篇文章继续延伸至更复杂的场景,跨团队/跨部门协作。 什么是协同? 协同也会被称为协作合作,无论是哪种类型的团队,无论哪种方式的工作场景,都一定会有协作发…

智慧公厕解决方案易集成好使用的智能硬件

在现代城市建设中,智慧公厕的需求日益增长。为了提供更好的用户体验和更高效的管理,易集成、好使用的智能硬件成为智慧公厕解决方案的关键组成部分。 1. 蹲位有人无人感应器:是用于检测厕位有人无人的设备,根据现场不同的安装条件…

Rumble Club加速器哪个好用 稳定好用的联机加速器推荐

Rumble Club加速器哪个好用 稳定好用的联机加速器推荐 说到Rumble Club这款游戏,各位休闲玩家肯定不陌生,这是一款基于物理定律的在线玩家对战游戏,玩法独特且充满乐趣。玩家可以使用各种富有想象力的方式推搡、击打和超越对手,以…

我在本地部署通义千问Qwen1.5大模型,并实现简单的对话和RAG

节前,我们星球组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、参加社招和校招面试的同学,针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 汇总…

1957C - How Does the Rook Move?

题目链接:How Does the Rook Move? 如图: 因为每行每列都只能放一个棋子,因此我们用绿点来表示下的棋子,发现一个规律,当红色格子都被绿线划过时,那么就不能下棋子。当这个白色点放在xy这个点&#xff0c…

Linux使用Docker部署DashDot访问本地服务器面板

文章目录 1. 本地环境检查1.1 安装docker1.2 下载Dashdot镜像 2. 部署DashDot应用 本篇文章我们将使用Docker在本地部署DashDot服务器仪表盘,并且结合cpolar内网穿透工具可以实现公网实时监测服务器系统、处理器、内存、存储、网络、显卡等,并且拥有API接…

【解决】echarts条形图纵坐标显示不全

先说结论: option:{...grid: {containLabel: true},... }这个属性是控制整体的坐标标签的。加上这个就可以显示完整了。然后再根据其他属性调整标签的字体、颜色之类的 yAxis : [{...axisLabel:{width:100,overflow:break,truncate:...,color:red,fontSize:10,},..…

配置nodejs的俩小脚本

介绍:共两个脚本。 脚本1,用来配置环境变量,生成环境变量所需的配置信息,然后自己添加到系统环境变量里去 特别注意:该脚本需要放到nodejs目录下面,如果不是,则无法生成环境变量配置文本内容 另…

换行符导致字符串无法匹配

我运行一句sql的时候,发现对应的字段查询不出结果 但是我使用navicat查看列表的时候并没有发现什么异常 然后我用接口调的时候发现了异常,这个数据的前面有个换行符,但是用navicat之类的工具是看不出来的,因此对于备注这种东西最…

mPEG-OPSS,Methoxy PEG OPSS常用于制备高分子材料和纳米材料

【试剂详情】 英文名称 mPEG-OPSS,Methoxy PEG OPSS 中文名称 聚乙二醇单甲醚二巯基吡啶, 甲氧基-聚乙二醇-巯基吡啶 外观性状 由分子量决定,液体或者固体 分子量 1k,2k,3.4k,5k,6k&…

【问题】java查询MySQL时,mysql查询tinyint类型 的数据时,0会被转为false,1或以上会转为true

在做接口测试的数据库断言时,发现type字段断言总是失败,期望是0,打印数据库实际值是false 查看数据库格式: 解决: 在数据库url上,添加:&tinyInt1isBitfalse ——可解决java查询MySQL时&a…

MySQL数据库中备份和查询

总所周知我们用到的数据有可能丢失,那么我们就可以通过备份把数据找回,如何操作:请先看下面讲解 备份: 导出: 先对数据库进行备份,然后提取备份文件中的SQL语句 这就是我们导出数据,导出了就有…