手把手教你移植LWIP(ENC28J60)

2019-07-13 17:56发布

这里只是移植,所以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.1contrib-1.4.1portswin32目录下找到sys_arch.c,在contrib-1.4.1contrib-1.4.1portswin32include找到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
save_snippets.png
  • 1
  • 2
  • 3
  • 4
  • 5
接着我们需要将ENC28J60的接收发送函数与LWIP协议的底层数据接发接口对接起来。这里对接文件是lwip-1.4.1src etif下的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
save_snippets.png
  • 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
save_snippets.png
  • 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
save_snippets.png
  • 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
save_snippets.png
  • 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的移植就结束了。