Chinaunix首页 | 论坛 | 博客
  • 博客访问: 399480
  • 博文数量: 87
  • 博客积分: 1171
  • 博客等级: 少尉
  • 技术积分: 1068
  • 用 户 组: 普通用户
  • 注册时间: 2012-09-19 14:34
文章分类

全部博文(87)

文章存档

2014年(10)

2013年(24)

2012年(53)

我的朋友

分类: 嵌入式

2012-10-13 21:58:28

 
 
     UIP在各种单片机上移植很广泛,用在ARM9上有点大才小用。os用的raw,具体在上可以看到,api和ucos类似,但是丰富了很多,有兴趣的朋友可以去上面看下,是国人写的开源os,目前正常增加外围,有实力和热情的朋友可以加入进来,txj是raw作者,资深软件构架师,现在全职专注于raw。

 

      整个过程分按层次划分:应用层(基于uip的web程序),os层(raw),驱动层(Dm9000),笔记将按照这三个部分来陈述。

 

    编译工具是arm-linux-gcc  4.3.5版本的。

   目标板是TQ2440,主芯片是S3C2440。

 

     第一部分:应用层,web服务,原理是将网页先转换成二进制的形式放到程序的代码里面,然后主机在浏览器里面输入板子的IP地址,就能访问到里面存储的网页,整个过程是当你在浏览器输入板子IP地址时候,浏览器自动将你的要访问的IP地址生成http的get命令,然后通过网线发送到板子,板子解析后得知是请求某个网页,然后在自己的文件系统里面查找,如果找到,则将相应的文件以二进制形式发送给PC,浏览器自动解析这些二进制数据,然后显示出来。具体介绍如下;

     

     首先说下逻辑,在uip里面有个简单的文件系统结构,

struct httpd_fsdata_file {
  const struct httpd_fsdata_file *next;
  const char *name;
  const char *data;
  const int len;
#ifdef HTTPD_FS_STATISTICS
#if HTTPD_FS_STATISTICS == 1
  u16_t count;
#endif /* HTTPD_FS_STATISTICS */
#endif /* HTTPD_FS_STATISTICS */
};

 

uip将每个网页以这种结构存在起来,用链表的形式串在一起,访问某个文件的时候,通过查询文件名找到相应的数据。如果想用自己的网页,可以用工具将你设计好的网页用工具转换成二进制文件,amoV1.2.6.exe这个小工具挺方便的。举例,错误代码404表示你访问的网页没有找到,它本身就是一个网页,前面是文件名字段,以字符0结尾,后面才是网页真正的二进制数据。只需将自己转换的二进制文件放到文件名后面就行了。

  • static const unsigned char data_404_html[] = {
     /* /404.html */
     0x2f, 0x34, 0x30, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0,
     0x3c, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0xa, 0x20, 0x20, 0x3c, 
     0x62, 0x6f, 0x64, 0x79, 0x20, 0x62, 0x67, 0x63, 0x6f, 0x6c, 
     0x6f, 0x72, 0x3d, 0x22, 0x77, 0x68, 0x69, 0x74, 0x65, 0x22, 
     0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x63, 0x65, 0x6e, 
     0x74, 0x65, 0x72, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 
     0x20, 0x3c, 0x68, 0x31, 0x3e, 0x34, 0x30, 0x34, 0x20, 0x2d, 
     0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 
     0x66, 0x6f, 0x75, 0x6e, 0x64, 0x3c, 0x2f, 0x68, 0x31, 0x3e, 
     0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x33, 
     0x3e, 0x47, 0x6f, 0x20, 0x3c, 0x61, 0x20, 0x68, 0x72, 0x65, 
     0x66, 0x3d, 0x22, 0x2f, 0x22, 0x3e, 0x68, 0x65, 0x72, 0x65, 
     0x3c, 0x2f, 0x61, 0x3e, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x65, 
     0x61, 0x64, 0x2e, 0x3c, 0x2f, 0x68, 0x33, 0x3e, 0xa, 0x20, 
     0x20, 0x20, 0x20, 0x3c, 0x2f, 0x63, 0x65, 0x6e, 0x74, 0x65, 
     0x72, 0x3e, 0xa, 0x20, 0x20, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 
     0x79, 0x3e, 0xa, 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 
    0};

 

