SMARTARM2200 ADS工程在IAR EWARM 5.3上的移植(6)-LwIP 1.2的移植(uCOSII部分)

常见的嵌入式TCPIP协议栈有LwIP,uIP,uC/TCPIP,TinyTcp等,相对来说LwIP功能较uIP(uIP更多用在8位51上),TinyTCP强点,但代码量小于uC-TCPIP,之前也尝试过移植uC-TCPIP,不过一直有点问题,当然uC-TCPIP还不是免费的.加上网上关于LwIP的资料也比较多.

1.LwIP简介
LwIP是瑞士计算机科学院(Swedish Institute of Computer Science)的Adam Dunkels等开发的一套用于嵌入式系统的开放源代码TCP/IP协议栈。LwIP的含义是Light Weight(轻型)IP协议,相对于uip。LwIP可以移植到操作系统上,也可以在无操作系统的情况下独立运行。LwIP TCP/IP实现的重点是在保持TCP协议主要功能的基础上减少对RAM的占用,一般它只需要几十K的RAM和40K左右的ROM就可以运行,这使LwIP协议栈适合在低端嵌入式系统中使用。LwIP的特性如下:支持多网络接口下的IP转发,支持ICMP协议 ,包括实验性扩展的的UDP(用户数据报协议),包括阻塞控制,RTT估算和快速恢复和快速转发的TCP(传输控制协议),提供专门的内部回调接口(Raw API)用于提高应用程序性能,并提供了可选择的Berkeley接口API。http://www.sics.se/~adam/lwip/http://savannah.nongnu.org/projects/lwip/

2.采用LwIP1.2,1.3的接口函数有些不一样,为了移植的方便还是使用1.2,毕竟基于1.2的参考资料更多些,移植的很多代码参考并借用了网上的例子,在此表示感谢!
更详细的信息可以参考我上传的工程http://download.csdn.net/source/1661278
将LwIP1.2加入到我们的IAR工程中,因为我们并不需要SLIP,PPP以及IPv6的支持,这些相关代码就不必加入到工程里了,将头文件路径添加到IAR include directories.

3.添加移植代码,如cc.h, lwipopts.h, sys_arch.h, sys_arch.c, 网卡驱动等
cc.h:定义数据类型,大小端格式等
lwipopts.h:lwip的配置文件
sys_arch.h/c:实现lwip与操作系统(这里是uCOSII)的接口,如任务创建,信号量邮箱操作等
rtl8019.h/c:网卡驱动程序,SMARTARM2200使用的网卡芯片是RTL8019AS

cc.h和lwipopts.h的内容就不详细介绍了,具体内容可参考上面链接给的工程,都是些宏定义,配置定义等

4.操作系统模拟层的实现(sys_arch.c)
因为我们使用操作系统,因此在sys_arch.c中需要实现任务,信号量,邮箱操作的函数.LwIP提供了这样的接口以供不同的操作系统去实现.
(1)sys_arch.h
头文件定义了LwIP最大任务数(其实LwIP只创建了一个任务tcpip_thread),起始优先级,临界访问函数,信号量邮箱队列变量或结构的定义

代码
1 #define LWIP_TASK_MAX 2
2
3  #define LWIP_TASK_START_PRIO 8
4
5  #define SYS_ARCH_PROTECT(lev) OS_ENTER_CRITICAL()
6  #define SYS_ARCH_UNPROTECT(lev) OS_EXIT_CRITICAL()
7
8 typedef struct
9 {
10 OS_EVENT * pQ; //uCOSII中指向事件控制块的指针
11   void * pvQEntries[MAX_QUEUE_ENTRIES]; //MAX_QUEUE_ENTRIES消息队列中最多的队列数
12 } Q_DESCR, *Q_DESCRPt;
13
14
15 typedef OS_EVENT* sys_sem_t;
16 typedef Q_DESCRPt sys_mbox_t;
17 typedef INT8U sys_thread_t;

(2)sys_init()

sys_init
1 void sys_init(void)
2 {
3 //初始化了一个内存缓冲池,用于放MAX_QUEUES个ucosii的消息队列。
4 unsigned char err;
5 QueueMemPt = OSMemCreate((void *)QueueMemoryPool, MAX_QUEUES, sizeof(Q_DESCR), &err);
6 curr_prio_offset = 0;
7 }

(3)任务创建sys_thread_new
在我们的应用环境下,只创建了一个任务tcpip_thread,在tcpip.c中tcpip_init创建
sys_thread_new(tcpip_thread, NULL, TCPIP_THREAD_PRIO);

sys_thread_new
1 sys_thread_t sys_thread_new(void (* thread)(void *arg), void *arg, int prio)
2 {
3 u8_t CreatState,TaskPrio;
4 //计算实际的优先级数值
5 //这个值由起始优先级LWIP_TASK_START_PRIO,偏移量和参数传递的prio相加而得.
6 //这里传递过来的是TCPIP_THREAD_PRIO(定义在opt.h,值为1)
7 //其他任务定义的值也都为1(虽然这里并未创建其他任务),由于uCOSII不支持相同优先级
8 //因此使用一个全局变量curr_prio_offset来累加,每创建一个任务,实际的优先级就加1
9 TaskPrio = LWIP_TASK_START_PRIO+curr_prio_offset+prio;
10 //判断是否超过定义的最大任务数
11 if(curr_prio_offset < LWIP_TASK_MAX)
12 {
13 //创建uCOSII任务
14 CreatState = OSTaskCreateExt((void (*)(void *)) thread,
15 (void *) 0,
16 (OS_STK *) &LwipTaskStack[curr_prio_offset][LWIP_TASK_STK_SIZE - 1],
17 (INT8U ) TaskPrio,
18 (INT16U ) TaskPrio,
19 (OS_STK *)&LwipTaskStack[0],
20 (INT32U ) LWIP_TASK_STK_SIZE,
21 (void *) 0,
22 (INT16U )(OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR));
23
24 //创建失败
25 if(CreatState)
26 {
27 return 0;
28 }
29 //打印调试信息, 优先级偏移量加1
30 print_string("task prio = %d created...\r\n",TaskPrio);
31 curr_prio_offset++;
32
33 }
34 else
35 {
36 print_string(" lwip task prio out of range ! error! ");
37 }
38 //返回创建成功的任务优先级
39 return TaskPrio;
40 }

