lwip-2.1.2的裸机移植(STM32F1 + SPL库 + lwip-2.1.2 + ENC28J60)

试验原因

产品板子上用的LWIP版本较旧,这次准备换成现在官方最新的lwip-2.1.2.
想重新作lwip移植的原因 : 产品板子有bug与lwip相关,应该是以前程序移植的lwip有问题(配置和使用lwip).
正好这次放长假, 仔细研究一下lwip的移植, 配置,使用,调试。

lwip中带调试日志,可以通过ITM打印到MDK的调试窗口或用串口打印到PC端的串口助手上, 这样再遇到lwip使用问题, 就可以根据具体的lwip报错信息来调整程序逻辑。

想在ucosii上作lwip移植, 先作裸机移植.
stm32f1裸机移植lwip-2.1.2的试验已经作完,记录一下。

移植好的工程(stm32f1 + spl lib + lwip-2.1.2 + enc28j60)

my_STM32F10x_StdPeriph_Template_FD_LWIP.zip

试验

lwip下载地址 : http://download.savannah.nongnu.org/releases/lwip/

lwip工程包含2个部分(lwip-2.1.2.zip + contrib-2.1.0.zip)
lwip-2.1.2.zip 是官方代码, 是lwip库实现。
contrib-2.1.0.zip 是大家贡献的代码, 可以看到移植,配置, 使用lwip的demo工程。
在这里插入图片描述

另外要准备一份最后版本的sys_arch.txt, 这个文件是官方对移植lwip的指南,但是在最新lwip版本中已经折腾没了(分散合并到了lwip工程中去了, 对新手特别不友好), 我从lwip git历史中找出了最后版本的sys_arch.txt, 整理了一份 (lwip-2.1.2 sys_arch.txt was deleted)

如果没有sys_arch.txt的指南, 新手根本不知道咋移植lwip到自己的工程, 开源工程最重要的是文档, 要不新手咋用啊。

lwip在stm32f1(SPL固件库)的移植,可以从自己准备的一个干净的SPL库模板上开始。
我整理了一份(STM32F10x_StdPeriph_Template)

移植完的工程目录概览

在这里插入图片描述
蓝色框部分是固件库模板工程原有的目录。
红色框部分是lwip移植的代码。

lwip的移植分为4部分:

  • lwip原始实现直接拷贝过来,不用改的部分
  • 从lwip-2.1.2和contrib-2.1.0实现拷贝过来,需要改的部分
  • 网卡控制器实现
  • 用户层代码

lwip移植相关的目录概览

lwip原始实现直接拷贝过来,不用改的部分

.h是不用移植的,拷贝到工程只是为了看的方便。

lwip/core <= \lwip-2.1.2\src\core (.c)
在这里插入图片描述
lwip/core/ipv4 <= lwip-2.1.2\src\core\ipv4 (
.c)
在这里插入图片描述
lwip/netif <= lwip-2.1.2\src\netif (.c), 子目录ppp不用拷贝(因为我移植完,是要实现一个http片上服务器, 如果你要实现点对点通讯,根据情况拷贝过来)
在这里插入图片描述
lwip/hdr <= D:\ls\STM32\LWIP_zhu_sheng_lin\doc\lwip-2.1.2\src\include\lwip(
.h)

在这里插入图片描述
在这里插入图片描述
lwip/hdr/port <= lwip-2.1.2\src\include\lwip\prot(.h)
在这里插入图片描述
lwip/hdr/netif <= lwip-2.1.2\src\include\netif(
.h)
在这里插入图片描述

从lwip-2.1.2和contrib-2.1.0实现拷贝过来,需要改的部分

在这里插入图片描述
cc.h <= contrib-2.1.0\ports\win32\include\arch
lwipopts.h <= contrib-2.1.0\examples\example_app
sys_arch.c <= contrib-2.1.0\ports\win32
bpstruct.h <= contrib-2.1.0\ports\win32\include\arch
epstruct.h <= contrib-2.1.0\ports\win32\include\arch
ppp_settings.h <= contrib-2.1.0\examples\example_app
sys_arch.h <= contrib-2.1.0\ports\win32\include\arch
ethernetif.c <= contrib-2.1.0\examples\ethernetif

网卡控制器实现

在这里插入图片描述
spi.c/spi.h/enc28j60.c/enc28j60.h 都是找的现成的enc28j60网卡控制器驱动, 因为我手头的开发板用的是enc28j60, 就直接从开发板厂商提供的例子中,将驱动拷贝出来。

我们产品板子上用的LAN8720, 那我后续试验,就将LAN8720驱动拷贝过来就行。

my_nic.c/my_nic.h 是对网卡控制器驱动接口的封装, 因为只要不是从0开始,驱动都是现成的,拷贝过来就行。

为了不用改驱动(维护方便,换网卡驱动时,不用动驱动本身), 应该将对驱动的操作封装成接口,实现网卡初始化,网卡发送,网卡接收,和其他一些网卡逻辑操作。

用户层代码

在这里插入图片描述
用户层代码,用来调用my_nic.h 中定义的网卡驱动初始化接口, 然后启动网卡处理流程(查询方式或中断方式)

LWIP移植的细节

LWIP移植相关文件的修改

cc.h

/*
 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 * 
 * Author: Adam Dunkels <adam@sics.se>
 *
 */
#ifndef LWIP_ARCH_CC_H
#define LWIP_ARCH_CC_H

#ifdef _MSC_VER
#pragma warning (disable: 4127) /* conditional expression is constant */
#pragma warning (disable: 4996) /* 'strncpy' was declared deprecated */
#pragma warning (disable: 4103) /* structure packing changed by including file */
#pragma warning (disable: 4820) /* 'x' bytes padding added after data member 'y' */
#pragma warning (disable: 4711) /* The compiler performed inlining on the given function, although it was not marked for inlining */
#endif

// \lwip\include\lwip\arch.h(48) : #include "arch/cc.h"
#ifndef LWIP_PROVIDE_ERRNO
#define LWIP_PROVIDE_ERRNO // 提供LWIP底层错误号, 如果报错,能看到具体错误是啥
#endif // #ifndef LWIP_PROVIDE_ERRNO

// \lwip\include\lwip\arch.h 中定义了lwip自己的数据类型 e.g. u8_t, 不用在这里定义
// typedef uint8_t   u8_t; // on \lwip\arch.h

// 结构体对齐的内存定义
// PACK_STRUCT_BEGIN 和 PACK_STRUCT_STRUCT 定义一个就行,但是定义了哪个就要使用哪个
// 或者定义结构时如下
/*
PACK_STRUCT_BEGIN
typedef struct _tag_info {
  PACK_STRUCT_FIELD(u8_t c1);
  PACK_STRUCT_FIELD(u32_t u1);
} TAG_INFO PACK_STRUCT_STRUCT;
PACK_STRUCT_END
*/

// 结构体内存字节对齐相关宏

// 结构体定义前的包含
#ifndef PACK_STRUCT_BEGIN
#define PACK_STRUCT_BEGIN
#endif /* PACK_STRUCT_BEGIN */

// 结构体定义后的包含
#ifndef PACK_STRUCT_END
#define PACK_STRUCT_END
#endif /* PACK_STRUCT_END */

// 结构体定义时的字节对齐
#ifndef PACK_STRUCT_STRUCT
#define PACK_STRUCT_STRUCT __attribute__((packed)) // 1字节对齐
#endif /* PACK_STRUCT_STRUCT */

// 结构体字段定义的内存对齐
#ifndef PACK_STRUCT_FIELD
#define PACK_STRUCT_FIELD(x) x
#endif /* PACK_STRUCT_FIELD */

/* Define an example for LWIP_PLATFORM_DIAG: since this uses varargs and the old
 * C standard lwIP targets does not support this in macros, we have extra brackets
 * around the arguments, which are left out in the following macro definition:
 */
 // 自己提供lwip提示信息处理函数
void lwip_stm32_platform_diag(const char *format, ...);
#define LWIP_PLATFORM_DIAG(x) lwip_stm32_platform_diag x

// lwip断言信息处理函数
#ifndef LWIP_PLATFORM_ASSERT
void lwip_stm32_platform_assert(const char *format, ...);
#endif

// lwip错误处理函数, 可以将打印那句改成自己的错误处理函数
#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
  printf("Assertion \"%s\" failed at line %d in %s\n", message, __LINE__, __FILE__); \
  fflush(NULL);handler;} } while(0)

/* Define platform endianness (might already be defined) */
// 网卡数据接收发送都是小端格式的数据
#ifndef BYTE_ORDER
#define BYTE_ORDER LITTLE_ENDIAN
#endif /* BYTE_ORDER */

typedef int sys_prot_t;

#ifdef _MSC_VER
/* define _INTPTR for Win32 MSVC stdint.h */
#define _INTPTR 2

/* Do not use lwIP default definitions for format strings 
 * because these do not work with MSVC 2010 compiler (no inttypes.h)
 */
#define LWIP_NO_INTTYPES_H 1

/* Define (sn)printf formatters for these lwIP types */
// 告诉lwip数据格式化时的选项 e.g. printf("%" X8_F "\n", uc_dat); => printf("%02x\n", uc_dat);
#define X8_F  "02x"
#define U16_F "hu"
#define U32_F "lu"
#define S32_F "ld"
#define X32_F "lx"

#define S16_F "hd"
#define X16_F "hx"
#define SZT_F "lu"
#endif /* _MSC_VER */

/* Compiler hints for packing structures */
// 是否使用 #pragma pack(push,1)/#pragma pack(pop) 来包裹其他实现来实现1字节对齐
// 看lwip实现, 就是将#pragma pack(push,1) 定义在 bpstruct.h
// #pragma pack(pop) 定义在 epstruct.h
// 然后根据PACK_STRUCT_USE_INCLUDES 包上这2个头文件(bpstruct.h/epstruct.h)
#define PACK_STRUCT_USE_INCLUDES

#ifdef _MSC_VER
/* C runtime functions redefined */
#if _MSC_VER < 1910
#define snprintf _snprintf
#endif
#define strdup   _strdup
#endif

// 如果定义了LWIP_NORAND, 需要自己提供随机数发生器
#ifndef LWIP_NORAND
extern unsigned int sys_win_rand(void);
#define LWIP_RAND() (sys_win_rand())
#endif

#define PPP_INCLUDE_SETTINGS_HEADER

#endif /* LWIP_ARCH_CC_H */

lwipopts.h

这个文件是配置文件, 在自己工程中,根据实际情况(片上内存的剩余量),可以调整这个配置文件, 来微调lwip的性能。参数需要调成啥,要根据自己板子的实际情况来。如果微调的不合适,可以通过打印出的lwip底层日志看问题出在哪.

e.g.
是否要打印日志?
日志级别?
是否响应ipv6?
内存预分配多大?
最多要响应多少个网络连接?

如果不是特殊要求,默认的配置就能工作的很好。

/*
 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 * 
 * Author: Adam Dunkels <adam@sics.se>
 *
 */
#ifndef LWIP_LWIPOPTS_H
#define LWIP_LWIPOPTS_H

#ifdef LWIP_OPTTEST_FILE
#include "lwipopts_test.h"
#else /* LWIP_OPTTEST_FILE */

// 网络通讯包是IPV4还是IPV6?
#define LWIP_IPV4                  1
// #define LWIP_IPV6                  1

// 是否有系统,如果有系统,就要提供一些同步函数
#define NO_SYS                     1 // 无系统 = 1, 有系统 = 0
#define LWIP_SOCKET                (NO_SYS==0)
#define LWIP_NETCONN               (NO_SYS==0)
#define LWIP_NETIF_API             (NO_SYS==0)

#define LWIP_IGMP                  LWIP_IPV4
#define LWIP_ICMP                  LWIP_IPV4

#define LWIP_SNMP                  LWIP_UDP
#define MIB2_STATS                 LWIP_SNMP
#ifdef LWIP_HAVE_MBEDTLS
#define LWIP_SNMP_V3               (LWIP_SNMP)
#endif

#define LWIP_DNS                   LWIP_UDP
#define LWIP_MDNS_RESPONDER        LWIP_UDP

#define LWIP_NUM_NETIF_CLIENT_DATA (LWIP_MDNS_RESPONDER)

#define LWIP_HAVE_LOOPIF           1
#define LWIP_NETIF_LOOPBACK        1
#define LWIP_LOOPBACK_MAX_PBUFS    10

#define TCP_LISTEN_BACKLOG         1

#define LWIP_COMPAT_SOCKETS        1
#define LWIP_SO_RCVTIMEO           1
#define LWIP_SO_RCVBUF             1

#define LWIP_TCPIP_CORE_LOCKING    1

// 在网络通讯时,是否使用回调
#define LWIP_NETIF_LINK_CALLBACK        1
#define LWIP_NETIF_STATUS_CALLBACK      1
#define LWIP_NETIF_EXT_STATUS_CALLBACK  1

