(换句话说,想要从0開始移植LwIP必须对操作网卡很熟悉)
【1】初始化 static void low_level_init(struct netif *netif) { struct ethernetif *ethernetif = netif->state; /* set MAC hardware address length */ netif->hwaddr_len = ETHARP_HWADDR_LEN; /* set MAC hardware address */ netif->hwaddr[0] = 'A'; netif->hwaddr[1] = 'R'; netif->hwaddr[2] = 'M'; netif->hwaddr[3] = 'N'; netif->hwaddr[4] = 'E'; netif->hwaddr[5] = 'T'; /* maximum transfer unit */ netif->mtu = 1500; /* 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. */ enc28j60_init(netif->hwaddr); // 【1】 } 【说明】 【1】 enc28j60_init(netif->hwaddr); low_level_init中指定了enc28j60中的网卡地址。 【2】发送 static err_t low_level_output(struct netif *netif, struct pbuf *p) { struct ethernetif *ethernetif = netif->state; struct pbuf *q; enc28j60_init_send(p->tot_len); //【1】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. */ enc28j60_writebuf( q->payload, q->len ); //【2】send data from(q->payload, q->len); } enc28j60_start_send(); //【3】signal that packet should be sent(); #if ETH_PAD_SIZE pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ #endif LINK_STATS_INC(link.xmit); return ERR_OK; } 【说明】 【1】enc28j60_init_send(p->tot_len); 初始化发送缓冲区大小, pbuf结构为一个链表,第一个pbuf结构体中的tot_len字段代表整个以太网数据包的大小。 【2】enc28j60_writebuf( q->payload, q->len ); 通过遍历链表把内容填入ENC28J60的缓冲区中。 【3】enc28j60_start_send();启动网卡发送。 【3】接收 static struct pbuf * low_level_input(struct netif *netif) { struct ethernetif *ethernetif = netif->state; struct pbuf *p, *q; u16_t len; len = enc28j60_packet_getlen(); // 【1】 #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 for(q = p; q != NULL; q = q->next) { enc28j60_readbuf (q->payload, q->len ); //【2】read data into(q->payload, q->len); } enc28j60_finish_receive(); //【3】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 { enc28j60_finish_receive(); //【4】drop packet(); LINK_STATS_INC(link.memerr); LINK_STATS_INC(link.drop); } return p; } 【说明】 【1】len = enc28j60_packet_getlen(); 获得网卡中数据包的长度。 【2】enc28j60_readbuf (q->payload, q->len);把网卡中的内容拷贝到内存池中。 【3】enc28j60_finish_receive();接收完毕,移动网卡中缓冲区指针。 【4】应用 【1】LwIP网卡硬件初始化调用ethernetif_init就可以,该函数中调用了low_level_init,并指定了网卡输出函数low_level_output。 【2】一旦网卡有数据进入。应马上代用ethernetif_input函数。能够使用中断方法或查询方法。
2.lwipopt.h配置简述 lwip中的配置选项很的多。了解全部的配置很不easy。本博文參考STM32官方的两个样例总结得到。#ifndef __LWIPOPTS_H__ #define __LWIPOPTS_H__ #define SYS_LIGHTWEIGHT_PROT 0 #define NO_SYS 1 #define NO_SYS_NO_TIMERS 1 /* ---------- Memory options ---------- */ /* MEM_ALIGNMENT: should be set to the alignment of the CPU for which lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2 byte alignment -> define MEM_ALIGNMENT to 2. */ #define MEM_ALIGNMENT 4 /* MEM_SIZE: the size of the heap memory. If the application will send a lot of data that needs to be copied, this should be set high. */ #define MEM_SIZE (5*1024) /* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application sends a lot of data out of ROM (or other static memory), this should be set high. */ #define MEMP_NUM_PBUF 10 /* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One per active UDP "connection". */ #define MEMP_NUM_UDP_PCB 6 /* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections. */ #define MEMP_NUM_TCP_PCB 10 /* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections. */ #define MEMP_NUM_TCP_PCB_LISTEN 6 /* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. */ #define MEMP_NUM_TCP_SEG 12 /* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts. */ #define MEMP_NUM_SYS_TIMEOUT 3 /* ---------- Pbuf options ---------- */ /* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */ #define PBUF_POOL_SIZE 10 /* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */ #define PBUF_POOL_BUFSIZE 1500 /* ---------- TCP options ---------- */ #define LWIP_TCP 1 #define TCP_TTL 255 /* Controls if TCP should queue segments that arrive out of order. Define to 0 if your device is low on memory. */ #define TCP_QUEUE_OOSEQ 0 /* TCP Maximum segment size. */ #define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */ /* TCP sender buffer space (bytes). */ #define TCP_SND_BUF (2*TCP_MSS) /* TCP sender buffer space (pbufs). This must be at least = 2 * TCP_SND_BUF/TCP_MSS for things to work. */ #define TCP_SND_QUEUELEN (6 * TCP_SND_BUF)/TCP_MSS /* TCP receive window. */ #define TCP_WND (2*TCP_MSS) /* ---------- ICMP options ---------- */ #define LWIP_ICMP 1 /* ---------- DHCP options ---------- */ /* Define LWIP_DHCP to 1 if you want DHCP configuration of interfaces. DHCP is not implemented in lwIP 0.5.1, however, so turning this on does currently not work. */ #define LWIP_DHCP 0 /* ---------- UDP options ---------- */ #define LWIP_UDP 1 #define UDP_TTL 255 /* ---------- Statistics options ---------- */ #define LWIP_STATS 0 #define LWIP_PROVIDE_ERRNO 1 /** * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) */ #define LWIP_NETCONN 0 /** * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) */ #define LWIP_SOCKET 0 #endif /* __LWIPOPTS_H__ */ 【详细说明和改动】 【1】未使用操作系统。全部NO_SYS定义为1。LWIP_NETCONN定义为0(表示不使用),LWIP_SOCKET定义为0(表示不使用)。 【2】NO_SYS_NO_TIMERS定义为1,该定义为LwIP1.4.0以上版本号添加。详细可參考LwIP改动文档。 【3】LWIP_DHCP被定义为0。关闭了DHCP功能以简化代码。 【4】相比STM32官方样例。去除了校验码相关配置全部使用软件校验。STM32官方案例中使用了代码EMAC功能的MCU,该系列MCU中包含硬件校验功能,可是ENC28J60并没有此功能,所以仅仅能开启LwIP中的软件校验功能。 3.LwIP相关初始化 void LwIP_Config (void) { struct ip_addr ipaddr; struct ip_addr netmask; struct ip_addr gw; // 调用LWIP初始化函数 lwip_init(); IP4_ADDR(&ipaddr, 192, 168, 1, 16); // 设置网络接口的ip地址 IP4_ADDR(&netmask, 255, 255, 255, 0); // 子网掩码 IP4_ADDR(&gw, 192, 168, 1, 1); // 网关 // 初始化enc28j60与LWIP的接口。參数为网络接口结构体、ip地址、 // 子网掩码、网关、网卡信息指针、初始化函数、输入函数 netif_add(&enc28j60, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input); // 把enc28j60设置为默认网卡 netif_set_default(&enc28j60); netif_set_up(&enc28j60); } 【说明】 【1】通过netif_add初始化网卡IP地址,子网掩码和网关地址。此处使用静态IP地址。 【2】netif_add须要传入两个函数指针,各自是网卡初始化函数和接收内容处理函数。ethernetif_init位于ethernetif.c而 ethernet_input并不位于ethernetif.c,此处也 不能使用ethernetif_input,事实上ethernet_input在函数ethernetif_input被调用,可是ethernet_input变了一个样子: netif-> input(p, netif)!=ERR_OK 【3】在带操作系统的移植中。最后一个參数使用tcpip_input。 4.while(1)部分 timer_typedef tcp_timer, arp_timer; /* 设定查询定时器 ARP定时器 */ timer_set(&tcp_timer, CLOCK_SECOND / 10); // tcp处理定时器 100ms timer_set(&arp_timer, CLOCK_SECOND * 5); // arp处理定时器 5s while (1) { if (enc28j60_packet_getcount() != 0) { ethernetif_input(&enc28j60); } // TCP 定时处理 if (timer_expired(&tcp_timer)) { timer_set(&tcp_timer, CLOCK_SECOND / 4); tcp_tmr(); } // ARP 定时处理 if (timer_expired(&arp_timer)) { timer_set(&arp_timer, CLOCK_SECOND * 5); etharp_tmr(); } } 【说明】 while(1)循环包含3个主要功能 【1】一旦接受到数据包,立马调用 ethernetif_input。此处使用查询法而不是中断法(中断法效果类似) 【2】定期处理TCP链接。定时时间为100ms。可依据情况适当缩小时间间隔。 【3】定期更新ARP缓冲,可依据情况适当扩大时间间隔。 【4】此处的timer通过systick实现。详细实现请參考代码仓库。 4.基本測试 【1】ping实验 此时网卡的静态IP地址为192.168.1.16,通过ping指令发送16个数据包 ping 192.168.1.16 -n 16 图1 ping实验 【2】TCP Echo样例 LwIP提供很多演示样例。TCP Echo演示样例位于contrib-1.4.1的apps目录中,目录名为tcpecho_raw)。改动TCP侦听port为10086。 err = tcp_bind(echo_pcb, IP_ADDR_ANY, 10086); 图2 TCP Echo样例 5.总结 【1】移植和应用LwIP一定要耐心仔细。 【2】一旦网卡接收到数据,应调用ethernetif_input函数。调用该函数让数据进入LwIP协议栈。 【3】 netif_add(&enc28j60, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input);最后一个參数为ethernet_input,千万必要写成ethernetif_input。 6.參考资料 很多其它细节内容请參考图书资料 【1】《嵌入式网络系统设计——基于Atmel ARM7系列》 【2】《STM32嵌入式系统开发实战指南——FreeRTOS与LwIP移植》
版权声明:本文博主原创文章,博客,未经同意,不得转载。
转载于:https://www.cnblogs.com/bhlsheji/p/4758553.html
相关资源:数据结构—成绩单生成器