【步骤】lwip-2.0.2在STM32F103RC+ENC28J60网卡上无操作系统移植(不使用STM32库函数)

【1】去lwip的官方网站下载最新的lwip-2.0.2.zip
【2】新建空白STM32工程,建工程时一定要在Manage Run-Time Environment对话框里勾选启动文件:Device/Startup和CMSIS/Core

(如果要想添加STM32F10x的库的话,再勾选Device/StdPeriph Drivers/Framework以及Device/StdPeriph Drivers/下面其他需要的外设项目就行了。不过本文为了简单起见不使用库函数)
【3】新建一个main.c文件,文件内容如下:

#include 
#include 
#include "lwip/etharp.h" // etharp_tmr函数所在的头文件
#include "lwip/init.h" // lwip_init函数所在的头文件
#include "lwip/priv/tcp_priv.h" // tcp_tmr函数所在的头文件
#include "netif/ethernet.h" // ethernet_input函数所在头文件
#include "ENC28J60.h"

// 这两个函数位于ethernetif.c中, 但没有头文件声明
err_t ethernetif_init(struct netif *netif);
void ethernetif_input(struct netif *netif);

// 声明httptest.c中的函数
void init_http(void);

// printf串口输出
// 必须要在项目属性里勾选Use MicroLIB后才能使用
int fputc(int ch, FILE *fp)
{
    if (fp == &__stdout)
    {
        USART1->DR = ch;
        while ((USART1->SR & USART_SR_TXE) == 0);
    }
    return ch;
}

int main(void)
{
    struct ip4_addr ipaddr, netmask, gw;
    struct netif enc28j60;
    uint8_t cnt = 0;
    
    // 配置PA口
    RCC->APB2ENR = RCC_APB2ENR_IOPAEN;
    GPIOA->CRH = 0x000004b3; // PA8为开发板上的一个LED灯
    GPIOA->CRL = 0xb4bb0080;
    GPIOA->BSRR = GPIO_BSRR_BS1; // PA1为网卡中断输出
    
    // 配置SPI
    RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
    SPI1->CR1 = SPI_CR1_MSTR | SPI_CR1_BR_1; // 主机模式, 时钟至少需要8分频(BR=010), 也就是72MHz/8=9MHz
    SPI1->CR2 = SPI_CR2_SSOE; // 开CS片选输出
    
    // 配置串口
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    USART1->BRR = 0x271; // 波特率: 115200
    USART1->CR1 = USART_CR1_UE | USART_CR1_TE;
    printf("Hello World!\r\n");
    
    // 配置定时器
    RCC->APB1ENR = RCC_APB1ENR_TIM6EN;
    TIM6->ARR = 2499; // 共2500个数, 2500*0.1ms=250ms
    TIM6->PSC = 7199; // 72MHz/7200=10kHz -> 0.1ms
    TIM6->CR1 = TIM_CR1_URS; // 防止UG=1时UIF置位
    TIM6->EGR = TIM_EGR_UG; // 应用上述设置
    TIM6->CR1 |= TIM_CR1_CEN; // 开定时器
    
    lwip_init();
    IP4_ADDR(&ipaddr, 192, 168, 0, 2); // IP地址
    IP4_ADDR(&netmask, 255, 255, 255, 0); // 子网掩码
    IP4_ADDR(&gw, 192, 168, 0, 1); // 网关
    
    netif_add(&enc28j60, &ipaddr, &netmask, &gw, NULL, ethernetif_init, ethernet_input);
    netif_set_default(&enc28j60); // 设为默认网卡
    netif_set_up(&enc28j60);
    
    printf("进入主循环...\r\n");
    init_http(); // 初始化HTTP服务
    while (1)
    {
        if (ENC28J60_GetPacketNum() != 0)
        {
            GPIOA->ODR ^= GPIO_ODR_ODR8; // PA8上的LED灯闪烁表明系统正常工作
            ethernetif_input(&enc28j60);
        }
        
        // 若定时器溢出
        if (TIM6->SR & TIM_SR_UIF)
        {
            // 250ms
            TIM6->SR &= ~TIM_SR_UIF; // 清除溢出标志
            cnt++;
            if (cnt >= 20)
            {
                // 250ms * 20 = 5s
                GPIOA->ODR ^= GPIO_ODR_ODR8; // LED灯每5秒钟改变一次状态
                cnt = 0;
                etharp_tmr(); // ARP定时处理
            }
            tcp_tmr(); // TCP定时处理
        }
    }
}

