干货|STM32基础知识—内存映射
击上方“果果小师弟”,选择“置顶/星标公众号”
干货福利,第一时间送达!
摘要:要想把STM32单片机学好,芯片的内部结构就要必须搞清楚。所谓基础不牢,地动山摇。今天带大家来看看STM32F429的Memory map。
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 着重传输数据,至于更详细的区分,不用关心。实际上 ICode、DCode 和 System 内部都包含三个部分,即地址总线、控制总线、数据总线。
高速总线
直接挂接在总线矩阵上的有哪些呢?
ICode、DCode、System;FLASH连接总线;SRAM 连接总线;高速外设连接总线 AHB1/AHB2/AHB3;连接“桥”的总线。
这些高速总线直接与总线矩阵连接在一起,其实这些高速总线实际上就是总线矩阵的延伸,或者说就是总线矩阵的一部分。
内存映射
这张图太重要了,看懂这张图,你的STM32已经可以掌握50%了,下面就来着重讲解这一张图。这张图来自STM32F429参考手册第84页,由于原版是英文的,搞了一个翻译过来的版本。
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以上地址信号其实并无意义,因为并不对应真实的存储器。
举个例子,政府给你化了10栋楼房的面积用来盖房子,但是实际上你没有那么多钱,只盖了3栋楼,其他的7栋房子预留的面积只能放在那里,这样说你应该明白了吧。
STM32中的32是32根地址线的意思吗?
答:不是,STM32的32不是32根地址线的意思,而是表示MCU芯片内部CPU在处理数据时,每次可以处理的数据位宽为32个bit。正是由于这个原因,STM32 内部的寄存器大小都是32位的,刚好等于位宽。
某个芯片是32位的,但是它的地址线完全可以只有16根、或者8根,对于 STM32 来说,刚好碰巧的是,CPU能够处理的数据位宽与地址线数量恰好都是 32,所以不少小伙伴往往被搞迷糊了,认为是一回事。
2、什么是存储器映射
映射其实就是对应的意思。事实上存储器本身并不具备地址,将芯片理论上的地址分配给存储器,这就是存储器映射。
举例理解:比如前面举的10M存储器的例子,这个10M存储器原本并没有地址,我将10M存储器映射到理论32根地址线可以传输4G个地址信号,每个地址信号访问一个字节,4G地址信号则可以访问4G个字节,所以理论上的可访问范围为4G。
地址0往后的10M范围,这10M的存储器就有了0—10M的地址,地址线所产生的0~10M之间的地址信号,就可以访问10M的这个真实存储器。至于在生产芯片时,在工艺和技术上具体是怎么实现我们所描述的映射的,我们无需关心。
3、STM32F429的存储器映射
STM32的所有片内外设其实都是存储器,所以所有的这些存储器都需要被映射,只是理论上的4G范围远远大与实际的存储器空间,也就说实际的存储器空间并没有4G。
存储器是很贵的,一个STM32单片机如果有4G存储器的话,那就很贵了,而且单片机的产品根本不需要这么大的存储空间。
理论上地址起始就是门牌号,存储中的每个字节就是房间,存储器生产出来后,这些房间是没有地址的(门牌号),映射的过程其实就是将这些门牌号分配给这些房间,分配好后,每个门牌号只能访问自己的房间,没有被分配的地址就是保留地址,所谓保留地址的意思就是,没有对应实际存储空间。
可不可以保留一些地址不分配呢?
当然可以,因为理论上可以有4G的地址,但是实际上不可能给你4G存储空间,否则这个单片机芯片你可买不起,PC机的内存也才4G/8G,单片机怎么可能真的给你4G存储空间呢?
寄存器映射
存储器本身没有地址,给存储器分配地址的过程叫存储器映射,那什么叫寄存器映射?寄存器到底是什么?
在存储器Block2这块区域,也就是地址从0x4000000—0x5FFFFFF这块区域,设计的是片上外设,它们以四个字节为一个单元,共32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。可以找到每个单元的起始地址,然后通过C语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,聪明的工程师就根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。
因此如果我想往0x40020410这个地址写入数值0xFFFF,应该怎么操作呢?
是不是只需要下面这一句话就可以了?
*(unsigned int*)(0x4002 0410) = 0xFFFF;
一直以为这句话很清楚,但是却发现有人看不懂这句话,那我来解释一下:
首先编译器不知道0x40020410是一个啥东西,它可能表示小猫,也可能表示小狗。但是我们知道这个16进制数是一个地址对吧?那么怎么把它变成一个地址呢?
是不是在它的前面加上(unsigned int*)变成(unsigned int*)(0x40020410)
就把这个数变成一个指针了也就是一个地址了?但是我们操作的是这个地址里面的内容,是不是再在前面加上一个星号变成*(unsigned int*)(0x40020410)
就可以了,然后就可以给它赋值了:*(unsigned int*)(0x4002 0410) = 0xFFFF;
地址重映射
自举(bootstrap)计算机设备使用硬件加载的程序,用于初始化足够的软件来查找并加载功能完整的操作系统。也用来描述加载自举程序的过程。什么是单片机的自举,单片机的自举就是单片机的启动。
我们说,单片机程序基本都是从0地址出开始运行的,F429的0x00000000-0x001FFFFF地址映射了到什么存储器上,那么就从该存储器上读取指令,开始运行。
至于说0x00000000-0x001FFFFF到底映射在了什么存储器上,这个要看F429 芯片 BOOT1、BOOT0这两个引脚的电平值,说白了就是,通过BOOT1和BOOT0 引脚的电平值,可以选择将0x00000000-0x001FFFFF映射到不同的存储器上。
STM32片内的FLASH分成两部分:主存储块、信息块。主存储块(主Flash)用于存储程序,我们写的程序一般存储在这里。信息块又分成两部分:系统存储器(系统FLASH)、选项字节。系统存储器存储用于存放在系统存储器自举模式下的启动程序(BootLoader),当使用ISP方式加载程序时,就是由这个程序执行。这个区域由芯片厂写入BootLoader,然后锁死,用户是无法改变这个区域的。选项字节存储芯片的配置信息及对主存储块的保护信息。
主FLASH的地址
主FLASH地址为0x0800 0000-0x081FFFFF,Jlink下载时的FLASH设置是不是通过Jlink下载到了地址为0x08000000的地方,大小是0x00100000,也就是1MB。
疑问:明明代码是下载到0x80000000往后的存储空间中,为什么说运行又是从0x00000000地址运行的呢?为什么不是从0x80000000开始运行的呢?
有关这个问题,就是我们说的单片机的自举。
正常情况下都是映射到主FLASH上,所以都是从主FLASH上启动的,为了从FLASH启动,我们需要将代码下载到主FLASH上。
什么是地址重映射
如果0x00000000-0x001FFFFF之前是映射在系统存储器或者嵌入式 SRAM上的,现在改变BOOT0、BOOT1 的电平为 0、x。0x00000000 -0x001FFFFF就被重新映射在了主FLASH上,这就是单片机的地址重映射。
重映射就是本来是和张三进行映射的的,现在改为了和李四映射。换句话说重映射就是0x00000000 -0x001FFFFF(1MB)本来映射在系统存储器 0x1FFF 0000-0x1FFF7A0F(30KB)上面,现在映射到了主FLASH 0x08000000 -0x081FFFFF(1MB)上面。
选择从主FLASH启动时,显然FLASH会被映射在了两片地址上。
原本映射的地址(1MB):0x08000000-0x081F FFFF,进行Jlink下载时使用这个地址 重映射的地址(1MB):0x00000000-0x001F FFFF,启动时CPU就是从重映射的地址读取指令
这两片地址都是有效的,重映射到FLASH上后,CPU从0地址开始运行时,就从FLASH上读取指令,当然前提是我们需要将代码下载FLASH中。
这就解释了为什么我们在keil中设置好程序的下载地址为0x8000000,但是单片机上电是确实从0开始执行。是因为我们在硬件上设置了BOOT0=1,BOOT1=X,从而导致了主FLASH区(也叫主闪存,大小1MB)被映射到了0x00000000-0x001FFFFF(1MB),故而代码是下载到 0x80000000 往后的存储空间中,却说运行又是从0x00000000地址运行的。
疑问:下载时,能不能使用0x00000000地址来下载?
答:这个不行,因为下载时,0x00000000-0x001FFFFF还没有被重映射到FLASH上,只能使用0x08000000来下载。
上面说的是我们用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 - 0x001FFFFF是不是被重映射到系统存储器上面,而程序代码在主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在系统编程,是指直接在目标电路板上对芯片进行编程,一般需要一个自举程序(BootLoader)来执行。ISP也有叫ICP在电路编程、在线编程。
IAP在应用中编程,是指最终产品出厂后,由最终用户在使用中对用户程序部分进行编程,实现在线升级。IAP要求将程序分成两部分:引导程序、用户程序。引导程序总是不变的。IAP也有叫在程序中编程。ISP与IAP的区别在于,ISP一般是对芯片整片重新编程,用的是芯片厂的自举程序。而IAP只是更新程序的一部分,用的是电器厂开发的IAP引导程序。综合来看,ISP受到的限制更多,而IAP由于是自己开发的程序,更换程序的时候更容易操作。
End
推荐好文 点击蓝色字体即可跳转
欢迎转发、留言、点赞、分享,感谢您的支持!