发新帖本帖赏金 60.00元(功能说明)我要提问
12下一页
返回列表
打印
[其他ST产品]

STM32存储器映射-寄存器基地址-偏移讲解

[复制链接]
2456|26
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 虚幻的是灵魂 于 2024-6-28 14:33 编辑

#申请原创# @21小跑堂
前言

在学习STM32的时候,我们看到很多的寄存器编程,
比方说LED灯:

    //GPIOB.5端口输出高电平
    GPIOB->ODR|=1<<5;      //PB.5 输出高
   
        GPIOE->ODR|=1<<5;      //PE.5输出高

   //GPIOB端口全部输出高电平

   *(unsigned int*)(0x4001 0C0C) = 0xFFFF;


就用到了寄存器,为什么对(0x4001 0C0C) 这个地址写0xFFFF,GPIOB就能输出高电平呢,这些寄存器的本质是什么,比方说GPIO,我们查看下GPIOB 和GPIOE 的定义

在stm32f10x.h里面,可以看到GPIOx都是由GPIOX_BASE,宏定义组成

再来看下GPIOX_BASE分别代表什么:

可以看到GPIOX_BASE 都是由APB2PERIPH_BASE 这个定义加上一定的值组成,APB2我们知道是总线,学过STM32的应该都知道初始化一个外设之前,要先使能外设对应总线的时钟,通过这里我们知道GPIO是在APB2总线上的

再来看一下APB2PERIPH_BASE 是由PERIPH_BASE 加上0x10000 的宏定义,而PERIPH_BASE的宏定义是0x40000000 也就是说,我们所操作的寄存器,本质就是对不同的地址写值,就可以实现想要的功能。

那么这些地址定义都是代表这什么呢,都是怎么来的呢,这就引出了我们今天的问题----STM32外设寄存器地址-基地址-偏移

STM32F1架构

STM32芯片基于ARM公司的Cortex-M3内核由ST公司设计生产,ARM公司并不生产芯片,而是出售其芯片技术授权。芯片生产厂商(SOC)如ST、TI、Freescale,负责在内核之外设计部件并生产整个芯片,这些内核之外的部件被称为核外外设或片上外设。内核与总线矩阵之间有ICode System (系统) 、DCode (数据) 三条信号线。内核通过总线矩阵与FLASH、SRAM、外设连接。而外设包括GPIO、USART、12C、SPI等。

内核

STM32F的内核CPU是cortex-M3

三大总线

指令总线、数据总线、系统总线

ICode 着重传输指令,DCode(Data 数据) 和 System 着重传输数据,至于更详细的区分,不用关心。实际上 ICode、DCode 和 System 内部都包含三个部分,即地址总线、控制总线、数据总线。

DMA总线

DMA Direct Memory Access缩写 直接存储器访问,可将数据从一个地址空间传输到另外一个地址空间,地址空间可以是外设到寄存器或者寄存器到寄存器。访问的数据可以是寄存器,也可以是SRAM,亦可是FLASH;数据可以被DCode和DMA同时访问到,因此为了避免访问冲突,需要总线来仲裁来决定哪个总线访问。

FLASH连接总线

内部的闪存存取器,即FLASH,程序存储在FLASH中,内核通过ICode读取指令。

SRAM 连接总线

内部的SRAM,即数据存储器RAM,程序的变量和堆栈开销在SRAM中。

高速外设连接总线AHB和APB桥

AHB和APB桥:AHB是高性能的系统总线,APB是外设总线。二者分别适用于高速和低速的设备连接。

AHB总线:全称Advanved High Performance Bus 高级高性能系统总线 简写:AHB

APB总线:全称Adanvced Peripheral Bus 高级外设总线 简写:APB

F1分为APB1和APB2 APB1=36MHZ APB2=72MHZ

Cortex-M3内存映射

我们这里以M3内核为例,STM32F1系列以Cortex-M3作为内核, 由于STM32系列芯片地址总线有32根,也就是一次最大可以访问00000000 00000000 00000000 00000000 — 11111111 11111111 11111111 11111111的2^32个地址,范围刚好为4G,可寻址空间为2^32=4GB;

