LV045-STM32存储器系统
一、基本概念
1. 寄存器
1.1 什么是寄存器?
寄存器是计算机和微处理器内部的一种高速、小容量的存储单元,主要用于暂存指令、数据和地址。它是CPU或其他硬件组件直接操作的对象,访问速度远高于内存(RAM)。
可以将其理解为 “工作台”:
- CPU内核寄存器 是工程师(CPU)手边最顺手的工具,直接参与加工。
- 外设寄存器 是工程师与外部机器(外设)进行控制和状态交互的控制面板。
1.2 寄存器分类
寄存器根据其功能和位置,可以分为两个截然不同的层级。
1.2.1 CPU内核寄存器 (Core Registers)
这类寄存器位于中央处理单元(CPU)内部,是CPU核心的一部分。他们直接参与算术逻辑运算(ALU)、指令译码、执行流程控制、数据寻址等核心操作。没有它们,CPU无法执行任何指令。有以下特点:
- 数量很少且固定,由CPU架构决定(例如:14个、16个、32个)。
- 访问速度极快(与CPU时钟同步)。
- 通过指令集中的特定寄存器名(如
AX,R0,SP) 直接访问。
常见的寄存器类型:
- 通用寄存器:用于暂存计算数据和地址(如x86的EAX, EBX;ARM的R0-R12)。
- 专用寄存器:
- 程序计数器 (PC / IP):存放下一条要执行的指令的地址。
- 栈指针 (SP):指向当前栈顶的地址。
- 链接寄存器 (LR):用于存储函数调用的返回地址。
- 状态寄存器(PSW / xPSR):存储上一次算术运算的结果状态(如零标志、进位标志、溢出标志)。
1.2.2 外设寄存器
外设寄存器 (Peripheral Registers / Memory-Mapped Registers) 不在CPU内部,而是分布在微控制器(MCU)或SoC的各个外设模块(如GPIO, UART, SPI, Timer)内部。作为软件(程序)与硬件(外设)交互的接口。程序员通过读写这些寄存器来配置、控制和监视外部设备。
本质上是一片在内存地址空间中划出的特殊区域(SRAM)。每个寄存器对应一个唯一的内存地址。特点:
- 数量非常多(成百上千个),取决于芯片集成的外设数量和复杂程度。
- 访问速度相对较慢(需要通过系统总线)。
- 通过内存地址进行访问(使用指针或厂家提供的库函数)。
常见的寄存器类型:
- 控制寄存器 (CR):配置外设的工作模式。例如,设置GPIO为输入/输出模式、使能串口发送、启动定时器。写操作有效。
- 数据寄存器 (DR):与外设进行数据交换。例如,向UART数据寄存器写入要发送的数据,从ADC数据寄存器读取转换结果。读/写操作。
- 状态寄存器 (SR):反映外设的当前状态。例如,查询串口数据是否发送完成、ADC转换是否结束。读操作有效。
1.2.3 总结
| 特性 | CPU内核寄存器 | 外设寄存器 |
|---|---|---|
| 位置 | CPU核心内部 | 各个外设模块内部 |
| 功能 | 直接参与指令执行与运算 | 软件与硬件之间的控制接口 |
| 数量 | 少且固定(几十个) | 多且可变(成百上千个) |
| 访问方式 | 通过指令和寄存器名 | 通过内存地址(指针) |
| 访问速度 | 极快(CPU时钟级别) | 较慢(总线时钟级别) |
| 依赖性 | 是CPU架构的一部分 | 依赖于芯片具体设计(外设种类) |
2. 存储器映射
我们会经常在英文参考手册中看到 Memory Map。它可以被翻译为存储器映射、内存映射有时也叫地址映射。
计算机最重要的功能单元之一是Memory,memory是众多存储单元的集合,为了使CPU准确地找到存储有某个信息的存储单元,必须为这些单元分配一个相互区别的编号,这个编号就是地址编码。在嵌入式处理器内,集成了多种类型的Memory,通常我们称同一类型的Memory为一个Memory Block。
一般情况下,处理器设计者会为每一个memory block分配一个数值连续、数目与其存储单元数相等、以16进制表示的自然数集合作为该memory block的地址编码。这种自然数集合与memory block的对应关系就是Memory Map(存储器映射、内存映射),有时也叫地址映射(Address Map)。
在这里需要强调的是memory Map是一个逻辑概念,是在计算机系统在(上电)复位后才建立起来的。Memory Map相当于这样一个函数:函数的输入量是地址编码,输出量是被寻址单元中的数据。当计算机系统掉电后或复位时,这个函数就不复存在,只剩下实现这个函数的物理基础——电路连接。也可以这样认为:Memory Map是计算机系统上电(复位)时的预备动作,是一个将CPU所拥有的地址编码资源向系统内各个物理存储器块分配的自动过程。简而言之,存储器映射就是为物理存储器按一定编码规则分配地址的行为。
在完成存储器映射后,用户就可以按照存储器地址去访问对应的物理存储器。值得注意,一部分地址空间由 Arm Cortex-Mx的系统外设所占用,且不可更改。此外,其余部分地址空间可由芯片供应商(ST,意法半导体)定义使用。用户只能用而不能改(为了降低不同客户在相同应用时的软件复杂度)。用户只能在挂外部RAM或FLASH的情况下可进行自定义。
给存储器分配地址的过程叫存储器映射,再分配一个地址叫重映射。后边会再详述。
二、存储器映射
1. STM32存储空间
芯片能访问的存储空间有多大,是由谁定的?这个是由芯片内CPU的地址总线的数量决来定的,STM32芯片内部的地址总线为32根。
1根地址线:可以传输的地址为0和1的,那么理论上就可以访问2个字节。
2根地址线:可以传输地址为00、01、10、11,理论上可以访问4个字节。
3根地址线:可以传输的地址为000、001、010、011、100、101、110、111,理论上可以访问8个字节。
32根地址线:可以产生00000000 00000000 00000000 00000000 ~ 11111111 11111111 11111111 11111111 也就是的2的32次方个地址,范围刚好为4G,所以我们就说STM32的32根地址线,理论上可以访问4G字节的存储器空间。