// 调试模式定义, 可以打印调试信息
// LWIP_DEBUG + LWIP_DBG_MIN_LEVEL + X_DEBUG 就可以决定打印出哪种类别(e.g. ICMP or TCP or IP)的哪种严重程度的日志(所有, 警告, 一般错误, 严重错误)
#if !defined(LWIP_DEBUG)
#define LWIP_DEBUG
#endif

#ifdef LWIP_DEBUG

// 日志级别
//  /** Debug level: ALL messages*/
//  #define LWIP_DBG_LEVEL_ALL     0x00 // 所有
//  /** Debug level: Warnings. bad checksums, dropped packets, ... */
//  #define LWIP_DBG_LEVEL_WARNING 0x01 // 警告
//  /** Debug level: Serious. memory allocation failures, ... */
//  #define LWIP_DBG_LEVEL_SERIOUS 0x02 // 一般错误
//  /** Debug level: Severe */
//  #define LWIP_DBG_LEVEL_SEVERE  0x03 // 严重错误

// LWIP_DBG_MIN_LEVEL 设置的范围如上 : LWIP_DBG_LEVEL_ALL ~ LWIP_DBG_LEVEL_SEVERE

// 如果LWIP_DBG_MIN_LEVEL不设置为LWIP_DBG_LEVEL_ALL, 那正常运行时,是看不到LWIP日志的
#define LWIP_DBG_MIN_LEVEL         LWIP_DBG_LEVEL_ALL

// 日志种类
// value range : LWIP_DBG_OFF/LWIP_DBG_ON
// 要打印哪种类型的通讯调试信息,就将下面的宏改为LWIP_DBG_ON,不要调试信息为LWIP_DBG_OFF
#define PPP_DEBUG                  LWIP_DBG_ON
#define MEM_DEBUG                  LWIP_DBG_ON
#define MEMP_DEBUG                 LWIP_DBG_ON
#define PBUF_DEBUG                 LWIP_DBG_ON
#define API_LIB_DEBUG              LWIP_DBG_ON
#define API_MSG_DEBUG              LWIP_DBG_ON
#define TCPIP_DEBUG                LWIP_DBG_ON
#define NETIF_DEBUG                LWIP_DBG_ON
#define SOCKETS_DEBUG              LWIP_DBG_ON
#define DNS_DEBUG                  LWIP_DBG_ON
#define AUTOIP_DEBUG               LWIP_DBG_ON
#define DHCP_DEBUG                 LWIP_DBG_ON
#define IP_DEBUG                   LWIP_DBG_ON
#define IP_REASS_DEBUG             LWIP_DBG_ON
#define ICMP_DEBUG                 LWIP_DBG_ON
#define IGMP_DEBUG                 LWIP_DBG_ON
#define UDP_DEBUG                  LWIP_DBG_ON
#define TCP_DEBUG                  LWIP_DBG_ON
#define TCP_INPUT_DEBUG            LWIP_DBG_ON
#define TCP_OUTPUT_DEBUG           LWIP_DBG_ON
#define TCP_RTO_DEBUG              LWIP_DBG_ON
#define TCP_CWND_DEBUG             LWIP_DBG_ON
#define TCP_WND_DEBUG              LWIP_DBG_ON
#define TCP_FR_DEBUG               LWIP_DBG_ON
#define TCP_QLEN_DEBUG             LWIP_DBG_ON
#define TCP_RST_DEBUG              LWIP_DBG_ON
#endif

#define LWIP_DBG_TYPES_ON         (LWIP_DBG_ON|LWIP_DBG_TRACE|LWIP_DBG_STATE|LWIP_DBG_FRESH|LWIP_DBG_HALT)


/* ---------- 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. */
/* MSVC port: intel processors don't need 4-byte alignment,
   but are faster that way! */
#define MEM_ALIGNMENT           4U

/* 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               10240

/* 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           16
/* MEMP_NUM_RAW_PCB: the number of UDP protocol control blocks. One
   per active RAW "connection". */
#define MEMP_NUM_RAW_PCB        3
/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
   per active UDP "connection". */
#define MEMP_NUM_UDP_PCB        4
/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP
   connections. */
#define MEMP_NUM_TCP_PCB        5
/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP
   connections. */
#define MEMP_NUM_TCP_PCB_LISTEN 8
/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP
   segments. */
#define MEMP_NUM_TCP_SEG        16
/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
   timeouts. */
#define MEMP_NUM_SYS_TIMEOUT    17

/* The following four are used only with the sequential API and can be
   set to 0 if the application only will use the raw API. */
/* MEMP_NUM_NETBUF: the number of struct netbufs. */
#define MEMP_NUM_NETBUF         2
/* MEMP_NUM_NETCONN: the number of struct netconns. */
#define MEMP_NUM_NETCONN        10
/* MEMP_NUM_TCPIP_MSG_*: the number of struct tcpip_msg, which is used
   for sequential API communication and incoming packets. Used in
   src/api/tcpip.c. */
#define MEMP_NUM_TCPIP_MSG_API   16
#define MEMP_NUM_TCPIP_MSG_INPKT 16


/* ---------- Pbuf options ---------- */
/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */
#define PBUF_POOL_SIZE          120

/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */
#define PBUF_POOL_BUFSIZE       256

/** SYS_LIGHTWEIGHT_PROT
 * define SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection
 * for certain critical regions during buffer allocation, deallocation and memory
 * allocation and deallocation.
 */
#define SYS_LIGHTWEIGHT_PROT    (NO_SYS==0)


/* ---------- TCP options ---------- */
#define LWIP_TCP                1
#define TCP_TTL                 255

#define LWIP_ALTCP              (LWIP_TCP)
#ifdef LWIP_HAVE_MBEDTLS
#define LWIP_ALTCP_TLS          (LWIP_TCP)
#define LWIP_ALTCP_TLS_MBEDTLS  (LWIP_TCP)
#endif


/* 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         1

/* TCP Maximum segment size. */
#define TCP_MSS                 1024

/* TCP sender buffer space (bytes). */
#define TCP_SND_BUF             2048

/* TCP sender buffer space (pbufs). This must be at least = 2 *
   TCP_SND_BUF/TCP_MSS for things to work. */
#define TCP_SND_QUEUELEN       (4 * TCP_SND_BUF/TCP_MSS)

/* TCP writable space (bytes). This must be less than or equal
   to TCP_SND_BUF. It is the amount of space which must be
   available in the tcp snd_buf for select to return writable */
#define TCP_SNDLOWAT           (TCP_SND_BUF/2)

/* TCP receive window. */
#define TCP_WND                 (20 * 1024)

/* Maximum number of retransmissions of data segments. */
#define TCP_MAXRTX              12

/* Maximum number of retransmissions of SYN segments. */
#define TCP_SYNMAXRTX           4


/* ---------- ARP options ---------- */
#define LWIP_ARP                1
#define ARP_TABLE_SIZE          10
#define ARP_QUEUEING            1


/* ---------- IP options ---------- */
/* Define IP_FORWARD to 1 if you wish to have the ability to forward
   IP packets across network interfaces. If you are going to run lwIP
   on a device with only one network interface, define this to 0. */
#define IP_FORWARD              1

/* IP reassembly and segmentation.These are orthogonal even
 * if they both deal with IP fragments */
#define IP_REASSEMBLY           1
#define IP_REASS_MAX_PBUFS      (10 * ((1500 + PBUF_POOL_BUFSIZE - 1) / PBUF_POOL_BUFSIZE))
#define MEMP_NUM_REASSDATA      IP_REASS_MAX_PBUFS
#define IP_FRAG                 1
#define IPV6_FRAG_COPYHEADER    1

/* ---------- ICMP options ---------- */
#define ICMP_TTL                255


/* ---------- DHCP options ---------- */
/* Define LWIP_DHCP to 1 if you want DHCP configuration of
   interfaces. */
#define LWIP_DHCP               LWIP_UDP

/* 1 if you want to do an ARP check on the offered address
   (recommended). */
#define DHCP_DOES_ARP_CHECK    (LWIP_DHCP)


/* ---------- AUTOIP options ------- */
#define LWIP_AUTOIP            (LWIP_DHCP)
#define LWIP_DHCP_AUTOIP_COOP  (LWIP_DHCP && LWIP_AUTOIP)


/* ---------- UDP options ---------- */
#define LWIP_UDP                1
#define LWIP_UDPLITE            LWIP_UDP
#define UDP_TTL                 255


/* ---------- RAW options ---------- */
#define LWIP_RAW                1


/* ---------- Statistics options ---------- */

#define LWIP_STATS              1
#define LWIP_STATS_DISPLAY      1

#if LWIP_STATS
#define LINK_STATS              1
#define IP_STATS                1
#define ICMP_STATS              1
#define IGMP_STATS              1
#define IPFRAG_STATS            1
#define UDP_STATS               1
#define TCP_STATS               1
#define MEM_STATS               1
#define MEMP_STATS              1
#define PBUF_STATS              1
#define SYS_STATS               1
#endif /* LWIP_STATS */

/* ---------- NETBIOS options ---------- */
#define LWIP_NETBIOS_RESPOND_NAME_QUERY 1

/* ---------- PPP options ---------- */

// #define PPP_SUPPORT             1      /* Set > 0 for PPP */

#if PPP_SUPPORT

#define NUM_PPP                 1      /* Max PPP sessions. */


/* Select modules to enable.  Ideally these would be set in the makefile but
 * we're limited by the command line length so you need to modify the settings
 * in this file.
 */
#define PPPOE_SUPPORT           1
#define PPPOS_SUPPORT           1

#define PAP_SUPPORT             1      /* Set > 0 for PAP. */
#define CHAP_SUPPORT            1      /* Set > 0 for CHAP. */
#define MSCHAP_SUPPORT          0      /* Set > 0 for MSCHAP */
#define CBCP_SUPPORT            0      /* Set > 0 for CBCP (NOT FUNCTIONAL!) */
#define CCP_SUPPORT             0      /* Set > 0 for CCP */
#define VJ_SUPPORT              1      /* Set > 0 for VJ header compression. */
#define MD5_SUPPORT             1      /* Set > 0 for MD5 (see also CHAP) */

#endif /* PPP_SUPPORT */

#endif /* LWIP_OPTTEST_FILE */

/* The following defines must be done even in OPTTEST mode: */

#if !defined(NO_SYS) || !NO_SYS /* default is 0 */
void sys_check_core_locking(void);
#define LWIP_ASSERT_CORE_LOCKED()  sys_check_core_locking()
void sys_mark_tcpip_thread(void);
#define LWIP_MARK_TCPIP_THREAD()   sys_mark_tcpip_thread()

#if !defined(LWIP_TCPIP_CORE_LOCKING) || LWIP_TCPIP_CORE_LOCKING /* default is 1 */
void sys_lock_tcpip_core(void);
#define LOCK_TCPIP_CORE()          sys_lock_tcpip_core()
void sys_unlock_tcpip_core(void);
#define UNLOCK_TCPIP_CORE()        sys_unlock_tcpip_core()
#endif
#endif

#endif /* LWIP_LWIPOPTS_H */

sys_arch.c

提供一些lwip要求的函数
被注释掉的函数是贡献者代码中提供的带操作系统相关的函数。
如果带了操作系统, 编译不过时,缺哪个函数就实现哪个函数。

/*
 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 * 
 * Author: Adam Dunkels <adam@sics.se>
 *         Simon Goldschmidt
 *
 */

#include "main.h"

#include <stdlib.h>
#include <stdio.h> /* sprintf() for task names */

#ifdef _MSC_VER
#pragma warning (push, 3)
#endif
// #include <windows.h>
#ifdef _MSC_VER
#pragma warning (pop)
#endif
#include <time.h>

#include <lwip/opt.h>
#include <lwip/arch.h>
#include <lwip/stats.h>
#include <lwip/debug.h>
#include <lwip/sys.h>
#include <lwip/tcpip.h>
#include <lwip/err.h>

//  /** Set this to 1 to enable assertion checks that SYS_ARCH_PROTECT() is only
//   * called once in a call stack (calling it nested might cause trouble in some
//   * implementations, so let's avoid this in core code as long as we can).
//   */
//  #ifndef LWIP_SYS_ARCH_CHECK_NESTED_PROTECT
//  #define LWIP_SYS_ARCH_CHECK_NESTED_PROTECT 1
//  #endif

//  /** Set this to 1 to enable assertion checks that SYS_ARCH_PROTECT() is *not*
//   * called before functions potentiolly involving the OS scheduler.
//   *
//   * This scheme is currently broken only for non-core-locking when waking up
//   * threads waiting on a socket via select/poll.
//   */
//  #ifndef LWIP_SYS_ARCH_CHECK_SCHEDULING_UNPROTECTED
//  #define LWIP_SYS_ARCH_CHECK_SCHEDULING_UNPROTECTED LWIP_TCPIP_CORE_LOCKING
//  #endif

//  #define LWIP_WIN32_SYS_ARCH_ENABLE_PROTECT_COUNTER (LWIP_SYS_ARCH_CHECK_NESTED_PROTECT || LWIP_SYS_ARCH_CHECK_SCHEDULING_UNPROTECTED)

