一般嵌入式开发流程便是先树立一个工程,再编写源文件,然后进行编译,把一切的*.s文件和*.c文件编译成一个*.o文件,再对方针文件进行链接和定位,编译成功后会生成一个*.hex文件和调试文件,接下来要进行调试,假如成功的话,就能够将它固化到flash里边去。
发动代码是用来初始化电路以及用来为高档言语写的软件作好运转前预备的一小段汇编言语,是任何处理器上电复位时的程序运转进口点。
比方,刚上电的进程中,PC机会对体系的一个运转频率进行锁定在一个固定的值,这个规划频率的进程便是在汇编源代码中进行的,也便是在发动代码中进行的。与此同时,设置完后,程序开端运转,留意,程序是在内存中运转的。这个时分,就需求把一些源文件从flash里边copy到内存中,又要对它们进行初始化读写,这又有频率的设置。这些都是初始化。
初始化完成后,咱们又要设置一些仓库,要跳到C言语的main函数里边运转。这就需求仓库。对一般的ARM CPU有这样一个要求:在肯定地址为零的当地要放置一个反常向量表,但并不是一切的ARM CPU都留有这个一个空间,这就需求用到映射的功用。咱们能够将其它当地的一些空间映射到肯定地址里边。当产生反常时,ARM核来读取反常中断表的时分,它会运用映射之后的那个表,这个就能够接着往下履行,否则在肯定地址零的当地找不到任何信息,程序就会死掉。这些运转的环境悉数树立好后,程序就会跳转到咱们的main函数里边。
总归,发动代码,便是对最小体系的初始化。包含晶振,CPU频率等。
发动代码的最小体系是:反常向量表的初始化–存储区分配–初始化仓库–高档言语进口函数调用– main()函数。
程序的发动进程:
以下面这个比如为例,编译完后,DEBUG后,咱们能够看到,光标指向肯定地址为零的当地,这儿寄存的便是一个反常向量表。
它对应在startup.s里的源文件如下:
运转后,立刻跳转到初始化CPU的频率。即初始化锁相环,将其锁在一个固定的频率。详细代码如下:
; Setup PLL
IFPLL_SETUP <> 0
LDRR0, =PLL_BASE
MOVR1, #0xAA
MOVR2, #0x55
;Configure and Enable PLL
MOVR3, #PLLCFG_Val
STRR3, [R0, #PLLCFG_OFS]
MOVR3, #PLLCON_PLLE
STRR3, [R0, #PLLCON_OFS]
STRR1, [R0, #PLLFEED_OFS]
STRR2, [R0, #PLLFEED_OFS]
;Wait until PLL Locked
PLL_LoopLDRR3, [R0, #PLLSTAT_OFS]
ANDSR3, R3, #PLLSTAT_PLOCK
BEQPLL_Loop
;Switch to PLL Clock
MOVR3, #(PLLCON_PLLE:OR:PLLCON_PLLC)
STRR3, [R0, #PLLCON_OFS]
STRR1, [R0, #PLLFEED_OFS]
STRR2, [R0, #PLLFEED_OFS]
ENDIF; PLL_SETUP
然后再初始化每一种形式的仓库,再进行单步运转的时分,下面咱们能够看到,它主动跳转到main()函数:
; Enter the C code
IMPORT__main
LDRR0, =__main
BXR0
IF:DEF:__M%&&&&&%ROLIB
EXPORT__heap_base
EXPORT__heap_limit
ELSE
这个时分,程序会运转各种scatterload函数,将咱们的仓库、全局变量等内容复制到内存中去。复制完后,就正式跳转到咱们的main()函数中来履行了。
这便是发动代码履行的全进程,呵呵,平常咱们看到认为仅仅履行main()函数就行了,是不是没有想到在履行 main() 函数后还有这么多学识呢?