有些问题疑惑了很久,过来问下amanda_s,也希望大家都能够讨论、相互学习下。
STM32F4在MDK-ARM中的启动代码是这样的:
定义了READWRITE属性的栈区;定义了READWRITE属性的堆区;定义了READWRITE属性的异常向量表;定义了READONLY属性的异常向量处理函数,和一个堆栈的初始化汇编代码。
1.假设从用户flash启动,硬件加电稳定后,系统从0x00出获取了MSP,接着PC读取了Reset_Handler的地址值(还未开始执行)。
我的问题是:此时内核直接读取了Flash是吗?
我的理解是内核直接通过地址总线读取了0x00处的sp,和0x04处的一个地址(就是复位向量地址值)。
2.系统通过PC的值开始执行如下代码(songbin,如何添加带有格式的代码啊?)
可以看到系统会转向执行SystemInit,在SystemInit中,会调用SystemInit_ExtMemCtl(我用了外部的SDRAM)。
我的问题是:此时在SystemInit及其迭代调用的任何函数中不能出现任何的C语言相关的变量,如果全局的、局部的等,因为此时还未初始化C语言需要的堆、栈等必须的配置。是这样的吗?
我看到,有些局部的变量被定义为了 register uint32_t index;这说明还是在直接使用寄存器,并未使用C相关的东西。
3.接着系统调用了__main,我在《Libraries and Floating-Point Support User Guide》找到了__rt_entry、__initial_sp等编译器相关的库函数,但是唯独没有找到__main的详细说明,这是为什么、在哪可以找到?
我在"Initialization of the execution environment and execution of the application"小结中,找到了如下内容
The entry point of a program is at __main in the C library where library code:
Copies non-root (RO and RW) execution regions from their load addresses to their execution addresses. Also, if any data sections are compressed, they are decompressed from the load address to the execution address.
Zeroes ZI regions.
Branches to __rt_entry.
这个__main也可以自己写,它主要是为了C语言能够执行,完成对内存的初始化等。
__rt_entry是这样说明的:
The library function __rt_entry() runs the program as follows:
__rt_entry()
Sets up the stack and the heap by one of a number of means that include calling __user_setup_stackheap(), calling __rt_stackheap_init(), or loading the absolute addresses of scatter-loaded regions.
__user_setup_stackheap()
__rt_stackheap_init()
Calls __rt_lib_init() to initialize referenced library functions, initialize the locale and, if necessary, set up argc and argv for main().
__rt_lib_init()
main()
For C++, calls the constructors for any top-level objects by way of __cpp_initialize__aeabi_.
Calls main(), the user-level root of the application.
From main(), your program might call, among other things, library functions.
Calls exit() with the value returned by main().
exit()
__rt_entry()完成了堆栈的初始化、初始化C/C++库,并转向C的main函数开始用户程序代码,如果main返回则调用exit(),但exit()干什么了不知道?如果用户从main中返回,那么是否从exit()也返回了,这样代码就回到了Reset_Handler,此时系统应该是停止了吧?
另外,在调用main之前,都不应该使用C相关的变量分配,因为在main之前还未对C/C++的库运行做好准备,是吧?
amanda_s
我发现针对F030,有不同的启动文件和复位向量函数。
在STM32F0Cube中,最新的v1.2.0,启动文件都行如startup_stm32f030x8.s,多出的x8不清楚是干什么的?但其复位向量函数和F4的一样:
但在MDK-ARM V5.13中,使用最新的keil::STM32F0xx_DFP v1.4.0,其中的启动文件都是形如startup_stm32f030.s,其中的复位向量就不同了:
; Reset handler routine
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =__initial_sp ; set stack pointer
MSR MSP, R0
;;Check if boot space corresponds to test memory
LDR R0,=0x00000004
LDR R1, [R0]
LSRS R1, R1, #24
LDR R2,=0x1F
CMP R1, R2
BNE ApplicationStart
;; SYSCFG clock enable
LDR R0,=0x40021018
LDR R1,=0x00000001
STR R1, [R0]
;; Set CFGR1 register with flash memory remap at address 0
LDR R0,=0x40010000
LDR R1,=0x00000000
ApplicationStart
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
就不知道上述多了一些判断和设置,为什么在Cube中没有(或者想反的在DFP中加入了)?
我发现Cube和DFP用的基本都是一样的示例和HAL库,只是命名和版本号不同,我看了各自的releaseNote,也是相同的日期和说明。但MDK-ARM中更新的略慢。是么?
比较了一下这两个.S文件,基本都差不多。
后者多了一些对系统时钟的配置。估计是把SystemInit()中的一部分工作放到前面来做了。
应该没什么太大的区别。
你确实看的很细啊!是在做平台移植,还是纯粹学习啊?:)
我认为区别还是挺大的,比如DFP中多出的如下:
F030应该是自动把栈顶指针放入MSP的,不用手动加入吧?这在Cube中的启动文件中是没有的(当然此处加了也没事)。
我看了下DFP中的SystemInit,这个函数和Cube中的功能是一样的,也就是说DFP中的启动文件多出了一些,但不知道却是为何?DFP或Cube这两者,该用哪个呢?
我是纯粹的学习,想知道的详细些。
我写论文所以要明确一些,但有些资料查着查着就没有更多的说明了。
不用太纠结。实话这些汇编的语句,我也没一条一条看过。
文件的最开头注释已经说了明了两个版本的区别,这些区别不是关键的。
你如果是用的cube库就用cube里面的那个吧。
这个问题大家讨论的都很好,现在先转为自由讨论,可以继续回帖发言,谢谢大家