lwip2.0.3协议栈的无操作系统移植过程:STM32单片机 + Marvell 88W8686(WM-G-MR-09) WiFi模块

总的程序代码:https://blog.csdn.net/zlk1214/article/details/80941657

其中,WiFi模块的驱动程序WiFi.c(平台无关)和WiFi_LowLevel.c(平台有关)位于数据链路层,lwip协议栈位于数据链路层、网络层、运输层和应用层。ethernetif.c作为连接lwip协议栈和WiFi模块驱动程序的接口,位于数据链路层。

lwip中各组件在协议栈中的位置:
网络层:ARP协议、ICMP协议、IGMP协议
运输层:TCP协议、UDP协议
应用层:DHCP客户端、NetBIOS计算机名服务、DNS客户端、HTTP服务器

MAC地址(网卡地址)是数据链路层上使用的地址,负责的是数据包从一个节点通过一段链路到达另一个节点。节点可以是路由器或主机,一段链路可以是一根网线或空中的无线传输媒体,中间不可以有节点存在。
IP地址是网络层上使用的地址,负责的是数据包从一台主机到达另一台主机,中间可通过无数段链路。网络层主要负责的就是把多个局域网连接在一起,构成更大的网络,使数据包可以在网络中从任何一个节点到达指定的另一个节点,但是不提供可靠的数据传输服务,数据包有可能传输失败。
交换机只负责转发数据包,将小局域网扩展成更大的局域网,交换机本身没有IP地址。
路由器不仅可以转发数据包,而且本身占有两个IP地址,连接两个局域网。路由器本身可以看做是一台有多个网络接口的主机,但是交换机不行。一台主机,有多少张连上网的网卡,就有多少个IP地址。
运输层主要负责的是数据的可靠传输、拥塞控制以及为一台主机分配供不同应用程序使用的端口号。

lwip2.0.3协议栈的无操作系统移植过程:STM32单片机 + Marvell 88W8686(WM-G-MR-09) WiFi模块_第1张图片

第一步:在工程的所在文件夹创建一个lwip-2.0.3文件夹。然后在lwip的官方网站下载lwip-2.0.3.zip,打开压缩包中的lwip-2.0.3/src文件夹,解压以下文件到工程的lwip-2.0.3目录下:
(1)必须的文件
core/*.c
core/ipv4/*.c
include/*
netif/ethernet.c
netif/ethernetif.c
(2)可选例程
apps/httpd/* (HTTP服务器例程)
apps/netbiosns/* (计算机名解析服务)
(3)WiFi无线网络WPA/WPA2认证用到的算法实现
netif/ppp/polarssl/arc4.c
netif/ppp/polarssl/md5.c
netif/ppp/polarssl/sha1.c
注意:这三个文件中必须将里面的#if条件编译中的PPP_SUPPORT条件去掉,例如:

#if /*PPP_SUPPORT && */LWIP_INCLUDED_POLARSSL_ARC4 // 注释掉前面的宏

第二步:解压后,将里面的c文件都添加到工程的lwip-2.0.3分组下。(apps/httpd文件夹下只添加fs.c和httpd.c)
具体添加的文件请看下图:

lwip2.0.3协议栈的无操作系统移植过程:STM32单片机 + Marvell 88W8686(WM-G-MR-09) WiFi模块_第2张图片

第三步:创建lwip-2.0.3/include/arch/cc.h文件(平台有关的宏定义),内容如下:

#define PACK_STRUCT_BEGIN __packed // struct前的__packed

这个关键字主要用在结构体的定义上:typedef __packed struct。加了这个关键字之后,产生的结构体大小就严格等于结构体内各成员的大小之和,不会添加额外的填充字段。

第四步:创建lwip-2.0.3/include/lwipopts.h文件(lwip协议栈选项),内容如下:

#define NO_SYS 1 // 无操作系统

#define LWIP_NETCONN 0
#define LWIP_SOCKET 0
#define LWIP_STATS 0

#define MEM_ALIGNMENT 4 // STM32单片机是32位的单片机, 因此是4字节对齐的

#define SYS_LIGHTWEIGHT_PROT 0 // 不进行临界区保护

// 配置DHCP
#define LWIP_DHCP 1
#define LWIP_NETIF_HOSTNAME 1

// 配置DNS
#define LWIP_DNS 1
#define LWIP_RAND() ((u32_t)rand())

