干货|STM32寄存器版的基础知识—内存映射

Posted 果果小师弟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了干货|STM32寄存器版的基础知识—内存映射相关的知识,希望对你有一定的参考价值。

STM32F429芯片系统结构

STM32F429 采用的是 Cortex-M4 内核,内核即 CPU,由 ARM公司设计。ARM 公司并不生产芯片,而是出售其芯片技术授权。芯片生产厂商(SOC)如 ST、TI、Freescale,负责在内核之外设计部件并生产整个芯片,这些内核之外的部件被称为核外外设或片上外设。如 GPIO、USART(串口)、I2C、SPI等都叫做片上外设。

从上图我们可以清除的看到芯片和外设之间通过各种总线连接,其中主控总线有 8条,被控总线有 7 条。主控总线通过一个总线矩阵来连接被控总线总线矩阵用于主控总线之间的访问仲裁管理,仲裁采用循环调度算法。比如数据从Cotex-M4 到高速外设USB,数据交给在总线矩阵后,总线矩阵就会判给USB,然后通过USB所在的 AHB1 传输给USB。

三大总线

ICode:指令总线
DCode:数据总线
System:系统总线

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

高速总线

直接挂接在“总线矩阵上”的有哪些呢?

1、ICode、DCode、System
2、FLASH连接总线
3、SRAM 连接总线
4、高速外设连接总线 AHB1/AHB2/AHB3
5、连接“桥”的总线
这些“高速总线”直接与“总线矩阵”连接在一起,其实这些高速总线实际上就是“总线矩阵”的延伸,或者说就是总线矩阵的一部分。

高速外设和低速外设

我们这里说“片内外设”时,暂都不包含 ROM(FLASH)和 SRAM。

1、高速外设
由于高速外设实在是太多了,一个总线不够,所以分了三个,分别是 AHB1/AHB2/AHB3,所有“高速外设”寄存器组分批挂接在 AHB1/AHB2/AHB3 上。

2、低速外设
“低速外设”的速度比较低,不能直接挂在“总线矩阵”上,所以先经过“桥”后再引出“低速总线”,挂在低速总线上。由于“低速外设”比较多,所以就分了两个低速总线,分别是 APB1/APB2,所有的“低速外
设”分批挂接在 APB1/APB2 上。

内存映射

这张图太重要了,看懂这张图,你的STM32已经可以掌握40%了,下面就来着重讲解这一张图。这张图来自STM32F407参考手册第61页,由于原版是英文的,搞了一个翻译过来的版本。

1、STM32存储空间

芯片能访问的存储空间有多大,是由谁定的?这个是由芯片内 CPU 的地址总线的数量决来定的,STM32 芯片内部的地址总线为32 根,

1、1 根地址线:可以传输的地址为 0 和 1 的,那么理论上就可以访问 2 个字节
2、2 跟地址线:可以传输地址为 00、01、10、11,理论上可以访问 4 个字节
3、3 个地址线:可以传输的地址为 000、001、010、011、100、101、110、111,理论上可以访
问 8 个字节。
4、32 根地址线:可以产生 00000000 00000000 00000000 00000000、…、11111111 11111111 11111111 11111111的 2^32个地址,范围刚好为 4G,所以我们就说STM32的32 根地址线,理论上可以访问4G字节的存储器空间。

在上图的最右边可以看到STM32地址是从0x0000 0000到0xFFFF FFFF,这就是4GB的存储空间。但是STM32真的有4GB的存储空间吗?
**你在想啥呢?**答案当然不是,我们的PC电脑也才4GB的内存。一个小小的单片机怎么可能有4GB的存储空间!这个4GB的是STM32理论分配的地址空间。也就是说实际上并不是有折磨大的存储单元。上图中第二排可以看到有很多预留的地址,这些地址并没有给他分配存储单元。

所有的存储器都是与地址线连着的,但是实际上如果你只接了一个 1M 的存储器,而且是从0地址开始映射的,那么32 根地址线所产生的0~1M 的地址信号其实才是有意义的,因为这些地址信号才有对应真实的存储器,而所产生的1M 以上地址信号其实并无意义,因为并不对应真实的存储器。

举个例子,政府给你化了10栋楼房的面积用来盖房子,但是实际上你没有那么多钱,只盖了3栋楼,其他的7栋房子预留的面积只能放在那里,凭什么不允许呢?这样说你应该明白了吧。

STM32中的32是32根地址线的意思吗?

答:不是,STM32的32不是32根地址线的意思,而是表示MCU芯片内部CPU在处理数据时,每次可以处理的数据位宽为32个bit,正是由于这个原因,STM32 内部的寄存器大小都是 32 位的,刚好等于位宽。

某个芯片是 32 位的,但是它的地址线完全可以只有 16 根、或者 8 根,对于 STM32 来说,刚好碰
巧的是,CPU 能够处理的数据“位宽”与地址线数量恰好都是 32,所以不少同学往往被搞迷糊了,认为是一回事。

2、什么是存储器映射

