图1 C库函数重定向
例子程序的代码如下所示。
extern void sendchar(char *ch);
int fputc(int ch, FILE *f)
{?? /* e.g. write a character to an UART */
char tempch = ch;
sendchar(&tempch);
return ch;
}
映象文件存储器映射调整
映像由域(Regions)和输出段(Output Sections)组成。每个域可以有不同的加载地址和执行地址。
分散加载可以更加方便准确的指定映像存储器映射,为映像组件分组和布局提供了全面控制。它能够描述由载入时和执行时分散在存储器映射中的多个区组成的复杂映像映射。虽然,分散加载可以用于简单映像,但它通常仅用于具有复杂存储器映射的映像。
要构建映像的存储器映射,必须向armlink 提供以下信息:
·? 分组信息? 决定如何将各输入段组织成相应的输出段和域;
·? 定位信息? 决定各域在存储空间的起始地址。
有两种方法可以实现指定映像文件的分组和定位信息:如果映像文件中地址映射关系比较简单,可以使用命令行选项;如果映像文件中地址映射关系比较复杂的情况,可以使用一个配置文件。使用该配置文件可以高速链接器相关的地址映射关系。配置文件又叫Scatter文件,是一个文本文件,通过下面的链接选项来实现。
-scatter? filename
复位和初始化
arm嵌入式系统的初始化序列如图2所示。系统启动时立即执行复位处理程序,然后进入$Sub$main()的代码执行。
复位处理程序是用汇编语言编写的代码块,它在系统复位时执行,完成系统必须初始化操作。对于具有局部存储器的内核,如Caches、紧密藕荷存储器 (TCM)、存储管理单元 (MMU) 和存储器保护单元 (MPU) 等,在初始化过程这一阶段完成必要的配置。复位处理程序在执行之后,通常跳转到 __main 以开始 C 库的初始化序列。
一般情况下,系统初始化代码和主应用程序是分开的。系统初始化要在主应用程序启动前完成。但部分与硬件相关的系统初始化过程,如启用Cache和中断,必须在C库初始化代码执行完成后才能执行。
为了在进入主应用程序之前,完成系统初始化,可以使用$sub和$super函数标识符在进入主程序之前插入一个例程。这一机制可以在不改变源代码的情况下扩展函数的功能。
下面的例子说明了如何使用$sub和$super函数标识。链接程序通过调用$sub$$main()函数取代对main()的调用。所以用户可以在自己编写的$sub$$main()例程中启用Cache或使能中断。
extern void $Super$$main(void);
void $Sub$$main(void)
{
cache_enable();??? // enables caches
int_enable();????? // enables interrupts
$Super$$main();??? // calls original main()
}
在$Sub$$main(void)函数中,链接程序通过调用$Super$$main(),是代码跳转到实际的main()函数。
[nextpgae]
在完成硬件初始化之后,必须考虑主应用程序运行在何种模式。如果应用程序运行在特权模式(Privileged mode),只需在退出复位处理程序前切换到适当的模式;如果应用程序运行在用户模式下,要在完成系统初始化之后,再切换到用户模式。模式的切换工作,一般在$Sub$$main(void)函数中完成。
图2 arm嵌入式系统的初始化序列