搜索
bottom↓
回复: 180

LWIP移植成功了

  [复制链接]

出0入0汤圆

发表于 2013-7-18 22:39:08 | 显示全部楼层 |阅读模式
成功移植lwip1.3.1 1.3.2 1.4.1,裸机能跑tcp客户端和服务器。
开心之余,又有些问题需要请教了,lwip里面如何处理断线重连的问题?

下面是我实验的现象:
我在裸机上使用lwip,tcp做了服务器和客户端,在连接后把网线拔掉,tcp_poll函数还是会被执行的,这时候把网线插上(还没出现abort错误),需要重新连接(端口没变,原连接已经没反应了),连接后上一次连接的pcb仍然存在,这时候会发现轮询时候有两个pcb连接在运行。等了好几分钟后(关闭了保活设置)第一个pcb连接出现连接错误,自动断开。。。
这两个pcb占用同一个端口不会产生冲突吗?

阿莫论坛20周年了!感谢大家的支持与爱护!!

该献的血还是要献的。你不献他不献。难道让我去献? --- 出自坛友:lovejp1981

出0入0汤圆

 楼主| 发表于 2013-7-18 22:44:31 | 显示全部楼层
即使以前的pcb连接没删除,但是新的pcb连接还是成功了。。

出0入0汤圆

发表于 2013-7-23 10:18:31 | 显示全部楼层
朋友,可不可以分享移植的步骤,我也在学习以太网,但最近也学了不少资料(比如老衲五木的)和常用的协议封包格式,但不知对LWIP源文件做什么样的修改,或是提供ENC28J60底层驱动,现在一头雾水。
能详细讲一下移植的步骤吗?

出0入0汤圆

 楼主| 发表于 2013-7-23 17:33:55 | 显示全部楼层
孙为 发表于 2013-7-23 10:18
朋友,可不可以分享移植的步骤,我也在学习以太网,但最近也学了不少资料(比如老衲五木的)和常用的协议封 ...

准备上传移植笔记的,但最近有点忙,资料还需要整理才能上传。

出0入0汤圆

发表于 2013-7-24 10:29:12 | 显示全部楼层
期待中。。。。。。。。。。。。。。

出0入0汤圆

发表于 2013-8-4 15:33:13 | 显示全部楼层

出0入0汤圆

发表于 2013-8-4 15:47:05 | 显示全部楼层
加一个中断处理,在拔出网线的时候会进入中断,然后释放掉之前的资源

出0入0汤圆

发表于 2013-8-4 16:13:50 | 显示全部楼层
mark                       

出0入0汤圆

发表于 2013-8-4 20:12:12 | 显示全部楼层
请问楼主有没有移植ucos+lwip啊?

出0入0汤圆

发表于 2013-8-5 11:06:46 | 显示全部楼层
野火的板子也有lwip的程序呀,教程也有

出0入0汤圆

发表于 2013-8-15 17:43:20 来自手机 | 显示全部楼层
mark…
顶一个…

出0入0汤圆

 楼主| 发表于 2013-8-15 21:47:14 | 显示全部楼层
快做完了,明天开始上传笔记。

出0入0汤圆

发表于 2013-8-15 23:38:25 来自手机 | 显示全部楼层
楼主加紧上传,等你的好资料,先谢谢了

出0入0汤圆

发表于 2013-8-15 23:46:45 来自手机 | 显示全部楼层
不错      

出0入0汤圆

 楼主| 发表于 2013-8-16 22:43:23 | 显示全部楼层
本帖最后由 xhyzjiji 于 2013-8-17 00:22 编辑

终于回到宿舍了,开始写使用笔记好了。
在写之前,想说明自己比较菜,对tcpip也有很多不懂的地方,还让大虾们高抬贵手。在写笔记前,先附上一些资料和lwip1.4.1的源码。
网速实在太慢了,发不上来,明天到实验室再补全。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-8-16 22:56:21 | 显示全部楼层
这个要顶

出200入0汤圆

发表于 2013-8-16 23:13:48 来自手机 | 显示全部楼层
手机上网,收藏

出0入0汤圆

 楼主| 发表于 2013-8-16 23:15:43 | 显示全部楼层
本帖最后由 xhyzjiji 于 2013-8-17 00:24 编辑

宿舍网速太慢了,上传不上去,暂时先传个源码吧

开始正题吧。如果拿到一个可移植的代码,首先关心的就是它给我们留下了什么接口,如何关联我们的底层驱动,如何对它进行裁剪扩充功能等等。
对于比较新手的我们,暂时可以不管lwip的内核是如何操作,只关心我们需要用到的地方。
先看源码的文件目录,可以发现api,core,include和netif四个文件夹。

./api:里面的文件基本是操作系统的应用层接口,鉴于我是裸机下使用和对操作系统一窍不通的尴尬情况,这里就略过好了。
./core:存储了lwip协议栈的重要内容,包括内存的管理,常用协议的内核以及我们需要用到的一些用户接口都在里面了。
./include:对应于.c的相关头文件。
./netif:顾名思义,是net interface,网络接口,这里有我们需要修改的,关联我们的底层驱动。
注:源码内带有sys_XXX字样的文件或者函数大多跟操作系统相关,对操作系统无力的我就直接忽略好了。