映射其实就是对应的意思。事实上存储器本身并不具备地址,将芯片理论上的地址分配给存储器,这就是存储器映射。

举例理解:比如前面举的 1M 存储器的例子,这个 1M 存储器原本并没有地址,我将 1M 存储器映射到理论32 根地址线可以传输 4G 个地址信号,每个地址信号访问一个字节,4G 地址信号则可以访问 4G个字节,所以理论上的可访问范围为 4G。

地址 0 往后的 1M 范围,这 1M 的存储器就有了 0—1M 的地址,地址线所产生的 0~1M 之间的地址信号,就可以访问 1M 的这个真实存储器。至于人家在生产芯片时,在工艺和技术上,具体是怎么实现我们所描述的映射的,这个我们无需关心。

3、STM32F429的存储器映射

STM32 的所有片内外设其实都是存储器,所以所有的这些存储器都需要被映射,只是理论上的 4G 范围远远大与实际的存储器空间,也就说实际的存储器空间并没有 4G。

其实存储器是很贵的,一个 STM32 单片机如果有4G存储器的话,那就很贵了,而且单片机的产
品根本不需要这么大的存储空间。

理论上地址起始就是门牌号,存储中的每个字节就是房间,存储器生产出来后,这些房间是没有地址的(门牌号),映射的过程其实就是将这些门牌号分配给这些房间,分配好后,每个门牌号只能访问自己的房间,没有被分配的地址就是保留地址,所谓保留地址的意思就是,没有对应实际存储空
间。

可不可以保留一些地址不分配呢?

当然可以,因为理论上可以有 4G 的地址,但是实际上不可能给你 4G 存储空间,否者这个单片机
芯片你可买不起,你想PC机的内存也才 4G/8G,单片机怎么可能真的给你 4G 存储空间呢?

寄存器映射

存储器本身没有地址,给存储器分配地址的过程叫存储器映射,那什么叫寄存器映射?寄存器到底是什么?

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

那么如果我想往0x4002 0410这个地址写入数值0xFFFF FFFF,应该怎么操作呢?

是不是只需要下面这一句话就可以了?

*(unsigned int*)(0x4002 0410) = 0xFFFF;

一直以为这句话很清楚,但是却发现有人看不懂这句话,那我来解释一下:

首先编译器不知道0x4002 0410是一个啥东西,它可能表示小猫,也可能表示小狗。但是我们知道这个16进制数是一个地址对吧?那么怎么把它编程一个地址呢?

是不是在它的前面加上(unsigned int*)变成*(unsigned int*)(0x4002 0410)就把这个数变成一个地址了?但是我们操作的是这个地址里面的内容,是不是再在前面加上一个星号变成*(unsigned int*)(0x4002 0410)就可以了,然后就可以给它赋值了:*(unsigned int*)(0x4002 0410) = 0xFFFF;

地址重映射

自举(bootstrap)计算机设备使用硬件加载的程序,用于初始化足够的软件来查找并加载功能完整的操作系统。也用来描述加载自举程序的过程。什么是单片机的自举,单片机的自举就是单片机的启动。

我们说,单片机程序基本都是从 0 地址出开始运行的,F429 的0x0000 0000 - 0x001F FFFF 地
址映射了到什么存储器上,那么就从该存储器上读取指令,开始运行。

至于说 0x0000 0000 - 0x001F FFFF 到底映射在了什么存储器上,这个要看 F429 芯片 BOOT1、
BOOT0 这两个引脚的电平值,说白了就是,通过 BOOT1 和 BOOT0 引脚的电平值,可以选择将 x00000000 - 0x001F FFFF 映射到不同的存储器上。

STM32片内的FLASH分成两部分:主存储块、信息块。 主存储块(主Flash)用于存储程序,我们写的程序一般存储在这里。 信息块又分成两部分:系统存储器(系统FLASH)、选项字节。 系统存储器存储用于存放在系统存储器自举模式下的启动程序(BootLoader),当使用ISP方式加载程序时,就是由这个程序执行。这个区域由芯片厂写入BootLoader,然后锁死,用户是无法改变这个区域的。 选项字节存储芯片的配置信息及对主存储块的保护信息。

请留意主 flash 的地址

“主FLASH”地址为 0x0800 0000 - 0x081F FFFF,大家回忆一下 STLINK 下载时的FLASH设置。是不是通过STLINK下载到了地址为0x0800 0000的地方,大小是0x0010 0000,也就是1MB。
疑问:明明代码是下载到 0x80000000 往后的存储空间中,为什么说运行又是从 0x00000000
地址运行的呢?为什么不是供 0x80000000 开始运行的呢?

有关这个问题,就是我们说的单片机的自举。
正常情况下都是映射到主FLASH上,所以都是从主FLASH上启动的,为了从FLASH启动,我们需
要将代码下载到主FLASH上。

什么是地址重映射

如果 0x0000 0000 - 0x001F FFFF 之前是映射在系统存储器或者嵌入式 SRAM上的,现在改变BOOT0、BOOT1 的电平为 0、x。0x0000 0000 - 0x001FFFFF 就被重新映射在了主FLASH上,这就是单片机的地址重映射

