移植之前先认识下开发工具很重要,这里使用MDK(V5.16a)工具,默认使用Armcc,它能支持把c以及c++编译成arm或者是thumb机器码,支持以下的标准:ISO C 1990,ISO C 1999,ISO C++ 2003,ISO C++2011,默认输出格式是ELF格式,在编译结束之后可以转换成Hex格式烧进ROM。
编译器支持在标准c,c++上提供一些GNU类似的扩展,支持使用不同的参数来选择使用不同的标准如:--c90,--strict,--c99,--cpp,--cp11等参数。还提供一些二进制格式的lib.例如最重要的部分就是标准C库和mircoC库,当然armcc库在标准库上面有一些扩展。
下面说几个比较重要的部分,当然这些都是针对MDK以及arm架构而言的,其他的工具如gcc,iar机会也是类似的处理方法可以参考相关文档:
1.ARM处理器是如何跑第一条指令,以及如何跑到C语言的main方法当中去的呢.
2.main方法是不是必须的(不是)
3.main方法是否可以带参数(可以)
4.main方法是否可以退出,如何退出,退出之后程序会跑到哪里去(可以退出,被clib接管)
下面来逐个讨论:
1.arm的第一条指令跟x86一样都是进入到rest异常当中,不同的是x86会先进入实模式,arm却是在svc模式。这个时候c语言是不能运行的(因为c语言需要堆,栈来保存中间值和传递参数),所以这个时候我们需要做几件事情,这些一般都是用汇编来实现。
a.最基本的初始化,时钟,模式等等,类似于EFI当中的SEC阶段,
b.内存初始化,类似PEI阶段
c.初始化堆栈
d.调用C库的初始化
e.由c库来调用main函数
f.退出main或者其他
上代码:
AREA RESET, CODE, READONLY
Vectors LDR PC, Reset_Addr
各种伪代码不解释,在表示是代码段,在链接脚本里面保证RESET在最前面被链接进去就行了,这样cpu就会在一上电就跳到Reset_Addr这个函数当中去,当然这个函数也是汇编写的。
接下来就是Reset_Addr表演了。这里不想说如何堆积arm指令和伪指令,也不想说如何初始化模式,clock,看门狗,内存,gpio等。
在上面的各种初始化完了之后会调用一个__main函数,这个函数是c库函数,它会干以下几件事情:
|
__rt_entry进去之后会干下面的几件事情:
|
通过上面的解释我们就可以明白了,处理器是如何执行第一条指令,下面我们要实际来编写汇编指令,实现对板子上的sdram的初始化,以及堆栈初始化,至于为何要这么做,原因在于freertos需要使用到堆来动态分配存储空间,所以我们先来做这一部分。