lwip不关心我们所使用的平台,所以事先要让lwip知道我们用到的平台信息。如我们用到的编译器支持的字节、半字以及字的定义,MCU数据大小端。
include/arch.h
由arch.h所需的头文件,可以知道存在头文件arch/cc.h。
在cc.h中可以看出,数据长度的关联信息,如图2所示。
这里则需要根据不同的编译器进行适当调整。由cc.h又可以知道有个cpu.h头文件,转入该文件中,发现需要设置大小端BYTE_ORDER,根据实际需要设置就好(图3)。
数据长度格式告知lwip内核之后,可以开始关联底层驱动。
*****************************************************************
lwip只是提供了一个协议栈要做的包的过滤,解包,打包,对于包的内容分析和包的接收与发送是需要我们自己来实现。
之前提到的./netif文件夹,里面有我们关心的文件ethernetif.c,打开它,大概浏览一下,就会发现如果不修改这个文件,是不可能编译过去的,因为,作者各种省略号。。。想编译通过都难。
不过,作者也用心良苦,与底层相关的函数直接用low_level_XXX命名,非常清晰。
static void low_level_init(struct netif *netif)
static err_t low_level_output(struct netif *netif, struct pbuf *p)
static struct pbuf * low_level_input(struct netif *netif)

这三个函数则需要手动修改和添加一些自己的硬件信息和关联驱动。

首先是low_level_init函数,ETHARP_HWADDR_LEN是我们的mac地址长度,6个字节。
紧随其后的是mac地址,
netif->hwaddr[0] = ;
  ...
  netif->hwaddr[5] =;将其补全即可。
题外话:是我们分配给网络芯片的物理地址,如果是局域网,只要不与其他设备地址冲突,是随意设置,但为了测试的可靠性,建议先在cmd窗口使用命令ipconfig /all记下自己电脑的mac地址,我们只对最后8位进行修改即可。如果连接外网,则需要申请一个唯一的mac地址。(不过没申请连外网好像也没发生冲突)。
netif->flags则可以自定义网络芯片的功能,具体可以阅读netif.h头文件,有详细注释,当然,有些功能需要硬件上的支持才可以用,这里相当于一个软件功能开关而已。
说到这里,这个netif变量是什么
这是个struct netif型全局变量,用于记录网络芯片的硬件信息,存储硬件信息也是一个链表,所以lwip支持多网络芯片同时工作,设置默认网络接口,修改网络芯片排序等功能,后面再一一介绍。

唉,写得真慢,要睡了,明天还要出差,明晚再见。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-8-17 00:00:57 | 显示全部楼层
检测网卡的状态,调用
tcpip_callback_with_block  函数。
网线插上:
tcpip_callback_with_block((tcpip_callback_fn) netif_set_link_up, &appNetif, 0);
网线断开:
tcpip_callback_with_block((tcpip_callback_fn) netif_set_link_down, &appNetif, 0);

参数参考相关说明。

出0入0汤圆

 楼主| 发表于 2013-8-17 00:20:01 | 显示全部楼层
flyleaf 发表于 2013-8-5 11:06
野火的板子也有lwip的程序呀,教程也有

野火是有,奋斗也有,但是对于我们新手还是有些太过于简陋了,这个笔记只是想记录移植需要注意的地方和一些使用心得,希望可以方便后面需要使用的同学快速上手。

出0入0汤圆

 楼主| 发表于 2013-8-17 00:21:31 | 显示全部楼层
kanprin 发表于 2013-8-17 00:00
检测网卡的状态,调用
tcpip_callback_with_block  函数。
网线插上:

对操作系统无力

出0入0汤圆

发表于 2013-8-17 08:38:32 来自手机 | 显示全部楼层
加油 ,看完希望自己可以搞定了,谢谢

出0入0汤圆

 楼主| 发表于 2013-8-17 23:01:16 | 显示全部楼层
本帖最后由 xhyzjiji 于 2013-8-17 23:25 编辑

继续写笔记咯。。。

接下来是low_level_input(还是把代码贴上来吧)红色是源码空缺部分,绿色为修改后的代码,后面也是如此
static struct pbuf *
low_level_input(struct netif *netif)
{
  struct ethernetif *ethernetif = netif->state;
  struct pbuf *p, *q;
  u16_t len;
  uint8_t* buffer;

  /* Obtain the size of the packet and put it into the "len"
     variable. */
  len = ;接收到的数据包总长度(包括以太网帧头和协议报头)
  len = enc28j60PacketReceive(1500, eth_rx_buf); //这里使用的是网络接口的接收驱动enc28j60PacketReceive,在接收一个数据包的同时将其存储于缓存eth_rx_buf中
  buffer = eth_rx_buf;


#if ETH_PAD_SIZE
  len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
#endif

  /* We allocate a pbuf chain of pbufs from the pool. */
  p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);  //为lwip协议栈的接收缓存开辟内存
  
  if (p != NULL) {

#if ETH_PAD_SIZE
    pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif

    /* We iterate over the pbuf chain until we have read the entire
     * packet into the pbuf. */
    for(q = p; q != NULL; q = q->next) {
      /* Read enough bytes to fill this pbuf in the chain. The
       * available data in the pbuf is given by the q->len
       * variable.
       * This does not necessarily have to be a memcpy, you can also preallocate
       * pbufs for a DMA-enabled MAC and after receiving truncate it to the
       * actually received size. In this case, ensure the tot_len member of the
       * pbuf is the sum of the chained pbuf len members.
       */
      read data into(q->payload, q->len); //请仔细阅读英文注释,这个语句提醒我们,将读到接收缓存的数据转移到lwip内核中去,由于lwip对于内存处理比较强大,考虑到零碎内存的存在,在分配内存时,对于len长度的数据,内存并非是连续的,而是分散的,如果是分散的,那将也是一个链表形式,因此这里出现了for循环体,注释也提到memcpy函数,则希望我们将数据复制到每个零碎的内存空间里。
             memcpy((uint8_t*)q->payload, (uint8_t*)&buffer, q->len);  //赋值接收缓冲区的数据到lwip内存池中
     i += q->len; //记录复制数据的位置

    }
    acknowledge that packet has been read(); //这里可以填上包接收完毕的一些消息或者指令,如果没有需要,可以注释掉该语句

#if ETH_PAD_SIZE
    pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif

    LINK_STATS_INC(link.recv);
  } else {
    drop packet();
    LINK_STATS_INC(link.memerr);
    LINK_STATS_INC(link.drop);
  }
  //记录接收数据数或者对链路状态异常情况的处理,如有需要请自行添加,否则注释掉即可

  return p;  
}