程序中用到了STM32F103RC中的基本定时器——TIM6。

如果是用的内存容量较小的STM32F103C8的话,由于只有TIM1~4四个定时器,只需要把源文件中所有的TIM6换成TIM4就行了。(之后会有专门的一篇文章讲解如何把这个程序移植到STM32F103C8上运行)

【4】创建ENC28J60的驱动程序文件ENC28J60.h和ENC28J60.c,放到工程的根目录中,文件内容见:

http://blog.csdn.net/zlk1214/article/details/68947781

【5】在工程的根目录下创建Library/lwip文件夹,把下载下来的lwip压缩包中的src文件夹里的core、include和netif三个文件夹解压到里面

【6】创建Library/lwip/include/arch/cc.h文件,内容为空(只是暂时为空)

【7】创建Library/lwip/include/lwipopts.h文件,内容如下:

#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__

/**
 * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain
 * critical regions during buffer allocation, deallocation and memory
 * allocation and deallocation.
 */
#define SYS_LIGHTWEIGHT_PROT 0

/**
 * NO_SYS==1: Provides VERY minimal functionality. Otherwise,
 * use lwIP facilities.
 */
#define NO_SYS 1 // 无操作系统
/**
 * NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1
 * Mainly for compatibility to old versions.
 */
#define NO_SYS_NO_TIMERS 1  // 这样就不需要定义sys_now函数

/* ---------- Memory options ---------- */
/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which
   lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2
   byte alignment -> define MEM_ALIGNMENT to 2. */
#define MEM_ALIGNMENT 4

/* MEM_SIZE: the size of the heap memory. If the application will send
a lot of data that needs to be copied, this should be set high. */
#define MEM_SIZE (5 * 1024)

/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application
   sends a lot of data out of ROM (or other static memory), this
   should be set high. */
#define MEMP_NUM_PBUF 10
/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
   per active UDP "connection". */
#define MEMP_NUM_UDP_PCB 6
/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP
   connections. */
#define MEMP_NUM_TCP_PCB 10
/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP
   connections. */
#define MEMP_NUM_TCP_PCB_LISTEN 6
/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP
   segments. */
#define MEMP_NUM_TCP_SEG 12
/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
   timeouts. */
#define MEMP_NUM_SYS_TIMEOUT 3

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

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

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

/* Controls if TCP should queue segments that arrive out of
   order. Define to 0 if your device is low on memory. */
#define TCP_QUEUE_OOSEQ 0

/* TCP Maximum segment size. */
#define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */

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

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

/* TCP receive window. */
#define TCP_WND (2 * TCP_MSS)

/* ---------- ICMP options ---------- */
#define LWIP_ICMP 1

/* ---------- DHCP options ---------- */
/* Define LWIP_DHCP to 1 if you want DHCP configuration of
   interfaces. DHCP is not implemented in lwIP 0.5.1, however, so
   turning this on does currently not work. */
#define LWIP_DHCP 0

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

/* ---------- Statistics options ---------- */
#define LWIP_STATS 0
#define LWIP_PROVIDE_ERRNO 1

// LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
#define LWIP_NETCONN 0

// LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
#define LWIP_SOCKET 0

// 这个必须要定义, 否则在执行lwip_init函数时会在串口中提示:
// Assertion "Struct packing not implemented correctly. Check your lwIP port." failed at line 345 in Library\lwip\core\init.c
#define PACK_STRUCT_BEGIN __packed