//  /* These functions are used from NO_SYS also, for precise timer triggering */
//  static LARGE_INTEGER freq, sys_start_time;
//  #define SYS_INITIALIZED() (freq.QuadPart != 0)

//  static DWORD netconn_sem_tls_index;

//  static HCRYPTPROV hcrypt;

//  u32_t
//  sys_win_rand(void)
//  {
//    u32_t ret;
//    if (CryptGenRandom(hcrypt, sizeof(ret), (BYTE*)&ret)) {
//      return ret;
//    }
//    LWIP_ASSERT("CryptGenRandom failed", 0);
//    return 0;
//  }

extern int g_i_tick_cnt;

// lwip需要的随机数发生器,根据需要实现。
unsigned int sys_win_rand(void)
{
  // 产生随机数的函数

  // @todo by ls
  return 0;
}

//  static void
//  sys_win_rand_init(void)
//  {
//    if (!CryptAcquireContext(&hcrypt, NULL, NULL, PROV_RSA_FULL, 0)) {
//      DWORD err = GetLastError();
//      LWIP_PLATFORM_DIAG(("CryptAcquireContext failed with error %d, trying to create NEWKEYSET", (int)err));
//      if(!CryptAcquireContext(&hcrypt, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
//        char errbuf[128];
//        err = GetLastError();
//        snprintf(errbuf, sizeof(errbuf), "CryptAcquireContext failed with error %d", (int)err);
//        LWIP_UNUSED_ARG(err);
//        LWIP_ASSERT(errbuf, 0);
//      }
//    }
//  }

//  static void
//  sys_init_timing(void)
//  {
//    QueryPerformanceFrequency(&freq);
//    QueryPerformanceCounter(&sys_start_time);
//  }

//  static LONGLONG
//  sys_get_ms_longlong(void)
//  {
//    LONGLONG ret;
//    LARGE_INTEGER now;
//  #if NO_SYS
//    if (!SYS_INITIALIZED()) {
//      sys_init();
//      LWIP_ASSERT("initialization failed", SYS_INITIALIZED());
//    }
//  #endif /* NO_SYS */
//    QueryPerformanceCounter(&now);
//    ret = now.QuadPart-sys_start_time.QuadPart;
//    return (u32_t)(((ret)*1000)/freq.QuadPart);
//  }

//  u32_t
//  sys_jiffies(void)
//  {
//    return (u32_t)sys_get_ms_longlong();
//  }

//  u32_t
//  sys_now(void)
//  {
//    return (u32_t)sys_get_ms_longlong();
//  }

// 提供系统时间
// 现在用的是时钟tick, 每ms发生一次的tick
u32_t sys_now(void)
{
  // 提供当前的tick
  
  // @note by ls
  return g_i_tick_cnt;
}

//  CRITICAL_SECTION critSec;
//  #if LWIP_WIN32_SYS_ARCH_ENABLE_PROTECT_COUNTER
//  static int protection_depth;
//  #endif

//  static void
//  InitSysArchProtect(void)
//  {
//    InitializeCriticalSection(&critSec);
//  }

//  sys_prot_t
//  sys_arch_protect(void)
//  {
//  #if NO_SYS
//    if (!SYS_INITIALIZED()) {
//      sys_init();
//      LWIP_ASSERT("initialization failed", SYS_INITIALIZED());
//    }
//  #endif
//    EnterCriticalSection(&critSec);
//  #if LWIP_SYS_ARCH_CHECK_NESTED_PROTECT
//    LWIP_ASSERT("nested SYS_ARCH_PROTECT", protection_depth == 0);
//  #endif
//  #if LWIP_WIN32_SYS_ARCH_ENABLE_PROTECT_COUNTER
//    protection_depth++;
//  #endif
//    return 0;
//  }

//  void
//  sys_arch_unprotect(sys_prot_t pval)
//  {
//    LWIP_UNUSED_ARG(pval);
//  #if LWIP_SYS_ARCH_CHECK_NESTED_PROTECT
//    LWIP_ASSERT("missing SYS_ARCH_PROTECT", protection_depth == 1);
//  #else
//    LWIP_ASSERT("missing SYS_ARCH_PROTECT", protection_depth > 0);
//  #endif
//  #if LWIP_WIN32_SYS_ARCH_ENABLE_PROTECT_COUNTER
//    protection_depth--;
//  #endif
//    LeaveCriticalSection(&critSec);
//  }

//  #if LWIP_SYS_ARCH_CHECK_SCHEDULING_UNPROTECTED
//  /** This checks that SYS_ARCH_PROTECT() hasn't been called by protecting
//   * and then checking the level
//   */
//  static void
//  sys_arch_check_not_protected(void)
//  {
//    sys_arch_protect();
//    LWIP_ASSERT("SYS_ARCH_PROTECT before scheduling", protection_depth == 1);
//    sys_arch_unprotect(0);
//  }
//  #else
//  #define sys_arch_check_not_protected()
//  #endif

//  static void
//  msvc_sys_init(void)
//  {
//    sys_win_rand_init();
//    sys_init_timing();
//    InitSysArchProtect();
//    netconn_sem_tls_index = TlsAlloc();
//    LWIP_ASSERT("TlsAlloc failed", netconn_sem_tls_index != TLS_OUT_OF_INDEXES);
//  }

//  void
//  sys_init(void)
//  {
//    msvc_sys_init();
//  }

//  #if !NO_SYS

//  struct threadlist {
//    lwip_thread_fn function;
//    void *arg;
//    DWORD id;
//    struct threadlist *next;
//  };

//  static struct threadlist *lwip_win32_threads = NULL;

//  err_t
//  sys_sem_new(sys_sem_t *sem, u8_t count)
//  {
//    HANDLE new_sem = NULL;

//    LWIP_ASSERT("sem != NULL", sem != NULL);

//    new_sem = CreateSemaphore(0, count, 100000, 0);
//    LWIP_ASSERT("Error creating semaphore", new_sem != NULL);
//    if(new_sem != NULL) {
//      if (SYS_INITIALIZED()) {
//        SYS_ARCH_LOCKED(SYS_STATS_INC_USED(sem));
//      } else {
//        SYS_STATS_INC_USED(sem);
//      }
//  #if LWIP_STATS && SYS_STATS
//      LWIP_ASSERT("sys_sem_new() counter overflow", lwip_stats.sys.sem.used != 0);
//  #endif /* LWIP_STATS && SYS_STATS*/
//      sem->sem = new_sem;
//      return ERR_OK;
//    }
//     
//    /* failed to allocate memory... */
//    if (SYS_INITIALIZED()) {
//      SYS_ARCH_LOCKED(SYS_STATS_INC(sem.err));
//    } else {
//      SYS_STATS_INC(sem.err);
//    }
//    sem->sem = NULL;
//    return ERR_MEM;
//  }

//  void
//  sys_sem_free(sys_sem_t *sem)
//  {
//    /* parameter check */
//    LWIP_ASSERT("sem != NULL", sem != NULL);
//    LWIP_ASSERT("sem->sem != NULL", sem->sem != NULL);
//    LWIP_ASSERT("sem->sem != INVALID_HANDLE_VALUE", sem->sem != INVALID_HANDLE_VALUE);
//    CloseHandle(sem->sem);

//    SYS_ARCH_LOCKED(SYS_STATS_DEC(sem.used));
//  #if LWIP_STATS && SYS_STATS
//    LWIP_ASSERT("sys_sem_free() closed more than created", lwip_stats.sys.sem.used != (u16_t)-1);
//  #endif /* LWIP_STATS && SYS_STATS */
//    sem->sem = NULL;
//  }

//  u32_t
//  sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
//  {
//    DWORD ret;
//    LONGLONG starttime, endtime;
//    LWIP_ASSERT("sem != NULL", sem != NULL);
//    LWIP_ASSERT("sem->sem != NULL", sem->sem != NULL);
//    LWIP_ASSERT("sem->sem != INVALID_HANDLE_VALUE", sem->sem != INVALID_HANDLE_VALUE);
//    if (!timeout) {
//      /* wait infinite */
//      starttime = sys_get_ms_longlong();
//      ret = WaitForSingleObject(sem->sem, INFINITE);
//      LWIP_ASSERT("Error waiting for semaphore", ret == WAIT_OBJECT_0);
//      endtime = sys_get_ms_longlong();
//      /* return the time we waited for the sem */
//      return (u32_t)(endtime - starttime);
//    } else {
//      starttime = sys_get_ms_longlong();
//      ret = WaitForSingleObject(sem->sem, timeout);
//      LWIP_ASSERT("Error waiting for semaphore", (ret == WAIT_OBJECT_0) || (ret == WAIT_TIMEOUT));
//      if (ret == WAIT_OBJECT_0) {
//        endtime = sys_get_ms_longlong();
//        /* return the time we waited for the sem */
//        return (u32_t)(endtime - starttime);
//      } else {
//        /* timeout */
//        return SYS_ARCH_TIMEOUT;
//      }
//    }
//  }

//  void
//  sys_sem_signal(sys_sem_t *sem)
//  {
//    BOOL ret;
//    sys_arch_check_not_protected();
//    LWIP_ASSERT("sem != NULL", sem != NULL);
//    LWIP_ASSERT("sem->sem != NULL", sem->sem != NULL);
//    LWIP_ASSERT("sem->sem != INVALID_HANDLE_VALUE", sem->sem != INVALID_HANDLE_VALUE);
//    ret = ReleaseSemaphore(sem->sem, 1, NULL);
//    LWIP_ASSERT("Error releasing semaphore", ret != 0);
//    LWIP_UNUSED_ARG(ret);
//  }

//  err_t
//  sys_mutex_new(sys_mutex_t *mutex)
//  {
//    HANDLE new_mut = NULL;

//    LWIP_ASSERT("mutex != NULL", mutex != NULL);

//    new_mut = CreateMutex(NULL, FALSE, NULL);
//    LWIP_ASSERT("Error creating mutex", new_mut != NULL);
//    if (new_mut != NULL) {
//      SYS_ARCH_LOCKED(SYS_STATS_INC_USED(mutex));
//  #if LWIP_STATS && SYS_STATS
//      LWIP_ASSERT("sys_mutex_new() counter overflow", lwip_stats.sys.mutex.used != 0);
//  #endif /* LWIP_STATS && SYS_STATS*/
//      mutex->mut = new_mut;
//      return ERR_OK;
//    }
//     
//    /* failed to allocate memory... */
//    SYS_ARCH_LOCKED(SYS_STATS_INC(mutex.err));
//    mutex->mut = NULL;
//    return ERR_MEM;
//  }

//  void
//  sys_mutex_free(sys_mutex_t *mutex)
//  {
//    /* parameter check */
//    LWIP_ASSERT("mutex != NULL", mutex != NULL);
//    LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL);
//    LWIP_ASSERT("mutex->mut != INVALID_HANDLE_VALUE", mutex->mut != INVALID_HANDLE_VALUE);
//    CloseHandle(mutex->mut);

//    SYS_ARCH_LOCKED(SYS_STATS_DEC(mutex.used));
//  #if LWIP_STATS && SYS_STATS
//    LWIP_ASSERT("sys_mutex_free() closed more than created", lwip_stats.sys.mutex.used != (u16_t)-1);
//  #endif /* LWIP_STATS && SYS_STATS */
//    mutex->mut = NULL;
//  }

//  void sys_mutex_lock(sys_mutex_t *mutex)
//  {
//    DWORD ret;
//    LWIP_ASSERT("mutex != NULL", mutex != NULL);
//    LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL);
//    LWIP_ASSERT("mutex->mut != INVALID_HANDLE_VALUE", mutex->mut != INVALID_HANDLE_VALUE);
//    /* wait infinite */
//    ret = WaitForSingleObject(mutex->mut, INFINITE);
//    LWIP_ASSERT("Error waiting for mutex", ret == WAIT_OBJECT_0);
//    LWIP_UNUSED_ARG(ret);
//  }

//  void
//  sys_mutex_unlock(sys_mutex_t *mutex)
//  {
//    sys_arch_check_not_protected();
//    LWIP_ASSERT("mutex != NULL", mutex != NULL);
//    LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL);
//    LWIP_ASSERT("mutex->mut != INVALID_HANDLE_VALUE", mutex->mut != INVALID_HANDLE_VALUE);
//    /* wait infinite */
//    if (!ReleaseMutex(mutex->mut)) {
//      LWIP_ASSERT("Error releasing mutex", 0);
//    }
//  }


//  #ifdef _MSC_VER
//  const DWORD MS_VC_EXCEPTION=0x406D1388;
//  #pragma pack(push,8)
//  typedef struct tagTHREADNAME_INFO
//  {
//    DWORD dwType; /* Must be 0x1000. */
//    LPCSTR szName; /* Pointer to name (in user addr space). */
//    DWORD dwThreadID; /* Thread ID (-1=caller thread). */
//    DWORD dwFlags; /* Reserved for future use, must be zero. */
//  } THREADNAME_INFO;
//  #pragma pack(pop)

