在嵌入式系统开发中,可以根据需要选择是否采用C库,如果应用程序较大,可能需要经常使用C库中的一些函数,如需要使用动态分配内存malloc,free,stdio.h中的printf,sprintf,string.h中的strcmp,strcpy等等。这时移入合适的C库是很好的选择,本文主要讲述在SDT2.5版本的ARM编译开发工具中使用C库需要解决的一些问题。
在SDT2.5版本的开发环境中,可以采用semihosted ANSI C library或者Embedded C library,Embedded C只是作为ANSI C的一个子集。ANSI C一般适用于早期的开发工作,可以和Angle,ARMulator等软件调试工具配合使用,方便软件的开发,在用户自己的代码执行之前,使用库前,所有的系统初始化工作必须完成。后者由于代码更小,不使用底层硬件资源,在与应用程序集成的时候比较适合与最终的版本发布。而且配合一些硬件调试仿真工具,对于程序的调试来讲,应该非常方便。
带Embedded C library应用程序执行流程:
1.系统启动之后,底层的一些初始化,
在跳转到C程序前,必须用汇编完成一些底层的一些初始化工作,只有包括异常中断跳转地址的处理,根据嵌入式系统的需要是否采用remap,以及异常中断的处理等等。
2.初始化嵌入式堆栈管理器
在C程序中,一般都需要采用动态分配内存,可以调用嵌入式C库中的堆管理器,根据堆的位置和大小来初始化堆,以便在C程序可以用malloc()函数动态分配内存。具体的动态内存分配算法在下面会继续阐述。
初始化调用的函数是:
struct Heap_Descriptor *__rt_embeddedalloc_init(void *base, size_t size);
该函数返回一个堆描述符的指针。
由于嵌入式C库被设计成可以完全可重入的,不包含静态数据,用户在自己的代码中必须提供回调函数 struct Heap_Descriptor *__rt_heapdescriptor(void),该函数返回__rt_embeddedalloc_init()的值,这样库可以根据返回值定位堆描述符。
3.调用顶层对象构建器
如果在嵌入式C库中使用C++库,则需要调用顶层对象构建器void __cpp_initialise(void)
4.执行主程序
5.调用顶层对象析构器
在程序终断前,必须调用顶层对象析构器 void _cpp_finalise(void)。
代码实例:
#include
extern struct Heap_Descriptor *__rt_embeddedalloc_init(void *base, size_t size);
void ApplicationCode(void);
struct Heap_Descriptor *hp;
extern struct Heap_Descriptor *__rt_heapdescriptor(void) {
return hp;
}
extern void C_Entry(void) {
hp = __rt_embeddedalloc_init((void *)0x2030000, 0x20000);
ApplicationCode();
}
使用Embedded C library其他需要注意的地方:
1)在SDT编译环境中,armlink选项的库搜索路径选择/arm/lib/embedded/,该目录下有四个库文件,分别对应16位big,little,32位big,little。
2)代码必须重新实现实现__main(),_main(),不然在SDT下编译会出错。
3 )如果使用 c 库函数,根据需要可能还要实现 __rt_trap, __rt_errno_addr, __rt_ft_status_addr ,具体实现同 __rt_heapdescriptor(), 但在 SDT 下不会编译出错,在 symbol table 中会出现 undefinded , weak reference 信息。