在上图的最右边可以看到STM32地址是从0x00000000到0xFFFFFFFF,这就是4GB的存储空间。但是STM32真的有4GB的存储空间吗?答案当然不是,我们的PC电脑也才4GB的内存。一个小小的单片机怎么可能有4GB的存储空间!这个4GB的是STM32理论分配的地址空间。也就是说实际上并不是有这么大的存储单元。上图中第二排可以看到有很多预留的地址,这些地址并没有给他分配存储单元。
所有的存储器都是与地址线连着的,但是实际上如果只接了一个10M的存储器,而且是从0地址开始映射的,那么32根地址线所产生的0~10M的地址信号其实才是有意义的,因为这些地址信号才有对应真实的存储器,而所产生的10M以上地址信号其实并无意义,因为并不对应真实的存储器。
STM32中的32是32根地址线的意思吗?
答:不是,STM32的32不是32根地址线的意思,而是表示MCU芯片内部CPU在处理数据时,每次可以处理的数据位宽为32个bit。正是由于这个原因,STM32 内部的寄存器大小都是32位的,刚好等于位宽。
某个芯片是32位的,但是它的地址线完全可以只有16根、或者8根,对于 STM32 来说,刚好碰巧的是,CPU能够处理的数据位宽与地址线数量恰好都是 32。
2. 什么是存储器映射
2.1 STM32F4
这里可以看一下STM32F429的中文数据手册(为啥看F429?因为网上查阅到的资料是F429为例啦,有一张图可以帮助理解,其他其实也一样,比如STM32F103系列的,在它们的数据手册中都会这样一个存储器映像的章节,对于STM32F103系列的,我们可以打开数据手册:STM32F10xxx英文数据手册,找到4 Memory mapping一节),F429的中文手册可以看这里:RM0090_STM32F40xxx、STM32F41xxx、STM32F42xxx、STM32F43xx参考手册

