【程序】Marvell 88W8686 WiFi模块(WM-G-MR-09)创建或连接热点,并使用l

2019-07-14 03:41发布

该程序是旧版本!最新版本为20180706版:https://blog.csdn.net/ZLK1214/article/details/80941657本程序所用的单片机型号为:STM32F103REPB12端口为外接的WiFi模块电源开关,当PB12输出低电平时接通电源。WiFi模块的电源引脚VCC不可直接连接到电源上,必须要串联一组PNP三极管(或场效应管),并把基极接到PB12端口上,基极要接限流电阻。
注意:WM-G-MR-09模块的芯片组(Chip Set)就是Marvell 88W8686。Keil5工程下载地址:https://pan.baidu.com/s/1Dw5skiXV5-OmDVqDFrewQA
代码说明:http://blog.csdn.net/ZLK1214/article/details/79278871
Windows下根据计算机名获取IP地址的C语言程序:http://blog.csdn.net/ZLK1214/article/details/79595245
此版本修复了上一个版本(20180129版)存在的许多问题,其中包括无法连接部分WPA/WPA2路由器的问题。添加了库函数版本的代码,还添加了SDIO错误处理代码,取消了数据帧的重传功能(因为802.11数据链路层上已经有了该功能)。该版本仍有如下问题未解决:
(1) 连接仅支持设置WPA2密码的路由器后(这类路由器通常仅支持WPA/WPA2混合模式,体现出来是扫描热点后同时拥有WPA_IE和RSN_IE信息结构), 有时路由器会进行Group Key Handshake更新GTK密钥(也就是组密钥),但程序回复的Message 2无效被路由器丢弃,最后会断开连接。解决该bug的困难在于,该类路由器往往不能在管理页面中修改组密钥更新频率,也无法通过发送EAPOL请求帧(key_info=REQUEST+MIC=0x902)请求GTK更新,并且更新频率为每天一次,给程序调试带来很大麻烦。临时解决办法:在WiFi_EventHandler中调用associate_example函数重新连接路由器,但这样会影响已连接的TCP和UDP客户,并且IP地址也有可能改变。
笔者在电脑上用Microsoft Network Monitor 3.4抓取电脑发送的Message 2认证帧,按照他的格式修改了程序发送的Message 2,经检验MIC的值是正确的,其他字段一模一样,但还是不能解决问题。说明发出去的帧内容上应该没有问题。
有一个简单的方法可以快速复现该bug。在WiFi_Associate_Callback函数中,把添加RSN参数的代码去掉,只添加WPA的Vendor参数(目的是在连接WPA/WPA2混合模式的路由器时,选WPA认证方式):cmd_size = (uint8_t *)(auth + 1) - wifi_buffer_command; if (security == WIFI_SECURITYTYPE_WPA || security == WIFI_SECURITYTYPE_WPA2) { // WPA网络必须在命令中加入Vendor参数才能成功连接 vendor = (MrvlIETypes_VendorParamSet_t *)(auth + 1); memcpy(vendor, &wifi_ssid_info.wpa, TLV_STRUCTLEN(wifi_ssid_info.wpa)); cmd_size += TLV_STRUCTLEN(wifi_ssid_info.wpa); }然后在WiFi_EAPOLProcess函数中,也把添加RSN参数的代码去掉(选择WPA方式认证):/* 在待发送的Message 2中添加Key Data信息 */ // https://community.arubanetworks.com/t5/Technology-Blog/A-closer-look-at-WiFi-Security-IE-Information-Elements/ba-p/198867 // 使用WPA的热点一定有WPA IE信息项,一定没有RSN IE信息项 // 使用WPA2的热点一定有RSN IE信息项,可能有WPA IE信息项 packet_tx = (WiFi_EAPOLKeyFrame *)WiFi_GetPacketBuffer(); kde = (WiFi_KDE *)packet_tx->key_data; if (wifi_ssid_info.wpa.header.type) { // 路由器提供了WPA IE信息项时可直接复制 //printf("WPA IE copied! "); kde->type = WIFI_MRVLIETYPES_VENDORPARAMSET; kde->length = wifi_ssid_info.wpa.header.length; memcpy(kde->oui, wifi_ssid_info.wpa.vendor, wifi_ssid_info.wpa.header.length); key_data_len = sizeof(kde->type) + sizeof(kde->length) + kde->length; } else { printf("IE not copied! "); key_data_len = 0; }这样,关联时就会强制采用WPA方式认证,每次关联时都可以产生Group key handshake,不用再等上一天了。
修改后就会发现,路由器产生了Group key handshake,程序也回应了Message 2但路由器没有接受,最后Deauthenticated!被强制断开连接。
进行WPA认证时组密钥握手失败的原因是:WiFi_EAPOLProcess函数中,CMD_802_11_KEY_MATERIAL命令帧和EAPOL回应数据帧不能同时发送。必须先发送其中一个,成功后在回调函数中发送另一个。
这个解决办法只能修复WPA认证时最开始握手失败的问题,仍不能解决WPA和WPA2认证成功过后一天内出现的组密钥更新失败的问题。

(2) 发送了很多数据帧后,有时会卡在WiFi_Wait(0x02): timeout!上,再也发不出任何数据帧了。临时解决办法:把WiFi.h中WIFI_DEFAULT_TIMEOUT_DATAACK的值改大,比如1000,10000等。
(3) 连接某些WPA的热点,卡在Waiting for authentication!上无法继续,接收不到Message 1。
(4) TCP和UDP的发送速率很慢,只有几到几十KB/s,并且UDP丢包率也很严重,大约达到了30%。临时解决办法:WiFi_SendPacket函数中将packet->tx_control的值改为0x001c(优先使用54Mbps传输速率,默认的最大重传次数2),这样能使UDP发送速率稳定在300~700KB/s左右,但不能改善UDP丢包率和TCP发送速率。
(5) 连接某些手机热点时,单片机复位后第一次扫描到的概率很大,但之后就很难再次扫描到了,有时长达好几分钟一直显示SSID not found!
(6) 长时间连接路由器后没有通信,电脑无法ping通WiFi开发板。

【勘误】2018年4月1日(重要):DHCP长时间获取不到IP地址,是因为sys_now函数的实现有问题。该函数没有考虑到毫秒向秒进位时发生的同步问题,有时候后调用的时间值小于先调用的时间值,使sys_check_timeouts函数中的diff值为负,破坏了next_timeout链表,导致sys_check_timeouts函数不能正常工作。下面是解决方法,把代码复制到工程里就可以修复此问题:/* 获取RTC分频计数器的值 */ uint32_t rtc_divider(void) { uint32_t div[2]; do { div[0] = RTC_GetDivider(); div[1] = RTC_GetDivider(); } while ((div[0] >> 16) != (div[1] >> 16)); // RTC_GetDivider函数先读取DIVH再读取DIVL // 如果更新发生在第一次读取DIVH或DIVL后, 则div[0]和div[1]的高16位不相等, 两个值都会被丢弃 // 如果更新发生在第二次读取DIVH后, 则div[0]和div[1]的高16位相等, 且div[0]值的高、低16位匹配,div[1]值的高、低16位不匹配 return div[0]; // 所以应该取第一次读取的值 } /* 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; } /* 获取RTC秒数 */ time_t time(time_t *timer) { // CNTH和CNTL是同时更新的 // 但由于这两个寄存器不是同时读取的, 所以有可能一个读到的是更新前的值, 另一个读到的是更新后的值 uint32_t now[2]; do { now[0] = RTC_GetCounter(); now[1] = RTC_GetCounter(); } while ((now[0] >> 16) != (now[1] >> 16)); // 使用循环可以避免中断的影响 // RTC_GetCounter函数先读取CNTL再读取CNTH // 如果更新发生在第一次读取CNTL后, 则now[0]和now[1]的高16位相等, 且now[0]值的高、低16位不匹配,now[1]值的高、低16位匹配 // 如果更新发生在第一次读取CNTH后或第二次读取CNTL后, 则now[0]和now[1]的高16位不相等, 两个值都会被丢弃 if (timer) *timer = now[1]; return now[1]; // 所以应该取第二次读取的值 }
Wi-Fi模块电源引脚的连接方法:
程序支持连接无密码的热点以及WEP、WPA-PSK和WPA2-PSK认证类型的热点,加密方式支持TKIP和AES。
支持创建无密码或是带有WEP密码的ADHOC热点,ADHOC模式下不支持WPA和WPA2!

注意:虽然SDIO标准规定可以总线上可以接多张SD卡,但STM32单片机的SDIO接口只支持接一张卡,STM32F103芯片手册Datasheet(不是参考手册)中有声明:
The current version supports only one SD/SDIO/MMC4.2 card at any one time and a stack of MMC4.1 or previous.
如果想要同时使用WiFi模块和SD内存卡,建议SD内存卡采用SPI总线通信。

【程序运行截图】连上路由器后DHCP分配得到IP地址:(花的时间有时候长,有时候短。解决方法见2018年4月1日的勘误)下面是把WiFi模块固件写入单片机芯片Flash固定区域的程序(用于减少调试主程序时下载程序的时间)的运行结果:’电脑上ping IP地址和计算机名:
通过计算机名在电脑上访问开发板上的HTTP服务器(lwip自带的httpd):
【程序运行结果(扫描热点并连接安卓手机开的WPA2热点)】STM32F103RE SDIO 88W8686 RESPCMD63, RESP1_90ff8000 RESPCMD63, RESP1_90300000 Number of I/O Functions: 1 Memory Present: 0 Relative Card Address: 0x0001 Card selected! RESP1_00001e00 SDIO Clock: 24MHz [CIS] func=0, ptr=0x00008000 Product Information: Marvell 802.11 SDIO ID: 0B Manufacturer Code: 0x02df Manufacturer Information: 0x9103 Card Function Code: 0x0c System Initialization Bit Mask: 0x00 Maximum Block Size: 256 Maximum Transfer Rate Code: 0x32 [CIS] func=1, ptr=0x00008080 Card Function Code: 0x0c System Initialization Bit Mask: 0x00 Maximum Block Size: 256 Firmware is successfully downloaded! MAC Addr: 00:1A:6B:A4:AA:B4 SSID 'CMCC-EDU', MAC 96:14:4B:6F:A5:DA, RSSI 73, Channel 1 Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure) SSID 'CMCC-Young', MAC 96:14:4B:6F:A5:DB, RSSI 69, Channel 1 Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure) SSID '???????', MAC BC:46:99:9B:1E:E4, RSSI 71, Channel 1 Capability: 0x0431 (Security: WPA2, Mode: Infrastructure) SSID 'CMCC-EDU', MAC E6:14:4B:57:40:0F, RSSI 75, Channel 1 Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure) SSID 'CMCC-Young', MAC E6:14:4B:57:40:00, RSSI 75, Channel 1 Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure) SSID 'CMCC-EDU', MAC F6:14:4B:6C:19:50, RSSI 86, Channel 6 Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure) SSID 'TP-LINK_PLC', MAC 30:FC:68:38:6E:2C, RSSI 66, Channel 6 Capability: 0x0431 (Security: WPA2, Mode: Infrastructure) SSID 'TP-LINK_ORANGE', MAC B0:95:8E:05:82:CA, RSSI 56, Channel 6 Capability: 0x0431 (Security: WPA2, Mode: Infrastructure) SSID 'vivo Y29L', MAC F4:29:81:98:F3:78, RSSI 49, Channel 6 Capability: 0x8431 (Security: WPA2, Mode: Infrastructure) SSID 'CDU_Free', MAC D4:61:FE:71:36:D0, RSSI 72, Channel 6 Capability: 0x8421 (Security: Unsecured, Mode: Infrastructure) SSID 'CDU', MAC D4:61:FE:71:36:D1, RSSI 73, Channel 6 Capability: 0x0431 (Security: WPA2, Mode: Infrastructure) SSID 'CMCC-EDU', MAC D6:14:4B:6F:A6:0E, RSSI 58, Channel 11 Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure) SSID 'CMCC-Young', MAC D6:14:4B:6F:A6:0F, RSSI 58, Channel 11 Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure) SSID 'xgxy666', MAC DC:FE:18:67:76:14, RSSI 73, Channel 11 Capability: 0x0431 (Security: WPA2, Mode: Infrastructure) SSID '10505diansai', MAC 50:FA:84:53:5B:8E, RSSI 77, Channel 13 Capability: 0x0411 (Security: WPA2, Mode: Infrastructure) Scan finished! Waiting for authentication! Message 1 received! Message 2 sent! Message 3 received! Message 4 sent! Authenticated! [Send] len=350 PTK & GTK set! [Recv] len=351 [Send] len=350 [Recv] len=351 [Send] len=42 [Send] len=42 [Send] len=42 [Send] len=42 [Send] len=42 [Send] len=42 [Send] len=42 DHCP supplied address at 0.65s! IP address: 192.168.43.64 Subnet mask: 255.255.255.0 Default gateway: 192.168.43.1 DNS Server: 192.168.43.1 [Send] len=42 Not in cache! err=-5 [Recv] len=42 [Send] len=76 [Recv] len=92 DNS Found IP: 106.186.126.193 Connecting to 106.186.126.193... [Send] len=58 [Recv] len=58 Connected! err=0 Connection is successfully closed! [Send] len=54 [Send] len=42 [Recv] len=54 [Send] len=54 [Send] len=42 [Send] len=42 [Send] len=42 [Recv] len=74 [Send] len=58 [Recv] len=54 [Recv] len=524 [Send] len=590 [Send] len=590 [Recv] len=54 [Send] len=590 [Recv] len=54 [Send] len=304 [Recv] len=54 [Recv] len=54 [Send] len=54 [Recv] len=74 [Send] len=58 [Recv] len=54 [Recv] len=488 [Send] len=590 [Send] len=349 [Recv] len=54 [Recv] len=54 [Send] len=54 [Recv] len=74 [Send] len=58 [Recv] len=54 [Recv] len=419 [Send] len=590 [Send] len=202 [Recv] len=54 [Recv] len=54 [Send] len=54
【程序主要代码(库函数版本)】main.c:#include // http服务器 #include // NetBIOS服务 #include // DHCP客户端 #include // DNS客户端 #include // lwip_init函数所在的头文件 #include // sys_check_timeouts函数所在的头文件 #include // ethernet_input函数所在头文件 #include #include #include "common.h" #include "WiFi.h" // 这两个函数位于ethernetif.c中, 但没有头文件声明 err_t ethernetif_init(struct netif *netif); void ethernetif_input(struct netif *netif); // 这两个函数位于dns_test.c中 void dns_test(void); void display_time(void); static struct netif wifi_88w8686; #if LWIP_DHCP static uint32_t dhcp_start_time = 0; #endif int fputc(int ch, FILE *fp) { if (fp == stdout) { if (ch == ' ') { while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // 等待前一字符发送完毕 USART_SendData(USART1, ' '); } while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); USART_SendData(USART1, ch); } return ch; } /* 通知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函数中设置 dhcp_start_time = sys_now(); #endif } } else { netif_set_down(netif); #if LWIP_DHCP dhcp_release(netif); dhcp_stop(netif); #endif } } /* WiFi认证成功回调函数 */ void WiFi_AuthenticationCompleteHandler(void) { printf("Authenticated! "); set_netif(&wifi_88w8686, 1); // 在lwip中启用WiFi网卡 } /* 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(" "); switch (event->event_id) { case 3: // 收不到信号 (例如和手机热点建立连接后, 把手机拿走), WiFi模块不会自动重连 printf("Beacon Loss/Link Loss "); set_netif(&wifi_88w8686, 0); break; case 4: // Ad-Hoc网络中不止1个结点, 且连接数发生了变化 printf("The number of stations in this ad hoc newtork has changed! "); set_netif(&wifi_88w8686, 1); break; case 8: // 认证已解除 (例如手机关闭了热点, 或者连接路由器后因认证失败而自动断开连接) printf("Deauthenticated! "); set_netif(&wifi_88w8686, 0); break; case 9: // 解除了关联 printf("Disassociated! "); set_netif(&wifi_88w8686, 0); break; case 17: // Ad-Hoc网络中只剩本结点 printf("All other stations have been away from this ad hoc network! "); set_netif(&wifi_88w8686, 0); break; case 30: printf("IBSS coalescing process is finished and BSSID has changed! "); break; } if (event->header.length > sizeof(WiFi_Event)) dump_data(event + 1, event->header.length - sizeof(WiFi_Event)); } /* WiFi模块收到新的数据帧 */ void WiFi_PacketHandler(const WiFi_DataRx *data) { ethernetif_input(&wifi_88w8686); // 交给lwip处理 } void associate_callback(void *arg, void *data, WiFi_Status status) { switch (status) { case WIFI_STATUS_OK: printf("Associated! "); set_netif(&wifi_88w8686, 1); // 在lwip中启用WiFi网卡 break; case WIFI_STATUS_NOTFOUND: printf("SSID not found! "); break; case WIFI_STATUS_FAIL: printf("Association failed! "); break; case WIFI_STATUS_INPROGRESS: printf("Waiting for authentication! "); break; default: printf("Unknown error! status=%d ", status); } } void associate_example(void) { WiFi_Connection conn; //WiFi_WEPKey wepkey = {0}; // 未使用的成员必须设为0 /* conn.security = WIFI_SECURITYTYPE_WEP; conn.ssid = "Oct1158-2"; conn.password = &wepkey; wepkey.keys[0] = "1234567890123"; wepkey.index = 0; // 密钥序号必须要正确 WiFi_AssociateEx(&conn, WIFI_AUTH_MODE_OPEN, -1, associate_callback, NULL); // 开放系统方式 //WiFi_AssociateEx(&conn, WIFI_AUTH_MODE_SHARED, -1, associate_callback, NULL); // 共享密钥方式 */ conn.security = WIFI_SECURITYTYPE_WPA; // WPA和WPA2都可以使用此选项 conn.ssid = "vivo Y29L"; conn.password = "2345678am"; // WPA密码直接指定, 不需要WiFi_WEPKey结构体 WiFi_AssociateEx(&conn, WIFI_AUTH_MODE_OPEN, -1, associate_callback, NULL); // 必须使用WIFI_AUTH_MODE_OPEN选项 } void adhoc_callback(void *arg, void *data, WiFi_Status status) { if (status == WIFI_STATUS_OK) printf("ADHOC %sed! ", (char *)arg); else printf("Cannot %s ADHOC! ", (char *)arg); } void adhoc_example(void) { WiFi_Connection conn; WiFi_WEPKey wepkey = {0}; // 未使用的成员必须设为0 /* conn.security = WIFI_SECURITYTYPE_WEP; conn.ssid = "Octopus-WEP"; conn.password = &wepkey; wepkey.keys[0] = "3132333435"; wepkey.index = 0; // 范围: 0~3 WiFi_JoinADHOCEx(&conn, -1, adhoc_callback, "join"); */ ///* // 注意: 电脑上无论密码输入是否正确都可以连接, 但只有正确的密码才可以通信 conn.security = WIFI_SECURITYTYPE_WEP; conn.ssid = "WM-G-MR-09"; conn.password = &wepkey; wepkey.keys[0] = "1234567890123"; wepkey.index = 0; WiFi_StartADHOCEx(&conn, adhoc_callback, "start"); //*/ /* conn.security = WIFI_SECURITYTYPE_NONE; conn.ssid = "Octopus-WEP"; WiFi_JoinADHOCEx(&conn, -1, adhoc_callback, "join"); */ /* conn.security = WIFI_SECURITYTYPE_NONE; conn.ssid = "WM-G-MR-09"; WiFi_StartADHOCEx(&conn, adhoc_callback, "start"); */ } void scan_callback(void *arg, void *data, WiFi_Status status) { if (status == WIFI_STATUS_OK) printf("Scan finished! "); else printf("Scan failed! "); //adhoc_example(); associate_example(); } // 获取网卡MAC地址成功后, 就立即将网卡添加到lwip中, 但暂不把网卡设为"已连接"状态 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 ", 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! "); } void stop_callback(void *arg, void *data, WiFi_Status status) { char *s1 = (char *)arg; char *s2 = s1 + strlen(s1) + 1; if (status == WIFI_STATUS_OK) { set_netif(&wifi_88w8686, 0); printf("%s %s! ", s1, s2); } else printf("%s not %s! ", s1, s2); } int main(void) { GPIO_InitTypeDef gpio; USART_InitTypeDef usart; #if LWIP_DHCP struct dhcp *dhcp; #endif uint8_t data; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE); // 串口发送引脚PA9设为复用推挽输出, 串口接收引脚PA10保持默认的浮空输入 gpio.GPIO_Mode = GPIO_Mode_AF_PP; gpio.GPIO_Pin = GPIO_Pin_9; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio); USART_StructInit(&usart); usart.USART_BaudRate = 115200; USART_Init(USART1, &usart); USART_Cmd(USART1, ENABLE); printf("STM32F103RE SDIO 88W8686 "); rtc_init(); WiFi_Init(); WiFi_GetMACAddress(mac_address_callback, NULL); lwip_init(); netbiosns_init(); netbiosns_set_name("STM32F103RE"); // 计算机名 httpd_init(); while (1) { // WiFi模块中断和超时处理 if (SDIO_GetFlagStatus(SDIO_FLAG_SDIOIT) == SET) { SDIO_ClearFlag(SDIO_FLAG_SDIOIT); WiFi_Input(); } else WiFi_CheckTimeout(); // lwip协议栈定时处理函数 sys_check_timeouts(); // 显示DHCP获取到的IP地址 #if LWIP_DHCP if (dhcp_supplied_address(&wifi_88w8686)) { if (dhcp_start_time != 0) { printf("DHCP supplied address at %.2fs! ", (sys_now() - dhcp_start_time) / 1000.0); dhcp_start_time = 0; dhcp = netif_dhcp_data(&wifi_88w8686); printf("IP address: %s ", ip4addr_ntoa(&dhcp->offered_ip_addr)); printf("Subnet mask: %s ", ip4addr_ntoa(&dhcp->offered_sn_mask)); printf("Default gateway: %s ", ip4addr_ntoa(&dhcp->offered_gw_addr)); #if LWIP_DNS printf("DNS Server: %s ", ip4addr_ntoa(dns_getserver(0))); dns_test(); #endif } } #endif // 串口调试命令 if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) { data = USART_ReceiveData(USART1); switch (data) { case 'A': // 离开ADHOC网络 WiFi_StopADHOC(stop_callback, "ADHOC