忘了个很重要的图没上传。。。
我们可以利用这个图顺藤摸瓜来了解lwip的工作流程。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-8-17 23:06:12 | 显示全部楼层
本帖最后由 xhyzjiji 于 2013-8-17 23:29 编辑

static err_t
low_level_output(struct netif *netif, struct pbuf *p)  //
{
  struct ethernetif *ethernetif = netif->state;
  struct pbuf *q;

  uint16_t offset = 0;

  initiate transfer(); //初始化传输设备,无需要则注释掉
  
#if ETH_PAD_SIZE
  pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif

  for(q = p; q != NULL; q = q->next) {
    /* Send the data from the pbuf to the interface, one pbuf at a
       time. The size of the data in each pbuf is kept in the ->len
       variable. */
    send data from(q->payload, q->len); //对比input,output也是如此,不过是个反过程,将lwip发送队列的内存池数据塞进我们网卡的发送缓存区中,准备发送
    memcpy((uint8_t*)&eth_tx_buf[offset], (uint8_t*)q->payload, q->len);
    offset += q->len;  //记录复制数据的位置

  }

  signal that packet should be sent();  //提示我们需要发送数据了,注释掉
  enc28j60PacketSend(offset, eth_tx_buf);  //调用底层驱动发送数据咯

#if ETH_PAD_SIZE
  pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif
  
  LINK_STATS_INC(link.xmit); //记录发送数据个数,不记录就注释可以了

  return ERR_OK;
}

至此,底层驱动已经于lwip关联起来,lwip可以自由调用底层驱动了。这告诉我们,底层驱动只需要两个函数,一个是接收数据包存储起来,并且可以返回数据包大小的函数;一个是发送指定缓冲区的数据,对于其他的网络芯片也是如此。

对于lwip的内存池和一些重要的数据结构,可能在后面的应用提及会比较好吧?

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-8-23 10:35:14 | 显示全部楼层
多谢楼主  最近研究下这方面的                                                                     

出0入0汤圆

 楼主| 发表于 2013-8-23 22:44:45 | 显示全部楼层
Name_006 发表于 2013-8-23 10:35
多谢楼主  最近研究下这方面的                                                                      ...

这几天都比较忙,后面再把应用层的使用笔记补上,不好意思了。

出0入85汤圆

发表于 2013-8-23 23:50:35 来自手机 | 显示全部楼层
嘿嘿。。。。。。

出0入0汤圆

 楼主| 发表于 2013-8-24 22:24:19 | 显示全部楼层
好吧,继续上个星期的。
lwip提供了两种内存分配管理机制,一种是c语言自带的内存机制,另外两种就是使用lwip自带的内存池以及内存堆。摘自网络
下面是opt.h源文件相关设置内容:
/**
* MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library
* instead of the lwip internal allocator. Can save code size if you
* already use it.
*/
#ifndef MEM_LIBC_MALLOC  
#define MEM_LIBC_MALLOC                 0
#endif

/**
* MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator.
* Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution
* speed and usage from interrupts!
*/
#ifndef MEMP_MEM_MALLOC
#define MEMP_MEM_MALLOC                 0
#endif
//以上定义置1:使用c库自带的内存管理,否则使用lwip重写的内存管理代码

/**
* MEM_USE_POOLS==1: Use an alternative to malloc() by allocating from a set
* of memory pools of various sizes. When mem_malloc is called, an element of
* the smallest pool that can provide the length needed is returned.
* To use this, MEMP_USE_CUSTOM_POOLS also has to be enabled.
*/
#ifndef MEM_USE_POOLS
#define MEM_USE_POOLS                   0
#endif

/**
* MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h
* that defines additional pools beyond the "standard" ones required
* by lwIP. If you set this to 1, you must have lwippools.h in your
* inlude path somewhere.
*/
#ifndef MEMP_USE_CUSTOM_POOLS
#define MEMP_USE_CUSTOM_POOLS           0
#endif

/**
* MEM_ALIGNMENT: should be set to the alignment of the CPU
*    4 byte alignment -> #define MEM_ALIGNMENT 4
*    2 byte alignment -> #define MEM_ALIGNMENT 2
*/
#ifndef MEM_ALIGNMENT //设置cpu内存对齐格式
#define MEM_ALIGNMENT                   1
#endif

struct mem { //动态内存结构体
  /** index (-> ram[next]) of the next struct */
  mem_size_t next;
  /** index (-> ram[prev]) of the previous struct */
  mem_size_t prev;
  /** 1: this area is used; 0: this area is unused */
  u8_t used;
};//由上述mem结构体可知,其为双向链表,且用used表示该内存区域是否被使用,便于内存回收
struct memp { //内存池结构体
  struct memp *next;
#if MEMP_OVERFLOW_CHECK  //支持内存溢出检测
  const char *file;
  int line;
#endif /* MEMP_OVERFLOW_CHECK */
};
动态内存堆分配策略原理就是在一个事先定义好大小的内存块中进行管理,其内存分配的策略是采用最快合适(First  Fit)方式,只要找到一个比所请求的内存大的空闲块,就从中切割出合适的块,并把剩余的部分返回到动态内存堆中。分配的内存块有个最小大小的限制,要求请求的分配大小不能小于MIN_SIZE,否则请求会被分配到MIN_SIZE大小的内存空间。一般MIN_SIZE为12字节,在这12个字节中前几个字节会存放内存分配器管理用的私有数据,该数据区不能被用户程序修改,否则导致致命问题。内存释放的过程是相反的过程,但分配器会查看该节点前后相邻的内存块是否空闲,如果空闲则合并成一个大的内存空闲块。采用这种分配策略,其优点就是内存浪费小,比较简单,适合用于小内存的管理,其缺点就是如果频繁的动态分配和释放,可能会造成严重的内存碎片,如果在碎片情况严重的话,可能会导致内存分配不成功。对于动态内存的使用,比较推荐的方法就是分配->释放->分配->释放,这种使用方法能够减少内存碎片。摘自老衲五木,同时也推荐大家去看看他对源码的解释,非常全面

