(如果要想添加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
【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服务时详细讲解。