//  static void
//  SetThreadName(DWORD dwThreadID, const char* threadName)
//  {
//    THREADNAME_INFO info;
//    info.dwType = 0x1000;
//    info.szName = threadName;
//    info.dwThreadID = dwThreadID;
//    info.dwFlags = 0;

//    __try {
//      RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
//    }
//    __except(EXCEPTION_EXECUTE_HANDLER) {
//    }
//  }
//  #else /* _MSC_VER */
//  static void
//  SetThreadName(DWORD dwThreadID, const char* threadName)
//  {
//    LWIP_UNUSED_ARG(dwThreadID);
//    LWIP_UNUSED_ARG(threadName);
//  }
//  #endif /* _MSC_VER */

//  static void
//  sys_thread_function(void* arg)
//  {
//    struct threadlist* t = (struct threadlist*)arg;
//  #if LWIP_NETCONN_SEM_PER_THREAD
//    sys_arch_netconn_sem_alloc();
//  #endif
//    t->function(t->arg);
//  #if LWIP_NETCONN_SEM_PER_THREAD
//    sys_arch_netconn_sem_free();
//  #endif
//  }

//  sys_thread_t
//  sys_thread_new(const char *name, lwip_thread_fn function, void *arg, int stacksize, int prio)
//  {
//    struct threadlist *new_thread;
//    HANDLE h;
//    SYS_ARCH_DECL_PROTECT(lev);

//    LWIP_UNUSED_ARG(name);
//    LWIP_UNUSED_ARG(stacksize);
//    LWIP_UNUSED_ARG(prio);

//    new_thread = (struct threadlist*)malloc(sizeof(struct threadlist));
//    LWIP_ASSERT("new_thread != NULL", new_thread != NULL);
//    if (new_thread != NULL) {
//      new_thread->function = function;
//      new_thread->arg = arg;
//      SYS_ARCH_PROTECT(lev);
//      new_thread->next = lwip_win32_threads;
//      lwip_win32_threads = new_thread;

//      h = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)sys_thread_function, new_thread, 0, &(new_thread->id));
//      LWIP_ASSERT("h != 0", h != 0);
//      LWIP_ASSERT("h != -1", h != INVALID_HANDLE_VALUE);
//      LWIP_UNUSED_ARG(h);
//      SetThreadName(new_thread->id, name);

//      SYS_ARCH_UNPROTECT(lev);
//      return new_thread->id;
//    }
//    return 0;
//  }

//  #if !NO_SYS
//  #if LWIP_TCPIP_CORE_LOCKING

//  static DWORD lwip_core_lock_holder_thread_id;

//  void
//  sys_lock_tcpip_core(void)
//  {
//    sys_mutex_lock(&lock_tcpip_core);
//    lwip_core_lock_holder_thread_id = GetCurrentThreadId();
//  }

//  void
//  sys_unlock_tcpip_core(void)
//  {
//    lwip_core_lock_holder_thread_id = 0;
//    sys_mutex_unlock(&lock_tcpip_core);
//  }
//  #endif /* LWIP_TCPIP_CORE_LOCKING */

//  static DWORD lwip_tcpip_thread_id;

//  void
//  sys_mark_tcpip_thread(void)
//  {
//    lwip_tcpip_thread_id = GetCurrentThreadId();
//  }

//  void
//  sys_check_core_locking(void)
//  {
//    /* Embedded systems should check we are NOT in an interrupt context here */

//    if (lwip_tcpip_thread_id != 0) {
//      DWORD current_thread_id = GetCurrentThreadId();

//  #if LWIP_TCPIP_CORE_LOCKING
//      LWIP_ASSERT("Function called without core lock", current_thread_id == lwip_core_lock_holder_thread_id);
//  #else /* LWIP_TCPIP_CORE_LOCKING */
//      LWIP_ASSERT("Function called from wrong thread", current_thread_id == lwip_tcpip_thread_id);
//  #endif /* LWIP_TCPIP_CORE_LOCKING */
//      LWIP_UNUSED_ARG(current_thread_id); /* for LWIP_NOASSERT */
//    }
//  }
//  #endif /* !NO_SYS */

//  err_t
//  sys_mbox_new(sys_mbox_t *mbox, int size)
//  {
//    LWIP_ASSERT("mbox != NULL", mbox != NULL);
//    LWIP_UNUSED_ARG(size);

//    mbox->sem = CreateSemaphore(0, 0, MAX_QUEUE_ENTRIES, 0);
//    LWIP_ASSERT("Error creating semaphore", mbox->sem != NULL);
//    if (mbox->sem == NULL) {
//      SYS_ARCH_LOCKED(SYS_STATS_INC(mbox.err));
//      return ERR_MEM;
//    }
//    memset(&mbox->q_mem, 0, sizeof(u32_t)*MAX_QUEUE_ENTRIES);
//    mbox->head = 0;
//    mbox->tail = 0;
//    SYS_ARCH_LOCKED(SYS_STATS_INC_USED(mbox));
//  #if LWIP_STATS && SYS_STATS
//    LWIP_ASSERT("sys_mbox_new() counter overflow", lwip_stats.sys.mbox.used != 0);
//  #endif /* LWIP_STATS && SYS_STATS */
//    return ERR_OK;
//  }

//  void
//  sys_mbox_free(sys_mbox_t *mbox)
//  {
//    /* parameter check */
//    LWIP_ASSERT("mbox != NULL", mbox != NULL);
//    LWIP_ASSERT("mbox->sem != NULL", mbox->sem != NULL);
//    LWIP_ASSERT("mbox->sem != INVALID_HANDLE_VALUE", mbox->sem != INVALID_HANDLE_VALUE);

//    CloseHandle(mbox->sem);

//    SYS_STATS_DEC(mbox.used);
//  #if LWIP_STATS && SYS_STATS
//    LWIP_ASSERT( "sys_mbox_free() ", lwip_stats.sys.mbox.used != (u16_t)-1);
//  #endif /* LWIP_STATS && SYS_STATS */
//    mbox->sem = NULL;
//  }

//  void
//  sys_mbox_post(sys_mbox_t *q, void *msg)
//  {
//    BOOL ret;
//    SYS_ARCH_DECL_PROTECT(lev);
//    sys_arch_check_not_protected();

//    /* parameter check */
//    LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
//    LWIP_ASSERT("q->sem != NULL", q->sem != NULL);
//    LWIP_ASSERT("q->sem != INVALID_HANDLE_VALUE", q->sem != INVALID_HANDLE_VALUE);

//    SYS_ARCH_PROTECT(lev);
//    q->q_mem[q->head] = msg;
//    q->head++;
//    if (q->head >= MAX_QUEUE_ENTRIES) {
//      q->head = 0;
//    }
//    LWIP_ASSERT("mbox is full!", q->head != q->tail);
//    ret = ReleaseSemaphore(q->sem, 1, 0);
//    LWIP_ASSERT("Error releasing sem", ret != 0);
//    LWIP_UNUSED_ARG(ret);

//    SYS_ARCH_UNPROTECT(lev);
//  }

//  err_t
//  sys_mbox_trypost(sys_mbox_t *q, void *msg)
//  {
//    u32_t new_head;
//    BOOL ret;
//    SYS_ARCH_DECL_PROTECT(lev);
//    sys_arch_check_not_protected();

//    /* parameter check */
//    LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
//    LWIP_ASSERT("q->sem != NULL", q->sem != NULL);
//    LWIP_ASSERT("q->sem != INVALID_HANDLE_VALUE", q->sem != INVALID_HANDLE_VALUE);

//    SYS_ARCH_PROTECT(lev);

//    new_head = q->head + 1;
//    if (new_head >= MAX_QUEUE_ENTRIES) {
//      new_head = 0;
//    }
//    if (new_head == q->tail) {
//      SYS_ARCH_UNPROTECT(lev);
//      return ERR_MEM;
//    }

//    q->q_mem[q->head] = msg;
//    q->head = new_head;
//    LWIP_ASSERT("mbox is full!", q->head != q->tail);
//    ret = ReleaseSemaphore(q->sem, 1, 0);
//    LWIP_ASSERT("Error releasing sem", ret != 0);
//    LWIP_UNUSED_ARG(ret);

//    SYS_ARCH_UNPROTECT(lev);
//    return ERR_OK;
//  }

//  err_t
//  sys_mbox_trypost_fromisr(sys_mbox_t *q, void *msg)
//  {
//    return sys_mbox_trypost(q, msg);
//  }

//  u32_t
//  sys_arch_mbox_fetch(sys_mbox_t *q, void **msg, u32_t timeout)
//  {
//    DWORD ret;
//    LONGLONG starttime, endtime;
//    SYS_ARCH_DECL_PROTECT(lev);

//    /* parameter check */
//    LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
//    LWIP_ASSERT("q->sem != NULL", q->sem != NULL);
//    LWIP_ASSERT("q->sem != INVALID_HANDLE_VALUE", q->sem != INVALID_HANDLE_VALUE);

//    if (timeout == 0) {
//      timeout = INFINITE;
//    }
//    starttime = sys_get_ms_longlong();
//    ret = WaitForSingleObject(q->sem, timeout);
//    if (ret == WAIT_OBJECT_0) {
//      SYS_ARCH_PROTECT(lev);
//      if (msg != NULL) {
//        *msg  = q->q_mem[q->tail];
//      }

//      q->tail++;
//      if (q->tail >= MAX_QUEUE_ENTRIES) {
//        q->tail = 0;
//      }
//      SYS_ARCH_UNPROTECT(lev);
//      endtime = sys_get_ms_longlong();
//      return (u32_t)(endtime - starttime);
//    } else {
//      LWIP_ASSERT("Error waiting for sem", ret == WAIT_TIMEOUT);
//      if (msg != NULL) {
//        *msg  = NULL;
//      }

//      return SYS_ARCH_TIMEOUT;
//    }
//  }

//  u32_t
//  sys_arch_mbox_tryfetch(sys_mbox_t *q, void **msg)
//  {
//    DWORD ret;
//    SYS_ARCH_DECL_PROTECT(lev);

//    /* parameter check */
//    LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
//    LWIP_ASSERT("q->sem != NULL", q->sem != NULL);
//    LWIP_ASSERT("q->sem != INVALID_HANDLE_VALUE", q->sem != INVALID_HANDLE_VALUE);

//    ret = WaitForSingleObject(q->sem, 0);
//    if (ret == WAIT_OBJECT_0) {
//      SYS_ARCH_PROTECT(lev);
//      if (msg != NULL) {
//        *msg  = q->q_mem[q->tail];
//      }

//      q->tail++;
//      if (q->tail >= MAX_QUEUE_ENTRIES) {
//        q->tail = 0;
//      }
//      SYS_ARCH_UNPROTECT(lev);
//      return 0;
//    } else {
//      LWIP_ASSERT("Error waiting for sem", ret == WAIT_TIMEOUT);
//      if (msg != NULL) {
//        *msg  = NULL;
//      }

//      return SYS_ARCH_TIMEOUT;
//    }
//  }

//  #if LWIP_NETCONN_SEM_PER_THREAD
//  sys_sem_t*
//  sys_arch_netconn_sem_get(void)
//  {
//    LPVOID tls_data = TlsGetValue(netconn_sem_tls_index);
//    return (sys_sem_t*)tls_data;
//  }

//  void
//  sys_arch_netconn_sem_alloc(void)
//  {
//    sys_sem_t *sem;
//    err_t err;
//    BOOL done;

//    sem = (sys_sem_t*)malloc(sizeof(sys_sem_t));
//    LWIP_ASSERT("failed to allocate memory for TLS semaphore", sem != NULL);
//    err = sys_sem_new(sem, 0);
//    LWIP_ASSERT("failed to initialise TLS semaphore", err == ERR_OK);
//    done = TlsSetValue(netconn_sem_tls_index, sem);
//    LWIP_UNUSED_ARG(done);
//    LWIP_ASSERT("failed to initialise TLS semaphore storage", done == TRUE);
//  }

//  void
//  sys_arch_netconn_sem_free(void)
//  {
//    LPVOID tls_data = TlsGetValue(netconn_sem_tls_index);
//    if (tls_data != NULL) {
//      BOOL done;
//      free(tls_data);
//      done = TlsSetValue(netconn_sem_tls_index, NULL);
//      LWIP_UNUSED_ARG(done);
//      LWIP_ASSERT("failed to de-init TLS semaphore storage", done == TRUE);
//    }
//  }
//  #endif /* LWIP_NETCONN_SEM_PER_THREAD */

//  #endif /* !NO_SYS */

//  /* get keyboard state to terminate the debug app on any kbhit event using win32 API */
//  int
//  lwip_win32_keypressed(void)
//  {
//    INPUT_RECORD rec;
//    DWORD num = 0;
//    HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
//    BOOL ret = PeekConsoleInput(h, &rec, 1, &num);
//    if (ret && num) {
//      ReadConsoleInput(h, &rec, 1, &num);
//      if (rec.EventType == KEY_EVENT) {
//        if (rec.Event.KeyEvent.bKeyDown) {
//          /* not a special key? */
//          if (rec.Event.KeyEvent.uChar.AsciiChar != 0) {
//            return 1;
//          }
//        }
//      }
//    }
//    return 0;
//  }

