最近给刚移植到s3c2440平台的uCOS3加入newlib库支持。
使用sourcery G++编译工具链,sourcery G++ 带有编译好的newlib库。我们要使用的时候只要链接libc.a他就可以了。(我使用eclipse+arm补丁为开发环境)
要使用newlib则首先要实现20个newlib桩函数:_open_r() _read_r() _write_r()等(具体看newlib文档);然后要实现文件描述符等。
文件描述符的实现将来再写。。。
下面主要说一下移植的一些关键问题:
int main (void) { ... stdin = fopen((const char *)"/dev/ttyS0", "r+"); stdout = fopen((const char *)"/dev/ttyS0", "w+"); stderr = fopen((const char *)"/dev/ttyS0", "w+"); errno = 1; /* test errno */ printf("printf test \n"); perror("error"); ... }
这样我们就可以使用printf 和perror两个函数了。
stdin、errno 等这些宏都是针对他们所在任务的:
#define _REENT _impure_ptr #define stdin (_REENT->_stdin) #define errno (*__errno()) int *__errno () { return &_REENT->_errno; }所以全局指针 _impure_ptr 要在任务切换的时候指向每个任务的reent实例:
void App_OS_TaskSwHook (void) { _impure_ptr = &OSTCBHighRdyPtr->reent; /* newlib 中可重入对象指针切换实例*/ }
给uCOS3的任务控制块TCB加入成员:
struct os_tcb { ... struct _reent reent; /* 每个任务要有一个具体的reent实例*/ }
任务创建的时候(OSTaskCreate())要给每个任务控制块的成员reent初始化:
void OSTaskCreate (...) { ... _REENT_INIT_PTR(&p_tcb->reent); /* 初始化 reent */ /* * 该函数会默认初始化stdin、stdout、stderr的fd为0,1,2 * 若不调用该函数,则printf检查到为初始化reent则会调用该函数,这样stdout=fopen(...), * 给stdout赋值好后会在printf后重新被初始化掉,所以在这里初始化。 */ CHECK_STD_INIT(&p_tcb->reent); /* * 由于CHECK_STD_INIT()初始化stdin、stdout、stderr的fd为0,1,2, * 这样导致即使任务中没给stdout赋值,只要fd=1的设备默认就是stdout,所以清除掉std指针 */ p_tcb->reent._stdin = NULL; p_tcb->reent._stdout = NULL; p_tcb->reent._stderr = NULL; ... }
以上之所以要这么初始化,是因为printf在调用的时候若发现未初始化当前任务reent,则会初始化reent,初始化的reent._stdin->file = 0,reent._stdout->file = 1,reent._stderr->file = 2,这就意味着之前的stdout = fopen((const char *)"/dev/ttyS0", "w+"); stdout赋值无效,会被重新初始化掉,当然如果你打开的顺序正好是按stdin stdout stderr 即fd 为 0, 1,2 则使用的时候也不出出现问题,但是这样我们给stdout赋值是没有起到实际效果的,只是恰好被分配到 fd为0,1,2.