手把手教你移植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的移植就結束了。