//  #include <stdarg.h>

//  /* This is an example implementation for LWIP_PLATFORM_DIAG:
//   * format a string and pass it to your output function.
//   */
//  void
//  lwip_win32_platform_diag(const char *format, ...)
//  {
//    va_list ap;
//    /* get the varargs */
//    va_start(ap, format);
//    /* print via varargs; to use another output function, you could use
//       vsnprintf here */
//    vprintf(format, ap);
//    va_end(ap);
//  }

// 给lwip提供信息处理函数
void lwip_stm32_platform_diag(const char *format, ...)
{
  // 打印格式化字符串的方法 - 1
  char sz_buf[0x100] = {'\0'};
  const char* psz_msg_prefix = "info:";
  
  va_list arglist;
  /* get the varargs */
  va_start(arglist, format);
  /* print via varargs; to use another output function, you could use
     vsnprintf here */
  // vprintf(format, ap);
  memset(sz_buf, 0, sizeof(sz_buf));
  
  // strncpy 并不会拷贝\0过去, 指定多长, 就拷贝多长(缓冲区后面都填0了)
  strncpy(sz_buf, psz_msg_prefix, sizeof(sz_buf) - 1); // 拷贝时, 留一个\0的位置
  
  // vsnprintf 会拷贝'\0', 不用特意留末尾'\0'位置
  vsnprintf(&sz_buf[strlen(psz_msg_prefix)], sizeof(sz_buf) - strlen(psz_msg_prefix), format, arglist);
  printf("%s", sz_buf);

  va_end(arglist);
}

// 给lwip提供断言处理函数
void lwip_stm32_platform_assert(const char *format, ...)
{
  // 打印格式化字符串的方法 - 2
  va_list ap;
  /* get the varargs */
  va_start(ap, format);
  /* print via varargs; to use another output function, you could use
     vsnprintf here */
  vprintf(format, ap); // 这个是C库提供的打印函数, 如果是自己
  va_end(ap);
}

// 给lwip提供错误号处理函数
const char *lwip_strerr(err_t err)
{
  const char* psz_err_string = NULL;
  char sz_err_sn[17] = {'\0'};
  
  switch (err) {
//  /** No error, everything OK. */
//    ERR_OK         = 0,
    case ERR_OK:
      psz_err_string = "No error, everything OK.";
      break;
    
//  /** Out of memory error.     */
//    ERR_MEM        = -1,
    case ERR_MEM:
      psz_err_string = "Out of memory error.";
      break;
    
//  /** Buffer error.            */
//    ERR_BUF        = -2,
    case ERR_BUF:
      psz_err_string = "Buffer error.";
      break;

//  /** Timeout.                 */
//    ERR_TIMEOUT    = -3,
    case ERR_TIMEOUT:
      psz_err_string = "Timeout.";
      break;

//  /** Routing problem.         */
//    ERR_RTE        = -4,
    case ERR_RTE:
      psz_err_string = "Routing problem.";
      break;

//  /** Operation in progress    */
//    ERR_INPROGRESS = -5,
    case ERR_INPROGRESS:
      psz_err_string = "Operation in progress";
      break;

//  /** Illegal value.           */
//    ERR_VAL        = -6,
    case ERR_VAL:
      psz_err_string = "Illegal value.";
      break;

//  /** Operation would block.   */
//    ERR_WOULDBLOCK = -7,
    case ERR_WOULDBLOCK:
      psz_err_string = "Operation would block.";
      break;

//  /** Address in use.          */
//    ERR_USE        = -8,
    case ERR_USE:
      psz_err_string = "Address in use.";
      break;

//  /** Already connecting.      */
//    ERR_ALREADY    = -9,
    case ERR_ALREADY:
      psz_err_string = "Already connecting.";
      break;

//  /** Conn already established.*/
//    ERR_ISCONN     = -10,
    case ERR_ISCONN:
      psz_err_string = "Conn already established.";
      break;

//  /** Not connected.           */
//    ERR_CONN       = -11,
    case ERR_CONN:
      psz_err_string = "Not connected.";
      break;

//  /** Low-level netif error    */
//    ERR_IF         = -12,
    case ERR_IF:
      psz_err_string = "Low-level netif error";
      break;

//  /** Connection aborted.      */
//    ERR_ABRT       = -13,
    case ERR_ABRT:
      psz_err_string = "Connection aborted.";
      break;

//  /** Connection reset.        */
//    ERR_RST        = -14,
    case ERR_RST:
      psz_err_string = "Connection reset.";
      break;

//  /** Connection closed.       */
//    ERR_CLSD       = -15,
    case ERR_CLSD:
      psz_err_string = "Connection closed.";
      break;

//  /** Illegal argument.        */
//    ERR_ARG        = -16
    case ERR_ARG:
      psz_err_string = "Illegal argument.";
      break;

    default:
      memset(sz_err_sn, 0, sizeof(sz_err_sn));
      snprintf(sz_err_sn, sizeof(sz_err_sn), "%d", err);
      psz_err_string = sz_err_sn;
      break;
  }
  
  return psz_err_string;
}

bpstruct.h

这个文件用来包含结构体字节对齐的开始语句.

#pragma pack(push,1)

epstruct.h

这个文件用来包含结构体字节对齐的结束语句

#pragma pack(pop)

ppp_settings.h

这个文件没用到,内容为空

#ifdef _MSC_VER
#pragma warning (disable: 4242) /* PPP only: conversion from 'x' to 'y', possible loss of data */
#pragma warning (disable: 4244) /* PPP only: conversion from 'x' to 'y', possible loss of data (again?) */
#pragma warning (disable: 4310) /* PPP only: cast truncates constant value */
#pragma warning (disable: 4706) /* PPP only: assignment within conditional expression */
#endif /* MSC_VER  */

sys_arch.h

这个文件提供带系统时的同步函数定义

/*
 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 * 
 * Author: Adam Dunkels <adam@sics.se>
 *
 */
#ifndef LWIP_ARCH_SYS_ARCH_H
#define LWIP_ARCH_SYS_ARCH_H

/* HANDLE is used for sys_sem_t but we won't include windows.h */
struct _sys_sem {
  void *sem;
};
typedef struct _sys_sem sys_sem_t;
#define sys_sem_valid_val(sema) (((sema).sem != NULL)  && ((sema).sem != (void*)-1))
#define sys_sem_valid(sema) (((sema) != NULL) && sys_sem_valid_val(*(sema)))
#define sys_sem_set_invalid(sema) ((sema)->sem = NULL)

/* HANDLE is used for sys_mutex_t but we won't include windows.h */
struct _sys_mut {
  void *mut;
};
typedef struct _sys_mut sys_mutex_t;
#define sys_mutex_valid_val(mutex) (((mutex).mut != NULL)  && ((mutex).mut != (void*)-1))
#define sys_mutex_valid(mutex) (((mutex) != NULL) && sys_mutex_valid_val(*(mutex)))
#define sys_mutex_set_invalid(mutex) ((mutex)->mut = NULL)

#ifndef MAX_QUEUE_ENTRIES
#define MAX_QUEUE_ENTRIES 100
#endif
struct lwip_mbox {
  void* sem;
  void* q_mem[MAX_QUEUE_ENTRIES];
  u32_t head, tail;
};
typedef struct lwip_mbox sys_mbox_t;
#define SYS_MBOX_NULL NULL
#define sys_mbox_valid_val(mbox) (((mbox).sem != NULL)  && ((mbox).sem != (void*)-1))
#define sys_mbox_valid(mbox) ((mbox != NULL) && sys_mbox_valid_val(*(mbox)))
#define sys_mbox_set_invalid(mbox) ((mbox)->sem = NULL)

/* DWORD (thread id) is used for sys_thread_t but we won't include windows.h */
typedef u32_t sys_thread_t;

sys_sem_t* sys_arch_netconn_sem_get(void);
void sys_arch_netconn_sem_alloc(void);
void sys_arch_netconn_sem_free(void);
#define LWIP_NETCONN_THREAD_SEM_GET()   sys_arch_netconn_sem_get()
#define LWIP_NETCONN_THREAD_SEM_ALLOC() sys_arch_netconn_sem_alloc()
#define LWIP_NETCONN_THREAD_SEM_FREE()  sys_arch_netconn_sem_free()

#define LWIP_EXAMPLE_APP_ABORT() lwip_win32_keypressed()
int lwip_win32_keypressed(void);

#endif /* LWIP_ARCH_SYS_ARCH_H */


ethernetif.c

这个文件实现网卡接口(封装调用实际的网卡驱动函数,相当于中间层)

/**
 * @file
 * Ethernet Interface Skeleton
 *
 */

/*
 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 *
 * Author: Adam Dunkels <adam@sics.se>
 *
 */

/*
 * This file is a skeleton for developing Ethernet network interface
 * drivers for lwIP. Add code to the low_level functions and do a
 * search-and-replace for the word "ethernetif" to replace it with
 * something that better describes your network interface.
 */

#include "lwip/opt.h"

#if 1 /* don't build, this is only a skeleton, see previous comment */

#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/stats.h"
#include "lwip/snmp.h"
#include "lwip/ethip6.h"
#include "lwip/etharp.h"
#include "netif/ppp/pppoe.h"

// 添加自己的网卡驱动头文件和自己的网卡操作中间层接口头文件
#include "ENC28J60.h"
#include "my_nic.h"

/* Define those to better describe your network interface. */
#define IFNAME0 'e'
#define IFNAME1 'n'

/**
 * Helper struct to hold private data used to operate your ethernet interface.
 * Keeping the ethernet address of the MAC in this struct is not necessary
 * as it is already kept in the struct netif.
 * But this is only an example, anyway...
 */
struct ethernetif {
  struct eth_addr *ethaddr;
  /* Add whatever per-interface state that is needed here. */
};

/* Forward declarations. */
void  ethernetif_input(struct netif *netif);

// 提供网卡初始化时的网卡地址, 如果网卡地址是根据自己的配置文件来的,这里要将使用MyMacID的地方改成接口来去网卡地址
// 4C-ED-FB-01-23-45
static const unsigned char MyMacID[6] = {0x4C,0xED,0xFB,0x01,0x23,0x45};

/**
 * In this function, the hardware should be initialized.
 * Called from ethernetif_init().
 *
 * @param netif the already initialized lwip network interface structure
 *        for this ethernetif
 */
 // 为lwip提供网卡初始化函数
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] = MyMacID[0];
  netif->hwaddr[1] = MyMacID[1];
  netif->hwaddr[2] = MyMacID[2];
  netif->hwaddr[3] = MyMacID[3];
  netif->hwaddr[4] = MyMacID[4];                
  netif->hwaddr[5] = MyMacID[5];

  /* 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;

#if LWIP_IPV6 && LWIP_IPV6_MLD
  /*
   * For hardware/netifs that implement MAC filtering.
   * All-nodes link-local is handled by default, so we must let the hardware know
   * to allow multicast packets in.
   * Should set mld_mac_filter previously. */
  if (netif->mld_mac_filter != NULL) {
    ip6_addr_t ip6_allnodes_ll;
    ip6_addr_set_allnodes_linklocal(&ip6_allnodes_ll);
    netif->mld_mac_filter(netif, &ip6_allnodes_ll, NETIF_ADD_MAC_FILTER);
  }
#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */

  /* Do whatever else is needed to initialize interface. */
  mymacinit(MyMacID); // 调用自己实际的网卡初始化函数
}

/**
 * This function should do the actual transmission of the packet. The packet is
 * contained in the pbuf that is passed to the function. This pbuf
 * might be chained.
 *
 * @param netif the lwip network interface structure for this ethernetif
 * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
 * @return ERR_OK if the packet could be sent
 *         an err_t value if the packet couldn't be sent
 *
 * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
 *       strange results. You might consider waiting for space in the DMA queue
 *       to become available since the stack doesn't retry to send a packet
 *       dropped because of memory failure (except for the TCP timers).
 */

// 为lwip提供网卡数据send实现
static err_t
low_level_output(struct netif *netif, struct pbuf *p)
{
  return PacketSend(p); // 调用自己的网卡数据发送函数
  
//  // struct ethernetif *ethernetif = netif->state;
//  struct pbuf *q;

//  // initiate transfer();

//#if ETH_PAD_SIZE
//  pbuf_remove_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. */
//    // send data from(q->payload, q->len);
//  }

//  // signal that packet should be sent();

//  MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p->tot_len);
//  if (((u8_t *)p->payload)[0] & 1) {
//    /* broadcast or multicast packet*/
//    MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts);
//  } else {
//    /* unicast packet */
//    MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
//  }
//  /* increase ifoutdiscards or ifouterrors on error */

//#if ETH_PAD_SIZE
//  pbuf_add_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
//#endif

//  LINK_STATS_INC(link.xmit);

//  return ERR_OK;
}

/**
 * Should allocate a pbuf and transfer the bytes of the incoming
 * packet from the interface into the pbuf.
 *
 * @param netif the lwip network interface structure for this ethernetif
 * @return a pbuf filled with the received packet (including MAC header)
 *         NULL on memory error
 */