(4)邮箱操作sys_mbox_new(),sys_mbox_free(),sys_mbox_post(),sys_arch_mbox_fetch()
这里邮箱实际上用到的是uCOSII中的消息队列,邮箱只能存放一个消息,而消息队列可以存放多个消息

sys_mbox_new 
sys_mbox_t sys_mbox_new(void)
{
unsigned
char err;
Q_DESCRPt QDescPt;

QDescPt
= OSMemGet(QueueMemPt, &err); //在内存池中分配出一个队列结构的空间,并将此空间赋成这个结构。
if(err == OS_NO_ERR){
QDescPt
->pQ = OSQCreate(&(QDescPt->pvQEntries[0]), MAX_QUEUE_ENTRIES); //创建队列,但是队列只是lwip队列结构的一个成员。
if(QDescPt->pQ != NULL){
return QDescPt;
}
}
return SYS_MBOX_NULL;
}
sys_mbox_free 
void sys_mbox_free(sys_mbox_t mbox)
{
unsigned
char err;

OSQFlush( mbox
->pQ ); //清除队列事件
OSQDel( mbox->pQ, OS_DEL_NO_PEND, &err); //删除队列
err = OSMemPut( QueueMemPt, mbox); //把队列所占内存放入内存池
}
sys_mbox_post 
void sys_mbox_post(sys_mbox_t mbox, void *data)
//由于lwip会发空消息,而ucosii对空消息会当错处理,所以我们把lwip的空做成ucosii的一个特定值。
{
unsigned
char err;
if(!data)
data
= (void *)0xffffffff;
err
= OSQPost(mbox->pQ, data);
}
sys_arch_mbox_fetch 
unsigned long sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, unsigned long timeout)
{
unsigned
char err;
unsigned
short ucos_timeout = 0;

/*由于lwip中的timeout时间是ms,而ucosii的是timer tick,所以要转换,将timeout转换成ucos_timeout*/
//首先确保输入参数的合理性
if(timeout){
ucos_timeout
= (timeout * OS_TICKS_PER_SEC)/1000;
if(ucos_timeout < 1)
ucos_timeout
= 1;
}

if(!mbox)
return SYS_ARCH_TIMEOUT;
//因为OSQPend()有返回值,则看有没有可返回的地址,有则返回,无则不返回。
if(msg != NULL){
*msg = OSQPend(mbox->pQ, (unsigned short)ucos_timeout, &err);
}
else{
OSQPend(mbox
->pQ, (unsigned short)ucos_timeout, &err);
}

if(err == OS_TIMEOUT){
timeout
= SYS_ARCH_TIMEOUT;
}
else{
if(*msg == (void *)0xffffffff)
*msg = NULL;
timeout
= (ucos_timeout - err) * (1000/OS_TICKS_PER_SEC);
}
return timeout;
}

(5)信号量操作sys_sem_new(),sys_sem_free,sys_sem_signal(),sys_arch_sem_wait()
[1]创建信号量

sys_sem_t sys_sem_new(unsigned char count)
{
sys_sem_t SemPt;
SemPt
= OSSemCreate(count);
if(SemPt != NULL)
return SemPt;
return SYS_SEM_NULL;
}

[2]删除信号量

void sys_sem_free(sys_sem_t sem)
{
unsigned
char err;
OSSemDel((OS_EVENT
*)sem, OS_DEL_NO_PEND, &err );
}

[3]发送信号量

void sys_sem_signal(sys_sem_t sem)
{
OSSemPost((OS_EVENT
*)sem);
}

[4]等待信号量

sys_arch_sem_wait
unsigned long sys_arch_sem_wait(sys_sem_t sem, unsigned long timeout) //同队列
{
unsigned
char err;
unsigned
short ucos_timeout = 0;

/*由于lwip中的timeout时间是ms,而ucosii的是timer tick,所以要转换,将timeout转换成ucos_timeout*/
//首先确保输入参数的合理性
if(timeout){
ucos_timeout
= (timeout * OS_TICKS_PER_SEC)/1000;
if(ucos_timeout < 1)
ucos_timeout
= 1;
}

OSSemPend ((OS_EVENT
*)sem,(u16_t)ucos_timeout, (u8_t *)&err);

if(err == OS_TIMEOUT){
return 0;
}
else{
return 1;
}
}

[5]设置超时事件

sys_arch_timeouts
struct sys_timeouts *sys_arch_timeouts(void)
{
unsigned
char CurrPrio;
signed
short err, offset;
OS_TCB CurrTaskPcb;

NullTimeouts.next
= NULL;

err
= OSTaskQuery(OS_PRIO_SELF, &CurrTaskPcb);

CurrPrio
= CurrTaskPcb.OSTCBPrio;

offset
= CurrPrio - LWIP_TASK_START_PRIO;

if(offset<0 || offset >= LWIP_TASK_MAX){
return &NullTimeouts;
}
return &LwipTimeouts[offset];
}

至此,LwIP与uCOSII相关的部分就移植完成了,下一步就是实现网卡(RTL8019AS)驱动了.

发表于 @ 2009年09月01日

posted on 2010-03-22 13:30  shevsten  阅读(1039)  评论(0编辑  收藏  举报