最后更新一下资料库。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-8-24 22:53:22 | 显示全部楼层
如果大家已经看过上传资料中的老衲五木的源码解析,可以了解到一些主要结构体的结构,这里先不在重复说明了(用到再提出)。
如何让lwip动起来?
之前提到,可以使用lwip自带的内存管理机制,那么在使用前,就必须先对相关内存进行初始化,
void LwIP_Init(void)
{
        struct ip_addr ipaddr;
        struct ip_addr netmask;
        struct ip_addr gw;
        //uint8_t macaddress[6]={0,0,0,0,0,1};
        //uint8_t macaddress[6]={0x3c,0x97,0x0e,0x34,0xeb,0x98};                //设置本地mac地址
       
        /* Initializes the dynamic memory heap defined by MEM_SIZE.*/
        mem_init();  //动态内存/内存堆的初始化函数,主要是告知动态内存/内存堆的起止地址,以及初始化空闲列表。摘自老衲五木的源码分析
       
        /* Initializes the memory pools defined by MEMP_NUM_x.*/
        memp_init();  //初始化内存池的函数,
               
        #if LWIP_DHCP  //如果开启DHCP,可以使用自动获取IP功能
                ipaddr.addr = 0;
                netmask.addr = 0;
                gw.addr = 0;
       
        #else  //设置本地IP地址,掩码,网关,在不适用自动获取IP的时候,需要手动赋予网络接口ip,掩码,网关。
        #if another
                IP4_ADDR(&ipaddr, 192, 168, 1, 111);  //IP4_ADDR函数:将ip地址组合成一个32位的数据,并记录在所指向的内存单元中
        #else
                IP4_ADDR(&ipaddr, 192, 168, 1, 112);
        #endif
                IP4_ADDR(&netmask, 255, 255, 255, 0);
                IP4_ADDR(&gw, 192, 168, 1, 1);
        #endif
       
        //Set_MAC_Address(macaddress);         //保存至全局变量
       
        /* - netif_add(struct netif *netif, struct ip_addr *ipaddr,
                struct ip_addr *netmask, struct ip_addr *gw,
                void *state, err_t (* init)(struct netif *netif),
                err_t (* input)(struct pbuf *p, struct netif *netif))
       
        Adds your network interface to the netif_list. Allocate a struct
        netif and pass a pointer to this structure as the first argument.
        Give pointers to cleared ip_addr structures when using DHCP,
        or fill them with sane numbers otherwise. The state pointer may be NULL.
       
        The init function pointer must point to a initialization function for
        your ethernet netif interface. The following code illustrates it's use.*/
        netif_add(&enc28j60, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &ethernet_input);  //对网络接口添加ip,掩码,网关信息,指定网络接口初始化和输入函数,这里的enc28j60是一个struct netif型的全局变量
       
        /*  Registers the default network interface.*/
        netif_set_default(&enc28j60);        //把enc28j60设置为默认网卡
               
        #if LWIP_DHCP
        /*  Creates a new DHCP client for this interface on the first call.
        Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at
        the predefined regular intervals after starting the client.
        You can peek in the netif->dhcp struct for the actual DHCP status.*/
                dhcp_start(&enc28j60);  //如果开启ip自动获取,则会产生一个udp的DHCP客户端,广播询问路由器,路由器给出回应并返回一个ip,客户端则继续广播,询问此ip是否被占用,若否则获取成功,并修改网络接口中的dhcp状态,具体请看后面的应用
        #endif
       
        /*  When the netif is fully configured this function must be called.*/
        netif_set_up(&enc28j60);  //使能enc28j60接口

        //printf("LwIP Init Succedd\n");
}

出0入0汤圆

 楼主| 发表于 2013-8-24 23:28:22 | 显示全部楼层
创建一个tcp服务器
1.初始化你的服务器
void tcp_server_init(void)
{
        struct tcp_pcb *pcb;

        //printf("tcp server init\n");
       
        pcb = tcp_new();  //分配一个struct tcp_pcb大小的内存,如果分配不成功,则尝试清除tcp记录列表中处于等待状态的tcp连接,再重新分配内存。分配成功后,对此tcp记录一些信息初始化(如设置优先级,默认为普通优先级)

        //print_server_pcb_state(pcb);  //显示返回tcp的状态,用于调试用的
          /*
               void print_server_pcb_state(struct tcp_pcb *pcb){
        printf("server state:%s\n", tcp_debug_state_str(pcb->state));
               }
               */
        tcp_bind(pcb, IP_ADDR_ANY, 1200);  //设置服务器端口号为1200,注意这里最好不要设置一些常被占用的端口号,如8080之类的
        //tcp_bind(pcb, &enc28j60.ip_addr, 2200);
        pcb = tcp_listen(pcb);  //将此tcp连接移至侦听tcp队列中去
        print_server_pcb_state(pcb);  //再次打印tcp连接状态
                                           
               tcp_accept(pcb, tcp_server_accept);         //初始化pcb->accept,当服务器接收到客户端链接申请,则自动调用服务器连接tcp中的pcb->accept指向的函数
}

出0入0汤圆

发表于 2013-8-25 01:01:35 | 显示全部楼层
移植lwip不错,,

出0入0汤圆

发表于 2013-8-25 01:08:21 | 显示全部楼层

出0入0汤圆

发表于 2013-8-25 02:08:36 | 显示全部楼层
终有一日会用得上,先留个脚印