// WPA/WPA2认证需要用到lwip中的arc4, md5和sha1函数
// 需要修改各文件的第42行, 注释掉条件编译宏
#define LWIP_INCLUDED_POLARSSL_ARC4 1
#define LWIP_INCLUDED_POLARSSL_MD5 1
#define LWIP_INCLUDED_POLARSSL_SHA1 1

第五步:编写数据链路层上数据包的收发函数。打开lwip-2.0.3/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"

// 包含头文件
#include "../../common.h"
#include "../../WiFi.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. */
/*static */void  ethernetif_input(struct netif *netif); // 必须去掉static

/**
 * 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 */
  //netif->hwaddr[0] = ;
  //...
  //netif->hwaddr[5] = ;
  // MAC地址已设置, 注释掉这段代码

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

/**
 * 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;
  uint8_t *buffer; // 添加此变量

  //initiate transfer();

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

  //for (q = p; q != NULL; q = q->next) {
    /* Send the data from the pbuf to the interface, one pbuf at a
       time. The size of the data in each pbuf is kept in the ->len
       variable. */
    //send data from(q->payload, q->len);
  //}
  buffer = WiFi_GetPacketBuffer(); // 获取发送缓冲区 (需要等待之前的帧发送完毕)
  pbuf_copy_partial(p, buffer, p->tot_len, 0); // 复制要发送的数据帧到发送缓冲区中
#ifdef WIFI_DISPLAY_PACKET_SIZE
  printf("[Send] len=%hd\n", p->tot_len);
#endif
#ifdef WIFI_DISPLAY_PACKET_TX
  dump_data(buffer, p->tot_len);
#endif

  //signal that packet should be sent();
  WiFi_SendPacket(buffer, p->tot_len, NULL, NULL, WIFI_DEFAULT_TIMEOUT_DATAACK);

  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;
  
  // 添加变量
  const WiFi_DataRx *data = WiFi_GetReceivedPacket();

  /* Obtain the size of the packet and put it into the "len"
     variable. */
  len = data->rx_packet_length;
#ifdef WIFI_DISPLAY_PACKET_SIZE
  printf("[Recv] len=%hd", len);
#ifdef WIFI_DISPLAY_PACKET_RXRATES
  printf(", SNR=%ddB, NF=-%ddBm, priority=%d", (int)data->snr, data->nf, data->priority);
  if (data->rx_rate != 0)
  {
    printf(", rates: ");
    WiFi_PrintRates(data->rx_rate);
  }
#endif
  printf("\n");
#endif
#ifdef WIFI_DISPLAY_PACKET_RX
  dump_data(data->payload, len);
#endif

#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);
    //}
    pbuf_take(p, data->payload, len); // 将数据帧内容复制到pbuf中
    //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_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
 */
/*static */void // 必须去掉static
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 = "STM32F103RE_SDIO"; // 路由器中显示的名称
#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 */

其中,low_level_output函数调用WiFi_SendPacket函数发送数据包,low_level_input函数调用WiFi_GetReceivedPacket接收数据包。ethernetif_init函数里面还有一个netif->hostname成员变量,该变量决定了设备在路由器管理页面中DHCP客户端列表中的显示名称。

第六步:将lwip的头文件夹“lwip-2.0.3\include”添加到项目属性中的Include路径中去:

lwip2.0.3协议栈的无操作系统移植过程:STM32单片机 + Marvell 88W8686(WM-G-MR-09) WiFi模块_第3张图片

另外,为了在程序中使用printf函数,Target选项卡下的Use MicroLIB复选框也要勾选上。


第七步:编写供lwip协议栈使用的sys_now函数。该函数返回一个32位的系统时间,单位为毫秒。该函数必须保证:除非定时器溢出,否则后获取的时间必须大于先获取的时间,不然会导致开机后DHCP长时间获取不到IP地址,并且TCP和UDP也不能正常工作。

例程中该函数位于common.c,函数使用STM32单片机的RTC外设提供时间信息,RTC使用的时钟源是LSI。

/* RTC时间转化为毫秒数 (lwip协议栈要求实现的函数) */
// 该函数必须保证: 除非定时器溢出, 否则后获取的时间必须大于先获取的时间
uint32_t sys_now(void)
{
  uint32_t sec[2];
  uint32_t div, milli;
  do
  {
    time(&sec[0]); // 秒
    div = rtc_divider();
    time(&sec[1]);
  } while (sec[0] != sec[1]);
  
  // CNT是在DIV从P-1跳变到P-2瞬间发生更新的 (P=RTC_PRESCALER)
  if (div == RTC_PRESCALER - 1)
    milli = div;
  else
    milli = RTC_PRESCALER - div - 2;
  milli = milli * 1000 / RTC_PRESCALER; // 毫秒
  return sec[0] * 1000 + milli;
}

