手把手教你移植LWIP(ENC28J60)

这里只是移植,所以LWIP那么多的协议都不需要管,只要知道哪里需要我们修改,为什么修改就可以了。
这里写图片描述
上图就是整个移植的基本思路,非常清晰的三个层次。其实想想,本质上就是收发数据,只是LWIP协议通过对数据的封装可以实现网络传输。从图中我们就可以看到这里首先需要ENC28J60的驱动,这个驱动需要实现数据的收发,驱动可以在网上找一个。其次就是要移植并修改LWIP协议了。

移植
1.下载LWIP1.4.1和contrib-1.4.1,可以到网上找一个下载,本质上LWIP1.4.1就是LWIP协议的全部,当时我们需要contrib-1.4.1才能完成整个移植。
2.将LWIP1.4.1拷贝到keil新建的工程下面,LWIP下的src是源文件,
这里写图片描述
在keil工程中添加几个组,LWIP-API,LWIP-CORE,LWIP-CORE-IPV4,LWIP-NETIF,LWIP-ARCH。分别将lwip1.4.1下的src中的api全部文件添加到LWIP-API,中,将core中的文件添加到LWIP-CORE中,将core下的ipv4下的文件全部添加到LWIP-CORE-IPV4,将netif中的文件添加到LWIP-NETIF中。到这里lwip的移植就结束了。
这里写图片描述
下面就是将H文件添加在keil中了。
这里写图片描述
3.在lwip的同一目录下,新建ARCH,NET,netconfig文件。在contrib-1.4.1\contrib-1.4.1\ports\win32目录下找到sys_arch.c,在contrib-1.4.1\contrib-1.4.1\ports\win32\include找到lwipopts.h和arch文件夹下的文件,将这些文件都拷贝到keil工程中新建的ARCH目录下。在netconfig目录下新建netconfig.c和netconfig.h文件,在NET目录下新建udp_app.c和udp_app.h文件。
4.好了,移植了这么多的文件也是够乱了,现在来解释一下。ARCH中的cc.h文件里是一些数据类型的定义,为了保证平台的无关性,协议栈只使用了自己定义的数据类型,此外cc中还定义了一些调试信息输出的宏。lwipopts.h定义了内核的预编译的宏,有些文件或服务不需要使用,可以改变这个文件中的宏定义取消编译。这个文件是内核的参数配置文件,非常的重要。sys_arch.c文件只有一个sys_now()函数是有用的,其余的都可以注释掉,这里也要修改一下sys_now()函数。其余的文件都不需要修改。

u32_t sys_now()
{
  return LocaTime;
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

接着我们需要将ENC28J60的接收发送函数与LWIP协议的底层数据接发接口对接起来。这里对接文件是lwip-1.4.1\src\netif下的ethernetif文件,这个文件定义了五个函数,

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)
static void ethernetif_input(struct netif *netif)
err_t ethernetif_init(struct netif *netif)
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

low_level_init函数初始化mac地址,

static void
low_level_init(struct netif *netif)
{
  /* set MAC hardware address length */
  netif->hwaddr_len = ETHARP_HWADDR_LEN;

  /* set MAC hardware address */
    netif->hwaddr[0] = macaddress[0];
    netif->hwaddr[1] = macaddress[1];
    netif->hwaddr[2] = macaddress[2];
    netif->hwaddr[3] = macaddress[3];
    netif->hwaddr[4] = macaddress[4];
    netif->hwaddr[5] = macaddress[5];
  /* maximum transfer unit */
  netif->mtu = netifMTU;

  /* device capabilities */
  /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
  netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;

  /* Do whatever else is needed to initialize interface. */  
    enc28j60Init(netif->hwaddr);
}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

我们将ENC28J60的初始化放在这里。
low_level_output函数是传递数据到内核里,我们将enc28j60PacketSend放在里面

static err_t
low_level_output(struct netif *netif, struct pbuf *p)
{
  struct pbuf *q;
    unsigned int i = 0;  

#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. */
        memcpy(&Tx_Data_Buf[i], (u8_t*)q->payload, q->len); 
                i = i + q->len;
  }

    enc28j60PacketSend(i,Tx_Data_Buf); //·¢ËÍÊý¾Ý°ü

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

  LINK_STATS_INC(link.xmit);

  return ERR_OK;
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

low_level_input函数接受内核数据,我们将enc28j60PacketReceive放到里面

static struct pbuf *
low_level_input(struct netif *netif)
{
  struct pbuf *p, *q;
  u16_t len;
    unsigned int i =0;
  /* Obtain the size of the packet and put it into the "len"
     variable. */
  len = enc28j60PacketReceive(1520 *4,  Data_Buf);  
    if(len == 0) return 0;

#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);

  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.
       */
                memcpy((u8_t*)q->payload, (u8_t*)&Data_Buf[i], q->len);

                i = i + q->len;
    }

#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;  
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

其余的就不需要我们了,这里直接移植的ethernetif文件默认是不编译的,我们将#if 0给取消掉。
新建的netconfig文件就是配置网络参数了,比如ip地址,mac地址,还有最重要的LwIP_Periodic_Handle函数,处理内核的定时函数,如何TCP定时,ARP定时。定时采用systick进行定时,每20ms中断一次,执行一次LwIP_Periodic_Handle。
基本上lwip的移植就结束了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值