#endif /* __LWIPOPTS_H__ */


【8】打开Library/lwip/netif/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 
 *
 */

/*
 * 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 0 /* don't build, this is only a skeleton, see previous comment */
#if 1 // 允许编译器编译该文件

#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"

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

#include "ENC28J60.h" // 网卡驱动头文件

/**
 * 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. */
// 这里必须去掉static
/*static */void  ethernetif_input(struct netif *netif);

/**
 * In this function, the hardware should be initialized.
 * Called from ethernetif_init().
 *
 * @param netif the already initialized lwip network interface structure
 *        for this ethernetif
 */
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 */
  // 设置MAC地址
  netif->hwaddr[0] = 0x00;
  netif->hwaddr[1] = 'R';
  netif->hwaddr[2] = 'M';
  netif->hwaddr[3] = 'N';
  netif->hwaddr[4] = 'E';
  netif->hwaddr[5] = 'T';

  /* maximum transfer unit */
  netif->mtu = 1500;

  /* device capabilities */
  /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
  netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;

#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. */
  ENC28J60_Init(netif->hwaddr); // 初始化网卡
}

/**
 * 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).
 */

static err_t
low_level_output(struct netif *netif, struct pbuf *p)
{
  struct ethernetif *ethernetif = netif->state;
  struct pbuf *q;

  //initiate transfer();
  ENC28J60_InitSend(p->tot_len); // 初始化

#if ETH_PAD_SIZE
  pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif

  for (q = p; q != NULL; q = q->next) {
    /* Send the data from the pbuf to the interface, one pbuf at a
       time. The size of the data in each pbuf is kept in the ->len
       variable. */
    //send data from(q->payload, q->len);
    ENC28J60_WriteBuf(q->payload, q->len); // 把内容放入缓冲区
  }

  //signal that packet should be sent();
  ENC28J60_BeginSend(); // 开始发送

  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_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
 */
static struct pbuf *
low_level_input(struct netif *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 = ENC28J60_GetPacketLength(); // 获取数据包长度

#if ETH_PAD_SIZE
  len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
#endif

  /* We allocate a pbuf chain of pbufs from the pool. */
  p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);

  if (p != NULL) {

#if ETH_PAD_SIZE
    pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif

    /* We iterate over the pbuf chain until we have read the entire
     * packet into the pbuf. */
    for (q = p; q != NULL; q = q->next) {
      /* Read enough bytes to fill this pbuf in the chain. The
       * available data in the pbuf is given by the q->len
       * variable.
       * This does not necessarily have to be a memcpy, you can also preallocate
       * pbufs for a DMA-enabled MAC and after receiving truncate it to the
       * actually received size. In this case, ensure the tot_len member of the
       * pbuf is the sum of the chained pbuf len members.
       */
      //read data into(q->payload, q->len);
      ENC28J60_ReadBuf(q->payload, q->len); // 读取缓冲区中的内容
    }
    //acknowledge that packet has been read();
    ENC28J60_EndReceive(); // 接收完成

    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_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif

    LINK_STATS_INC(link.recv);
  } else {
    //drop packet();
    ENC28J60_EndReceive(); // 丢弃数据包
    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
 */
// 必须把static去掉, 让其他文件可以调用该函数
/*static */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
 */
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...) */
  netif->output = etharp_output;
#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 */


给网卡设置MAC地址的时候一定要注意!

MAC地址一定要设置一个合适的地址。否则有些比较“高级”的路由器可能会无法识别,导致网卡只能接收数据不能发送数据。数据虽然发送出去了,但路由器直接丢掉。

只要发现设置的MAC地址不合适,马上更换

【9】在项目根目录下创建httptest.c文件(一个简单的测试文件),内容如下:

#include 
#include "lwip/tcp.h" // 一般情况下需要包含的头文件

#define STR_AND_SIZE(str) (str), strlen(str)

err_t http_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    char len[10]; // 存放HTML内容的长度
    char str[200]; // 存放HTML内容
    
    char name[100];
    char *pstr;
    uint8_t i = 0;
    if (p != NULL)
    {
        // 提取页面名称
        pstr = (char *)p->payload;
        while (*pstr++ != ' ');
        while (*pstr != ' ')
            name[i++] = *pstr++;
        name[i] = '\0'; // 不要忘了结束name字符串
        tcp_recved(tpcb, p->tot_len);
        
        // 生成HTML内容
        sprintf(str, "获取网页名称
请求的网页文件名称是: %s
", name); sprintf(len, "%d", strlen(str)); // HTML内容的长度 tcp_write(tpcb, STR_AND_SIZE("HTTP/1.1 200 OK\r\nContent-Length: "), TCP_WRITE_FLAG_MORE); tcp_write(tpcb, STR_AND_SIZE(len), TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE); tcp_write(tpcb, STR_AND_SIZE("\r\nKeep-Alive: timeout=5, max=100\r\nConnection: Keep-Alive\r\nContent-Type: text/html\r\n\r\n"), TCP_WRITE_FLAG_MORE); tcp_write(tpcb, STR_AND_SIZE(str), TCP_WRITE_FLAG_COPY); // 发送HTML内容 pbuf_free(p); } return ERR_OK; } err_t http_accept(void *arg, struct tcp_pcb *newpcb, err_t err) { tcp_recv(newpcb, http_recv); return ERR_OK; } void init_http(void) { struct tcp_pcb *tpcb = tcp_new(); tcp_bind(tpcb, IP_ADDR_ANY, 80); tpcb = tcp_listen(tpcb); tcp_accept(tpcb, http_accept); }


【10】将根目录下的c文件,以及以下文件夹中的文件(不含子目录)添加到工程中:
Library/lwip/core/
Library/lwip/core/ipv4/
还有两个单独的文件:
Library/lwip/netif/ethernet.c
Library/lwip/netif/ethernetif.c

【步骤】lwip-2.0.2在STM32F103RC+ENC28J60网卡上无操作系统移植(不使用STM32库函数)_第1张图片

【11】打开项目属性
(1) 在Target选项卡中勾选Use MicroLIB复选框(这样就可以执行printf函数)
(2) 在C/C++选项卡下的Include Paths文本框中输入C语言头文件的包含路径:
.\Library\lwip\include;.\
(3) 如果使用JLink下载程序,则应该在Debug选项卡的设置中勾选Reset and Run复选框,这样下载程序后程序就会自动运行

【12】把网线直接插到电脑的网卡上,电脑网卡的IP地址和网关设为192.168.0.1,子网掩码保持默认。

(或者插在路由器上也可以,但是一定要设置合适的MAC地址,并且程序中的网关地址一定要和路由器的相同)
下载并运行程序,用浏览器访问http://192.168.0.2/或在控制台中执行ping 192.168.0.2。


假设路由器的IP是192.168.1.1,且电脑和手机的IP都是DHCP自动分配的,那么单片机IP可以这样填写:
IP4_ADDR(&ipaddr, 192, 168, 1, 53); // IP地址
IP4_ADDR(&netmask, 255, 255, 255, 0); // 子网掩码
IP4_ADDR(&gw, 192, 168, 1, 1); // 网关(路由器的地址)


还有一点需要说明的是,由于网卡驱动在初始化网卡的时候将ERXFCON寄存器的BCEN设成了0,因此现在网卡只能接收一部分广播数据包(目的地址为FF:FF:FF:FF:FF:FF的数据包),这个会在配置NetBIOS服务时详细讲解。



你可能感兴趣的:(【步骤】lwip-2.0.2在STM32F103RC+ENC28J60网卡上无操作系统移植(不使用STM32库函数))