第八步:在主函数main中初始化lwip协议栈:

lwip_init();
netbiosns_init();
netbiosns_set_name("STM32F103RE"); // 计算机名
httpd_init();

第九步:成功获得WiFi模块的MAC地址后,将无线网卡添加到lwip中:

// 获取网卡MAC地址成功后, 就立即将网卡添加到lwip中, 但暂不把网卡设为"已连接"状态
// 只需要在单片机复位的时候获取一次网卡地址就行, 后面重新连接热点则无需调用此函数
static void mac_address_callback(void *arg, void *data, WiFi_Status status)
{
#if !LWIP_DHCP
  struct ip4_addr ipaddr, netmask, gw;
#endif
  if (status == WIFI_STATUS_OK)
  {
    WiFi_Scan(scan_callback, NULL); // 发送扫描热点的命令
    
    memcpy(wifi_88w8686.hwaddr, data, 6); // 将获得的MAC地址复制到全局变量中
    printf("MAC Addr: %02X:%02X:%02X:%02X:%02X:%02X\n", wifi_88w8686.hwaddr[0], wifi_88w8686.hwaddr[1], wifi_88w8686.hwaddr[2], wifi_88w8686.hwaddr[3], wifi_88w8686.hwaddr[4], wifi_88w8686.hwaddr[5]);
    
#if LWIP_DHCP
    netif_add(&wifi_88w8686, IP_ADDR_ANY, IP_ADDR_ANY, IP_ADDR_ANY, NULL, ethernetif_init, ethernet_input);
#else
    IP4_ADDR(&ipaddr, 192, 168, 43, 15); // IP地址
    IP4_ADDR(&netmask, 255, 255, 255, 0); // 子网掩码
    IP4_ADDR(&gw, 192, 168, 43, 1); // 网关
    netif_add(&wifi_88w8686, &ipaddr, &netmask, &gw, NULL, ethernetif_init, ethernet_input); // 添加WiFi模块到lwip中

#if LWIP_DNS
    IP4_ADDR(&ipaddr, 8, 8, 8, 8); // 首选DNS服务器
    dns_setserver(0, &ipaddr);
    IP4_ADDR(&ipaddr, 8, 8, 4, 4); // 备用DNS服务器
    dns_setserver(1, &ipaddr);
#endif
#endif
    netif_set_default(&wifi_88w8686); // 设为默认网卡
  }
  else
    printf("Cannot get MAC address!\n");
}

其中wifi_88w8686是定义在main.c中的全局变量:

static struct netif wifi_88w8686;

请注意DHCP启用/未启用时的区别。若启用了DHCP,则IP地址不需要手动填写,由lwip自动从路由器中获取。
另外,如果是在low_level_init函数中自己设定MAC地址,那么需要注意,MAC地址的第一个字节必须是偶数,不能是奇数。因为如果是奇数的话,这个MAC地址是一个广播地址,不是单播地址,发出的数据包可能会被某些路由器拒收。

第十步:当网络连接成功时,调用netif_set_up通知lwip。当网络连接断开时,调用netif_set_down通知lwip。
如果启用了DHCP,则还需要调用dhcp_start和dhcp_stop。

