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

news2024/12/23 7:23:11

简介:

        在学完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.coloradmin.cn/o/1615480.html

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

相关文章

nodejs版本过高导致vue-cli无法启动的解决方案

目录 前言异常现象解决方案总结 前言 之前使用软件管家升级了Nodejs,今天在运行Vue项目的时候老是报错,查了很多资料,最后确定是Nodejs版本过高导致的。 异常现象 E:\project\ry\RuoYi-Cloud\ruoyi-ui>npm run dev> ruoyi3.6.4 dev …

Attention和Transformer灵魂七问

1. 引言 最近,ChatGPT和其他聊天机器人将大语言模型LLMs推到了风口浪尖。这就导致了很多不是学ML和NLP领域的人关注并学习attention和Transformer模型。在本文中,我们将针对Transformer模型结构提出几个问题,并深入探讨其背后的技术理论。这…

实验2 组合逻辑电路与时序逻辑电路设计

实验目的: 1.构建基于verilog语言的组合逻辑电路和时序逻辑电路; 2.掌握verilog语言的电路设计技巧。 3.完成如下功能:加法器、译码器、多路选择器、计数器、移位寄存器等。 实验内容及步骤: 一、实验原理 原理图文件《数字系统设计_sch.pdf》,找到如下两个部分: 图…

Vim编辑器的安装及使用教程

文章目录 1:Ubuntu安装Vim1.1:图形界面安装1.2:命令行安装vim1.3:判断vim是否安装成功 2:vim简介3:vim的三种模式4:vim常用按键说明4.1 命令模式4.2 搜索和替换4.3 复制、粘贴和删除4.4 一般模式…

网络工程师---第十天

ARP表: 提起ARP表必然先想起ARP(address resolution protocol)协议,地址解析协议。 在实际应用中,我们经常遇到这样的问题:已知一个机器的IP地址,但在实际网络的链路上传送数据帧时,…

20240331-1-基于深度学习的模型

基于深度学习的模型 知识体系 主要包括深度学习相关的特征抽取模型,包括卷积网络、循环网络、注意力机制、预训练模型等。 CNN TextCNN 是 CNN 的 NLP 版本,来自 Kim 的 [1408.5882] Convolutional Neural Networks for Sentence Classification 结…

[ICCV2023]DIR-用于从单个RGB图像重建交互手部的解耦迭代细化框架

这篇论文的标题是《Decoupled Iterative Refinement Framework for Interacting Hands Reconstruction from a Single RGB Image》,作者是Pengfei Ren, Chao Wen, Xiaozheng Zheng, Zhou Xue, Haifeng Sun, Qi Qi, Jingyu Wang, Jianxin Liao。他们来自北京邮电大学…

Nodejs安装与配置--基于Linux系统--RedHat7.9

nodejs安装从未这么简单 1、nodejs版本设置? curl -fsSL https://rpm.nodesource.com/setup_16.x | sudo bash - 其他版本如下: * https://rpm.nodesource.com/setup_16.x — Node.js 16 "Gallium" (deprecated) * https://rpm.nodesource.co…

vue-project-tree vue3 树形结构展示组件

GitHub:vue-project-tree by one-ccs Gitee:vue-project-tree by one-ccs 遵循 MIT 开源协议 文章目录 vue-project-tree一、使用二、API1、属性2、事件3、方法4、插槽 vue-project-tree 使用 Vue3 TS 实现的树形结构展示组件,有拖拽、排序…

数字化革新:可视化墨水屏引领基板工艺MSAP贴膜阶段迈向无纸化高端制造应用背景

随着科技的飞速发展和环境保护意识的日益增强,制造印刷电路板(PCB)行业正面临着提升生产效率、降低资源消耗和推动绿色制造的迫切需求。 问题: PCB生产过程对洁净度要求高,传统打印的纸张会有粉尘,纸屑&am…

cookie与session区别和联系

在Web应用中,HTTP协议是无状态的,每次请求都是独立的,服务器无法直接识别一个用户的不同请求之间的关联。这就导致了如果我们希望在一个会话中保持一些数据的状态,比如用户的身份认证信息、购物车内容等,就需要借助Coo…

网络靶场实战-Qiling Fuzz实例分析

背景 在上一小节中,介绍了qiling框架的背景和基础使用,并以相关的CTF和qilinglab实例进行练习加深对qiling框架的使用,后续并简单介绍了qiling fuzz的功能。 在这一小节,我们将对qiling fuzz iot设备进行测试以及以实例的方式对…

【LLM】LLM API 开发

文章目录 LLM API 开发LLM入门基本概念LLM API使用实名认证创建应用使用API Prompt Engineering思考总结 参考文章 什么是提示工程(Prompt Engineering)? ChatGPT Prompt 最佳指南一 LLM API 开发 LLM入门基本概念 Prompt Prompt 最初是 NL…

EelasticSearch的介绍和基于docker安装

1.概述 Elasticsearch 是一个基于 Apache Lucene 构建的开源分布式搜索引擎和分析引擎。它专为云计算环境设计,提供了一个分布式的、高可用的实时分析和搜索平台。Elasticsearch 可以处理大量数据,并且具备横向扩展能力,能够通过增加更多的硬…

AR爆发的前夜,Rokid站在了门口

文|刘俊宏 摆脱6寸的手机屏幕,栖居在300寸大屏的智慧生活是什么样子? 4月20日,Rokid在新品AR Lite空间计算套装的发布会上,“硬刚”了苹果的Vision Pro。 Rokid AR Lite空间计算套装 Rokid AR Lite与苹果Vision Pro…

必应搜索广告与谷歌搜索广告对比那个更好?

搜索引擎广告作为企业获取潜在客户的重要渠道之一,其效果直接关系到营销策略的成功与否。两大搜索引擎巨头——谷歌(Google)和必应(Bing)各自提供了广告平台,即谷歌广告(Google Ads)…

eNSP-路由引入与过滤简单配置

目录 实验要求 IP配置 配置动态路由协议 RIP OSPF 查看建邻情况 双向重发布 路由过滤 地址前缀列表 静默接口 实验要求 1、按照图示配置 IP 地址,R1,R3,R4 上使用 loopback 口模拟业务网段 2、R1 和R2 运行 RIPv2,R2&am…

Mendix是谁?作为致力于企业低代码服务平台的领头羊,它解决了哪些问题?

一、Mendix 成立的背景 Mendix的成立是为了解决软件开发中最大的问题:业务和IT之间的脱节。这一挑战在各个行业和地区都很普遍,很简单:业务需求通常被描述为IT无法正确解释并转化为软件。业务和IT之间缺乏协作的原因是传统的代码将开发过程限…

前端开发攻略---拖动归类,将元素拖拽到相应位置

1、演示 2、代码 <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" /><meta name"viewport" content"widthdevice-…

001-谷粒商城-微服务剖析

1、架构图 还是很强的&#xff0c;该有的都有 2、微服务模块 SpringCloudAlibaba组件包括 SentinelNacosRocketMQSeata 搭配SpringCloudAlibaba组件 OpenFeignGateWayRibbn gateway使用了SpringWebFlux&#xff0c;前几天研究到&#xff0c;为什么springboot不直接使用Spri…