也就是STM32系列最大有4GB的使用空间,下面我们先来看下M3内核的内存映射图。

如图这是M3内核的存储器映射图,从0x00000000到0xFFFFFFFF,一共4G的空间

这个图是ARM官方给出的M3内核图,所有使用Cortex-M3内核的芯片,内部的地址空间基本都是这样用的(ARM官方推荐厂商根据这个设计,厂商也可以自己设计。当然基本上芯片厂商是根据这个设计的)。

再来看下中文的图:

cortex-m3内核把4G空间分成了各个部分


Code代码区

用于存放和运行代码,存储用户下载的代码,还有出厂时固化的一些底层代码

SRAM

SRAM是运行时临时存放代码的地方。不同类型的STM32单片机的SRAM大小是不一样的,但是他们的起始地址都是0x2000 0000,终止地址都是0x2000 0000+其固定的容量大小。SRAM的理解比较简单,其作用是用来存取各种动态的输入输出数据、中间计算结果以及与外部存储器交换的数据和暂存数据,用于程序运行的堆栈开销。设备断电后,SRAM中存储的数据就会丢失。

Peripherals

用于设计片内外设,根据总线速度的不同,被分为了APB和AHB。在stm32中,有三大总线,AHB总线,APB1总线以及APB2总线。不同的外设挂载在不同的总线上边。比如GPIO,串口1,ADC以及部分定时器挂载在APB2总线上。我们通过操作这些外设对应的地址,便能控制这些外设寄存器。



下面看一下STM32官方给出的内存映射图,这个图可以详细的看出来STM32各个部分的映射,注意这个图是STM32F4的图,也就是M4内核的图

什么是存储器映射

可以看到各个部分详细的分类,

映射其实就是对应的意思。事实上存储器本身不具备地址,所以把芯片内核所预先设定好的地址分配给寄存器,就是存储器映射。因为stm32把这个4G的虚拟存储空间和芯片内部外设进行一一对应,每个外设和其对应的寄存器都有一个确定的地址,也就是给存储器分配地址,即存储器映射。


也就是说,ARM给M3内核的使用空间,设计了一个规范,而其本身就是一个4GB的空间,然后其中的一部分用于存储数据(RAM) ,一部分用来运行代码(code)还有一部分被赋予了某种意义(Peripherals),比方说GPIO,TIM定时器,ADC,IIC等等,就成为了片上外设,也就是寄存器 只是理论上的4G范围远远大与实际的存储器空间,也就说实际的存储器空间并没有4G。



寄存器的实际地址

我们可以看到在M4内核中,片上外设分配的地址是0x40000000-0x5FFFFFFF 而在上面的例子中,可以看到STM32官方的外设基地址PERIPH_BASE的宏定义就是是0x40000000,在这部分空间中,不同的外设对应不同的地址。

STM32F10x.h这个头文件正是吧STM32的所有寄存器进行地址映射,此文件通过宏定义的方式,将各个寄存器的地址转换为相应的符号名称,

寄存器以四个字节为一个单元,也就是32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过C语言指针的操作方式来访问这些地址单元,对这些地址写入数据,就是对寄存器所对应的外设进行操作。

但是如果每次都是通过这种地址的方式来访问,不仅不好**还容易出错,你可以想象一下一个项目几千行地址赋值调用,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。




而我们正常使用的寄存器的地址 = 基地址+偏移地址

(1)总线基地址

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

从上图可以看到 APB1 总线基地址是 0x4000 0000,相对外设基地址的偏移量是 0,所以此总线也是外设的基地址。


(2)外设基地址

每条总线上都会挂接着很多的外设,这些外设也会有自己的地址范围, 外设的最低地址就是外设的基地址,也称作边界地址。以 GPIO 外设来讲解外设基地址。其他的外设也是同样分析。

从图可以知道,外设 GPIOx 都是挂接在 APB2 总线上,属于高速的外设,而 APB2 总线的基地址是 0x4001 0000,故 GPIOA 的相对 APB2 总线的地址偏移是 800。