/* 通知lwip网卡的连接状态 */
static void set_netif(struct netif *netif, uint8_t up)
{
  if (up)
  {
    if (!netif_is_up(netif))
    {
      netif_set_up(netif);
#if LWIP_DHCP
      dhcp_start(netif); // 路由器中显示的DHCP客户名称在ethernetif_init函数中设置
#endif
    }
  }
  else
  {
    netif_set_down(netif);
#if LWIP_DHCP
    dhcp_stop(netif);
#endif
  }
}
连接路由器后,如果WPA/WPA2认证成功,则会调用WiFi_AuthenticationCompleteHandler函数,通知lwip网络已连上:
/* WiFi认证成功回调函数 */
void WiFi_AuthenticationCompleteHandler(void)
{
  printf("Authenticated!\n");
  set_netif(&wifi_88w8686, 1); // 在lwip中启用WiFi网卡
}
网络掉线时,则是在WiFi_EventHandler函数中调用set_netif(&wifi_88w8686, 0)通知lwip:
/* WiFi事件回调函数 */
void WiFi_EventHandler(const WiFi_Event *event)
{
  printf("[Event %d] size=%d", event->event_id, event->header.length);
  if (event->header.length >= sizeof(WiFi_Event) - sizeof(event->mac_addr))
    printf(", reason=%d", event->reason_code);
  if (event->header.length >= sizeof(WiFi_Event))
    printf(", MAC: %02X:%02X:%02X:%02X:%02X:%02X", event->mac_addr[0], event->mac_addr[1], event->mac_addr[2], event->mac_addr[3], event->mac_addr[4], event->mac_addr[5]);
  printf("\n");
  
  switch (event->event_id)
  {
    case 3:
      // 收不到信号 (例如和手机热点建立连接后, 把手机拿走), WiFi模块不会自动重连
      printf("Beacon Loss/Link Loss\n");
      set_netif(&wifi_88w8686, 0);
      break;
    case 4:
      // Ad-Hoc网络中不止1个结点, 且连接数发生了变化
      printf("The number of stations in this ad hoc newtork has changed!\n");
      set_netif(&wifi_88w8686, 1);
      break;
    case 8:
      // 认证已解除 (例如手机关闭了热点, 或者连接路由器后因认证失败而自动断开连接)
      printf("Deauthenticated!\n");
      set_netif(&wifi_88w8686, 0);
      break;
    case 9:
      // 解除了关联
      printf("Disassociated!\n");
      set_netif(&wifi_88w8686, 0);
      break;
    case 17:
      // Ad-Hoc网络中只剩本结点
      printf("All other stations have been away from this ad hoc network!\n");
      set_netif(&wifi_88w8686, 0);
      break;
    case 30:
      printf("IBSS coalescing process is finished and BSSID has changed!\n");
      break;
  }
  
  if (event->header.length > sizeof(WiFi_Event))
    dump_data(event + 1, event->header.length - sizeof(WiFi_Event));
  
  // 断线自动重连
  /*
  if (!netif_is_up(&wifi_88w8686))
  {
    printf("Reconnecting...\n");
    associate_example(); // 只需要调用这个函数重新关联热点就行了, 不需要重新获取MAC地址
  }
  */
}
如果连接路由器时使用的不是WPA/WPA2认证,则在associate_callback中status的参数为WIFI_STATUS_OK,此时无需经过EAPOL认证,直接就可以判定网络已连接上:
static void associate_callback(void *arg, void *data, WiFi_Status status)
{
  switch (status)
  {
    case WIFI_STATUS_OK:
      printf("Associated!\n");
      set_netif(&wifi_88w8686, 1); // 在lwip中启用WiFi网卡
      break;
    case WIFI_STATUS_NOTFOUND:
      printf("SSID not found!\n");
      break;
    case WIFI_STATUS_FAIL:
      printf("Association failed!\n");
      break;
    case WIFI_STATUS_INPROGRESS:
      printf("Waiting for authentication!\n");
      break;
    default:
      printf("Unknown error! status=%d\n", status);
  }
}

第十一步:当网卡收到新的数据包时,调用ethernetif_input将数据包交给lwip处理。该函数会在内部调用前面提到的low_level_input函数。

/* WiFi模块收到新的数据帧 */
void WiFi_PacketHandler(const WiFi_DataRx *data)
{
  ethernetif_input(&wifi_88w8686); // 交给lwip处理
}

第十二步:在main函数的主循环while(1)中调用sys_check_timeouts函数,用于完成lwip内部的各种定时处理。

// lwip协议栈定时处理函数
sys_check_timeouts();
第十三步:当DHCP成功获得IP地址后,输出IP地址:
/* 显示DHCP分配的IP地址 */
#if LWIP_DHCP
static void display_ip(void)
{
  static uint8_t ip_displayed = 0;
  struct dhcp *dhcp;
  
  if (dhcp_supplied_address(&wifi_88w8686))
  {
    if (ip_displayed == 0)
    {
      ip_displayed = 1;
      dhcp = netif_dhcp_data(&wifi_88w8686);
      printf("DHCP supplied address!\n");
      printf("IP address: %s\n", ip4addr_ntoa(&dhcp->offered_ip_addr));
      printf("Subnet mask: %s\n", ip4addr_ntoa(&dhcp->offered_sn_mask));
      printf("Default gateway: %s\n", ip4addr_ntoa(&dhcp->offered_gw_addr));
#if LWIP_DNS
      printf("DNS Server: %s\n", ip4addr_ntoa(dns_getserver(0)));
      dns_test();
#endif
    }
  }
  else
    ip_displayed = 0;
}
#endif
该函数也是在main函数的主循环中调用的:
// 显示DHCP获取到的IP地址
#if LWIP_DHCP
  display_ip();
#endif


你可能感兴趣的:(STM32)