出0入0汤圆

发表于 2013-8-26 17:58:18 | 显示全部楼层
先下班了,明天继续拜读。。。

出0入0汤圆

发表于 2013-8-27 14:14:33 | 显示全部楼层
xhyzjiji 发表于 2013-8-24 23:28
创建一个tcp服务器
1.初始化你的服务器
void tcp_server_init(void)

我用raw api编写程序,同样的测试例子用lwip1.3.2就OK,用lwip1.4.1就不行,ping可以ping通,网页打开不正常。你有碰到这个问题吗?

出0入0汤圆

发表于 2013-8-27 14:17:23 | 显示全部楼层
xhyzjiji 发表于 2013-8-16 22:43
终于回到宿舍了,开始写使用笔记好了。
在写之前,想说明自己比较菜,对tcpip也有很多不懂的地方,还让大虾 ...

arch     貌似是带OS的啊

出0入0汤圆

发表于 2013-8-27 14:26:18 | 显示全部楼层
马克一下 LWIP

出40入42汤圆

发表于 2013-8-28 19:41:53 | 显示全部楼层
请问楼主,裸系统移植LwIP-1.3.2后,要升级过度到1.4.1版本,有哪些要注意的地方啊?
用TI的官方例程里的1.3.2的模板移植1.4.1的源码,错误警告一大串啊。
麻烦楼主讲讲呗。

出0入0汤圆

发表于 2013-8-28 20:54:38 | 显示全部楼层
学习一下。

出0入0汤圆

 楼主| 发表于 2013-9-1 20:55:21 | 显示全部楼层
robin45853258 发表于 2013-8-27 14:14
我用raw api编写程序,同样的测试例子用lwip1.3.2就OK,用lwip1.4.1就不行,ping可以ping通,网页打开不 ...

lwip修改了内部发送数据的函数,有朋友用的时候也有问题,但上os后是可以的,我帮你问问五木。

出0入0汤圆

 楼主| 发表于 2013-9-1 21:22:25 | 显示全部楼层
ljt80158015 发表于 2013-8-27 14:17
arch     貌似是带OS的啊

不带系统也需要指出使用的处理器的数据格式的,在\lwip-1.4.1\lwip-1.4.1\src\include\lwip中arch头文件中可以看到。
lwip是这样做的(def.c)
#if (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN)

/**
* Convert an u16_t from host- to network byte order.
*
* @param n u16_t in host byte order
* @return n in network byte order
*/
u16_t
lwip_htons(u16_t n)
{
  return ((n & 0xff) << 8) | ((n & 0xff00) >> 8);
}

/**
* Convert an u16_t from network- to host byte order.
*
* @param n u16_t in network byte order
* @return n in host byte order
*/
u16_t
lwip_ntohs(u16_t n)
{
  return lwip_htons(n);
}

/**
* Convert an u32_t from host- to network byte order.
*
* @param n u32_t in host byte order
* @return n in network byte order
*/
u32_t
lwip_htonl(u32_t n)
{
  return ((n & 0xff) << 24) |
    ((n & 0xff00) << 8) |
    ((n & 0xff0000UL) >> 8) |
    ((n & 0xff000000UL) >> 24);
}

/**
* Convert an u32_t from network- to host byte order.
*
* @param n u32_t in network byte order
* @return n in host byte order
*/
u32_t
lwip_ntohl(u32_t n)
{
  return lwip_htonl(n);
}

#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */

出0入0汤圆

 楼主| 发表于 2013-9-1 21:30:02 | 显示全部楼层
落叶知秋 发表于 2013-8-28 19:41
请问楼主,裸系统移植LwIP-1.3.2后,要升级过度到1.4.1版本,有哪些要注意的地方啊?
用TI的官方例程里的1. ...

1.4.1将tcp_write等一些函数进行了改动,但process基本没变,但有一点需要注意,是需要手动写一个sys_now函数的函数体,尽管没有用操作系统,也需要写,这个与超时,连接计时有关。
u32_t sys_now(void){
        return system_time;
}
在硬件定时器中对其进行计时操作即可。其他跟1.3.2配置没有太大改变。

出0入0汤圆

 楼主| 发表于 2013-9-1 21:31:08 | 显示全部楼层
落叶知秋 发表于 2013-8-28 19:41
请问楼主,裸系统移植LwIP-1.3.2后,要升级过度到1.4.1版本,有哪些要注意的地方啊?
用TI的官方例程里的1. ...

也可以把错误警告贴上图,大家研究一下

出0入0汤圆

发表于 2013-9-1 21:44:28 | 显示全部楼层
正在学lwip,买了本“老衲五木”的,不过没板子,看来还要参考lz的移植心得啊

出0入0汤圆

发表于 2013-9-1 21:53:16 | 显示全部楼层
xhyzjiji 发表于 2013-9-1 21:31
也可以把错误警告贴上图,大家研究一下

兄弟,你不是说你的笔记的吗?传上来了吗?
难道是你在28楼发的那个?28楼我记得好象你说过是老衲五木的吧。

出0入4汤圆

发表于 2013-9-2 09:13:11 | 显示全部楼层
mark一下,后面慢慢看

出0入0汤圆

发表于 2013-9-2 11:26:24 | 显示全部楼层
本人刚开始走51+uIP,先MARK,回头整一下STM32+LwIP

出0入0汤圆

发表于 2013-9-2 12:03:53 | 显示全部楼层
mark                  

出0入0汤圆

 楼主| 发表于 2013-9-2 18:08:42 | 显示全部楼层
kinsno 发表于 2013-9-1 21:53
兄弟,你不是说你的笔记的吗?传上来了吗?
难道是你在28楼发的那个?28楼我记得好象你说过是老衲五木的 ...

