0. Preface
Last year (2013) The LWIP related code was collated and "Bare Ben" succeeded on STM32. There has been no time to go into the finishing, here to borrow blog to summarize. lwIP's transplant process is a lot of detail, and it's impossible to explain one by one in detail to the individual parts only donuts.
"The main points of this article"
"1" lwIP porting without operating system, LWIP version number 1.4.1.
The "2" MCU is Stm32f103ve and the NIC is enc28j60.
The "3" transplant process focuses on describing ETHERNETIF.C and LWIP macro configuration. "4" A Simple TCP echo example.
"5" Strive for simplicity. There is no DHCP function, not even practical to the NIC interrupt.
The Code warehouse
code warehouse is located in BitBucket (please click here to source). Blog post can not describe every detail description clearly. For a lot of other content, please refer to the Code warehouse for detailed code.
"Hardware description"
test platform using the struggle version, schematic please refer to the doc directory in the Code warehouse.
"Kauboven"
learning the embedded network is a gradual process, from shallow into deep from simple to complex. "1" enc28j60 study notes-Learning Network card "2" stm32net study notes--index--Understanding TCPIP Stack "3" UIP Learning notes--Initial application Stack "4" Yeelink platform use-remote control RT Thread + lwip+ stm32--more practical procedure
1.ETHERNETIF.C Related Changes Although the
LWIP transplant process is more complex, but only to combine the details of the network card, the patient changes ETHERNETIF.C can be. ETHERNETIF.C focus on the implementation of the network card three functions, initialization, sending and receiving.
In order to better cooperate with LWIP, we have changed some driving functions in enc28j60 learning notes.
(in other words, to migrate from 0 lwIP must be familiar with operating network cards)
"1" Initialization
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】
}
"description" "1" enc28j60_init (NETIF->HWADDR); The address of the NIC in the ENC28J60 is specified in the Low_level_init.
" 2 "Send
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;
}
"description" "1" enc28j60_init_send (P->tot_len); Initializes the send buffer size, the PBUF structure is a linked list, and the Tot_len field in the first PBUF structure represents the size of the entire Ethernet packet.
"2" Enc28j60_writebuf (Q->payload, Q->len); The contents are filled into the enc28j60 buffer by traversing the linked list. "3" enc28j60_start_send (); Start network card sending.
"3" Receive
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;
}
"description" "1" len = Enc28j60_packet_getlen (); Gets the length of the packet in the NIC. "2" Enc28j60_readbuf (Q->payload, Q->len); Copy the contents of the NIC into the memory pool. "3" enc28j60_finish_receive (); After receiving, move the buffer pointer in the NIC.
"4" Application
"1" LWIP NIC hardware initialization call Ethernetif_init, the function calls the Low_level_init, and specifies the network card output function Low_level_output.
"2" Once the NIC has data entry. The Ethernetif_input function should be substituted immediately.
The ability to use interrupt methods or query methods.
2.lwipopt.h Configuration Brief
lwIP There are many configuration options in the. Understanding the full configuration is not easy. Two examples of the official STM32 are summarized in this blog post.
 