重映射就是本来是和张三进行映射的的,现在改为了和李四映射。换句话说重映射就是0x0000 0000 - 0x001F FFFF(1MB)本来映射在系统存储器 0x1FFF 0000 - 0x1FFF 7A0F(30KB)上面,现在映射到了主FLASH 0x0800 0000 - 0x081F FFFF(1M8)上面。

选择从主FLASH启动时,显然FLASH会被映射在了两片地址上。

  • 原本映射的地址(1MB):0x0800 0000 - 0x081F FFFF,进行 ST-LINK 下载时使用这个地址
  • 重映射的地址(1MB):0x0000 0000 - 0x001F FFFF,启动时CPU就是从重映射的地址读取指令

这两片地址都是有效的,重映射到 FLASH 上后,CPU 从0地址开始运行时,就从 FLASH 上读
取指令,当然前提是我们需要将代码下载FLASH中。

这就解释了为什么我们在keil中设置好程序的下载地址为0x800 0000,但是单片机上电是确实从0开始执行是因为我们在硬件上设置了BOOT0=1,BOOT1=X,从而导致了主FLASH区(也叫主闪存,大小1MB)被映射到了0x0000 0000 - 0x001F FFFF(1MB),故而代码是下载到 0x80000000 往后的存储空间中,却说运行又是从 0x00000000地址运行的。

疑问:下载时,能不能使用 0x0000 0000 地址来下载?
答:这个不行,因为下载时,0x0000 0000 - 0x001F FFFF 还没有被重映射到 flash 上,只能使
用 0x0800 0000 来下载。

上面说的是我们用JLink下载器下载代码,但是有时候我们还听说可以用串口来下载程序,这又是怎么回事?

用串口下载程序,也就是我们说的ISP在系统中编程。从系统存储器启动,即STM32的ISP了。此时硬件电路B00T0=1,B00T1=0。由于串口不能直接把程序下载到主FLASH里面,所以需要使用到ST公司内嵌于系统存储区的Bootloader来引导把程序下载到主FLASH里面。JLink能直接把程序下载到内置的FLASH里面,是因为JLink下载器内部有Bootloader来引导把程序下载到FLASH里面。 程序下载完成后还需要配置BOOT引脚为BOOT0=0,BOOT1=X(即从主闪存存储器启动),复位后才能正常启动程序。如果你不修改BOOT引脚的话也就是B00T0=1,B00T1=0,那么0x0000 0000 - 0x001F FFFF是不是被重映射到系统存储器上面,而程序代码在主FLASH里面。你复位后程序肯定不能正常运行,只有在使用串口下载程序后配置BOOT引脚为BOOT0=0,BOOT1=X,复位后才能正常执行代码。你明白了吗?

总结:使用JLink下载代码,JLink下载器内部的Bootloader将程序引导下载到主FLASH里面。使用串口下载代码,由于串口没有Bootloader,就要使用ST官方内置在芯片系统存储区的Bootloader代码,将程序引导下载止主FLASH。又因为程序是从0开始执行的,所以我们复位后运行程序时一定要让BOOT0=0,BOOT1=X,将0x00000000 - 0x001FFFFF是重映射到主FLASH我们代码存在的地方,从0开始执行代码。

下图是使用FlyMcu串口下载程序,这个串口是USB-TTL,下载程序时让BOOT0=0,BOOT1=X即可。不是说在系统中编程需要将B00T0=1,B00T1=0吗?这是因为我们使用的是这个软件,这个软件可以通过DTR和RTS改变BOOT的引脚电平,达到不用修改BOOT引脚就可以下载运行代码,实际上是软件替我们做了改变BOOT引脚的操作,具体介绍可以看上面的说明。

关于ISP与IAP

ISP(In System Programming)在系统编程,是指直接在目标电路板上对芯片进行编程,一般需要一个自举程序(BootLoader)来执行。ISP也有叫ICP(In Circuit Programming)、在电路编程、在线编程。

IAP(In Application Programming)在应用中编程,是指最终产品出厂后,由最终用户在使用中对用户程序部分进行编程,实现在线升级。IAP要求将程序分成两部分:引导程序、用户程序。引导程序总是不变的。IAP也有叫在程序中编程。 ISP与IAP的区别在于,ISP一般是对芯片整片重新编程,用的是芯片厂的自举程序。而IAP只是更新程序的一部分,用的是电器厂开发的IAP引导程序。综合来看,ISP受到的限制更多,而IAP由于是自己开发的程序,更换程序的时候更容易操作。

以上是关于干货|STM32寄存器版的基础知识—内存映射的主要内容,如果未能解决你的问题,请参考以下文章

2. STM32 存储器映射和寄存器映射

STM32 flash 内存分布介绍

STM32的GPIO 7个寄存器地址是多少 映射地址怎么设置

4 . 寄存器和存储器的操作

stm32启动过程cortex-m3架构堆栈代码位置编译汇编链接分析

STM32 PWM输出(映射)