那网页的数据主要集中在Httpd-fsdata.h和Http-fsdata.inc两个文件里面,链表也是在http-fsdata.inc,静态链接在一起的:

 const struct httpd_fsdata_file file_processes_shtml[] = {{(void*)0, data_processes_shtml, data_processes_shtml + 17, sizeof(data_processes_shtml) - 17}};

const struct httpd_fsdata_file file_404_html[] = {{file_processes_shtml, data_404_html, data_404_html + 10, sizeof(data_404_html) - 10}};

 

data_processes_shtml + 17 后面加个数字,数字的意思是文件名的长度+1,将你的网页以这种形式静态的初始化就行了。至于具体的访问过程,uip中的web 例程已经给你做好了,包括应答,重传等等。详细过程可以自己看代码。


     第二部分:操作系统层,操作系统用的raw,和UIP相关的部分:

1:对底层接收和要发送数据的API进行了封装,管理接收的数据,

 

 

static void Eint7_ISR(void)
{


 raw_enter_interrupt();


 uint32 len,i;

 void *Buffer;
 NET_MSG *msg;
  
 

 ClearPending(BIT_EINT4_7);

 rEINTPEND |= 1<<7;
 


 

 raw_block_allocate(&MEM,&Buffer,0);
 
 msg=Buffer;
 msg->buffer=msg+sizeof(NET_MSG);
 msg->length =receivepacket(msg->buffer);
 
 if(msg->length==0)
 raw_block_release(&MEM,Buffer);
 else
 {
 /*Printf("in ISR  test  msg_data \n");
 for(i=0;ilength;i++)
 Printf("%x ",msg->buffer[i]);*/
 raw_queue_end_post(&driver_queue, msg, RAW_NO_WAIT); 
 
 }
 
 raw_finish_int();

}

这是DM9000 接收数据后,会产生中断,这里中断接入到ARM9的7号中断,这是中断服务函数,产生中断后判断是否数据,决定是否分配。在测试的时候发现有时候没有接收到数据,但是却发生中断,这里还有些问题。

RAW_U16 network_device_read(RAW_U32 timeout)
{


#if 1
 int i;
 void *msg_app1;
 NET_MSG *msg;
 RAW_U16 length;
 RAW_U16 ret;
 
 ret = raw_queue_receive (&driver_queue, timeout, &msg_app1);

 if (ret !=  RAW_SUCCESS) {
  //raw_printk("don't get socket \n");
  
  return 0;
 }
  
 
 msg = msg_app1;
 memcpy(uip_buf, msg->buffer, msg->length);
 length = msg->length;

 /*raw_printk("\nin network  read receive msg-length is %d \n",msg->length);
 for(i=0;ilength;i++)
 raw_printk("%x ",uip_buf[i]);
 raw_printk("\n");*/
 

 raw_block_release(&MEM,msg_app1);
 return length;
#endif
}

void net_send_packet()
{

 DM9000_sendPcket(uip_buf,uip_len);
}

虽然uip只是用一个uip_buf[]数组来接收和保存数据,但是如过不加入缓冲区的话,会存在严重的丢包情况,所以这里先将接收的所有报文用循环缓冲区接收下来,然后uip每次在缓冲区里面取数据到uip_buf,虽然效率降低了,但是效过变好了。因为查询是否有报文到达是有时间间隔的,如果这个时间来了多个报文,就会产生覆盖,所以,用缓冲区是很有必要的。这里用到了

 

 raw_queue_create(&driver_queue, "driver_queue", (RAW_VOID **)&queue1_storge, 100);
 raw_block_pool_create(&MEM,"Mem_pool",2000,(void*)memory,200000);

 