(3)外设寄存器地址

外设的寄存器就分布在其对应的外设地址范围内。这里我们以 GPIO 外设为例,GPIO 有很多个寄存器,每一个都有特定的功能。每个寄存器为 32bit,占四个字节,这些寄存器都是按顺序依次排列在外设的基地址上。寄存器的位置都以相对该外设基地址的偏移地址来描述。这里我们以 GPIOB 端口为例,来说明 GPIOB都有哪些寄存器,如所示。

这个就回到了我们一开始的那个问题:

//GPIOB.5端口输出高电平
    GPIOB->ODR|=1<<5;      //PB.5 输出高
   //GPIOB端口全部输出高电平

   *(unsigned int*)(0x4001 0C0C) = 0xFFFF;


`0x4001 0C0C 也就是GPIOB 端口ODR寄存器,一共32位,其中低16位控制这端口的输出(output data) 所以我们对这个地址写FFFF 也就是 1111 1111 1111 1111 就是让GPIOB的所有端口输出高电平

总结

每个寄存器都有一个访问地址,每个外设中的所有寄存器的位置都是固定的,每组寄存器的起始地址在《STM32参考手册》的表1中列明;

寄存器的地址 = 基地址+偏移地址 比如:

整个外设的基地址 = AHB1 的偏移+GPIOB 寄存器组的偏移+GPIOB_OSPEEDR 寄存器的偏移


0x4002 0410 = 0x4000 0000 + 0x0002 0000 + 0x0C00 + 0x10
























使用特权

评论回复

打赏榜单

21小跑堂 打赏了 60.00 元 2024-06-28
理由:恭喜通过原创审核!期待您更多的原创作品~

评论
21小跑堂 2024-6-28 16:27 回复TA
一篇从底层储存器及寄存器角度剖析单片机的文章,这也是很重要但是前期极易忽视的知识点,从寄存器和储存器的角度更容易理解单片机的底层逻辑。便于更深层次的开发单片机,同时也更容易读懂单片机的手册。文章略有粗糙,新手读起来可能较为困难,但是涵盖面较广,便于总体了解单片机的底层。 
沙发
狄克爱老虎油| | 2024-7-5 10:08 | 只看该作者
定义的是要操作的地址

使用特权

评论回复
板凳
Jacquetry| | 2024-7-5 11:00 | 只看该作者
这要背寄存器吧

使用特权

评论回复
地板
CarterERO| | 2024-7-5 11:03 | 只看该作者
直接操作寄存器比较快

使用特权

评论回复
5
埃娃| | 2024-7-5 11:10 | 只看该作者
Jacquetry 发表于 2024-7-5 11:00
这要背寄存器吧

背寄存器不是基础的嘛

使用特权

评论回复
6
kkzz| | 2024-7-9 22:19 | 只看该作者
STM32的寄存器可以分为两类:

状态寄存器:这些寄存器用于存储外设的状态信息,比如中断标志位、错误标志位等。
控制寄存器:这些寄存器用于配置外设的工作模式、 enable/disable 外设、设置优先级等。

使用特权

评论回复
7
everyrobin| | 2024-7-9 23:39 | 只看该作者
GPIOA_BASE_ADDRESS这样的宏定义来表示基地址,以及类似GPIOA_CRH_OFFSET这样的宏定义来表示偏移量。

使用特权

评论回复
8
linfelix| | 2024-7-10 01:51 | 只看该作者
偏移量是指某个寄存器地址相对于其外设基地址的差值。在STM32中,每个外设的寄存器都是按照一定的顺序排列在外设基地址之后的,因此可以通过基地址加上偏移量的方式来访问特定的寄存器。

使用特权

评论回复
9
burgessmaggie| | 2024-7-10 05:45 | 只看该作者
STM32的存储器映射通常分为几个主要区域:

代码区(Code Region):用于存放程序代码,通常是只读的,地址范围从0x0000_0000到0x00FF_FFFF。
数据区(Data Region):用于存放变量和数据,通常是可读写的,地址范围从0x2000_0000到0x3FFFFFFF。
外设区(Peripheral Region):用于存放外设寄存器的映射地址,通常是可读写的,地址范围从0x4000_0000到0x5FFFFFFF。

使用特权

评论回复
10
mollylawrence| | 2024-7-10 21:39 | 只看该作者
寄存器基地址是外设的第一个寄存器的起始地址。每个外设都有一系列的寄存器,用来控制和读取外设的状态。

使用特权

评论回复
11
jackcat| | 2024-7-12 18:46 | 只看该作者
偏移是在基地址的基础上加上一个数值,用于访问特定寄存器或数据缓冲区。

使用特权

评论回复
12
cemaj| | 2024-7-13 11:19 | 只看该作者
在STM32微控制器中,存储器映射是一种将微控制器的硬件资源(如CPU、外设寄存器、RAM和ROM)映射到统一的地址空间的方法,使得软件可以直接通过访问特定的内存地址来读写硬件设备的状态。这种映射方式允许CPU像访问内存一样访问外设寄存器,而不需要特殊的I/O指令。

使用特权

评论回复
13
elsaflower| | 2024-7-13 20:50 | 只看该作者
寄存器基地址是指每个外设或功能模块在内存中的起始地址。在STM32中,每个外设都有一个唯一的基地址,这个地址用于访问该外设的控制寄存器和数据缓冲区。

例如,STM32的GPIOA外设的基地址是0x40020000。通过这个基地址,我们可以访问GPIOA的各种控制寄存器,如GPIOA->CRL、GPIOA->CRH等。

使用特权

评论回复
14
macpherson| | 2024-7-14 12:16 | 只看该作者
每个外设都有一个基地址,这是外设在存储器映射中的起始地址。例如,在STM32F1系列中,GPIOA的基地址通常是0x40020000。这个基地址标志着该外设的所有寄存器的起始点。

使用特权

评论回复
15
updownq| | 2024-7-15 14:45 | 只看该作者
寄存器基地址是指外设寄存器的起始地址。在STM32中,不同的外设挂载在不同的总线上(如APB1、APB2、AHB总线),每个外设都有一个对应的基地址。这个基地址是访问该外设所有寄存器的基础。

使用特权

评论回复
16
loutin| | 2024-7-16 16:06 | 只看该作者
对GPIOA->ODR的访问实际上就是在访问地址0x40020014处的寄存器。

使用特权

评论回复
17
mattlincoln| | 2024-7-16 20:10 | 只看该作者
如果我们想要访问GPIOA的CRL寄存器,我们需要先找到GPIOA的基地址(0x40020000),然后加上CRL寄存器的偏移量(0x00),最终得到的地址就是0x40020000 + 0x00 = 0x40020000。

使用特权

评论回复
18
geraldbetty| | 2024-7-17 11:37 | 只看该作者
STM32的寄存器映射通常是由硬件抽象层(HAL)库管理的,你不需要直接处理这些地址和偏移量。HAL库提供了一系列的函数来访问和设置寄存器,这些函数的名字通常与外设的名称有关,并遵循一定的命名规则。

使用特权

评论回复
19
albertaabbot| | 2024-7-19 17:25 | 只看该作者
外设寄存器通常通过结构体来表示。结构体的成员对应于寄存器,且成员的地址偏移与硬件寄存器的实际偏移相匹配。

使用特权

评论回复
20
mattlincoln| | 2024-7-19 22:01 | 只看该作者
假设STM32的某个外设(如GPIOA)的基地址是0x4002_0000,而该外设的一个寄存器(如GPIOA_CRH)相对于基地址的偏移是0x0040,则GPIOA_CRH寄存器的绝对地址可以通过以下计算得到:

寄存器地址 = 基地址 + 偏移
寄存器地址 = 0x4002_0000 + 0x0040
寄存器地址 = 0x4002_0040
这样,通过访问0x4002_0040这个地址,就可以操作GPIOA_CRH寄存器。

使用特权

评论回复
发新帖 本帖赏金 60.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

14

主题

115

帖子

1

粉丝