这里有另外一张图,会更加详细,这也是为什么这里用F429来学习,就是因为这张图更详细,更容易理解,而自己又懒得画,哈哈:

映射其实就是对应的意思,事实上存储器本身并不具备地址,将芯片理论上的地址分配给存储器,这就是存储器映射。
存储器在产家制作完成后是一片没有任何信息的物理存储器,而CPU要进行访存就涉及到内存地址的概念,因此存储器映射就是为物理内存按一定编码规则分配地址的行为。值得注意,存储器映射一般是由产家规定,用户不能随意更改。
比如前面举的10M存储器的例子,这个10M存储器原本并没有地址,我将10M存储器映射到理论32根地址线可以传输4G个地址信号,每个地址信号访问一个字节,4G地址信号则可以访问4G个字节,所以理论上的可访问范围为4G。地址0往后的10M范围,这10M的存储器就有了0~10M的地址,地址线所产生的0~10M之间的地址信号,就可以访问10M的这个真实存储器。至于在生产芯片时,在工艺和技术上具体是怎么实现我们所描述的映射的,我们无需关心。
STM32的所有片内外设其实都是存储器,所以所有的这些存储器都需要被映射,只是理论上的4G范围远远大与实际的存储器空间,也就说实际的存储器空间并没有4G。
理论上地址其实就是门牌号,存储中的每个字节就是房间,存储器生产出来后,这些房间是没有地址的(门牌号),映射的过程其实就是将这些门牌号分配给这些房间,分配好后,每个门牌号只能访问自己的房间,没有被分配的地址就是保留地址,所谓保留地址的意思就是,没有对应实际存储空间。
可不可以保留一些地址不分配呢?
当然可以,因为理论上可以有4G的地址,但是实际上不可能给4G存储空间,否则这个单片机芯片可买不起,PC机的内存也才4G/8G,单片机怎么可能真的给4G存储空间呢?
所以呢,总的来说,对于将要学习的STM32来说,存储器映射就是将被控单元的FLASH,RAM,FSMC和AHB到APB的桥(即片上外设),这些功能部件共同排列在一个4GB的地址空间内。我们在编程的时候,可以通过他们的地址找到他们,然后来操作他们(通过C语言对它们进行数据的读和写)。
2.2 STM32F103ZET6
可以再来看一下STM32F103ZET6的存储器映像,可以看 Datasheet - STM32F103xE 的4 Memory mapping:

这里和前面其实是类似的。
3. 存储器区域功能划分
在这4GB的地址空间中,ARM已经粗线条的平均分成了8个块,每块512MB,每个块也都规定了用途,具体分类见下表(每个块的大小都有512MB,显然这是非常大的,芯片厂商在每个块的范围内设计各具特色的外设时并不一定都用得完,都是只用了其中的一部分而已。
| 序号 | 用途 | 地址范围 |
|---|---|---|
| Block 0 | Code | 0x0000 0000 ~ 0x1FFF FFFF(512MB) |
| Block 1 | SRAM | 0x2000 0000 ~ 0x3FFF FFFF(512MB) |
| Block 2 | 片上外设 | 0x4000 0000 ~ 0x5FFF FFFF(512MB) |
| Block 3 | FSMC的bank1 ~ bank2 | 0x6000 0000 ~ 0x7FFF FFFF(512MB) |
| Block 4 | FSMC的bank3 ~ bank4 | 0x8000 0000 ~ 0x9FFF FFFF(512MB) |
| Block 5 | FSMC 寄存器 | 0xA000 0000 ~ 0xCFFF FFFF(512MB) |
| Block 6 | 没有使用 | 0xD000 0000 ~ 0xDFFF FFFF(512MB) |
| Block 7 | Cortex-M3内部外设 | 0xE000 0000 ~ 0xFFFF FFFF(512MB) |
在这8个Block里面,有3个块非常重要,也是我们最关心的三个块。Block0用来设计成内部FLASH,Block1用来设计成内部RAM,Block2用来设计成片上的外设,下面我们简单的介绍下这三个Block里面的具体区域的功能划分。
三、寄存器映射
1. 寄存器与寄存器映射
存储器本身没有地址,给存储器分配地址的过程叫存储器映射,那什么叫寄存器映射?寄存器到底是什么?这里还是以STM32F4为例:

在存储器Block2这块区域,也就是地址从0x4000000~0x5FFFFFF这块区域,设计的是片上外设,它们以四个字节为一个单元,共32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。可以找到每个单元的起始地址,然后通过C语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,工程师就根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。

2. STM32的外设地址映射
片上外设区分为三条总线,根据外设速度的不同,不同总线挂载着不同的外设,APB1挂载低速外设,APB2和AHB挂载高速外设。相应总线的最低地址我们称为该总线的基地址,总线基地址也是挂载在该总线上的首个外设的地址。其中APB1总线的地址最低,片上外设从这里开始,也叫外设基地址。
2.1 总线基地址
| 总线名称 | 总线基地址 | 相对外设基地址的偏移 |
|---|---|---|
| APB1 | 0x4000 0000 | 0x0 |
| APB2 | 0x4001 0000 | 0x0001 0000 |
| AHB | 0x4002 0000 | 0x0002 0000 |
这个数据怎么来的,我们可以查看参考手册或者数据手册的存储器映像来找。
2.2 外设基地址
总线上挂载着各种外设,这些外设也有自己的地址范围,特定外设的首个地址称为“XX外设基地址”,也叫XX外设的边界地址。具体有关STM32F10xx外设的边界地址请参考《STM32F10xx参考手册》的 2.3小节的存储器映射的表1:STM32F10xx 寄存器组起始地址 。
2.3 外设寄存器
在XX外设的地址范围内,分布着的就是该外设的寄存器。以GPIO外设为例,GPIO是通用输入输出端口的简称,简单来说就是STM32可控制的引脚,基本功能是控制引脚输出高电平或者低电平。最简单的应用就是把GPIO的引脚连接到LED灯的阴极,LED灯的阳极接电源,然后通过STM32控制该引脚的电平,从而实现控制LED灯的亮灭。
GPIO有很多个寄存器,每一个都有特定的功能。每个寄存器为32bit,占四个字节,在该外设的基地址上按照顺序排列,寄存器的位置都以相对该外设基地址的偏移地址来描述。有关外设的寄存器说明可参考《STM32F10xx参考手册》中具体章节的寄存器描述部分,在编程的时候我们需要反复的查阅外设的寄存器说明。
四、大端小端
突然在STM32中文参考手册的存储器组织一节中发现了一段话,作为笔记记在这里吧:
程序存储器、数据存储器、寄存器和输入输出端口被组织在同一个4GB的线性地址空间内。数据字节以小端格式存放在存储器中。一个字里的最低地址字节被认为是该字的最低有效字节,而最高地址字节是最高有效字节。
所以从这里来看,我们使用的STM32是小端模式,不看手册的话我们其实也是可以通过代码来测的,详情可以看前边的C语言的笔记。
参考资料:
[深入理解STM32内存管理_stm32存储器分为几个区域-CSDN博客](https://blog.csdn.net/zhuguanlin121/article/details/119799860)
STM32F0xx参考手册 STM32F0xx系列的中文文档 STM32F0xx参考手册 STM32F0xx系列的英文文档 STM32F051xx数据手册 STM32F051xx系列数据手册的英文文档 STM32F4xx参考手册 TM32F405/415, STM32F407/417, STM32F427/437 and STM32F429/439单片机中文参考手册 STM32F4xx数据手册 STM32F427和STM32F429单片机的中文数据手册 STM32F10xxx参考手册(RM0008) STM32F10xxx单片机的中文参考手册 STM32F10xxx参考手册(RM0008) STM32F10xxx单片机的英文参考手册 STM32F103xx数据手册 STM32F103xx英文数据手册