申请了个用这里申请了100 个指针队列,和100大小为2000 的内存块,毕竟是arm9,内存还是很大的,申请1M的空间还是能够满足的,raw暂时(2012 8 28)还没将slab内存分配版本发布出来,正在测试阶段。到时候更新了可以讲raw核更新一下。

 

2:第二就是用到了raw的内核tick,为uip提供了定时器,没有用直接用板子上定时器。在TCP_thread.c 中 对clock_time_t clock_time()已经了修改。

 在主循环中 uip_len = network_device_read(20); 这里的20,也是只tick数。uip这个函数里面是没有数字的,大循环是个死循环,但是在os里面必须加延时函数这也是造成设置缓冲区的重要原因,大循环在raw只是一个任务而已,不能一直霸占CPU。所以这也需要修改下。

 

3:重新定义了clock.h中的#define CLOCK_SECOND   RAW_TICKS_PER_SECOND。

 

4:在Net_msg.h中定义了消息结构NET_MSG,因为uip中uip_len需要知道本次消息的长度是多少。在收到数据入队列的时候,需要将数据封装成NET_MSG格式,然后在读取数据的时候,从里面提取出来,数据和长度。

typedef struct NET_MSG {

 RAW_U8  *buffer; 
 RAW_U16 length;
 
}NET_MSG;

 

 

第三部分:驱动层,arm9用的DM9000网卡,在嵌入式设备中很常见,这里驱动不是我写的,而是在网上下载的,芯片手册我看了,结果发现全是讲寄存器的,结果很郁闷,后面发现原来发行的还有一本应用手册,一时偷懒,就用别人的了。需要修改的地方就是在上面提到过的中断服务函数,里面对接收数据进行了包装,再加上头文件就行了。

 

 

 

     特别说明:

     1,头文件的问题,在linux做的移植工作,自己makefile不太熟悉,导致头文件那里出了些问题,在TCP_Thread.c中包含了另一个文件夹下的uip_opt.h,而uip_opt.h又包含了TCP_Thread.h中的uip_conf.h头文件,这里报错找不到uip_conf.h,这是正常的,只需要将uip_opt.h中的uip_conf.h改为相对路径就行了。

 

      但是疑惑的是,既然是预编译的就应该报错,怎么生成了很多目标文件了才报错。这里是犯了个低级错误,所谓的预编译并不是对整个工程的每个地方都同意经行预编译,而是编译到某个c文件的时候,针对它经行预编译。所以 当时报错是正常的。

 

    2,在uip_conf.h中有个#define UIP_CONF_BYTE_ORDER      LITTLE_ENDIAN  因为我在头文件里包含了#include,当时不知道这个头文件里竟然有#define  LITTLE_ENDIAN  1234 导致我的HTONS这个宏出错,用了很久才找到这个问题,这个太巧了,没想到被我碰上了,这里将这个宏定义改下就行了,#define UIP_CONF_BYTE_ORDER      LITTLE_ENDIAN_ABC 随便就行了。

  

   3,在移植的时候有个很重要的问题就是arm的字对齐要求,这个问题比较隐蔽,大部分新手是不会主要到这个问题的,我也是反复测试程序最后才发现的。在uip_eth_hdr

结构体的时候用了  #program pack(1)  经行了处理,本以为会很麻烦很多地方要改,没想到就改这个地方就通过了,O(∩_∩)O~

 

#pragma pack(1)
struct uip_eth_hdr {
  struct uip_eth_addr dest;
  struct uip_eth_addr src;
  u16_t type;
};
#pragma pack()

  

 #pragma pack(1)
struct uip_eth_addr {
  u8_t addr[6];
};
#pragma pack()

  

 

 

 

 

      有朋友需要工程的,可以发邮件到      (常用,毕竟QQ经常登录,可以很快回复),也可以在  直接下载。

 

     大家在看源码的时候,如果发现有什么不对的地方或者有心得建议,欢迎指导 交流。

阅读(2306) | 评论(0) | 转发(0) |
0

上一篇:编程 小得

下一篇:uip 移植 (一)

给主人留下些什么吧!~~