// 为lwip提供网卡数据接收函数
static struct pbuf *
low_level_input(struct netif *netif)
{
  return PacketReceive(netif); // 调用自己的网卡数据接收处理函数
//  // struct ethernetif *ethernetif = netif->state;
//  struct pbuf *p, *q;
//  u16_t len;

//  /* Obtain the size of the packet and put it into the "len"
//     variable. */
//  // len = ;

//#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_remove_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.
//       */
//      // read data into(q->payload, q->len);
//    }
//    // acknowledge that packet has been read();

//    MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len);
//    if (((u8_t *)p->payload)[0] & 1) {
//      /* broadcast or multicast packet*/
//      MIB2_STATS_NETIF_INC(netif, ifinnucastpkts);
//    } else {
//      /* unicast packet*/
//      MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
//    }
//#if ETH_PAD_SIZE
//    pbuf_add_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);
//    MIB2_STATS_NETIF_INC(netif, ifindiscards);
//  }

//  return p;
}

/**
 * This function should be called when a packet is ready to be read
 * from the interface. It uses the function low_level_input() that
 * should handle the actual reception of bytes from the network
 * interface. Then the type of the received packet is determined and
 * the appropriate input function is called.
 *
 * @param netif the lwip network interface structure for this ethernetif
 */
// lwip框架自己的网卡接收处理函数,不用改
void
ethernetif_input(struct netif *netif)
{
  // struct ethernetif *ethernetif;
  // struct eth_hdr *ethhdr;
  struct pbuf *p;

  // ethernetif = netif->state;

  /* move received packet into a new pbuf */
  p = low_level_input(netif);
  /* if no packet could be read, silently ignore this */
  if (p != NULL) {
    /* pass all packets to ethernet_input, which decides what packets it supports */
    if (netif->input(p, netif) != ERR_OK) {
      LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
      pbuf_free(p);
      p = NULL;
    }
  }
}

/**
 * Should be called at the beginning of the program to set up the
 * network interface. It calls the function low_level_init() to do the
 * actual setup of the hardware.
 *
 * This function should be passed as a parameter to netif_add().
 *
 * @param netif the lwip network interface structure for this ethernetif
 * @return ERR_OK if the loopif is initialized
 *         ERR_MEM if private data couldn't be allocated
 *         any other err_t on error
 */
 // lwip自己的网卡初始化封装函数,不用改。
err_t
ethernetif_init(struct netif *netif)
{
  struct ethernetif *ethernetif;

  LWIP_ASSERT("netif != NULL", (netif != NULL));

  ethernetif = mem_malloc(sizeof(struct ethernetif));
  if (ethernetif == NULL) {
    LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));
    return ERR_MEM;
  }

#if LWIP_NETIF_HOSTNAME
  /* Initialize interface hostname */
  netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */

  /*
   * Initialize the snmp variables and counters inside the struct netif.
   * The last argument should be replaced with your link speed, in units
   * of bits per second.
   */
  // MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);

  netif->state = ethernetif;
  netif->name[0] = IFNAME0;
  netif->name[1] = IFNAME1;
  /* We directly use etharp_output() here to save a function call.
   * You can instead declare your own function an call etharp_output()
   * from it if you have to do some checks before sending (e.g. if link
   * is available...) */
#if LWIP_IPV4
  netif->output = etharp_output;
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
  netif->output_ip6 = ethip6_output;
#endif /* LWIP_IPV6 */
  netif->linkoutput = low_level_output;

  ethernetif->ethaddr = (struct eth_addr *) & (netif->hwaddr[0]);

  /* initialize the hardware */
  low_level_init(netif);

  return ERR_OK;
}

#endif /* 0 */

提供实际的网卡驱动

为了可维护,找到的现成网卡实现不要动,自己封装一个中间层。

// @file my_nic.h

#if !defined(__MY_NIC_H__)
#define __MY_NIC_H__

#include "lwip/pbuf.h"
#include "lwip/netif.h"

void my_nic_setting(void);
struct pbuf *PacketReceive(struct netif *netif);
err_t PacketSend (struct pbuf *p);

extern void process_mac(void);

#endif // #if defined(__MY_NIC_H__)

// @file my_nic.c

#include "my_nic.h"

#include "main.h"

#include "lwip/opt.h"
#include "lwip/netif.h"
#include "lwip/ip.h"
#include "lwip/tcp.h"
#include "lwip/init.h"
#include "netif/etharp.h"
#include "lwip/ip_addr.h"
#include "lwip/pbuf.h"

#include "ENC28J60.h"

//extern functions
extern err_t ethernetif_init(struct netif *netif);
extern void  ethernetif_input(struct netif *netif);

//global data
struct netif g_my_netif;

void my_nic_setting(void)
{
	struct ip4_addr _ipaddr;
  struct ip4_addr _netmask;
  struct ip4_addr _gw;

  lwip_init();
	IP4_ADDR(&_ipaddr, 192, 168, 1, 66);
	IP4_ADDR(&_netmask, 255, 255, 255, 0);
	IP4_ADDR(&_gw, 192, 168, 1, 1);

  netif_add(&g_my_netif, &_ipaddr, &_netmask, &_gw, NULL, ethernetif_init,ethernet_input);
	netif_set_default(&g_my_netif);
	netif_set_up(&g_my_netif);
}

/****************************************************************************
* 名    称:void PacketSend (struct pbuf *p)
* 功    能:发送一包数据	完成pbuf中数据的发送																	 
* 入口参数:
* 出口参数: 
* 说    明:基于uip的驱动程序完成LwIP的数据包发送和接收
* 调用方法:将pbuf中的数据拷贝到全局数组MyDatabuf中,然后调用上面的函数enc28j60PacketSend发送数据
****************************************************************************/ 
//以太网数据帧的最大长度1500,定义这个数组会增大内存开销,但是驱动程序变得简单
static unsigned char  MySendbuf[1500]; 
err_t PacketSend (struct pbuf *p)
{
	struct pbuf *q = NULL;
	unsigned int templen = 0;

	for(q = p;q!=NULL;q = q->next)
	{
		memcpy(&MySendbuf[templen],q->payload,q->len);	 //将pbuf中的数据拷贝到全局数组MyDatabuf中
		templen += 	q->len ;

		if(templen > 1500 || templen > p->tot_len)	 	//有效性校验,防止数据溢出
		{
			LWIP_PLATFORM_DIAG(("PacketSend: error,tmplen=%"U32_F",tot_len=%"U32_F"\n\t", templen, p->tot_len));
			return ERR_BUF;
		}
	}
	
	//拷贝完毕,下面进行数据的发送工作
	if(templen == p->tot_len)
	{
		enc28j60PacketSend(templen, MySendbuf);		   //调用网卡发送函数
		return ERR_OK; 
	}
	
	LWIP_PLATFORM_DIAG(("PacketSend: length mismatch ,tmplen=%"U32_F",tot_len=%"U32_F"\n\t", templen, p->tot_len));
	return ERR_BUF;
}
/****************************************************************************
* 名    称:struct pbuf *PacketReceive(struct netif *netif)
* 功    能:完成LwIP需要的数据包接收																	 
* 入口参数:
* 出口参数: 
* 说    明:基于uip的驱动程序完成LwIP的数据包发送和接收
* 调用方法:网卡的数据拷贝到全局数组MyRecvbuf中,再组装成pbuf
****************************************************************************/ 
//以太网数据帧的最大长度1500,定义这个数组会增大内存开销,但是驱动程序变得简单
static unsigned char  MyRecvbuf[1500]; 
struct pbuf *PacketReceive(struct netif *netif)
{
	struct pbuf *p = NULL;	
	unsigned int recvlen = 0;
	unsigned int i = 0;
	struct pbuf *q = NULL;
    
	recvlen = enc28j60PacketReceive(1500, MyRecvbuf);

	if(!recvlen)	       //接收数据长度为0,直接返回空
	{
	    return NULL;
	}
	
	//申请内核pbuf空间,为RAM类型
	p = pbuf_alloc(PBUF_RAW, recvlen, PBUF_RAM);
	
	if(!p)			       //申请失败,则返回空
	{
	    LWIP_PLATFORM_DIAG(("PacketReceive: pbuf_alloc fail ,len=%"U32_F"\n\t", recvlen));
		return NULL;
	 }
    //申请成功,拷贝数据到pbuf中
	q = p;
		
	while(q != NULL)
	{   
		memcpy(q->payload,&MyRecvbuf[i],q->len);
		i += q->len;
		q = q->next;
		if(i >= recvlen)  break;
	}
		
	return p;
}

void process_mac(void)
{
  bool b_loop = false;
  
   do {
      ethernetif_input(&g_my_netif);
   } while(b_loop);
}

用户层代码

用户层代码只是调用网卡初始化函数,然后用中断方式处理网卡响应(收/发).
在手头这块开发板上,enc28j60的中断引脚接到了MCU的外中断1引脚。
这样,只要有网卡数据接收事件,就会先进去外中断1,然后就可以处理网卡数据的收发了。

// @file main.h

#if !defined(__MAIN_H__)
#define __MAIN_H__

#include "stm32f10x.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#include <stdarg.h>
#include <string.h>

extern int g_i_tick_cnt;

#define LED1_ON GPIO_SetBits(GPIOB, GPIO_Pin_5);  
#define LED1_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_5); 

#define LED2_ON GPIO_SetBits(GPIOD, GPIO_Pin_6);  
#define LED2_OFF GPIO_ResetBits(GPIOD, GPIO_Pin_6); 

#define LED3_ON GPIO_SetBits(GPIOD, GPIO_Pin_3);  
#define LED3_OFF GPIO_ResetBits(GPIOD, GPIO_Pin_3);  

#define ITM_Port8(n)    (*((volatile unsigned char *)(0xE0000000+4*n)))
#define ITM_Port16(n)   (*((volatile unsigned short*)(0xE0000000+4*n)))
#define ITM_Port32(n)   (*((volatile unsigned long *)(0xE0000000+4*n)))
#define DEMCR           (*((volatile unsigned long *)(0xE000EDFC)))
#define TRCENA          0x01000000

#endif // #if !defined(__MAIN_H__)

// @file main.c

#include "main.h"
#include "spi.h"
#include "my_nic.h"

/*
选择芯片后就不需要在option->c/c++中再定义类似STM32F103_HD的东西了
因为选择芯片时已经加进去了。而且当你的定义和你选择的芯片不同时,会报错:
..\..\Libraries\CMSIS\stm32f10x.h(298): error: #67: expected a "}"
 ADC1_2_IRQn = 18,
*/

void RCC_Configuration(void);
void NVIC_Configuration(void);
void GPIO_Configuration(void);
  
void show_mcu_type(void);

void delay_ms(int i_ms);

int main(void)
{
  int i_loop_cnt = 0;
  
  RCC_Configuration(); // 设置时钟频率和tick节拍
  printf(">> main\n");
  
  // 这里测试过了, tick非常准, 1ms一个tick中断
//  printf(">> 1s done\n");
//  do {} while (g_i_tick_cnt < 1000 * 60);
//  printf("<< 1s done\n");
  
  // options => c/c++ => Define 那里只填USE_STDPERIPH_DRIVER时, 到底要填啥 STM32F10X_XX宏
  show_mcu_type(); // show mcu macro is STM32F10X_HD or other
  
  NVIC_Configuration(); // 设置中断等级, 外部中断(网卡中断接在外中断1)
  
  GPIO_Configuration(); // 设置管脚配置(外中断1, 3个LED, 4个SPI设备)
  SPI1_Init(); // Enc28j60网卡控制器在SPI1上
  my_nic_setting(); // 设置网卡参数
  
  // 并不能放外中断1中处理, 因为网卡设置时, 就进了外中断, 导致网卡并没有设置完就卡在外中断处理中死循环了
  // process_mac(); // 处理网卡数据包

  while (1)
  {
    if (0 == (i_loop_cnt % 10)) {
      printf("i_loop_cnt = %d\n", i_loop_cnt);
    }
    
    ++i_loop_cnt;
    
  	LED1_ON; LED2_OFF; LED3_OFF;		//LED1亮  LED2,LED3灭(LED2,LED3 仅V5  V3,V2,V2.1板有)
    delay_ms(1000);

    LED1_OFF; LED2_ON; LED3_OFF;		//LED2亮  LED1,LED3灭(LED2,LED3 仅V5  V3,V2,V2.1板有)
    delay_ms(1000);

    LED1_OFF; LED2_OFF; LED3_ON;		//LED3亮  LED1,LED2灭(LED2,LED3 仅V5  V3,V2,V2.1板有)
    delay_ms(1000);
    
    // for remove warning
    if (i_loop_cnt < 0) {
      i_loop_cnt = 0;
    }
    
    if (i_loop_cnt < 0) {
      break;
    }
  }
  
  return 0;
}

void RCC_Configuration(void)
{   
  SystemInit();
  SysTick_Config(1 * 72000);
  
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

  // STM32F103有5个GPIO(A, B, C, D, E)
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);

  // RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
}