#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__ */
"Detailed description and changes" "1" is not used by the operating system. All no_sys are defined as 1. The lwip_netconn is defined as 0 (not used), and lwip_socket is defined as 0 (which means it is not used).
The "2" no_sys_no_timers is defined as 1, and the definition is added for LwIP1.4.0 or later. Detailed LWIP to change the document. The "3" LWIP_DHCP is defined as 0. The DHCP feature is turned off to simplify the code.
"4" compared to STM32 official example. The checksum-related configuration is removed using software verification. STM32 in the official case of the MCU using the Code EMAC function, this series of MCU contains hardware check function, but enc28j60 does not have this function, so only can turn on the software check function in lwIP.
3.LwIP related initialization
void LwIP_Config (void)
{
struct ip_addr ipaddr;
struct ip_addr netmask;
struct ip_addr gw;
// call LWIP initialization function
lwip_init ();
IP4_ADDR (& ipaddr, 192, 168, 1, 16); // Set the IP address of the network interface
IP4_ADDR (& netmask, 255, 255, 255, 0); // subnet mask
IP4_ADDR (& gw, 192, 168, 1, 1); // gateway
// Initialize the interface between enc28j60 and LWIP. The parameters are the network interface structure, ip address,
// Subnet mask, gateway, network card information pointer, initialization function, input function
netif_add (& enc28j60, & ipaddr, & netmask, & gw, NULL, eernetif_init, eernet_input);
// Set enc28j60 as the default network card
netif_set_default (& enc28j60);
netif_set_up (& enc28j60);
}
"description" "1" initializes the NIC IP address, subnet mask, and gateway address via Netif_add. Static IP addresses are used here.
The
"2" Netif_add need to pass in two function pointers, each of which is the NIC initialization function and the receive content handler function. Ethernetif_init is located in ETHERNETIF.C and ethernet_input is not located in Ethernetif.c and is also cannot use ethernetif_ Input , in fact Ethernet_input is called in the function ethernetif_input, but Ethernet_input changed a look: Netif->input (P, netif)!=err_ok "3" in a migration with an operating system. The last parameter uses Tcpip_input.
4.while
(1) Section
timer_typedef tcp_timer, arp_timer;
/ * Set query timer ARP timer * /
timer_set (& tcp_timer, CLOCK_SECOND / 10); // tcp processing timer 100ms
timer_set (& arp_timer, CLOCK_SECOND * 5); // arp handles timer 5s
while (1) {
if (enc28j60_packet_getcount ()! = 0) {
ethernetif_input (& enc28j60);
}
// TCP timing processing
if (timer_expired (& tcp_timer)) {
timer_set (& tcp_timer, CLOCK_SECOND / 4);
tcp_tmr ();
}
// ARP timing processing
if (timer_expired (& arp_timer)) {
timer_set (& arp_timer, CLOCK_SECOND * 5);
etharp_tmr ();
}
}
"description"
while (1) loop contains 3 main functions "1" Once the packet is received, call Ethernetif_input immediately. Here the TCP link is processed periodically using the Query method instead of the interrupt method (similar to the interrupt effect) "2". The timing time is 100ms. The time interval can be reduced appropriately, depending on the situation. "3" periodically updates the ARP buffer, which can be scaled up to the appropriate time interval. "4" The timer here is implemented by Systick. For detailed implementation please refer to the Code warehouse.
4. Basic Test
"1" ping experiment
at this time the static IP address of the network card is 192.168.1.16, send 16 packets ping 192.168.1.16-n 16 with ping instruction
watermark/2/text/ahr0cdovl2jsb2cuy3nkbi5uzxqvehvrywk4nzexmdu=/font/5a6l5l2t/fontsize/400/fill/i0jbqkfcma==/ Dissolve/70/gravity/southeast "/> Figure 1 Ping experiment " 2 "TCP Echo Sample LWIP provides a number of demo examples. The TCP Echo Demo sample is located in the contrib-1.4.1 's apps directory, and the directory name is Tcpecho_raw. Modify TCP to listen on port 10086.
Err = Tcp_bind (ECHO_PCB, Ip_addr_any, 10086);
watermark/2/text/ahr0cdovl2jsb2cuy3nkbi5uzxqvehvrywk4nzexmdu=/font/5a6l5l2t/fontsize/400/fill/i0jbqkfcma==/ Dissolve/70/gravity/southeast "/>
Figure 2 TCP Echo Sample
5. Summary
"1" porting and applying LWIP must be patient and careful. "2" Once the NIC receives the data, the Ethernetif_input function should be called. Call this function to get the data into the LWIP protocol stack. "3" netif_add (&enc28j60, &ipaddr, &netmask, &GW, NULL, ðernetif_init, & Ethernet_input); The last parameter is Ethernet_input, and it must be written ethernetif_input.
6. Reference Information
Many other details please refer to the book Information " 1 "Embedded network system design-- Based on the Atmel ARM7 series "2" "STM32 Embedded system Development Practice Guide--freertos and lwIP transplant"
Copyright notice: This article Bo Master original articles, blogs, without consent, may not be reproduced.
LWIP Study notes--stm32 enc28j60 transplant and Getting started