五木的笔记已经将lwip大致原理说得很明白,只是缺少了一些调试的例子。我想借助五木的笔记和加上一些调试的记录可以给刚使用lwip的朋友一些帮助。但因为手头的事情有点多,平时没什么时间可以上网,所以进度非常慢,笔记还在写,请谅解。

出0入0汤圆

 楼主| 发表于 2013-9-2 18:10:10 | 显示全部楼层
本帖最后由 xhyzjiji 于 2013-9-2 18:20 编辑
efayu 发表于 2013-9-2 11:26
本人刚开始走51+uIP,先MARK,回头整一下STM32+LwIP


uIP移植起来也比较简单,过会也把移植的uIP1.0上传吧
有同学说可以把lwip的ping的速度提升到小于1ms,裁剪了很多不使用的内容,但他不太愿意公开他的方法。。。。

出0入0汤圆

发表于 2013-9-2 20:03:33 | 显示全部楼层
xhyzjiji 发表于 2013-9-2 18:08
五木的笔记已经将lwip大致原理说得很明白,只是缺少了一些调试的例子。我想借助五木的笔记和加上一些调试 ...

哈哈,兄弟,预估一下什么时候,你的笔记可以面世?让我也有个盼头!1周?2周?1个月?2个月?是不是?嘿嘿。

出40入42汤圆

发表于 2013-9-5 12:10:17 | 显示全部楼层
xhyzjiji 发表于 2013-9-1 21:31
也可以把错误警告贴上图,大家研究一下

添加了sys_now()这个函数后,LwIP1.4.1可以跑起来了。可以ping通。
但是,在测试中遇到了这样一个问题:(图片中发了1000字节,只回发了512字节)

     tcp_write()了之后,数据发了一遍后,就发不出去了。

多次测试发现,当建立一个新的连接后,板子把接收到的数据回发,
只能回发1个TCP_MSS(设置的是512)的数据,之后就发不了。

串口调试:在慢启动tcp_slowtmr()里不断重发未发出数据,但PC这边
没有收到,重发次数超限后,出现:
tcp_slowtmr: max DATA retries reached //超出数据重发次数
tcp_pcb_purge //清除tcp_pcb
tcp_pcb_purge: not all data sent
tcp_pcb_purge: data left on ->unacked
tcp_slowtmr: no active pcbs
tcp_slowtmr: no active pcbs
tcp_slowtmr: no active pcbs

用wireshark抓包也有这个现象。

楼主有遇到过这种情况吗?使用1.4.1有没有遇到问题啊?裸系统的。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-9-5 12:24:36 | 显示全部楼层
以太网好东西,占个座,

出0入0汤圆

发表于 2013-9-5 13:00:35 来自手机 | 显示全部楼层
顶,谢谢分享,

出0入0汤圆

发表于 2013-9-5 13:25:23 | 显示全部楼层
不错的学习资料

出0入0汤圆

发表于 2013-9-5 14:18:51 | 显示全部楼层
值得学习!

出40入42汤圆

发表于 2013-9-7 16:06:01 | 显示全部楼层
落叶知秋 发表于 2013-9-5 12:10
添加了sys_now()这个函数后,LwIP1.4.1可以跑起来了。可以ping通。
但是,在测试中遇到了这样一个问题:( ...

问题已解决!
是IAR5.41编译器的BUG!lwip源码中的src/include/lwip/tcp_impl.h文件中,有这样一段宏定义:

#define TCP_SEQ_LT(a,b)     ((s32_t)((u32_t)(a) - (u32_t)(b)) < 0)
#define TCP_SEQ_LEQ(a,b)    ((s32_t)((u32_t)(a) - (u32_t)(b)) <= 0)
#define TCP_SEQ_GT(a,b)     ((s32_t)((u32_t)(a) - (u32_t)(b)) > 0)
#define TCP_SEQ_GEQ(a,b)    ((s32_t)((u32_t)(a) - (u32_t)(b)) >= 0)

由于直接用lwip官网下载的源码移植,忽略了这个BUG的存在!以下是TI官网配套的lwip-1.3.2源码中的修改:

/* Modified by TI to work around an IAR 5.41 compiler bug */
#if 0
#define TCP_SEQ_LT(a,b)     ((s32_t)((a)-(b)) < 0)
#define TCP_SEQ_LEQ(a,b)    ((s32_t)((a)-(b)) <= 0)
#define TCP_SEQ_GT(a,b)     ((s32_t)((a)-(b)) > 0)
#define TCP_SEQ_GEQ(a,b)    ((s32_t)((a)-(b)) >= 0)
#else
/* TI replacement definitions... */
#define TCP_SEQ_LT(a,b)     ((s32_t)(a) < (s32_t)(b))
#define TCP_SEQ_LEQ(a,b)    ((s32_t)(a) <= (s32_t)(b))
#define TCP_SEQ_GT(a,b)     ((s32_t)(a) > (s32_t)(b))
#define TCP_SEQ_GEQ(a,b)    ((s32_t)(a) >= (s32_t)(b))
#endif

覆盖后,tcp_write()就可以成功发送数据了。
出现发送不成功的原因,查了一下,是在函数tcp_in()中,由于上述的BUG,进不来一下判断:

/*五个连续的判断,DUP_Ack*/
if (TCP_SEQ_LEQ(ackno, pcb->lastack))
{……}
else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt))
{……}//<--------这里进不来,假设为A部分

在A部分中有pcb->unacked的处理,由于BUG的影响,一直没有处理,即一直都是有unack的标志。
然后,在慢启动函数tcp_slowtmr()中,会由于pcb->unacked != NULL而启动重发。
所以有了这个问题。
总结:编译器还是用最新版本的比较好……
P.S. 硬件平台:LM3S8962
      软件环境:IAR 5.41

出0入0汤圆

发表于 2013-9-9 17:13:09 | 显示全部楼层
新版IAR破解比以前麻烦了