void delay_ms(int i_ms)
{
  int i_tick_now = 0;

  // 防止tick溢出那一瞬间
  do {
    i_tick_now = g_i_tick_cnt;
  } while (i_tick_now < 0);

  do {
  } while ((g_i_tick_cnt - i_tick_now) < i_ms);
}

void show_mcu_type(void)
{
  #if defined(STM32F10X_LD)
  printf("STM32F10X_LD: STM32 Low density devices\n\
 - Low-density devices are STM32F101xx, STM32F102xx and STM32F103xx microcontrollers\n\
   where the Flash memory density ranges between 16 and 32 Kbytes.\n\
");
  #elif defined(STM32F10X_LD_VL)
  printf("STM32F10X_LD_VL: STM32 Low density Value Line devices\n\
 - Low-density value line devices are STM32F100xx microcontrollers where the Flash\n\
   memory density ranges between 16 and 32 Kbytes.\n\
");
  #elif defined(STM32F10X_MD)
   printf("STM32F10X_MD: STM32 Medium density devices\n\
 - Medium-density devices are STM32F101xx, STM32F102xx and STM32F103xx microcontrollers\n\
   where the Flash memory density ranges between 64 and 128 Kbytes.\n\
");
  #elif defined(STM32F10X_MD_VL)
   printf("STM32F10X_MD_VL: STM32 Medium density Value Line devices\n\
 - Medium-density value line devices are STM32F100xx microcontrollers where the\n\
   Flash memory density ranges between 64 and 128 Kbytes.\n\
");
  #elif defined(STM32F10X_HD)
   // f103ZE 是 STM32F10X_HD
   printf("STM32F10X_HD: STM32 High density devices\n\
 - High-density devices are STM32F101xx and STM32F103xx microcontrollers where\n\
   the Flash memory density ranges between 256 and 512 Kbytes.\n\
");
  #elif defined(STM32F10X_HD_VL)
   printf("STM32F10X_HD_VL: STM32 High density value line devices\n\
 - High-density value line devices are STM32F100xx microcontrollers where the\n\
   Flash memory density ranges between 256 and 512 Kbytes.\n\
");
  #elif defined(STM32F10X_XL)
  printf("STM32F10X_XL: STM32 XL-density devices\n\
 - XL-density devices are STM32F101xx and STM32F103xx microcontrollers where\n\
   the Flash memory density ranges between 512 and 1024 Kbytes.\n\
");
  #elif defined(STM32F10X_CL)
  printf("STM32F10X_CL: STM32 Connectivity line devices\n\
 - Connectivity line devices are STM32F105xx and STM32F107xx microcontrollers.\n\
");
  #else
  printf("unknown STM32F10X_ ...\n"); // 如果只选cpu, 不填STM32F10X_xx, 到了这,所以STM32F10X_xx必须填写
  #endif
}

int fputc(int ch, FILE *f)
{ 	
	if (DEMCR & TRCENA) 
	{
			while (ITM_Port32(0) == 0);
			ITM_Port8(0) = ch;
	}

	return(ch);
		
	/*
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
	USART1->DR = (u8) ch;      
	return ch;
	*/
}

void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  EXTI_InitTypeDef EXTI_InitStructure;
  /* Configure one bit for preemption priority */
  /* 优先级组 说明了抢占优先级所用的位数,和子优先级所用的位数   在这里是1, 7 */    
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  
  /* Enable the EXTI2 Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;				 //外部中断2
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	     //抢占优先级 0
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;			 //子优先级0  
  //NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				 //使能
  NVIC_Init(&NVIC_InitStructure);

  //用于配置AFIO外部中断配置寄存器AFIO_EXTICR1,用于选择EXTI2外部中断的输入源是PE2。
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);     //外部中断配置AFIO--ETXI2

  EXTI_InitStructure.EXTI_Line = EXTI_Line1;					  //PE2 作为键盘的行线。检测状态
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;			  //中断模式
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;		  //下降沿触发
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
}

void GPIO_Configuration(void)
{
  // my code
  GPIO_InitTypeDef GPIO_InitStructure;
  
  // UART/LED的GPIO速度设为50MHZ
  // SPI的GPIO速度设置成10MHZ
  
  // 3个LED - PB5, PD3, PD6
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; // LED1  V6, 将V6,V7,V8 配置为通用推挽输出  
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 口线翻转速度为50MHz
  GPIO_Init(GPIOB, &GPIO_InitStructure);					 

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_3; // LED2, LED3 V7 V8
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOD, &GPIO_InitStructure);
  
  // 设置外中断1管脚 - PA1
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; // ENC28J60接收完成中断引脚 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 内部上拉输入
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);		 

  // SPI1管脚 - PA5-SCK, PA6-MISO, PA7-MOSI 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
  	
  // 如果一个SPI上接着多个设备, 操作之间是会有影响的.
  // 只有禁掉了其他设备的片选,只保留一个片选。才能正常操作一个SPI设备
  //
  // 板子的SPI1上有4个设备, 片选如下:
  // PA4 - 网卡(ENC28J60)的片选
  // PB7 - LCD(TFT液晶显示屏)的片选
  // PB12 - VS1003的片选(SPI2)
  // PC4 - 存储(SST25VF016B)的片选
  
  // SPI片选引脚
  
  // PA4 - 网卡(ENC28J60)的片选
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用功能
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  GPIO_SetBits(GPIOA, GPIO_Pin_4); // 禁止片选

  // PB7 - LCD(TFT液晶显示屏)的片选
  // PB12 - VS1003的片选(SPI2)
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_12;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用功能
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);

  GPIO_SetBits(GPIOB, GPIO_Pin_7); // 禁止片选 
  GPIO_SetBits(GPIOB, GPIO_Pin_12); // 禁止片选 

  // PC4 - 存储(SST25VF016B)的片选
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用功能
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  GPIO_SetBits(GPIOC, GPIO_Pin_4); // 禁止片选  
}

/**
  ******************************************************************************
  * @file    Project/STM32F10x_StdPeriph_Template/stm32f10x_it.h 
  * @author  MCD Application Team
  * @version V3.5.0
  * @date    08-April-2011
  * @brief   This file contains the headers of the interrupt handlers.
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
  ******************************************************************************
  */ 

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F10x_IT_H
#define __STM32F10x_IT_H

#ifdef __cplusplus
 extern "C" {
#endif 

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"

/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
/* Exported functions ------------------------------------------------------- */

void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);

#ifdef __cplusplus
}
#endif

#endif /* __STM32F10x_IT_H */

/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

/**
  ******************************************************************************
  * @file    Project/STM32F10x_StdPeriph_Template/stm32f10x_it.c 
  * @author  MCD Application Team
  * @version V3.5.0
  * @date    08-April-2011
  * @brief   Main Interrupt Service Routines.
  *          This file provides template for all exceptions handler and 
  *          peripherals interrupt service routine.
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_it.h"
#include "main.h"
#include "my_nic.h"

int g_i_tick_cnt = 0;

/** @addtogroup STM32F10x_StdPeriph_Template
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/******************************************************************************/
/*            Cortex-M3 Processor Exceptions Handlers                         */
/******************************************************************************/

/**
  * @brief  This function handles NMI exception.
  * @param  None
  * @retval None
  */
void NMI_Handler(void)
{
}

/**
  * @brief  This function handles Hard Fault exception.
  * @param  None
  * @retval None
  */
void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles Memory Manage exception.
  * @param  None
  * @retval None
  */
