STM32具有和PC机类似的结构组成。那么我们平时听到的RAM和ROM就相当于PC的内存条和硬盘,当然了PC的硬盘和单片机的ROM也并不是所谓的只读,只不过以前的技术原因很难做到多次读写,因为最初的存储器是纸带、熔丝或者其他一次性存储器,因此只能读取,也就是ROM,当然那时候也不需要持续的修改。随着技术的革新,这些最初的ROM慢慢的增大内存、增加写功能、提高读写速度。而ROM的叫法也一直延续下来。
扩展一下,为什么ROM也可以写,不用ROM当RAM呢?因为ROM还是太慢了,读取可还行(比如F7、H7就支持QSPI运行程序),写就是肉眼可见级别的延迟了。所以RAM这种需要高速读写的还是让SRAM、SDRAM、DDR等等来干吧。
那这两大块内存在单片机上是怎么分布的呢?以F103C8T6为例。
ROM&RAM声明 这里就勾选使能了两块内存,一个是ROM,一个是RAM,当然也有的支持两块ROM或者ROM,这里先不管,ROM的起始地址是0x0800 0000,大小是0x2400。RAM的地址是0x2000 0000,大小是0x5000。(为什么ROM比RAM还小,因为这是个Bootloader,要占用尽可能小的空间,不然的结果就是你买的64G手机,什么都没装就还有56G)。
或者看一下默认的分散加载文件,也可以得到ROM和RAM的地址和大小。
分散加载内容 关于ROM和RAM做什么用的你应该很清楚了,不清楚我们就再叙述一下。RAM是随机存取存储器,可读可写,并且想怎么读怎么读,想怎么取怎么取,还很快。正是这种特点RAM才适合做CPU和ROM的中介,上可速度对等CPU,下可衔接ROM永久存储数据。但是呢?一断电它就清零了,就跟你敲了半天的word崩了打开就空了一样。而ROM就不会,并且ROM的空间大小可以做到很大很大。而ROM的缺点就是写比较慢,如果是串行的传输方式那么会更慢。
CPU就非常快了,上到GHz级别,因此如果需要进行数据的读写,显然ROM速度完全跟不上,但是RAM一断电就没了,所以完美的结构是先从ROM读取到RAM,然后再运行。这就是为什么同时需要ROM和RAM。
ROM保存着需要固化的内容,比如函数到底存储着哪些寄存器操作指令,程序到底定义了哪些全局变量,程序使用了哪些字库、图片或者数组(比如使用查表加速计算)。你下载的程序就存储在这里,比如你使用fromelf --bin !L -o .\BIN\Firmware_F4.bin指令生成了bin文件。(bin文件名称:Firmware_F4.bin 可以自定义)那么这些数据就会固化在0x0800 0000这里,就像这样。
BIN文件数据 而你定义的数组则会存在0x2000 0000这块区域,具体是哪里可以使用取址指令得到地址,例如value数组的地址是:0x20000898。
数组地址打印 或者你也可以直接看变量窗口的地址,如下。
变量地址查看 当你写了数据,相应的就可以看到RAM的数值变化。比如执行到60行的时候,第59行的赋值就有了效果。
变量地址&数据查看 相对应的内存处也产生了变化。
数组数据变化 随着数组内容的写入可以看到数据的变化过程。
数组数据变化 甚至你还可以看到main函数存在了哪里。
主函数存储地址 又或者你直接查看Map文件。
map文件main函数存储地址 这是你定义的字库的地址:0x08007B96,或者你也可以用同样的方式把它打印出来。
字库存储地址 同样是数组,一个在RAM,一个在ROM,因为字库不需要更改,所以使用const关键字定义在常量区,也就是ROM。而变量数组需要更改,这里没有赋0以外的值,所以你需要多大的空间,编译器到时候给你去RAM分配就是了,然后编译器再把这些分配地址的指令写进BIN,然后你下载存储到0x0800 0000的单片机的硬盘里,到时候单片机的Cortex-M内核就知道0x2000 0000的那一块地址是属于你的数组了。
如果赋值不是0呢?可以看到,地址还是0x2000 0000这块区域。而main函数的局部变量就需要分配了,有初始非0值相当于预定了,而局部变量只有运行的时候才有会给分配地址。
全局非0初始化存储地址 就好像有初始值相当于交了押金,会给你预定包间,虽然你还没住但是已经有了房间号,而你不初始化只有到了再订包间,所以在这之前你完全不知道你的房间号。并且如果你来晚了还有可能没有包间可用,已经被别人包完了,对应到单片机就是堆栈溢出了。那如果你预定都预定不到的话,就是内存不够了。
|