出40入42汤圆

发表于 2013-9-9 19:50:31 | 显示全部楼层
落叶知秋 发表于 2013-9-7 16:06
问题已解决!
是IAR5.41编译器的BUG!lwip源码中的src/include/lwip/tcp_impl.h文件中,有这样一段宏定义 ...

更正!

第一:用了IAR 6.4(目前最新版本)进行了同样的源码编译,还是会有同样的问题(发不出数据)!
没有具体比较过汇编的异同,只知道是不同的!如果有哪位高人知道,请告知!!

第二:用TI的代码替换后,会衍生出新的问题!就是边界问题!
TI的代码比较是用的s32_t类型,即是有符号数据类型!到达了符号位后,有问题!比如0x7FFFFFFF和0x80000000!
现象是:发送的数据量到达了一定后,比较的结果不对就会导致数据传输出问题(即是停止不动)!

还在苦逼调试中……

出40入42汤圆

发表于 2013-9-10 09:49:07 | 显示全部楼层
落叶知秋 发表于 2013-9-9 19:50
更正!

第一:用了IAR 6.4(目前最新版本)进行了同样的源码编译,还是会有同样的问题(发不出数据)!

发了一个关于这个问题的帖子
LM3S8962 + LwIP + 无操作系统,长时间数据收发问题的解决
http://www.amobbs.com/thread-5550592-1-1.html
有兴趣的朋友可以去看看。
现在发了14个小时都没有停下来,希望板子不会自己停啦……念咒中……

出0入0汤圆

 楼主| 发表于 2013-10-6 17:04:33 | 显示全部楼层
落叶知秋 发表于 2013-9-10 09:49
发了一个关于这个问题的帖子
LM3S8962 + LwIP + 无操作系统,长时间数据收发问题的解决
http://www.amobb ...

出差的时间有点长,能调试出来就是经验了,向你学习。

出0入0汤圆

发表于 2013-10-6 22:31:36 | 显示全部楼层
做个记号,希望以后能用上

出0入0汤圆

发表于 2013-10-6 23:06:07 | 显示全部楼层
恭喜你移植成功 哈哈

出0入0汤圆

发表于 2013-10-8 19:17:12 | 显示全部楼层
lwip移植不难,要稳定很难

出0入0汤圆

发表于 2013-10-10 20:34:41 | 显示全部楼层
好贴,谢谢

出0入0汤圆

发表于 2013-10-15 14:15:13 | 显示全部楼层
讲得很详细。ethernetif_init() 和 ethernetif_input() 这两个函数麻烦LZ也讲一下哈。谢谢啊!

出0入0汤圆

发表于 2013-10-15 16:44:29 | 显示全部楼层
MARK一下,以后有时间好好移植一下!

出0入0汤圆

发表于 2013-10-15 17:27:51 | 显示全部楼层
xhyzjiji 发表于 2013-8-16 23:15
宿舍网速太慢了,上传不上去,暂时先传个源码吧

开始正题吧。如果拿到一个可移植的代码,首先关 ...

学习了,感谢楼主的这种精神......

出0入0汤圆

发表于 2013-10-23 19:05:10 | 显示全部楼层
确实有很多好资料    先标记下

出0入0汤圆

发表于 2013-10-23 20:46:55 | 显示全部楼层
好贴,收藏了先,楼主给力,赞一个

出0入0汤圆

发表于 2013-10-24 17:30:48 | 显示全部楼层
xhyzjiji 发表于 2013-10-6 17:04
出差的时间有点长,能调试出来就是经验了,向你学习。

lwip提供三种API:1)RAW API 2)lwip API 3)BSD API。我们常用的是哪种API?

出0入0汤圆

发表于 2013-10-24 18:45:32 | 显示全部楼层
xhyzjiji 发表于 2013-8-16 23:15
宿舍网速太慢了,上传不上去,暂时先传个源码吧

开始正题吧。如果拿到一个可移植的代码,首先关 ...

同学,我要想你学习哈····

出0入0汤圆

发表于 2013-10-24 18:48:51 | 显示全部楼层
顶一个,有空折腾学习!

出0入0汤圆

发表于 2013-10-24 23:19:01 | 显示全部楼层
我来顶一下!

出0入0汤圆

发表于 2013-10-28 13:21:34 | 显示全部楼层
需要裸机的DHCP功能啊,一直解决不了,固定IP都没有问题,如果运行操作系统的话DHCP可以实现,裸机的DHCP不行,程序会跑飞掉,

出0入0汤圆

发表于 2013-10-29 09:27:58 | 显示全部楼层
注册一个tcp_err回调,断线应该会产生err,在回调函数中关pcb重连

出0入0汤圆

发表于 2013-11-6 10:19:12 | 显示全部楼层
xhyzjiji 发表于 2013-10-6 17:04
出差的时间有点长,能调试出来就是经验了,向你学习。

期待楼主再更新啊!

出0入0汤圆

发表于 2013-11-28 23:11:45 | 显示全部楼层
顶楼主

出0入0汤圆

发表于 2013-11-29 11:41:34 | 显示全部楼层
好东西,支持。

出0入0汤圆

发表于 2013-11-29 21:59:46 | 显示全部楼层
学习了,先顶起再来慢慢看

出0入0汤圆

发表于 2013-12-1 02:25:26 | 显示全部楼层
谢谢楼主,先收藏了

出0入0汤圆

发表于 2013-12-1 08:10:34 | 显示全部楼层
留着以后研究,mark

出0入0汤圆

发表于 2013-12-1 08:27:01 | 显示全部楼层
哇,楼主研究得很透彻~~~我当初也能用就行

出0入0汤圆

发表于 2013-12-13 17:23:03 | 显示全部楼层
xl_1120 发表于 2013-10-28 13:21
需要裸机的DHCP功能啊,一直解决不了,固定IP都没有问题,如果运行操作系统的话DHCP可以实现,裸机的DHCP不 ...