void MemManage_Handler(void)
{
  /* Go to infinite loop when Memory Manage exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles Bus Fault exception.
  * @param  None
  * @retval None
  */
void BusFault_Handler(void)
{
  /* Go to infinite loop when Bus Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles Usage Fault exception.
  * @param  None
  * @retval None
  */
void UsageFault_Handler(void)
{
  /* Go to infinite loop when Usage Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles SVCall exception.
  * @param  None
  * @retval None
  */
void SVC_Handler(void)
{
}

/**
  * @brief  This function handles Debug Monitor exception.
  * @param  None
  * @retval None
  */
void DebugMon_Handler(void)
{
}

/**
  * @brief  This function handles PendSVC exception.
  * @param  None
  * @retval None
  */
void PendSV_Handler(void)
{
}

/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
  // printf(">> SysTick_Handler\n");
  g_i_tick_cnt++;
}

void EXTI1_IRQHandler(void)
{
//  int ret = 0;

  if (EXTI_GetITStatus(EXTI_Line1) != RESET) {
    printf(">> EXTI1_IRQHandler() : (EXTI_GetITStatus(EXTI_Line1) != RESET)\n");
    EXTI_ClearITPendingBit(EXTI_Line1);		//清除中断请求标志
    
    process_mac();
  }
}

/******************************************************************************/
/*                 STM32F10x Peripherals Interrupt Handlers                   */
/*  Add here the Interrupt Handler for the used peripheral(s) (PPP), for the  */
/*  available peripheral interrupt handler's name please refer to the startup */
/*  file (startup_stm32f10x_xx.s).                                            */
/******************************************************************************/

/**
  * @brief  This function handles PPP interrupt request.
  * @param  None
  * @retval None
  */
/*void PPP_IRQHandler(void)
{
}*/

/**
  * @}
  */ 


/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

移植完成后的测试

打开LWIP日志选项
首先是编译过,将编译警告都去掉. 警告不是东西。
程序跑起来,可以看到MDK调试窗口接收到的ITM打印信息。能看到arp包的处理。
再ping一下,可以看到LWIP的调试信息,源IP和目标IP都打印出来了,在PC端命令行窗口能得ping结果,说明LWIP移植成功。

>> main
STM32F10X_HD: STM32 High density devices
 - High-density devices are STM32F101xx and STM32F103xx microcontrollers where
   the Flash memory density ranges between 256 and 512 Kbytes.
info:netif: netmask of interface info:netif: GW address of interface info:netif_set_ipaddr: netif address being changed
info:netif: added interface lo IPinfo: addr info:127.0.0.1info: netmask info:255.0.0.0info: gw info:127.0.0.1info:
info:igmp_init: initializing
info:dns_init: initializing
info:netif: netmask of interface info:netif: GW address of interface info:netif_set_ipaddr: netif address being changed
mac addr = 4C-ED-FB-01-23-45
info:netif: added interface en IPinfo: addr info:192.168.1.66info: netmask info:255.255.255.0info: gw info:192.168.1.1info:
info:netif: setting default interface en
info:pbuf_alloc(length=28)
info:pbuf_alloc(length=28) == 200011f0
info:pbuf_add_header: old 20001210 new 20001202 (14)
info:pbuf_free(200011f0)
info:pbuf_free: deallocating 200011f0
i_loop_cnt = 0
>> EXTI1_IRQHandler() : (EXTI_GetITStatus(EXTI_Line1) != RESET)
info:pbuf_alloc(length=60)
info:pbuf_alloc(length=60) == 200011f0
info:pbuf_remove_header: old 20001200 new 2000120e (14)
info:autoip_arp_reply()
info:pbuf_free(200011f0)
info:pbuf_free: deallocating 200011f0
>> EXTI1_IRQHandler() : (EXTI_GetITStatus(EXTI_Line1) != RESET)
info:pbuf_alloc(length=60)
info:pbuf_alloc(length=60) == 200011f0
info:pbuf_remove_header: old 20001200 new 2000120e (14)
info:autoip_arp_reply()
info:pbuf_free(200011f0)
info:pbuf_free: deallocating 200011f0
>> EXTI1_IRQHandler() : (EXTI_GetITStatus(EXTI_Line1) != RESET)
info:pbuf_alloc(length=60)
info:pbuf_alloc(length=60) == 200011f0
info:pbuf_remove_header: old 20001200 new 2000120e (14)
info:autoip_arp_reply()
info:pbuf_free(200011f0)
info:pbuf_free: deallocating 200011f0
>> EXTI1_IRQHandler() : (EXTI_GetITStatus(EXTI_Line1) != RESET)
info:pbuf_alloc(length=60)
info:pbuf_alloc(length=60) == 200011f0
info:pbuf_remove_header: old 20001200 new 2000120e (14)
info:autoip_arp_reply()
info:pbuf_alloc(length=28)
info:pbuf_alloc(length=28) == 20001244
info:pbuf_add_header: old 20001264 new 20001256 (14)
info:pbuf_free(20001244)
info:pbuf_free: deallocating 20001244
info:pbuf_free(200011f0)
info:pbuf_free: deallocating 200011f0
>> EXTI1_IRQHandler() : (EXTI_GetITStatus(EXTI_Line1) != RESET)
info:pbuf_alloc(length=74)
info:pbuf_alloc(length=74) == 200011f0
info:pbuf_remove_header: old 20001200 new 2000120e (14)
info:ip_input: iphdr->dest 0x4201a8c0 netif->ip_addr 0x4201a8c0 (0x1a8c0, 0x1a8c0, 0x42000000)
info:ip4_input: packet accepted on interface en
info:ip4_input: 
info:IP header:
info:+-------------------------------+
info:| 4 | 5 |  0x00 |        60     | (v, hl, tos, len)
info:+-------------------------------+
info:|    62749      |000|       0   | (id, flags, offset)
info:+-------------------------------+
info:|   64  |    1  |    0x01ab     | (ttl, proto, chksum)
info:+-------------------------------+
info:|  192  |  168  |    1  |  102  | (src)
info:+-------------------------------+
info:|  192  |  168  |    1  |   66  | (dest)
info:+-------------------------------+
info:ip4_input: p->len 60 p->tot_len 60
info:pbuf_remove_header: old 2000120e new 20001222 (20)
info:icmp_input: ping
info:pbuf_add_header: old 20001222 new 20001200 (34)
info:pbuf_remove_header: old 20001200 new 20001222 (34)
info:pbuf_add_header: old 20001222 new 2000120e (20)
info:ip4_output_if: en1
info:IP header:
info:+-------------------------------+
info:| 4 | 5 |  0x00 |        60     | (v, hl, tos, len)
info:+-------------------------------+
info:|    62749      |000|       0   | (id, flags, offset)
info:+-------------------------------+
info:|  255  |    1  |    0x42aa     | (ttl, proto, chksum)
info:+-------------------------------+
info:|  192  |  168  |    1  |   66  | (src)
info:+-------------------------------+
info:|  192  |  168  |    1  |  102  | (dest)
info:+-------------------------------+
info:ip4_output_if: call netif->output()
info:pbuf_add_header: old 2000120e new 20001200 (14)
info:pbuf_free(200011f0)
info:pbuf_free: deallocating 200011f0
>> EXTI1_IRQHandler() : (EXTI_GetITStatus(EXTI_Line1) != RESET)
info:pbuf_alloc(length=74)
info:pbuf_alloc(length=74) == 200011f0
info:pbuf_remove_header: old 20001200 new 2000120e (14)
info:ip_input: iphdr->dest 0x4201a8c0 netif->ip_addr 0x4201a8c0 (0x1a8c0, 0x1a8c0, 0x42000000)
info:ip4_input: packet accepted on interface en
info:ip4_input: 
info:IP header:
info:+-------------------------------+
info:| 4 | 5 |  0x00 |        60     | (v, hl, tos, len)
info:+-------------------------------+
info:|    62750      |000|       0   | (id, flags, offset)
info:+-------------------------------+
info:|   64  |    1  |    0x01aa     | (ttl, proto, chksum)
info:+-------------------------------+
info:|  192  |  168  |    1  |  102  | (src)
info:+-------------------------------+
info:|  192  |  168  |    1  |   66  | (dest)
info:+-------------------------------+
info:ip4_input: p->len 60 p->tot_len 60
info:pbuf_remove_header: old 2000120e new 20001222 (20)
info:icmp_input: ping
info:pbuf_add_header: old 20001222 new 20001200 (34)
info:pbuf_remove_header: old 20001200 new 20001222 (34)
info:pbuf_add_header: old 20001222 new 2000120e (20)
info:ip4_output_if: en1
info:IP header:
info:+-------------------------------+
info:| 4 | 5 |  0x00 |        60     | (v, hl, tos, len)
info:+-------------------------------+
info:|    62750      |000|       0   | (id, flags, offset)
info:+-------------------------------+
info:|  255  |    1  |    0x42a9     | (ttl, proto, chksum)
info:+-------------------------------+
info:|  192  |  168  |    1  |   66  | (src)
info:+-------------------------------+
info:|  192  |  168  |    1  |  102  | (dest)
info:+-------------------------------+
info:ip4_output_if: call netif->output()
info:pbuf_add_header: old 2000120e new 20001200 (14)
info:pbuf_free(200011f0)
info:pbuf_free: deallocating 200011f0
>> EXTI1_IRQHandler() : (EXTI_GetITStatus(EXTI_Line1) != RESET)
info:pbuf_alloc(length=74)
info:pbuf_alloc(length=74) == 200011f0
info:pbuf_remove_header: old 20001200 new 2000120e (14)
info:ip_input: iphdr->dest 0x4201a8c0 netif->ip_addr 0x4201a8c0 (0x1a8c0, 0x1a8c0, 0x42000000)
info:ip4_input: packet accepted on interface en
info:ip4_input: 
info:IP header:
info:+-------------------------------+
info:| 4 | 5 |  0x00 |        60     | (v, hl, tos, len)
info:+-------------------------------+
info:|    62751      |000|       0   | (id, flags, offset)
info:+-------------------------------+
info:|   64  |    1  |    0x01a9     | (ttl, proto, chksum)
info:+-------------------------------+
info:|  192  |  168  |    1  |  102  | (src)
info:+-------------------------------+
info:|  192  |  168  |    1  |   66  | (dest)
info:+-------------------------------+
info:ip4_input: p->len 60 p->tot_len 60
info:pbuf_remove_header: old 2000120e new 20001222 (20)
info:icmp_input: ping
info:pbuf_add_header: old 20001222 new 20001200 (34)
info:pbuf_remove_header: old 20001200 new 20001222 (34)
info:pbuf_add_header: old 20001222 new 2000120e (20)
info:ip4_output_if: en1
info:IP header:
info:+-------------------------------+
info:| 4 | 5 |  0x00 |        60     | (v, hl, tos, len)
info:+-------------------------------+
info:|    62751      |000|       0   | (id, flags, offset)
info:+-------------------------------+
info:|  255  |    1  |    0x42a8     | (ttl, proto, chksum)
info:+-------------------------------+
info:|  192  |  168  |    1  |   66  | (src)
info:+-------------------------------+
info:|  192  |  168  |    1  |  102  | (dest)
info:+-------------------------------+
info:ip4_output_if: call netif->output()
info:pbuf_add_header: old 2000120e new 20001200 (14)
info:pbuf_free(200011f0)
info:pbuf_free: deallocating 200011f0
>> EXTI1_IRQHandler() : (EXTI_GetITStatus(EXTI_Line1) != RESET)
info:pbuf_alloc(length=60)
info:pbuf_alloc(length=60) == 200011f0
info:pbuf_remove_header: old 20001200 new 2000120e (14)
info:autoip_arp_reply()
info:pbuf_free(200011f0)
info:pbuf_free: deallocating 200011f0
>> EXTI1_IRQHandler() : (EXTI_GetITStatus(EXTI_Line1) != RESET)
info:pbuf_alloc(length=74)
info:pbuf_alloc(length=74) == 200011f0
info:pbuf_remove_header: old 20001200 new 2000120e (14)
info:ip_input: iphdr->dest 0x4201a8c0 netif->ip_addr 0x4201a8c0 (0x1a8c0, 0x1a8c0, 0x42000000)
info:ip4_input: packet accepted on interface en
info:ip4_input: 
info:IP header:
info:+-------------------------------+
info:| 4 | 5 |  0x00 |        60     | (v, hl, tos, len)
info:+-------------------------------+
info:|    62752      |000|       0   | (id, flags, offset)
info:+-------------------------------+
info:|   64  |    1  |    0x01a8     | (ttl, proto, chksum)
info:+-------------------------------+
info:|  192  |  168  |    1  |  102  | (src)
info:+-------------------------------+
info:|  192  |  168  |    1  |   66  | (dest)
info:+-------------------------------+
info:ip4_input: p->len 60 p->tot_len 60
info:pbuf_remove_header: old 2000120e new 20001222 (20)
info:icmp_input: ping
info:pbuf_add_header: old 20001222 new 20001200 (34)
info:pbuf_remove_header: old 20001200 new 20001222 (34)
info:pbuf_add_header: old 20001222 new 2000120e (20)
info:ip4_output_if: en1
info:IP header:
info:+-------------------------------+
info:| 4 | 5 |  0x00 |        60     | (v, hl, tos, len)
info:+-------------------------------+
info:|    62752      |000|       0   | (id, flags, offset)
info:+-------------------------------+
info:|  255  |    1  |    0x42a7     | (ttl, proto, chksum)
info:+-------------------------------+
info:|  192  |  168  |    1  |   66  | (src)
info:+-------------------------------+
info:|  192  |  168  |    1  |  102  | (dest)
info:+-------------------------------+
info:ip4_output_if: call netif->output()
info:pbuf_add_header: old 2000120e new 20001200 (14)
info:pbuf_free(200011f0)
info:pbuf_free: deallocating 200011f0
>> EXTI1_IRQHandler() : (EXTI_GetITStatus(EXTI_Line1) != RESET)
info:pbuf_alloc(length=60)
info:pbuf_alloc(length=60) == 200011f0
info:pbuf_remove_header: old 20001200 new 2000120e (14)
info:autoip_arp_reply()
info:pbuf_free(200011f0)
info:pbuf_free: deallocating 200011f0
>> EXTI1_IRQHandler() : (EXTI_GetITStatus(EXTI_Line1) != RESET)
info:pbuf_alloc(length=60)
info:pbuf_alloc(length=60) == 200011f0
info:pbuf_remove_header: old 20001200 new 2000120e (14)
info:autoip_arp_reply()
info:pbuf_free(200011f0)
info:pbuf_free: deallocating 200011f0
i_loop_cnt = 10


总结

看第三方的教程和资料,和自己作一遍的体会完全不一样。
有些细节,教程和资料上压根都没说,自己做了试验才有体会。

e.g.
为啥要移植这个文件过来?
这个文件哪来的?
这个要给lwip提供的函数咋实现?
如果只想打印某类lwip通讯处理的报错信息(e.g. tcp通讯错误, 内存分配失败), 怎么整?

备注 - 2020_0223_1807
用中断方式响应网络请求有问题,放了一会,再ping就没响应了。
网上的解决方法:

void
ethernetif_input(struct netif *netif)
{
  // struct ethernetif *ethernetif;
  // struct eth_hdr *ethhdr;
  struct pbuf* p = NULL;
  int i_pkt_len = 0; // 包长度

  // ethernetif = netif->state;

  do {
    /* move received packet into a new pbuf */
    p = low_level_input(netif);
    if (NULL == p) {
      /* if no packet could be read, silently ignore this */
      break;
    }
    
    /* pass all packets to ethernet_input, which decides what packets it supports */
    if (netif->input(p, netif) != ERR_OK) {
      LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
    }

    pbuf_free(p);
    p = NULL;
    
    // 检查网卡接收缓冲区内是否还有没处理的包, 直到全部处理完, 再出去
    // 防止网卡缓冲区内还有包,导致数据堆积, 不再响应网卡中断
    i_pkt_len = enc28j60Read(EPKTCNT);
    if (0 == i_pkt_len) {
      // 收到的以太网数据包长度   EPKCNT中记录了接收到的以太网包的数据长度信息;
      break;
    } else {
      // 提示一下,有下一个包要处理, 包长度为i_pkt_len
      LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input() : have next %d recv data need process\n", i_pkt_len));
    }
  } while (1);
}

用网上的解决方法不好使,会出现以下报错
我猜: 中断一来就处理或者处理完一个包,继续处理网卡接收缓冲区内剩下的接收内容,可能包还没收全(因为通过调试,看到处理的包有的才4个字节,这就不是一个完整的包),导致包分析无效,再后续的包处理就不对了(字节错开了,不是从真正的包头开始处理的)。

assert:Assertion "pbuf_free: p->ref > 0" failed at line 753 in lwip\core\pbuf.c
info:pbuf_free: 20001234 has ref 255, ending here.

刚才试了,ping设备可以ping的通,但是MDK打印出的ITM信息,确实有上述报错。这样虽然能用,却不是规范的解决方法。

我用的时候是带操作系统的,这问题先放这。

  • 7
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32F1是意法半导体公司推出的一款32位ARM Cortex-M3单片机系列产品,具有良好的性能和扩展能力。它采用了低功耗技术,集成了丰富的外设和内存,适用于广泛的应用领域。 寄存器是计算机体系结构中的重要组成部分,用于存储和操作数据。STM32F1芯片内部集成了大量的寄存器,包括通用寄存器、特殊功能寄存器和外设寄存器等。通过对寄存器的读写操作,可以实现对芯片内部各种功能的配置和控制。 LwIP-2.1.2是一个开源的轻量级网络协议栈,适用于嵌入式系统。它提供了TCP/IP协议栈的实现,支持各种网络协议和服务,例如IP、TCP、UDP、ARP、DHCP、DNS等。LwIP-2.1.2具有较小的内存占用和高性能的特点,适用于资源有限的嵌入式环境。 ENC28J60是一款低成本的SPI以太网控制器芯片,由微芯科技(Microchip Technology)公司推出。它支持10Mbps以太网通信,采用硬件SPI接口和内部缓存,能够有效减少主控制器的负担。ENC28J60STM32F1可以通过SPI总线进行连接,用于实现嵌入式设备与以太网的通信。 综上所述,STM32F1是一款强大的单片机系列产品,具备丰富的外设和可编程寄存器,可以灵活配置和控制芯片内部功能。LwIP-2.1.2是一个轻量级的网络协议栈,用于实现嵌入式系统的网络通信。ENC28J60是一款低成本的以太网控制器芯片,可以与STM32F1通过SPI总线进行连接。这些技术的结合可以实现嵌入式设备的网络功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值