1. 程式人生 > >手把手教你移植LWIP(ENC28J60)

手把手教你移植LWIP(ENC28J60)

2016年07月11日 15:00:38 hwj666 閱讀數:13160 標籤: 移植網路 更多

個人分類: 微控制器

版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/he_wen_jie/article/details/51880845

這裡只是移植,所以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;
}

接著我們需要將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)

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

我們將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;

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

其餘的就不需要我們了,這裡直接移植的ethernetif檔案預設是不編譯的,我們將#if 0給取消掉。 
新建的netconfig檔案就是配置網路引數了,比如ip地址,mac地址,還有最重要的LwIP_Periodic_Handle函式,處理核心的定時函式,如何TCP定時,ARP定時。定時採用systick進行定時,每20ms中斷一次,執行一次LwIP_Periodic_Handle。 
基本上lwip的移植就結束了。