一直是在OS下用的LWIP,裸机的问题比较多呢。

出0入0汤圆

发表于 2013-12-24 21:21:06 | 显示全部楼层
xhyzjiji 发表于 2013-8-24 23:28
创建一个tcp服务器
1.初始化你的服务器
void tcp_server_init(void)

楼主可不可以说明一下移植的步骤,或者上传一个已经移植好的例程学习学习

出0入0汤圆

发表于 2013-12-24 21:29:02 | 显示全部楼层
已标记。

出0入0汤圆

发表于 2013-12-24 23:24:04 | 显示全部楼层
mark 明天上班再看

出0入0汤圆

 楼主| 发表于 2013-12-25 01:05:28 | 显示全部楼层
chengqun 发表于 2013-12-24 21:21
楼主可不可以说明一下移植的步骤,或者上传一个已经移植好的例程学习学习  ...

已经上传了,不过上传的工程里面有个地方有问题,是应用层的数据结构的问题,
将struct http_server_app_arg{
        uint8_t app_state;
        uint8_t textlen;
        uint8_t* dataptr;
};
更改成
struct http_server_app_arg{
        uint8_t app_state;
        uint16_t textlen;
        uint8_t* dataptr;
};即可

出0入0汤圆

 楼主| 发表于 2013-12-25 01:07:32 | 显示全部楼层
xl_1120 发表于 2013-10-28 13:21
需要裸机的DHCP功能啊,一直解决不了,固定IP都没有问题,如果运行操作系统的话DHCP可以实现,裸机的DHCP不 ...

我是用DHCP好像没问题,而且与外网通信也可以。可能我使用的时间不长,有空让它长时间连续工作试试。

出0入0汤圆

发表于 2013-12-26 19:42:22 | 显示全部楼层
xhyzjiji 发表于 2013-12-25 01:05
已经上传了,不过上传的工程里面有个地方有问题,是应用层的数据结构的问题,
将struct http_server_app_ ...

怎么没看到?

出0入0汤圆

发表于 2013-12-26 20:12:07 | 显示全部楼层
xhyzjiji 发表于 2013-12-25 01:05
已经上传了,不过上传的工程里面有个地方有问题,是应用层的数据结构的问题,
将struct http_server_app_ ...

楼主能不能问下一般像这个#define DP83848_PHY_ADDRESS       0x01 /* Relative to STM324xG-EVAL Board */   这里宏定义的值是怎么定的?如果我用的不是这款芯片,而且也不是它的板子。再就是这个地址一般有什么用?

出0入0汤圆

发表于 2013-12-26 21:55:53 | 显示全部楼层
恭喜楼主

出0入0汤圆

 楼主| 发表于 2013-12-29 16:43:19 | 显示全部楼层
chengqun 发表于 2013-12-26 20:12
楼主能不能问下一般像这个#define DP83848_PHY_ADDRESS       0x01 /* Relative to STM324xG-EVAL Board  ...

DP83848这个是PHY控制器,你用的是f4xx的芯片么,里面自带MAC。PHY位于物理层,是不会影响你的数据包(或者说PHY不关心你将要发送的数据),他只是完成一些物理电路上的传输参数(物理层定义了数据传送与接收所需要的电与光信号、线路状态、时钟基准、数据编码和电路等,并向数据链路层设备提供标准接口。物理层的芯片称之为PHY。摘自百度百科--PHY),而MAC则可以为你填写ETH(以太网)报头,过滤一些不正确或者不感兴趣的数据包,而IP TCP层的解析或者生成工作则有lwip这个协议栈代劳。
至于那个宏是指的是什么,要看这个芯片的手册才知道(可能是DP83848的一个寄存器地址或者是访问DP83848的IO地址,这个要看芯片手册跟电路图)。

出0入0汤圆

 楼主| 发表于 2013-12-29 16:45:11 | 显示全部楼层
东海傲虾 发表于 2013-12-13 17:23
一直是在OS下用的LWIP,裸机的问题比较多呢。

我也想用操作系统,寒假一定要好好把握时间

出0入0汤圆

 楼主| 发表于 2013-12-29 16:45:35 | 显示全部楼层

谢谢,大家一起学习~~

出0入0汤圆

发表于 2013-12-29 18:08:51 | 显示全部楼层
xhyzjiji 发表于 2013-12-29 16:43
DP83848这个是PHY控制器,你用的是f4xx的芯片么,里面自带MAC。PHY位于物理层,是不会影响你的数据包(或 ...

谢谢,这个问题我搞定了。

出0入0汤圆

发表于 2013-12-29 18:20:22 | 显示全部楼层
xhyzjiji 发表于 2013-12-29 16:43
DP83848这个是PHY控制器,你用的是f4xx的芯片么,里面自带MAC。PHY位于物理层,是不会影响你的数据包(或 ...

大侠,我还想问下,轮询还是中断是不是也是要通过寄存器来设置的?

出0入0汤圆

发表于 2013-12-30 00:03:56 | 显示全部楼层
mark lwip移植

出0入0汤圆

发表于 2013-12-30 08:16:10 | 显示全部楼层
xhyzjiji 发表于 2013-12-29 16:45
我也想用操作系统,寒假一定要好好把握时间

我用的是ucos,挺稳定,就是有一个小问题,就是服务器软重启的时候无法重新自动获取到IP地址,需要复位设备,其他的断网线,硬重启服务器都没有问题。

出0入0汤圆

发表于 2013-12-30 10:37:27 | 显示全部楼层
东海傲虾 发表于 2013-12-30 08:16
我用的是ucos,挺稳定,就是有一个小问题,就是服务器软重启的时候无法重新自动获取到IP地址,需要复位设 ...

能分享一下吗?
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-9-3 14:08

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表