st-flash能够将代码下载至STM32的任何位置。甚至是SRAM。在下载完毕之后,不用我们手动设置,他自动的设置pc至你下载代码的位置,并从pc处开始运行。
反汇编
先来反汇编我们点亮ledhttps://www.stmcu.org.cn/module/forum/thread-603787-1-1.html的代码。使用arm-none-eabi-objdump -d blink.elf生成反汇编代码,如下图所示:
等等,为什么第一条指令的地址是0x0800_0188?我们的代码不是应该从0x0800_0000开始吗?可能看到这样的代码会有这样的疑惑。那就要来看看ARMCM4的架构了。规定“0”地址(STM32当使用FLASH启动的时候,0x0800_0000会被映射为0地址。SRAM启动的时候,0x2000_0000映射为0地址)的第一个数据为sp(栈指针)。以后连续的地址用来存放中断向量表。向量表之后的地址用来存放实际的代码。
打开启动文件数一下共有多少个中断向量:
从139行开始到235行,共97个中断向量。外加0地址的sp的值,共98个数据,每一个数据占用4个字节,合计98×4=392=0x188。从0地址开始存放的话,存放到0x187位置结束。所以0x188作为第一条指令的起点。
由于ARM中,很多指令是与地址紧密相关的。虽然可以将这份程序强制下载到ARM中,但是因为地址的原因并不能运行。
分析flash.ld
在flash.ld链接文件中定义了_estack。这个值在汇编文件中使用,作为0地址下的sp的值。一般来说,栈的初始值是可以随便设定的。只要在RAM中就可以。但是为了避免内存碎片化,会将sp的初始值放置RAM中的最后一个地址。照这样来算,64K的RAM最后一个地址为0x2001_0000。这也就是将_estack设置为这个值的原因。
在MEMORY{}中设置了RAM,FLASH的起始地址和大小。如果你更换了其他的芯片,根据不同的FLASH,RAM的大小,只需要更改ORIGIN和LENGTH就行了(当然,也不要忘记更改_estack)。
在我看来,这里的_Min_Heap_Size和_Min_Stack_Size并没有什么用。只是用来保证你程序中bss段不至于过大而已。比如你在程序中申请了一个比较大的数组,char buf[64*1024]。这样你的bss段至少占用了64K的内存。在加上_Min_Heap_Size和_Min_Stack_Size的大小,已经超出了RAM的实际大小了。这样在链接的时候便会给你报错。
不同于MDK,MDK的汇编文件中有一个数值,可以设置heap的大小,用于malloc等内存分配。但是在gcc中,即使你将链接文件中的_Min_Heap_Size更改为0,arm gcc的malloc函数一样能够分配到内存。因为他使用不一样的机制。他的内存分配是malloc和sbrk共同作用的。sbrk这个系统调用需要你自己来实现。
更改FLASH的地址
要想让程序在RAM中能够运行,只需要更改FLASH的地址为RAM中的地址就可以了。因为Nucleo303有64K的内存,所以分配20K内存给RAM,分配40K内存当作FLASH使用。更改如下:
修改Makefile
在Makefile中添加make sram用于生成在SRAM中运行的代码。添加make burns使用st-flash将代码烧写到SRAM中运行。
修改main函数
将原先代码的延时时间缩短,由之前的720000改为72000。
到目前位置,一切更改完毕。只需要使用make clean,make sram生成用于在sram中运行的程序。生成之后使用sudo make burns下载到SRAM中运行即可。如果你的flash中的程序还是上一个led灯的程序,未下载之前可以看到闪烁很慢,大约1s一次。当使用make burns下载成功之后,闪烁明显加快。在按一下复位键之后,再次运行存放在FLASH中的程序。闪烁较慢。
下载到SRAM中的时候,相比FLASH的下载,少了擦除那一项:
对程序进行反汇编,可以看到第一条指令是在0x2000_5188这个地址。
完了吗?当然没完
看到流水灯能够闪烁,你可能觉得使用st-link将程序下载到SRAM中运行很简单。从表面上看,这个程序确实奏效了。但是其中隐藏这巨大的bug。那就是中断向量表的问题。因为这个程序并没有使用中断,表面上看起来是好的。但一旦使用中断的时候,这个Makefile产生的程序必然无法运行。我们将在下一节使用串口的时候进行修正。
提前提示一下,问题是SCB->VTOR的原因。