PB12端口为外接的WiFi模块电源开关,当PB12输出低电平时接通电源。WiFi模块的电源引脚VCC不可直接连接到电源上,必须要串联一组PNP三极管(或场效应管),并把基极接到PB12端口上,基极要接限流电阻。
注意:WM-G-MR-09模块的芯片组(Chip Set)就是Marvell 88W8686。
该程序目前暂不支持WPA和WPA2加密方式!
88W8686 WiFi模块的固件(Firmware)内容:http://blog.csdn.net/zlk1214/article/details/76166140
【勘误】
2018年2月3日:while (WiFi_GetPacketLength() == 0xfedc);中的==应该改为!=
请下载程序的最新版本:http://blog.csdn.net/ZLK1214/article/details/79531191
注意必须把STM32启动文件*.s中的函数堆栈大小Stack_Size改大,否则函数中无法创建大数组!
Stack_Size EQU 0x00004000
连上(或创建)热点后请最好不要再调用WiFi_SendCommand和WiFi_ReceiveResponse函数,因为收到的回应既可能是数据包也可能是命令回应。
另外,虽然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总线通信。
【main.c(寄存器版)】
#include <stdio.h> #include <stm32f10x.h> #include "lwip/init.h" // lwip_init函数所在的头文件 #include "lwip/timeouts.h" // sys_check_timeouts函数所在的头文件 #include "netif/ethernet.h" // ethernet_input函数所在头文件 #include "WiFi.h" // 这两个函数位于ethernetif.c中, 但没有头文件声明 err_t ethernetif_init(struct netif *netif); void ethernetif_input(struct netif *netif); // 一个很简单的http服务器 void init_http(void); // printf函数的实现, 必须在项目属性中勾选Use MicroLIB后才能用 int fputc(int ch, FILE *fp) { if (fp == stdout) { if (ch == '\n') { while ((USART1->SR & USART_SR_TXE) == 0); USART1->DR = '\r'; } while ((USART1->SR & USART_SR_TXE) == 0); // 等待发送寄存器变为空 USART1->DR = ch; } return ch; } // RTC时间转化为毫秒数 uint32_t sys_now(void) { uint32_t sec = (RTC->CNTH << 16) | RTC->CNTL; // 秒 uint32_t milli = (39999 - RTC->DIVL) * 1000 / 40000; // 毫秒 (DIV寄存器的范围是0~PRL-1) return sec * 1000 + milli; } // 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"); break; case 4: // Ad-Hoc网络中不止1个结点, 且连接数发生了变化 printf("The number of stations in this ad hoc newtork has changed!\n"); break; case 8: // 认证已解除 (例如手机关闭了热点, 或者是连接路由器后一直没有输入密码而自动断开连接) printf("Deauthenticated!\n"); break; case 9: printf("Disassociated!\n"); break; case 17: // Ad-Hoc网络中只剩本结点 printf("All other stations have been away from this ad hoc network!\n"); break; } if (event->header.length > sizeof(WiFi_Event)) dump_data(event + 1, event->header.length - sizeof(WiFi_Event)); } int main(void) { struct ip4_addr ipaddr, netmask, gw; struct netif wifi_88w8686; uint32_t last_check = 0; // HCLK=PCLK2=72MHz, PCLK1=36MHz RCC->AHBENR |= RCC_AHBENR_SDIOEN; // 打开SDIO外设的时钟, 注意AHBENR寄存器有初值 #ifdef WIFI_USEDMA RCC->AHBENR |= RCC_AHBENR_DMA2EN; #endif RCC->APB1ENR = RCC_APB1ENR_PWREN | RCC_APB1ENR_TIM6EN; // TIM6为延时用的定时器 RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | RCC_APB2ENR_USART1EN; // GPIO寄存器的初值: GPIOA->CRH=0x88844444, GPIOA->ODR=0xa000, GPIOB->CRL=0x44484444, GPIOB->ODR=0x0010 GPIOA->CRH = 0x888444b4; // 串口1发送引脚PA9设为复用推挽50MHz输出(b), 接收引脚PA10设为浮空输入(4), 调试用引脚PA13~15为默认的带上/下拉电阻输入 // WiFi模块的VCC引脚不可直接连接电源, 必须要串联一个PNP型三极管, 并把基极接到PB12上, 否则STM32复位时WiFi模块无法自动复位 // 复位时PB12输出高阻态, 三极管截止, WiFi模块断电, 配置完CRH寄存器后立即输出低电平, WiFi模块通电 GPIOB->CRH = 0x44434444; // PB12设为推挽50MHz输出(3) GPIOC->CRH = 0x444bbbbb; // PC8~11为SDIO的4位数据引脚, PC12为SDIO时钟引脚, 设为复用推挽50MHz输出(b) GPIOD->CRL = 0x44444b44; // PD2为SDIO命令引脚, 设为复用推挽50MHz输出(b) USART1->BRR = 625; // 72000000/115200=625, 波特率设为115200 USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; // 打开串口1, 允许发送和接收 PWR->CR = PWR_CR_DBP; // 允许写后备寄存器(如RCC->BDCR) RCC->CSR |= RCC_CSR_LSION; // 因为板上没有LSE晶振, 所以RTC时钟选LSI while ((RCC->CSR & RCC_CSR_LSIRDY) == 0); // 等待LSI启动 // 若RTC未打开, 则初始化RTC if ((RCC->BDCR & RCC_BDCR_RTCEN) == 0) { // 必须要先选择时钟, 然后再开启RTC时钟 RCC->BDCR |= RCC_BDCR_RTCSEL_1 | RCC_BDCR_RTCEN; // 选LSI作为RTC时钟, 并开启RTC时钟, RTC开始走时 RTC->CRL |= RTC_CRL_CNF; // 进入RTC配置模式 RTC->PRLH = 0; // 设置分频系数 RTC->PRLL = 39999; // LSI晶振的频率是40kHz, 定时1s, 注意PRLH/L寄存器只能写不能读 //RTC->CNTH = 0; // 设置初始时间 //RTC->CNTL = 50; // STM32F1系列的RTC没有年月日、小时等寄存器, 只有一个32位的计数器, 要想实现日期和时间的功能必须调用C库<time.h>中的mktime函数, 用软件来实现这些功能 RTC->CRL &= ~RTC_CRL_CNF; // 保存设置 while ((RTC->CRL & RTC_CRL_RTOFF) == 0); // 等待设置生效 } else { // 等待RTC与APB1时钟同步 RTC->CRL &= ~RTC_CRL_RSF; while ((RTC->CRL & RTC_CRL_RSF) == 0); } WiFi_Init(); WiFi_Scan(); // 显示附近的热点信息 (可以注释掉) WiFi_SetWEP(WIFI_ACT_ADD, "Hello, World!"); // 设置热点WEP密码 (若不想设置密码请删除掉这句话) WiFi_StartADHOC("Marvell 88W8686"); // 创建Ad-Hoc热点 //WiFi_SetWEP(WIFI_ACT_ADD, "48656c6c6f2c20576f726c6421"); // 设置热点WEP密码(第二种方式): Hello, World! //WiFi_Connect("Octopus-WEP", BSS_INDEPENDENT); // 连接Ad-Hoc热点 (Win7电脑可以创建WEP加密的Ad-Hoc型热点) //WiFi_Connect("10507", BSS_INFRASTRUCTURE); // 连接路由器建立的AP热点 //WiFi_Connect("vivo Y29L", BSS_INFRASTRUCTURE); // 连接手机开的没有密码的AP热点 lwip_init(); IP4_ADDR(&ipaddr, 192, 168, 43, 10); // 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); netif_set_default(&wifi_88w8686); // 设为默认网卡 netif_set_up(&wifi_88w8686); init_http(); while (1) { if (WiFi_PacketArrived()) ethernetif_input(&wifi_88w8686); // sys_check_timeouts函数千万不能调用的太频繁, 否则会出错!(例如开机后要等1~2分钟DHCP才能分配到IP地址) if (sys_now() - last_check > 200) { last_check = sys_now(); sys_check_timeouts(); } } }【WiFi.h(寄存器版)】
#ifndef _BV #define _BV(n) (1u << (n)) #endif #define CMD52_WRITE _BV(31) #define CMD52_READAFTERWRITE _BV(27) #define CMD53_WRITE _BV(31) #define CMD53_BLOCKMODE _BV(27) #define CMD53_INCREMENTING _BV(26) /* 6.9 Card Common Control Registers (CCCR) */ #define SDIO_CCCR_IOEN 0x02 #define SDIO_CCCR_IOEN_IOE1 _BV(1) #define SDIO_CCCR_IORDY 0x03 #define SDIO_CCCR_IORDY_IOR1 _BV(1) #define SDIO_CCCR_INTEN 0x04 #define SDIO_CCCR_INTEN_IENM _BV(0) #define SDIO_CCCR_INTEN_IEN1 _BV(1) #define SDIO_CCCR_BUSIFCTRL 0x07 // Bus Interface Control #define SDIO_CCCR_BUSIFCTRL_BUSWID_1Bit 0 #define SDIO_CCCR_BUSIFCTRL_BUSWID_4Bit 0x02 #define SDIO_CCCR_BUSIFCTRL_BUSWID_8Bit 0x03 #define SDIO_SUCCEEDED() ((SDIO->STA & ~SDIO_STA_SDIOIT) == 0) // 16.5 SDIO Card Metaformat #define CISTPL_NULL 0x00 // Null tuple #define CISTPL_VERS_1 0x15 // Level 1 version/product-information #define CISTPL_MANFID 0x20 // Manufacturer Identification String Tuple #define CISTPL_FUNCID 0x21 // Function Identification Tuple #define CISTPL_FUNCE 0x22 // Function Extensions #define CISTPL_END 0xff // The End-of-chain Tuple /* WiFi配置 */ #define WIFI_DEFBUFSIZE 256 // WiFi命令默认的缓冲区大小(兼容不支持字节流传输的SDIO高速模式) #define WIFI_DEFTIMEOUT 1500 // WiFi命令回应的超时时间(ms) #define WIFI_HIGHSPEED // 采用SDIO高速模式 #define WIFI_USEDMA // SDIO采用DMA方式收发数据(高速模式必须采用DMA) /* WiFi寄存器 */ #define WIFI_IOPORT0 0x00 #define WIFI_IOPORT1 0x01 #define WIFI_IOPORT2 0x02 #define WIFI_INTMASK 0x04 // Host Interrupt Mask #define WIFI_INTMASK_HOSTINTMASK 0x0f // enable/disable SDU to SD host interrupt #define WIFI_INTSTATUS 0x05 // Host Interrupt Status #define WIFI_INTSTATUS_UPLD _BV(0) // Upload Host Interrupt Status (可随时手动清除, 无论UPLDCARDRDY是否为1) #define WIFI_SQREADBASEADDR0 0x10 #define WIFI_SQREADBASEADDR1 0x11 #define WIFI_SQREADBASEADDR2 0x12 #define WIFI_SQREADBASEADDR3 0x13 #define WIFI_CARDSTATUS 0x20 // Card Status #define WIFI_CARDSTATUS_IOREADY _BV(3) // I/O Ready Indicator #define WIFI_CARDSTATUS_CISCARDRDY _BV(2) // Card Information Structure Card Ready #define WIFI_CARDSTATUS_UPLDCARDRDY _BV(1) // Upload Card Ready (CMD53读写命令均会清除该位) #define WIFI_CARDSTATUS_DNLDCARDRDY _BV(0) // Download Card Ready #define WIFI_SCRATCHPAD4_0 0x34 #define WIFI_SCRATCHPAD4_1 0x35 /* Capability information */ #define WIFI_CAPABILITY_BSS _BV(0) #define WIFI_CAPABILITY_IBSS _BV(1) #define WIFI_CAPABILITY_CF_POLLABLE _BV(2) #define WIFI_CAPABILITY_CF_POLL_REQUEST _BV(3) #define WIFI_CAPABILITY_PRIVACY _BV(4) #define WIFI_CAPABILITY_SHORT_PREAMBLE _BV(5) #define WIFI_CAPABILITY_PBCC _BV(6) #define WIFI_CAPABILITY_CHANNEL_AGILITY _BV(7) #define WIFI_CAPABILITY_SPECTRUM_MGMT _BV(8) #define WIFI_CAPABILITY_QOS _BV(9) #define WIFI_CAPABILITY_SHORT_SLOT _BV(10) #define WIFI_CAPABILITY_DSSS_OFDM _BV(13) #define WIFI_SDIOFRAME_DATA 0x00 #define WIFI_SDIOFRAME_COMMAND 0x01 #define WIFI_SDIOFRAME_EVENT 0x03 /* Command List */ #define CMD_802_11_SCAN 0x0006 // Starts the scan process #define CMD_802_11_ASSOCIATE 0x0012 // Initiate an association with the AP #define CMD_802_11_SET_WEP 0x0013 // Configures the WEP keys #define CMD_MAC_CONTROL 0x0028 // Controls hardware MAC #define CMD_802_11_AD_HOC_START 0x002b // Starts an Ad-Hoc network #define CMD_802_11_AD_HOC_JOIN 0x002c // Join an Ad-Hoc network #define CMD_802_11_MAC_ADDR 0x004d // WLAN MAC address #define CMD_802_11_KEY_MATERIAL 0x005e // Gets/sets key material used to do Tx encryption or Rx decryption #define CMD_802_11_BG_SCAN_CONFIG 0x006b // Gets/sets background scan configuration #define CMD_802_11_BG_SCAN_QUERY 0x006c // Gets background scan results #define CMD_802_11_SUBSCRIBE_EVENT 0x0075 // Subscribe to events and set thresholds /* Command Result Codes */ #define CMD_STATUS_SUCCESS 0x0000 // No error #define CMD_STATUS_ERROR 0x0001 // Command failed #define CMD_STATUS_UNSUPPORTED 0x0002 // Command is not supported #define WIFI_ACT_GET 0 #define WIFI_ACT_SET 1 #define WIFI_ACT_ADD 2 #define WIFI_ACT_BITWISE_SET 2 #define WIFI_ACT_BITWISE_CLR 3 #define WIFI_ACT_REMOVE 4 /* Authentication Type to be used to authenticate with AP */ #define AUTH_MODE_OPEN 0x00 #define AUTH_MODE_SHARED 0x01 #define AUTH_MODE_NETWORK_EAP 0x80 /* WiFi_Associate return value */ #define WIFI_ASSOCIATION_NOTFOUND 0xfffe #define WIFI_ASSOCIATION_ERROR 0xffff #define WIFI_ASSOCIATION_SUCCESS 0x0000 // 连接成功 #define WIFI_ASSOCIATION_INTERNALERROR 0x0101 #define WIFI_ASSOCIATION_AUTHUNHANDLED(ret) (((ret) & 0xff00) == 0x200) // 未处理的认证帧 #define WIFI_ASSOCIATION_UNSUPPORTEDAUTHALG 0x0213 #define WIFI_ASSOCIATION_INVALIDSEQUENCENUMBER 0x0214 #define WIFI_ASSOCIATION_AUTHREFUSED(ret) (((ret) & 0xff00) == 0x300) // 认证失败 #define WIFI_ASSOCIATION_TIMEOUT(ret) (((ret) & 0xff00) == 0x400) // 超时 #define WIFI_ASSOCIATION_ASSOCTIMEOUT 0x0401 // 连接超时 #define WIFI_ASSOCIATION_AUTHTIMEOUT 0x402 // 认证超时 #define WIFI_ASSOCIATION_NETWORKJOINTIMEOUT 0x403 // 加入网络时超时 #define WIFI_KEYTYPE_WEP 0 #define WIFI_KEYTYPE_TKIP 1 #define WIFI_KEYTYPE_AES 2 #define WIFI_KEYINFO_KEYENABLED _BV(2) #define WIFI_KEYINFO_UNICASTKEY _BV(1) #define WIFI_KEYINFO_MULTICASTKEY _BV(0) #define WIFI_MACCTRL_RX _BV(0) #define WIFI_MACCTRL_TX _BV(1) // 此位必须要置1才能发送数据!!! #define WIFI_MACCTRL_LOOPBACK _BV(2) #define WIFI_MACCTRL_WEP _BV(3) #define WIFI_MACCTRL_ETHERNET2 _BV(4) #define WIFI_MACCTRL_PROMISCUOUS _BV(7) #define WIFI_MACCTRL_ALLMULTICAST _BV(8) #define WIFI_MACCTRL_ENFORCEPROTECTION _BV(10) // strict protection #define WIFI_MACCTRL_ADHOCGPROTECTIONMODE _BV(13) // 802.11g protection mode #define WIFI_WEPKEYTYPE_40BIT 1 #define WIFI_WEPKEYTYPE_104BIT 2 /* BSS type */ #define BSS_INFRASTRUCTURE 0x01 #define BSS_INDEPENDENT 0x02 #define BSS_ANY 0x03 /* Table 45: IEEE 802.11 Standard IE Translated to Marvell IE */ /* PDF中的表45有一些拼写错误, MRVIIE应该改为MRVLIE */ #define MRVLIETYPES_SSIDPARAMSET 0x0000 #define MRVLIETYPES_RATESPARAMSET 0x0001 #define MRVLIETYPES_DSPARAMSET 0x0003 #define MRVLIETYPES_CFPARAMSET 0x0004 #define MRVLIETYPES_IBSSPARAMSET 0x0006 #define MRVLIETYPES_RSNPARAMSET 0x0030 #define MRVLIETYPES_VENDORPARAMSET 0x00dd #define MRVLIETYPES_KEYPARAMSET 0x0100 #define MRVLIETYPES_CHANLISTPARAMSET 0x0101 #define MRVLIETYPES_TSFTIMESTAMP 0x0113 #define MRVLIETYPES_AUTHTYPE 0x011f /* 已知结构体大小sizeof(tlv), 求数据域的大小, 一般用于给header.length赋值 */ // 例如定义一个MrvlIETypes_CfParamSet_t param变量, 赋值param.header.length=TLV_PAYLOADLEN(param) #define TLV_PAYLOADLEN(tlv) (sizeof(tlv) - sizeof((tlv).header)) /* 已知数据域大小, 求整个结构体的大小 */ // 例如定义一个很大的buffer, 然后定义一个IEEEType *的指针p指向该buffer // buffer接收到数据后, 要求出接收到的IEEEType数据的实际大小显然不能用sizeof(IEEEType), 因为定义IEEEType结构体时data的长度定义的是1 // 此时就应该使用TLV_STRUCTLEN(*p) #define TLV_STRUCTLEN(tlv) (sizeof((tlv).header) + (tlv).header.length) // 已知本TLV的地址和大小, 求下一个TLV的地址 #define TLV_NEXT(tlv) ((uint8_t *)tlv + TLV_STRUCTLEN(*tlv)) /* TLV (Tag Length Value) of IEEE IE Type Format */ typedef __packed struct { uint8_t type; uint8_t length; // 数据域的大小 } IEEEHeader; // information element parameter // 所有IEEETypes_*类型的基类型 typedef __packed struct { IEEEHeader header; uint8_t data[1]; } IEEEType; typedef __packed struct { IEEEHeader header; uint8_t channel; } IEEETypes_DsParamSet_t; typedef __packed struct { IEEEHeader header; uint16_t atim_window; } IEEETypes_IbssParamSet_t; /* TLV (Tag Length Value) of MrvllEType Format */ typedef __packed struct { uint16_t type; uint16_t length; } MrvlIEHeader; // 所有MrvlIETypes_*类型的基类型 typedef __packed struct { MrvlIEHeader header; uint8_t data[1]; } MrvlIEType; typedef __packed struct { MrvlIEHeader header; uint16_t auth_type; } MrvlIETypes_AuthType_t; typedef __packed struct { MrvlIEHeader header; uint8_t count; uint8_t period; uint16_t max_duration; uint16_t duration_remaining; } MrvlIETypes_CfParamSet_t; /*typedef __packed struct { MrvlIEHeader header; __packed struct { uint8_t band_config_type; uint8_t chan_number; } channels[1]; } MrvlIETypes_ChanBandList_t;*/ typedef __packed struct { MrvlIEHeader header; __packed struct { uint8_t band_config_type; uint8_t chan_number; uint8_t scan_type; uint16_t min_scan_time; uint16_t max_scan_time; } channels[1]; } MrvlIETypes_ChanListParamSet_t; typedef __packed struct { MrvlIEHeader header; uint8_t channel; } MrvlIETypes_PhyParamDSSet_t; typedef __packed struct { MrvlIEHeader header; uint16_t key_type_id; uint16_t key_info; uint16_t key_len; uint8_t key[32]; } MrvlIETypes_KeyParamSet_t; typedef __packed struct { MrvlIEHeader header; uint8_t rates[14]; } MrvlIETypes_RatesParamSet_t; typedef __packed struct { MrvlIEHeader header; uint8_t rsn[64]; } MrvlIETypes_RsnParamSet_t; typedef __packed struct { MrvlIEHeader header; uint8_t ssid[32]; } MrvlIETypes_SSIDParamSet_t; typedef __packed struct { MrvlIEHeader header; uint64_t tsf_table[1]; } MrvlIETypes_TsfTimestamp_t; // 整个结构体的最大大小为256字节 typedef __packed struct { MrvlIEHeader header; uint8_t vendor[64]; // 通常情况下64字节已足够 } MrvlIETypes_VendorParamSet_t; /* WiFi模块所有类型的帧的头部 */ typedef __packed struct { uint16_t length; // 大小包括此成员本身 uint16_t type; } WiFi_SDIOFrameHeader; /* WiFi模块命令帧的头部 */ typedef __packed struct { WiFi_SDIOFrameHeader frame_header; uint16_t cmd_code; uint16_t size; uint16_t seq_num; uint16_t result; } WiFi_CommandHeader; /* WiFi模块接收的数据帧 */ /* Table 2: Fields in Receive Packet Descriptor */ typedef __packed struct { WiFi_SDIOFrameHeader header; uint16_t reserved1; uint8_t snr; // Signal to noise ratio for this packet (dB) uint8_t reserved2; uint16_t rx_packet_length; // Number of bytes in the payload uint8_t nf; // Noise floor for this packet (dBm). Noise floor is always negative. The absolute value is passed. uint8_t rx_rate; // Rate at which this packet is received uint32_t rx_packet_offset; // Offset from the start of the packet to the beginning of the payload data packet uint32_t reserved3; uint8_t priority; // Specifies the user priority of received packet uint8_t reserved4[3]; uint8_t payload[1]; // 数据链路层上的帧 } WiFi_DataRx; /* WiFi模块发送的数据帧 */ /* Table 3: Fields in Transmit Packet Descriptor */ typedef __packed struct { WiFi_SDIOFrameHeader header; uint32_t reserved1; uint32_t tx_control; // See 3.2.1 Per-Packet Settings uint32_t tx_packet_offset; // Offset of the beginning of the payload data packet (802.3 or 802.11 frames) from the beginning of the packet (bytes) uint16_t tx_packet_length; // Number of bytes in the payload data frame uint16_t tx_dest_addr_high; // Destination MAC address bytes 4 to 5 uint32_t tx_dest_addr_low; // Destination MAC address bytes 0 to 3 uint8_t priority; // Specifies the user priority of transmit packet uint8_t flags; uint8_t pkt_delay_2ms; // Amount of time the packet has been queued in the driver layer for WMM implementations uint8_t reserved2; uint8_t payload[1]; // 数据链路层上的帧 } WiFi_DataTx; /* WiFi模块事件帧 */ typedef __packed struct { WiFi_SDIOFrameHeader header; uint32_t event_id; // Enumerated identifier for the event uint16_t reason_code; // IEEE Reason Code as described in the 802.11 standard uint8_t mac_addr[6]; // Peer STA Address } WiFi_Event; /* WiFi模块中的各种命令帧 */ typedef __packed struct { WiFi_CommandHeader header; uint16_t action; } WiFi_Cmd_KeyMaterial; typedef __packed struct { WiFi_CommandHeader header; uint16_t action; uint8_t mac_addr[6]; } WiFi_Cmd_MACAddr; typedef __packed struct { WiFi_CommandHeader header; uint16_t action; uint16_t reserved; } WiFi_Cmd_MACCtrl; typedef __packed struct { WiFi_CommandHeader header; uint16_t action; uint16_t tx_key_index; // Key set being used for transmit (0~3) uint8_t wep_types[4]; // use 40 or 104 bits uint8_t keys[4][16]; } WiFi_Cmd_SetWEP; typedef __packed struct { WiFi_CommandHeader header; uint16_t action; uint16_t events; } WiFi_Cmd_SubscribeEvent; typedef __packed struct { WiFi_CommandHeader header; uint8_t ssid[32]; uint8_t bss_type; uint16_t bcn_period; uint8_t reserved1; IEEETypes_IbssParamSet_t ibss_param_set; // ATIM window length in TU uint32_t reserved2; IEEETypes_DsParamSet_t ds_param_set; // The channel for ad-hoc network uint16_t reserved3[3]; uint16_t cap_info; // Capability information uint8_t data_rate[14]; } WiFi_CmdRequest_ADHOCStart; typedef __packed struct { WiFi_CommandHeader header; uint8_t bssid[6]; // MAC address uint8_t ssid[32]; uint8_t bss_type; uint16_t bcn_period; uint8_t dtim_period; // Specify DTIM period (TBTTs) uint8_t timestamp[8]; uint8_t start_ts[8]; // Starting timestamp IEEETypes_DsParamSet_t ds_param_set; // IEEE DS parameter set element uint32_t reserved1; IEEETypes_IbssParamSet_t ibss_param_set; // IEEE IBSS parameter set uint32_t reserved2; uint16_t cap_info; uint8_t data_rates[14]; uint32_t reserved3; } WiFi_CmdRequest_ADHOCJoin; typedef __packed struct { WiFi_CommandHeader header; uint8_t peer_sta_addr[6]; // Peer MAC address uint16_t cap_info; // Capability information uint16_t listen_interval; // Listen interval uint16_t bcn_period; // Beacon period uint8_t dtim_period; // DTIM period } WiFi_CmdRequest_Associate; typedef __packed struct { WiFi_CommandHeader header; uint8_t bss_type; uint8_t bss_id[6]; } WiFi_CmdRequest_Scan; typedef __packed struct { WiFi_CommandHeader header; uint16_t capability; uint16_t status_code; uint16_t association_id; IEEEType ie_buffer; } WiFi_CmdResponse_Associate; typedef __packed struct { WiFi_CommandHeader header; uint16_t buf_size; uint8_t num_of_set; } WiFi_CmdResponse_Scan; typedef __packed struct { uint16_t ie_length; // Total information element length (不含sizeof(ie_length)) uint8_t bssid[6]; // BSSID uint8_t rssi; // RSSI value as received from peer /* Probe Response/Beacon Payload */ uint64_t pkt_time_stamp; // Timestamp uint16_t bcn_interval; // Beacon interval uint16_t cap_info; // Capabilities information IEEEType ie_parameters; // 存放的是一些IEEE类型的数据 } WiFi_BssDescSet; typedef __packed struct { uint8_t oui[3]; uint8_t oui_type; uint8_t oui_subtype; uint8_t version; } WiFi_Vendor; /* WiFi热点信息 */ typedef __packed struct { MrvlIETypes_SSIDParamSet_t ssid; uint8_t mac_addr[6]; uint16_t cap_info; uint16_t bcn_period; uint8_t channel; MrvlIETypes_RatesParamSet_t rates; MrvlIETypes_RsnParamSet_t rsn; MrvlIETypes_VendorParamSet_t wpa; MrvlIETypes_VendorParamSet_t wwm; MrvlIETypes_VendorParamSet_t wps; } WiFi_SSIDInfo; void delay(uint16_t nms); void dump_data(const void *data, uint32_t len); void timeout(uint16_t nms); #define WiFi_DropPacket() WiFi_ReceivePacket(0, 0) #define WiFi_GetSDIOBlockSize() _BV((SDIO->DCTRL & SDIO_DCTRL_DBLOCKSIZE) >> 4) #define WiFi_ResendCommand(cmd, bufsize) WiFi_SendCommand(0, (cmd), 0, bufsize) uint16_t WiFi_Associate(const char *ssid); uint16_t WiFi_Connect(const char *ssid, uint8_t type); uint8_t WiFi_DownloadFirmware(void); uint16_t WiFi_GetPacketLength(void); void WiFi_Init(void); uint16_t WiFi_JoinADHOC(const char *ssid); uint16_t WiFi_KeyMaterial(MrvlIETypes_KeyParamSet_t *keys, uint16_t size, uint8_t action); uint16_t WiFi_MACAddr(uint8_t addr[6], uint8_t action); uint16_t WiFi_MACControl(uint16_t action); uint8_t WiFi_PacketArrived(void); uint8_t WiFi_Read(uint8_t func, uint32_t addr); uint8_t WiFi_ReadData(uint8_t func, uint32_t addr, void *data, uint16_t count, uint16_t bufsize, uint32_t flags); uint8_t WiFi_ReadPort(void *data, uint16_t size, uint16_t bufsize); uint8_t WiFi_ReceivePacket(void *buf, uint16_t bufsize); uint8_t WiFi_ReceiveResponse(void *buf, uint16_t bufsize); void WiFi_Scan(void); uint8_t WiFi_ScanSSID(const char *ssid, WiFi_SSIDInfo *info, uint8_t *buffer, uint16_t bufsize); void WiFi_SendCMD52(uint8_t func, uint32_t addr, uint8_t data, uint32_t flags); void WiFi_SendCMD53(uint8_t func, uint32_t addr, uint16_t count, uint32_t flags); uint8_t WiFi_SendCommand(uint16_t com_code, void *data, uint16_t size, uint16_t bufsize); uint8_t WiFi_SendPacket(WiFi_DataTx *packet, uint16_t packet_len, uint16_t bufsize); void WiFi_SetBlockSize(uint8_t func); uint8_t WiFi_SetKeyMaterial(uint16_t type, uint16_t info, const uint8_t *key, uint16_t len); uint16_t WiFi_SetWEP(uint8_t action, const char *key); void WiFi_ShowCIS(uint8_t func); void WiFi_ShowKeyMaterials(void); uint8_t WiFi_StartADHOC(const char *ssid); uint16_t WiFi_SubscribeEvent(uint16_t action, uint16_t events); uint8_t WiFi_TranslateTLV(MrvlIEType *mrvlie_tlv, const IEEEType *ieee_tlv, uint16_t mrvlie_payload_size); uint8_t WiFi_Wait(uint8_t status); uint8_t WiFi_Write(uint8_t func, uint32_t addr, uint8_t value); uint8_t WiFi_WriteData(uint8_t func, uint32_t addr, const void *data, uint16_t count, uint32_t bufsize, uint32_t flags); uint8_t WiFi_WritePort(const void *data, uint16_t size, uint32_t bufsize); // 外部自定义回调函数 void WiFi_EventHandler(const WiFi_Event *event);【WiFi.c(寄存器版)】
#include <stdio.h> #include <stm32f10x.h> #include <string.h> #include "WiFi.h" extern const unsigned char firmware_helper_sd[2516]; extern const unsigned char firmware_sd8686[122916]; //const uint8_t wifi_mac_addr[] = {0x62, 0x1d, 0x2f, 0x00, 0x4e, 0x2d}; // MAC地址的第一个字节必须为偶数! 否则为多播地址 static uint16_t wifi_macctrl = WIFI_MACCTRL_ETHERNET2 | WIFI_MACCTRL_TX | WIFI_MACCTRL_RX; static uint16_t wifi_timeout = WIFI_DEFTIMEOUT; static uint32_t io_addr; // 默认情况下startup_stm32f10x_hd.s设定的栈大小Stack_Size为0x400, 函数内最多只能创建1KB的变量 // 为了创建大缓冲区, 栈大小应该改大, 比如0x4000, 即16KB // 延时n毫秒 void delay(uint16_t nms) { timeout(nms); while ((TIM6->SR & TIM_SR_UIF) == 0); // 等待定时器溢出 TIM6->SR &= ~TIM_SR_UIF; // 清除溢出标志 } // 显示数据内容 void dump_data(const void *data, uint32_t len) { const uint8_t *p = data; while (len--) printf("%02X", *p++); printf("\n"); } // 启动定时器, 超时时间设为n毫秒 void timeout(uint16_t nms) { TIM6->ARR = 10 * nms - 1; TIM6->PSC = 7199; // APB1总线上的定时器时钟为72MHz TIM6->CR1 = TIM_CR1_OPM | TIM_CR1_URS; // OPM=1: UIF置位后自动关闭定时器 TIM6->EGR = TIM_EGR_UG; // 应用上面的设置, 当URS=1时UIF保持0 TIM6->CR1 |= TIM_CR1_CEN; // 开始计时 } /* 关联一个热点 */ uint16_t WiFi_Associate(const char *ssid) { uint8_t buffer[2048]; WiFi_SSIDInfo info; WiFi_CmdRequest_Associate *cmd = (WiFi_CmdRequest_Associate *)buffer; WiFi_CmdResponse_Associate *resp = (WiFi_CmdResponse_Associate *)buffer; MrvlIETypes_PhyParamDSSet_t *ds; MrvlIETypes_CfParamSet_t *cf; MrvlIETypes_AuthType_t *auth; MrvlIETypes_RsnParamSet_t *rsn; if (!WiFi_ScanSSID(ssid, &info, buffer, sizeof(buffer))) { printf("Cannot find AP: %s!\n", ssid); return WIFI_ASSOCIATION_NOTFOUND; } memcpy(cmd->peer_sta_addr, info.mac_addr, sizeof(info.mac_addr)); cmd->cap_info = info.cap_info; cmd->listen_interval = 10; cmd->bcn_period = info.bcn_period; cmd->dtim_period = 1; memcpy(cmd + 1, &info.ssid, TLV_STRUCTLEN(info.ssid)); ds = (MrvlIETypes_PhyParamDSSet_t *)((uint8_t *)(cmd + 1) + TLV_STRUCTLEN(info.ssid)); ds->header.type = MRVLIETYPES_DSPARAMSET; ds->header.length = 1; ds->channel = info.channel; cf = (MrvlIETypes_CfParamSet_t *)(ds + 1); memset(cf, 0, sizeof(MrvlIETypes_CfParamSet_t)); cf->header.type = MRVLIETYPES_CFPARAMSET; cf->header.length = TLV_PAYLOADLEN(*cf); memcpy(cf + 1, &info.rates, TLV_STRUCTLEN(info.rates)); auth = (MrvlIETypes_AuthType_t *)((uint8_t *)(cf + 1) + TLV_STRUCTLEN(info.rates)); auth->header.type = MRVLIETYPES_AUTHTYPE; auth->header.length = TLV_PAYLOADLEN(*auth); auth->auth_type = AUTH_MODE_OPEN; rsn = (MrvlIETypes_RsnParamSet_t *)(auth + 1); if (info.rsn.header.type) { // WPA2网络必须在命令中加入RSN参数才能成功连接 memcpy(rsn, &info.rsn, TLV_STRUCTLEN(info.rsn)); WiFi_SendCommand(CMD_802_11_ASSOCIATE, buffer, (uint8_t *)rsn - buffer + TLV_STRUCTLEN(info.rsn), sizeof(buffer)); } else WiFi_SendCommand(CMD_802_11_ASSOCIATE, buffer, (uint8_t *)rsn - buffer, sizeof(buffer)); // 其余网络不需要RSN参数 if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) { printf("Association with %s failed!\n", ssid); return WIFI_ASSOCIATION_ERROR; } //printf("capability=0x%04x, status_code=0x%04x, aid=0x%04x\n", resp->capability, resp->status_code, resp->association_id); if (resp->association_id == 0xffff) return ((-resp->capability) << 8) | resp->status_code; return WIFI_ASSOCIATION_SUCCESS; } /* 连接WiFi热点, 超时自动重连 */ uint16_t WiFi_Connect(const char *ssid, uint8_t type) { uint16_t ret; do { if (type == BSS_INDEPENDENT) // 连接Ad-Hoc型热点 ret = WiFi_JoinADHOC(ssid); else if (type == BSS_INFRASTRUCTURE) // 连接普通热点 ret = WiFi_Associate(ssid); else { printf("WiFi_Connect: incorrect network type!\n"); return WIFI_ASSOCIATION_ERROR; } if (ret != WIFI_ASSOCIATION_SUCCESS) { printf("%s returned 0x%04x\n", (type == BSS_INDEPENDENT) ? "WiFi_JoinADHOC" : "WiFi_Associate", ret); delay(2000); // 等待一段时间后重连 } } while (WIFI_ASSOCIATION_TIMEOUT(ret) || ret == WIFI_ASSOCIATION_NOTFOUND); // 若连接超时, 或未扫描到热点, 则重连 if (ret != WIFI_ASSOCIATION_SUCCESS) return ret; printf("Connected to %s!\n", ssid); return ret; } /* 下载固件 */ // 参考文档: marvell-88w8686-固件下载程序说明.doc uint8_t WiFi_DownloadFirmware(void) { uint8_t helper_buf[64]; const uint8_t *data; uint16_t size; uint32_t len; // 块大小设为32 SDIO->DCTRL = (SDIO->DCTRL & ~SDIO_DCTRL_DBLOCKSIZE) | SDIO_DCTRL_DBLOCKSIZE_2 | SDIO_DCTRL_DBLOCKSIZE_0; WiFi_SetBlockSize(1); // 应用到Function 1 // 下载helper io_addr = WiFi_Read(1, WIFI_IOPORT0) | (WiFi_Read(1, WIFI_IOPORT1) << 8) | (WiFi_Read(1, WIFI_IOPORT2) << 16); data = firmware_helper_sd; len = sizeof(firmware_helper_sd); while (len) { // 每次下载64字节, 其中前4字节为本次下载的数据量 size = (len > 60) ? 60 : len; *(uint32_t *)helper_buf = size; memcpy(helper_buf + 4, data, size); WiFi_Wait(WIFI_CARDSTATUS_DNLDCARDRDY); WiFi_WritePort(helper_buf, sizeof(helper_buf), sizeof(helper_buf)); len -= size; data += size; } *(uint32_t *)helper_buf = 0; WiFi_WritePort(helper_buf, sizeof(helper_buf), sizeof(helper_buf)); // 以空数据包结束 // 下载固件 data = firmware_sd8686; len = sizeof(firmware_sd8686); while (len) { WiFi_Wait(WIFI_CARDSTATUS_DNLDCARDRDY); while ((size = WiFi_Read(1, WIFI_SQREADBASEADDR0) | (WiFi_Read(1, WIFI_SQREADBASEADDR1) << 8)) == 0); // 获取本次下载的字节数 //printf("Required: %d bytes, Remaining: %d bytes\n", size, len); if (size & 1) { // 若size为奇数(如17), 则说明接收端有CRC校验错误, 应重新传送上一次的内容(这部分代码省略) printf("Error: an odd size is invalid!\n"); return 0; } if (size > len) size = len; // len为缓冲区剩余大小 #ifdef WIFI_HIGHSPEED // 高速模式下不能使用多字节传输模式, 只能使用块传输模式, 因此缓冲区要足够大 if (len < 32) { // 若缓冲区空间不足一个数据块, 则借用helper_buf memcpy(helper_buf, data, size); WiFi_WritePort(helper_buf, size, sizeof(helper_buf)); } else #endif WiFi_WritePort(data, size, len); if (!SDIO_SUCCEEDED()) { printf("Data transfer error! SDIO->STA=0x%08x\n", SDIO->STA); return 0; } len -= size; data += size; } // 等待Firmware启动 while (WiFi_GetPacketLength() == 0xfedc); printf("Firmware is successfully downloaded!\n"); return 1; } /* 获取数据帧大小 */ uint16_t WiFi_GetPacketLength(void) { // 读Scratch pad 4寄存器的低16位 return WiFi_Read(1, WIFI_SCRATCHPAD4_0) | (WiFi_Read(1, WIFI_SCRATCHPAD4_1) << 8); } /* 初始化WiFi模块 */ // SDIO Simplified Specification Version 3.00: 3. SDIO Card Initialization void WiFi_Init(void) { uint16_t rca; // 虽然SDIO标准规定SDIO接口上可以接多张SD卡, 但是STM32的SDIO接口只能接一张卡(芯片手册上有说明), 软件片选RCA无意义 SDIO->POWER = SDIO_POWER_PWRCTRL; // 打开SDIO外设 SDIO->CLKCR = SDIO_CLKCR_CLKEN | 178; // 初始化时最高允许的频率: 72MHz/(178+2)=400kHz SDIO->DCTRL = SDIO_DCTRL_SDIOEN; // 开启SDIO模式 delay(10); // 延时可防止CMD5重发 // 不需要发送CMD0, 因为SD I/O card的初始化命令是CMD52 // An I/O only card or the I/O portion of a combo card is NOT reset by CMD0. (See 4.4 Reset for SDIO) /* 发送CMD5: IO_SEND_OP_COND */ SDIO->ARG = 0; SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 5; while (SDIO->STA & SDIO_STA_CMDACT); while (SDIO->STA & SDIO_STA_CTIMEOUT) { SDIO->ICR = SDIO_ICR_CTIMEOUTC; // 清除标志 printf("Timeout! Resend CMD%d\n", SDIO->CMD & SDIO_CMD_CMDINDEX); delay(5); SDIO->CMD = SDIO->CMD; // 重发 while (SDIO->STA & SDIO_STA_CMDACT); } if (SDIO->STA & SDIO_STA_CMDREND) { SDIO->ICR = SDIO_ICR_CMDRENDC; printf("Command response received: CMD%d, RESP_%08x\n", SDIO->RESPCMD, SDIO->RESP1); } /* 设置参数VDD Voltage Window: 3.2~3.4V, 并再次发送CMD5 */ SDIO->ARG = 0x300000; SDIO->CMD = SDIO->CMD; while (SDIO->STA & SDIO_STA_CMDACT); if (SDIO->STA & SDIO_STA_CMDREND) { SDIO->ICR = SDIO_ICR_CMDRENDC; printf("Command response received: CMD%d, RESP_%08x\n", SDIO->RESPCMD, SDIO->RESP1); if (SDIO->RESP1 & _BV(31)) { // Card is ready to operate after initialization printf("Number of I/O Functions: %d\n", (SDIO->RESP1 >> 28) & 7); printf("Memory Present: %d\n", (SDIO->RESP1 & _BV(27)) != 0); } } /* 获取WiFi模块地址 (CMD3: SEND_RELATIVE_ADDR, Ask the card to publish a new relative address (RCA)) */ SDIO->ARG = 0; SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 3; while (SDIO->STA & SDIO_STA_CMDACT); if (SDIO->STA & SDIO_STA_CMDREND) { SDIO->ICR = SDIO_ICR_CMDRENDC; rca = SDIO->RESP1 >> 16; printf("Relative card address: 0x%04x\n", rca); } /* 选中WiFi模块 (CMD7: SELECT/DESELECT_CARD) */ SDIO->ARG = rca << 16; SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 7; while (SDIO->STA & SDIO_STA_CMDACT); if (SDIO->STA & SDIO_STA_CMDREND) { SDIO->ICR = SDIO_ICR_CMDRENDC; printf("Card selected! status=0x%08x\n", SDIO->RESP1); } /* 提高时钟频率, 超时时间为0.1s */ #ifdef WIFI_HIGHSPEED SDIO->CLKCR = (SDIO->CLKCR & ~SDIO_CLKCR_CLKDIV) | 1; // 72MHz/(1+2)=24MHz SDIO->DTIMER = 2400000; printf("SDIO Clock: 24MHz\n"); #else SDIO->CLKCR = (SDIO->CLKCR & ~SDIO_CLKCR_CLKDIV) | 70; // 72MHz/(70+2)=1MHz SDIO->DTIMER = 100000; printf("SDIO Clock: 1MHz\n"); #endif /* 选择总线宽度 (Wide Bus Selection) */ // For an SDIO card a write to the CCCR using CMD52 is used to select bus width. (See 4.5 Bus Width) // CMD52: IO_RW_DIRECT, CCCR: Card Common Control Registers WiFi_Write(0, SDIO_CCCR_BUSIFCTRL, WiFi_Read(0, SDIO_CCCR_BUSIFCTRL) | SDIO_CCCR_BUSIFCTRL_BUSWID_4Bit); // Bus Width: 4-bit bus SDIO->CLKCR |= SDIO_CLKCR_WIDBUS_0; // 初始化Function 1 WiFi_Write(0, SDIO_CCCR_IOEN, SDIO_CCCR_IOEN_IOE1); // IOE1=1 (Enable Function) while ((WiFi_Read(0, SDIO_CCCR_IORDY) & SDIO_CCCR_IORDY_IOR1) == 0); // 等到IOR1=1 (I/O Function Ready) WiFi_Write(0, SDIO_CCCR_INTEN, SDIO_CCCR_INTEN_IENM | SDIO_CCCR_INTEN_IEN1); // 打开SDIO中断请求 WiFi_Write(1, WIFI_INTMASK, WIFI_INTMASK_HOSTINTMASK); // 利用中断标志位来判定是否有数据要读取, 可靠性更高 // 显示CIS信息(SDIO卡信息) WiFi_ShowCIS(0); WiFi_ShowCIS(1); #ifdef WIFI_USEDMA // 必须在DPSM禁用的时候开关DMA请求 SDIO->DCTRL |= SDIO_DCTRL_DMAEN; #endif // 下载固件 if (!WiFi_DownloadFirmware()) while (1); // 设置数据块大小为256字节 SDIO->DCTRL = (SDIO->DCTRL & ~SDIO_DCTRL_DBLOCKSIZE) | SDIO_DCTRL_DBLOCKSIZE_3; WiFi_SetBlockSize(0); WiFi_SetBlockSize(1); // 允许发送和接收 // 不使用带有LLC子层的802.2SNAP帧格式, 这样LLC和SNAP这8个字节的内容就不会夹在数据链路层的源地址字段与数据字段之间 WiFi_MACControl(wifi_macctrl); } /* 加入Ad-Hoc网络 */ uint16_t WiFi_JoinADHOC(const char *ssid) { uint8_t buffer[2048]; WiFi_SSIDInfo info; WiFi_CmdRequest_ADHOCJoin *cmd = (WiFi_CmdRequest_ADHOCJoin *)buffer; if (!WiFi_ScanSSID(ssid, &info, buffer, sizeof(buffer))) { printf("Cannot find AP: %s!\n", ssid); return WIFI_ASSOCIATION_NOTFOUND; } memcpy(cmd->bssid, info.mac_addr, sizeof(cmd->bssid)); memset(cmd->ssid, 0, sizeof(cmd->ssid)); strncpy((char *)cmd->ssid, ssid, sizeof(cmd->ssid)); cmd->bss_type = BSS_ANY; cmd->bcn_period = info.bcn_period; cmd->dtim_period = 1; memset(cmd->timestamp, 0, sizeof(cmd->timestamp) + sizeof(cmd->start_ts)); cmd->ds_param_set.header.type = MRVLIETYPES_DSPARAMSET; cmd->ds_param_set.header.length = TLV_PAYLOADLEN(cmd->ds_param_set); cmd->ds_param_set.channel = info.channel; cmd->reserved1 = 0; cmd->ibss_param_set.header.type = MRVLIETYPES_IBSSPARAMSET; cmd->ibss_param_set.header.length = TLV_PAYLOADLEN(cmd->ibss_param_set); cmd->ibss_param_set.atim_window = 0; cmd->reserved2 = 0; cmd->cap_info = info.cap_info; memcpy(cmd->data_rates, info.rates.rates, sizeof(cmd->data_rates)); cmd->reserved3 = 0; WiFi_SendCommand(CMD_802_11_AD_HOC_JOIN, buffer, sizeof(WiFi_CmdRequest_ADHOCJoin), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) return WIFI_ASSOCIATION_ERROR; return cmd->header.result; } /* 获取或设置密钥 */ uint16_t WiFi_KeyMaterial(MrvlIETypes_KeyParamSet_t *keys, uint16_t size, uint8_t action) { uint8_t buffer[WIFI_DEFBUFSIZE]; uint8_t ret_size; WiFi_Cmd_KeyMaterial *cmd = (WiFi_Cmd_KeyMaterial *)buffer; cmd->action = action; if (action == WIFI_ACT_SET) { memcpy(cmd + 1, keys, size); WiFi_SendCommand(CMD_802_11_KEY_MATERIAL, buffer, sizeof(WiFi_Cmd_KeyMaterial) + size, sizeof(buffer)); } else WiFi_SendCommand(CMD_802_11_KEY_MATERIAL, buffer, sizeof(WiFi_Cmd_KeyMaterial), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) return (action == WIFI_ACT_GET) ? 0 : CMD_STATUS_ERROR; if (action == WIFI_ACT_GET) { ret_size = cmd->header.size - sizeof(cmd->header) - sizeof(cmd->action); if (ret_size <= size) memcpy(keys, cmd + 1, ret_size); else printf("WiFi_KeyMaterial: Buffer size is too small! %d bytes required!\n", ret_size); return ret_size; // action=get时返回读取的数据大小 } else return cmd->header.result; // action=set时返回命令执行结果值 } /* 获取或设置MAC地址 */ uint16_t WiFi_MACAddr(uint8_t addr[6], uint8_t action) { uint8_t buffer[WIFI_DEFBUFSIZE]; WiFi_Cmd_MACAddr *cmd = (WiFi_Cmd_MACAddr *)buffer; cmd->action = action; if (action == WIFI_ACT_SET) memcpy(cmd->mac_addr, addr, 6); WiFi_SendCommand(CMD_802_11_MAC_ADDR, buffer, sizeof(WiFi_Cmd_MACAddr), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) return CMD_STATUS_ERROR; if (action == WIFI_ACT_GET) memcpy(addr, cmd->mac_addr, 6); return cmd->header.result; } /* 配置MAC */ uint16_t WiFi_MACControl(uint16_t action) { uint8_t buffer[WIFI_DEFBUFSIZE]; WiFi_Cmd_MACCtrl *cmd = (WiFi_Cmd_MACCtrl *)buffer; cmd->action = action; cmd->reserved = 0; WiFi_SendCommand(CMD_MAC_CONTROL, buffer, sizeof(WiFi_Cmd_MACCtrl), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) return CMD_STATUS_ERROR; return cmd->header.result; } /* 判断是否有新数据包到来 */ uint8_t WiFi_PacketArrived(void) { //return (WiFi_Read(1, WIFI_CARDSTATUS) & WIFI_CARDSTATUS_UPLDCARDRDY) != 0; // 可靠性差, 标志位会被CMD53命令清除, 不建议使用 return (WiFi_Read(1, WIFI_INTSTATUS) & WIFI_INTSTATUS_UPLD) != 0; // 可靠性高, 标志位需要手动清除, 必须打开中断后才能使用 } /* 读寄存器 */ uint8_t WiFi_Read(uint8_t func, uint32_t addr) { WiFi_SendCMD52(func, addr, NULL, NULL); if (SDIO->STA & SDIO_STA_CMDREND) { SDIO->ICR = SDIO_ICR_CMDRENDC; return SDIO->RESP1 & 0xff; } else { printf("WiFi_Read failed, SDIO->STA=0x%08x!\n", SDIO->STA); return 0; } } /* 接收数据 */ // count为要接收的字节数或块数, bufsize为data缓冲区的大小 // bufsize=0时, 只读取数据不存入缓冲区 uint8_t WiFi_ReadData(uint8_t func, uint32_t addr, void *data, uint16_t count, uint16_t bufsize, uint32_t flags) { uint32_t dctrl = SDIO->DCTRL | SDIO_DCTRL_DTDIR; uint32_t len; // 实际要接收的字节数 #ifdef WIFI_USEDMA uint32_t temp; // 丢弃数据用的变量 #else uint32_t *p = data; #endif if (flags & CMD53_BLOCKMODE) { // 块传输模式 (DTMODE=0) dctrl &= ~SDIO_DCTRL_DTMODE; len = count * WiFi_GetSDIOBlockSize(); // 块数*块大小 } else { // 多字节传输模式 (DTMODE=1, SDIO的频率低于16MHz时才支持) dctrl |= SDIO_DCTRL_DTMODE; len = count; if (len % 4 != 0) { len += 4 - len % 4; // WiFi模块要求字节数必须为4的整数倍 count = len; // 传入CMD53的数据大小参数也应该是4的倍数 } } if (bufsize > 0 && bufsize < len) { printf("WiFi_ReadData: a buffer of at least %d bytes is required! bufsize=%d\n", len, bufsize); return 0; } #ifdef WIFI_USEDMA DMA2_Channel4->CPAR = (uint32_t)&SDIO->FIFO; DMA2_Channel4->CNDTR = len / 4; DMA2_Channel4->CCR = DMA_CCR4_PL | DMA_CCR4_MSIZE_1 | DMA_CCR4_PSIZE_1; if (bufsize > 0) { DMA2_Channel4->CMAR = (uint32_t)data; DMA2_Channel4->CCR |= DMA_CCR4_MINC; } else DMA2_Channel4->CMAR = (uint32_t)&temp; // 数据丢弃模式 DMA2_Channel4->CCR |= DMA_CCR4_EN; #endif SDIO->DLEN = len; SDIO->DCTRL = dctrl | SDIO_DCTRL_DTEN; WiFi_SendCMD53(func, addr, count, flags); #ifdef WIFI_USEDMA while ((DMA2->ISR & DMA_ISR_TCIF4) == 0); DMA2->IFCR = DMA_IFCR_CTCIF4; DMA2_Channel4->CCR &= ~DMA_CCR4_EN; #else while (len) { // 如果有数据到来就读取数据 if (SDIO->STA & SDIO_STA_RXDAVL) { len -= 4; if (bufsize > 0) *p++ = SDIO->FIFO; else SDIO->FIFO; // 读寄存器, 但不保存数据 } if (SDIO->STA & SDIO_STA_DTIMEOUT) { printf("Data timeout!\n"); break; } else if (SDIO->STA & SDIO_STA_DCRCFAIL) { printf("Data CRC check failed! %d bytes are lost\n", len); break; } } #endif while (SDIO->STA & (SDIO_STA_CMDACT | SDIO_STA_RXACT)); SDIO->DCTRL &= ~SDIO_DCTRL_DTEN; SDIO->ICR = SDIO_STA_DATAEND | SDIO_ICR_CMDRENDC; if (flags & CMD53_BLOCKMODE) SDIO->ICR = SDIO_ICR_DBCKENDC; SDIO->STA; // 读状态寄存器后标志位才能真正清除 return SDIO_SUCCEEDED(); } uint8_t WiFi_ReadPort(void *data, uint16_t size, uint16_t bufsize) { uint16_t block_num, block_size; block_size = WiFi_GetSDIOBlockSize(); WiFi_Wait(0); // 发送CMD53前必须IOReady=1 WiFi_Write(1, WIFI_INTSTATUS, WiFi_Read(1, WIFI_INTSTATUS) & ~WIFI_INTSTATUS_UPLD); // 读端口前先清除UPLD中断标志位 #ifndef WIFI_HIGHSPEED if (size > 512 || size % block_size == 0) // 若size为数据块大小的整数倍, 则采用数据块方式接收 { #endif block_num = size / block_size; if (size % block_size != 0) block_num++; // 大于512字节时不能采用字节流方式接收(如712字节), 仍采用数据块方式 return WiFi_ReadData(1, io_addr, data, block_num, bufsize, CMD53_BLOCKMODE); #ifndef WIFI_HIGHSPEED } else return WiFi_ReadData(1, io_addr, data, size, bufsize, 0); // 否则采用字节流方式接收 #endif } /* 接收数据帧, 返回是否成功 */ uint8_t WiFi_ReceivePacket(void *buf, uint16_t bufsize) { uint8_t ret; uint16_t size = WiFi_GetPacketLength(); WiFi_SDIOFrameHeader *hdr = (WiFi_SDIOFrameHeader *)buf; ret = WiFi_ReadPort(buf, size, bufsize); if (ret && hdr->type != WIFI_SDIOFRAME_DATA) { if (WIFI_SDIOFRAME_EVENT) WiFi_EventHandler(buf); ret = 0; } return ret; } /* 接收WiFi命令的回应 */ uint8_t WiFi_ReceiveResponse(void *buf, uint16_t bufsize) { uint8_t ret; uint8_t wait = 0; uint16_t size; WiFi_CommandHeader *cmd = (WiFi_CommandHeader *)buf; // 等待数据准备好 // 固件下载完后发送的第一个WiFi命令偶尔会收不到回应 // 为了保证系统的可靠性, 命令的超时重传非常重要 while (!WiFi_Wait(WIFI_CARDSTATUS_UPLDCARDRDY)) { // 若WiFi_Wait返回0, 则说明超时 wait++; if (wait >= 5) { printf("No response!\n"); return 0; } if (cmd->frame_header.type == WIFI_SDIOFRAME_COMMAND) { // 若超时后还没收到数据, 则重发命令, 然后再次执行WiFi_Wait printf("Resend WiFi command 0x%04x! size=%d\n", cmd->cmd_code, cmd->frame_header.length); WiFi_ResendCommand(buf, bufsize); } else return 0; // 若buf中的内容不是命令, 则不重发直接退出 } size = WiFi_GetPacketLength(); ret = WiFi_ReadPort(buf, size, bufsize); if (ret && cmd->frame_header.type != WIFI_SDIOFRAME_COMMAND) { ret = 0; // buf中的内容已被接收到的数据覆盖, 无法重发命令 if (cmd->frame_header.type == WIFI_SDIOFRAME_EVENT) WiFi_EventHandler(buf); } return ret; } /* 扫描全部热点 (仅显示) */ void WiFi_Scan(void) { // 必须把STM32启动文件*.s中的Stack_Size改大, 否则函数中无法创建大数组 uint8_t buffer[2048]; // 用于接收返回的WiFi接入点信息, 需要较大的内存空间来存放 uint8_t i, j, n; uint8_t ssid[33], channel, wpa; uint16_t ie_size; WiFi_CmdRequest_Scan *cmd = (WiFi_CmdRequest_Scan *)buffer; // 用buffer空间来存放要发送的命令 MrvlIETypes_ChanListParamSet_t *chanlist = (MrvlIETypes_ChanListParamSet_t *)(cmd + 1); // 这里的+1指的是前进sizeof(指针类型)个地址单元, 而非只前进1个地址单元 WiFi_CmdResponse_Scan *resp = (WiFi_CmdResponse_Scan *)buffer; WiFi_BssDescSet *bss_desc_set; WiFi_Vendor *vendor; IEEEType *ie_params; //MrvlIETypes_TsfTimestamp_t *tft_table; // 分4次扫描14个通道 for (i = 0; i < 4; i++) { cmd->bss_type = BSS_ANY; memset(cmd->bss_id, 0, sizeof(cmd->bss_id)); // 通道的基本参数 n = (i == 3) ? 2 : 4; // 本次要扫描的通道数 chanlist->header.type = MRVLIETYPES_CHANLISTPARAMSET; chanlist->header.length = n * sizeof(chanlist->channels); for (j = 0; j < n; j++) { chanlist->channels[j].band_config_type = 0; chanlist->channels[j].chan_number = 4 * i + j + 1; // 通道号 chanlist->channels[j].scan_type = 0; chanlist->channels[j].min_scan_time = 0; chanlist->channels[j].max_scan_time = 100; } // 发送命令并接收数据 WiFi_SendCommand(CMD_802_11_SCAN, buffer, sizeof(WiFi_CmdRequest_Scan) + TLV_STRUCTLEN(*chanlist), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) // 接收的数据会将cmd和chanlist中的内容覆盖掉 { printf("WiFi_Scan: no response!\n"); continue; } // 显示热点信息, num_of_set为热点数 if (resp->num_of_set > 0) { bss_desc_set = (WiFi_BssDescSet *)(resp + 1); for (j = 0; j < resp->num_of_set; j++) { wpa = 0; ie_params = &bss_desc_set->ie_parameters; ie_size = bss_desc_set->ie_length - (sizeof(WiFi_BssDescSet) - sizeof(bss_desc_set->ie_length) - sizeof(bss_desc_set->ie_parameters)); while (ie_size > 0) { switch (ie_params->header.type) { case MRVLIETYPES_SSIDPARAMSET: // SSID名称 memcpy(ssid, ie_params->data, ie_params->header.length); ssid[ie_params->header.length] = '\0'; break; case MRVLIETYPES_DSPARAMSET: // 通道号 channel = ie_params->data[0]; break; case MRVLIETYPES_RSNPARAMSET: wpa = 2; // 表示WPA版本号(0表示没有使用WPA) break; case MRVLIETYPES_VENDORPARAMSET: if (wpa == 0) { vendor = (WiFi_Vendor *)ie_params->data; if (vendor->oui[0] == 0x00 && vendor->oui[1] == 0x50 && vendor->oui[2] == 0xf2 && vendor->oui_type == 0x01) wpa = 1; } break; } ie_size -= TLV_STRUCTLEN(*ie_params); ie_params = (IEEEType *)TLV_NEXT(ie_params); } if (ie_size != 0) printf("ie_parameters error!\n"); printf("SSID '%s', ", ssid); // 热点名称 printf("MAC %02X:%02X:%02X:%02X:%02X:%02X, ", bss_desc_set->bssid[0], bss_desc_set->bssid[1], bss_desc_set->bssid[2], bss_desc_set->bssid[3], bss_desc_set->bssid[4], bss_desc_set->bssid[5]); // MAC地址 printf("RSSI %d, Channel %d\n", bss_desc_set->rssi, channel); // 信号强度和通道号 //printf(" Timestamp %lld, Beacon interval %d\n", bss_desc_set->pkt_time_stamp, bss_desc_set->bcn_interval); printf(" Capability: 0x%04x (Security: ", bss_desc_set->cap_info); if (bss_desc_set->cap_info & WIFI_CAPABILITY_PRIVACY) { if (wpa == 1) printf("WPA"); else if (wpa == 2) printf("WPA2"); else printf("WEP"); } else printf("unsecured"); printf(", Mode: "); if (bss_desc_set->cap_info & WIFI_CAPABILITY_IBSS) printf("Ad-Hoc"); else printf("Infrastructure"); printf(")\n"); // 转向下一个热点信息 bss_desc_set = (WiFi_BssDescSet *)((uint8_t *)bss_desc_set + sizeof(bss_desc_set->ie_length) + bss_desc_set->ie_length); } // resp->buf_size就是bss_desc_set的总大小 // 因此tft_table == buffer + sizeof(WiFi_CmdResponse_Scan) + resp->buf_size /* tft_table = (MrvlIETypes_TsfTimestamp_t *)bss_desc_set; if (tft_table->header.type == MRVLIETYPES_TSFTIMESTAMP && tft_table->header.length == resp->num_of_set * sizeof(uint64_t)) { printf("Timestamps: "); for (j = 0; j < resp->num_of_set; j++) printf("%lld ", tft_table->tsf_table[j]); printf("\n"); } */ // TSF timestamp table是整个数据的末尾, 后面没有Channel/band table //if (((uint8_t *)tft_table - buffer) + TLV_STRUCTLEN(*tft_table) == cmd->header.frame_header.length) // printf("data end!\n"); } } } /* 获取指定名称的热点的信息 */ // buffer用来存放返回的全部结果, 因此bufsize应该足够大 // info用来存放从buffer中提取出来的一些常用信息 uint8_t WiFi_ScanSSID(const char *ssid, WiFi_SSIDInfo *info, uint8_t *buffer, uint16_t bufsize) { uint8_t i, ret; uint16_t ie_size; WiFi_CmdRequest_Scan *cmd = (WiFi_CmdRequest_Scan *)buffer; WiFi_CmdResponse_Scan *resp = (WiFi_CmdResponse_Scan *)buffer; WiFi_BssDescSet *bss_desc_set = (WiFi_BssDescSet *)(resp + 1); MrvlIETypes_ChanListParamSet_t *chan_list; IEEEType *ie_params; WiFi_Vendor *vendor; cmd->bss_type = BSS_ANY; memset(cmd->bss_id, 0, sizeof(cmd->bss_id)); // 添加ssid参数 info->ssid.header.type = MRVLIETYPES_SSIDPARAMSET; info->ssid.header.length = strlen(ssid); memcpy(info->ssid.ssid, ssid, info->ssid.header.length); memcpy(cmd + 1, &info->ssid, TLV_STRUCTLEN(info->ssid)); chan_list = (MrvlIETypes_ChanListParamSet_t *)((uint8_t *)(cmd + 1) + TLV_STRUCTLEN(info->ssid)); chan_list->header.type = MRVLIETYPES_CHANLISTPARAMSET; chan_list->header.length = 14 * sizeof(chan_list->channels); // 一次性扫描14个通道 for (i = 0; i < 14; i++) { chan_list->channels[i].band_config_type = 0; chan_list->channels[i].chan_number = i + 1; chan_list->channels[i].scan_type = 0; chan_list->channels[i].min_scan_time = 0; chan_list->channels[i].max_scan_time = 100; } WiFi_SendCommand(CMD_802_11_SCAN, buffer, ((uint8_t *)chan_list - buffer) + TLV_STRUCTLEN(*chan_list), bufsize); wifi_timeout = 3000; // 延长超时时间 ret = WiFi_ReceiveResponse(buffer, bufsize); wifi_timeout = WIFI_DEFTIMEOUT; if (!ret || resp->num_of_set == 0) return 0; // 失败 // bss_desc_set以扫描到的第一个信息项为准 memcpy(info->mac_addr, bss_desc_set->bssid, sizeof(info->mac_addr)); info->cap_info = bss_desc_set->cap_info; info->bcn_period = bss_desc_set->bcn_interval; // 若type=0, 则表明没有该项的信息 (除SSID结构体外, 因为SSID的type=MRVLIETYPES_SSIDPARAMSET=0) info->rates.header.type = 0; info->rsn.header.type = 0; info->wpa.header.type = 0; info->wwm.header.type = 0; info->wps.header.type = 0; ie_params = &bss_desc_set->ie_parameters; ie_size = bss_desc_set->ie_length - (sizeof(WiFi_BssDescSet) - sizeof(bss_desc_set->ie_length) - sizeof(bss_desc_set->ie_parameters)); // 所有IEEE_Type数据的总大小 while (ie_size > 0) { switch (ie_params->header.type) { case MRVLIETYPES_RATESPARAMSET: // 速率 WiFi_TranslateTLV((MrvlIEType *)&info->rates, ie_params, sizeof(info->rates.rates)); break; case MRVLIETYPES_DSPARAMSET: // 通道号 info->channel = ie_params->data[0]; break; case MRVLIETYPES_RSNPARAMSET: // 通常只有一个RSN信息 (与WPA2相关) // printf("RSN len=%d\n", ie_params->length); WiFi_TranslateTLV((MrvlIEType *)&info->rsn, ie_params, sizeof(info->rsn.rsn)); break; case MRVLIETYPES_VENDORPARAMSET: // 通常会有多项VENDOR信息 (与WPA相关) vendor = (WiFi_Vendor *)ie_params->data; if (vendor->oui[0] == 0x00 && vendor->oui[1] == 0x50 && vendor->oui[2] == 0xf2) { switch (vendor->oui_type) { case 0x01: // wpa_oui WiFi_TranslateTLV((MrvlIEType *)&info->wpa, ie_params, sizeof(info->wpa.vendor)); break; case 0x02: // wmm_oui if (ie_params->header.length == 24) // 合法大小 WiFi_TranslateTLV((MrvlIEType *)&info->wwm, ie_params, sizeof(info->wwm.vendor)); break; case 0x04: // wps_oui WiFi_TranslateTLV((MrvlIEType *)&info->wps, ie_params, sizeof(info->wps.vendor)); break; } } break; } // 转向下一个TLV ie_size -= TLV_STRUCTLEN(*ie_params); ie_params = (IEEEType *)TLV_NEXT(ie_params); } return 1; // 成功 } void WiFi_SendCMD52(uint8_t func, uint32_t addr, uint8_t data, uint32_t flags) { SDIO->ARG = (func << 28) | (addr << 9) | data | flags; SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 52; while (SDIO->STA & SDIO_STA_CMDACT); } void WiFi_SendCMD53(uint8_t func, uint32_t addr, uint16_t count, uint32_t flags) { // 当count=512时, 和0x1ff相与后为0, 符合要求 SDIO->ARG = (func << 28) | (addr << 9) | (count & 0x1ff) | flags; SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 53; } /* 发送WiFi命令 */ uint8_t WiFi_SendCommand(uint16_t com_code, void *data, uint16_t size, uint16_t bufsize) { static uint16_t seq_num = 0; WiFi_CommandHeader *cmdhdr = (WiFi_CommandHeader *)data; if (size != 0) { cmdhdr->frame_header.length = size; cmdhdr->frame_header.type = WIFI_SDIOFRAME_COMMAND; cmdhdr->cmd_code = com_code; cmdhdr->size = size - sizeof(WiFi_SDIOFrameHeader); // 命令大小包括命令头部, 但不包括SDIO帧头部 cmdhdr->seq_num = seq_num++; cmdhdr->result = 0; } else size = cmdhdr->frame_header.length; // 重发命令时不填写cmdhdr return WiFi_WritePort(data, size, bufsize); } /* 发送数据帧 */ uint8_t WiFi_SendPacket(WiFi_DataTx *packet, uint16_t packet_len, uint16_t bufsize) { uint8_t ret; // 有关发送数据包的细节, 请参考Firmware Specification PDF的Chapter 3: Data Path packet->header.length = sizeof(WiFi_DataTx) - sizeof(packet->payload) + packet_len; packet->header.type = WIFI_SDIOFRAME_DATA; packet->reserved1 = 0; packet->tx_control = 0; // 控制信息的格式请参考3.2.1 Per-Packet Settings packet->tx_packet_offset = sizeof(WiFi_DataTx) - sizeof(packet->payload) - sizeof(packet->header); // 不包括SDIOFrameHeader packet->tx_packet_length = packet_len; memcpy((void *)&packet->tx_dest_addr_high, packet->payload, 6); packet->priority = 0; packet->flags = 0; packet->pkt_delay_2ms = 0; packet->reserved2 = 0; ret = WiFi_WritePort(packet, packet->header.length, bufsize); if (ret) ret &= WiFi_Wait(WIFI_CARDSTATUS_DNLDCARDRDY); // WiFi模块收到数据后, 会将该位置1 return ret; } /* 将SDIO->DCTRL中的块大小信息应用到WiFi模块指定的功能区上 */ void WiFi_SetBlockSize(uint8_t func) { // Part E1: 6.9 Card Common Control Registers (CCCR), 6.10 Function Basic Registers (FBR) uint16_t size = WiFi_GetSDIOBlockSize(); WiFi_Write(0, (func << 8) | 0x10, size & 0xff); WiFi_Write(0, (func << 8) | 0x11, size >> 8); } /* 设置密钥 */ uint8_t WiFi_SetKeyMaterial(uint16_t type, uint16_t info, const uint8_t *key, uint16_t len) { MrvlIETypes_KeyParamSet_t key_param; if (len > sizeof(key_param.key)) return CMD_STATUS_ERROR; key_param.header.type = MRVLIETYPES_KEYPARAMSET; key_param.header.length = (key_param.key - (uint8_t *)&key_param) - sizeof(key_param.header) + len; key_param.key_type_id = type; key_param.key_info = info; key_param.key_len = len; // 当len=0时可保留key的值, 只更新key_info if (len) memcpy(key_param.key, key, len); return WiFi_KeyMaterial(&key_param, TLV_STRUCTLEN(key_param), WIFI_ACT_SET); } /* 设置WEP密钥 (长度必须为5或13个字符) */ // action: WIFI_ACT_ADD / WIFI_ACT_REMOVE uint16_t WiFi_SetWEP(uint8_t action, const char *key) { uint8_t buffer[WIFI_DEFBUFSIZE]; uint8_t i, len; uint16_t ret; uint32_t temp; WiFi_Cmd_SetWEP *cmd = (WiFi_Cmd_SetWEP *)buffer; cmd->action = action; cmd->tx_key_index = 0; memset(cmd->wep_types, 0, sizeof(cmd->wep_types) + sizeof(cmd->keys)); if (action == WIFI_ACT_ADD) { len = strlen(key); if (len == 5 || len == 13) { // 5个或13个ASCII密钥字符 if (len == 5) cmd->wep_types[0] = WIFI_WEPKEYTYPE_40BIT; else if (len == 13) cmd->wep_types[0] = WIFI_WEPKEYTYPE_104BIT; memcpy(cmd->keys[0], key, len); } else if (len == 10 || len == 26) { // 10个或26个16进制密钥字符 if (len == 10) cmd->wep_types[0] = WIFI_WEPKEYTYPE_40BIT; else if (len == 26) cmd->wep_types[0] = WIFI_WEPKEYTYPE_104BIT; for (i = 0; i < len; i++) { if (!(key[i] >= '0' && key[i] <= '9') && !(key[i] >= 'a' && key[i] <= 'f') && !(key[i] >= 'A' && key[i] <= 'F')) { printf("WiFi_SetWEP: The hex key contains invalid characters!\n"); return 0xffff; } if (i % 2 == 0) { sscanf(key + i, "%02x", &temp); cmd->keys[0][i / 2] = temp; } } } else { printf("WiFi_SetWEP: The key length is invalid!\n"); return 0xffff; } wifi_macctrl |= WIFI_MACCTRL_WEP; } else wifi_macctrl &= ~WIFI_MACCTRL_WEP; ret = WiFi_MACControl(wifi_macctrl); if (ret != CMD_STATUS_SUCCESS) return ret; WiFi_SendCommand(CMD_802_11_SET_WEP, buffer, sizeof(WiFi_Cmd_SetWEP), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) return CMD_STATUS_ERROR; return cmd->header.result; } /* 显示WiFi模块信息 */ void WiFi_ShowCIS(uint8_t func) { uint8_t data[255]; uint8_t i, len; uint8_t tpl_code, tpl_link; // 16.2 Basic Tuple Format and Tuple Chain Structure uint32_t cis_ptr; printf("-------------- CIS of Function %d ----------------\n", func); // 获取CIS的地址 cis_ptr = (func << 8) | 0x9; cis_ptr = WiFi_Read(0, cis_ptr) | (WiFi_Read(0, cis_ptr + 1) << 8) | (WiFi_Read(0, cis_ptr + 2) << 16); printf("Pointer to Function %d Card Information Structure (CIS): 0x%08x\n", func, cis_ptr); // 遍历CIS, 直到尾节点 while ((tpl_code = WiFi_Read(0, cis_ptr++)) != CISTPL_END) { if (tpl_code == CISTPL_NULL) continue; tpl_link = WiFi_Read(0, cis_ptr++); // 本结点数据的大小 for (i = 0; i < tpl_link; i++) data[i] = WiFi_Read(0, cis_ptr + i); printf("[CIS Tuple 0x%02x] addr=0x%08x size=%d\n", tpl_code, cis_ptr - 2, tpl_link); dump_data(data, tpl_link); switch (tpl_code) { case CISTPL_VERS_1: i = 2; while (data[i] != 0xff) { len = strlen((char *)&data[i]); if (len != 0) printf("%s\n", data + i); i += len + 1; } break; case CISTPL_MANFID: // 16.6 CISTPL_MANFID: Manufacturer Identification String Tuple printf("SDIO Card manufacturer code: 0x%04x\n", *(uint16_t *)data); // TPLMID_MANF printf("manufacturer information (Part Number and/or Revision): 0x%04x\n", *(uint16_t *)(data + 2)); // TPLMID_CARD break; case CISTPL_FUNCID: // 16.7.1 CISTPL_FUNCID: Function Identification Tuple printf("Card function code: 0x%02x\n", data[0]); // TPLFID_FUNCTION printf("System initialization bit mask: 0x%02x\n", data[1]); // TPLFID_SYSINIT break; case CISTPL_FUNCE: // 16.7.2 CISTPL_FUNCE: Function Extension Tuple if (data[0] == 0) { // 16.7.3 CISTPL_FUNCE Tuple for Function 0 (Extended Data 00h) printf("maximum block size: %d\n", *(uint16_t *)(data + 1)); printf("maximum transfer rate code: 0x%02x\n", data[3]); } else { // 16.7.4 CISTPL_FUNCE Tuple for Function 1-7 (Extended Data 01h) printf("maximum block size: %d\n", *(uint16_t *)(data + 0x0c)); // TPLFE_MAX_BLK_SIZE } } cis_ptr += tpl_link; if (tpl_link == 0xff) break; // 当TPL_LINK为0xff时说明当前结点为尾节点 } printf("--------------------------------------------------\n"); } /* 显示所有密钥 */ void WiFi_ShowKeyMaterials(void) { uint8_t buffer[WIFI_DEFBUFSIZE]; uint16_t size; MrvlIETypes_KeyParamSet_t *key = (MrvlIETypes_KeyParamSet_t *)buffer; size = WiFi_KeyMaterial(key, sizeof(buffer), WIFI_ACT_GET); while (size) { printf("Type %d, Info 0x%04x, Len %d\n", key->key_type_id, key->key_info, key->key_len); dump_data(key->key, key->key_len); size -= TLV_STRUCTLEN(*key); key = (MrvlIETypes_KeyParamSet_t *)((uint8_t *)key + TLV_STRUCTLEN(*key)); } if (size == 0) printf("data end!\n"); } /* 创建一个Ad-Hoc型的WiFi热点 */ uint8_t WiFi_StartADHOC(const char *ssid) { uint8_t buffer[WIFI_DEFBUFSIZE] = {0}; WiFi_CmdRequest_ADHOCStart *cmd = (WiFi_CmdRequest_ADHOCStart *)buffer; strncpy((char *)cmd->ssid, ssid, sizeof(cmd->ssid)); cmd->bss_type = BSS_INDEPENDENT; cmd->bcn_period = 100; cmd->ibss_param_set.header.type = MRVLIETYPES_IBSSPARAMSET; cmd->ibss_param_set.header.length = TLV_PAYLOADLEN(cmd->ibss_param_set); cmd->ibss_param_set.atim_window = 0; cmd->ds_param_set.header.type = MRVLIETYPES_DSPARAMSET; cmd->ds_param_set.header.length = TLV_PAYLOADLEN(cmd->ds_param_set); cmd->ds_param_set.channel = 1; cmd->cap_info = WIFI_CAPABILITY_IBSS; if (wifi_macctrl & WIFI_MACCTRL_WEP) cmd->cap_info |= WIFI_CAPABILITY_PRIVACY; *(uint32_t *)cmd->data_rate = 0x968b8482; WiFi_SendCommand(CMD_802_11_AD_HOC_START, buffer, sizeof(WiFi_CmdRequest_ADHOCStart), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) return CMD_STATUS_ERROR; return cmd->header.result; } /* 设置WiFi事件 */ uint16_t WiFi_SubscribeEvent(uint16_t action, uint16_t events) { uint8_t buffer[WIFI_DEFBUFSIZE]; WiFi_Cmd_SubscribeEvent *cmd = (WiFi_Cmd_SubscribeEvent *)buffer; cmd->action = action; if (action == WIFI_ACT_GET) cmd->events = 0; else cmd->events = events; WiFi_SendCommand(CMD_802_11_SUBSCRIBE_EVENT, buffer, sizeof(WiFi_Cmd_SubscribeEvent), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) return CMD_STATUS_ERROR; return cmd->events; } /* 将IEEE型的TLV转换成MrvlIE型的TLV */ uint8_t WiFi_TranslateTLV(MrvlIEType *mrvlie_tlv, const IEEEType *ieee_tlv, uint16_t mrvlie_payload_size) { mrvlie_tlv->header.type = ieee_tlv->header.type; if (ieee_tlv->header.length > mrvlie_payload_size) mrvlie_tlv->header.length = mrvlie_payload_size; // 若源数据大小超过缓冲区最大容量, 则丢弃剩余数据 else mrvlie_tlv->header.length = ieee_tlv->header.length; memcpy(mrvlie_tlv->data, ieee_tlv->data, mrvlie_tlv->header.length); // 复制数据 return mrvlie_tlv->header.length == ieee_tlv->header.length; // 返回值表示缓冲区大小是否足够 } /* 在规定的超时时间内, 等待指定的卡状态位置位(自动包括IO Ready位), 若成功则返回1 */ uint8_t WiFi_Wait(uint8_t status) { status |= WIFI_CARDSTATUS_IOREADY; timeout(wifi_timeout); while ((WiFi_Read(1, WIFI_CARDSTATUS) & status) != status) { if (TIM6->SR & TIM_SR_UIF) { // 若超时时间已到 TIM6->SR &= ~TIM_SR_UIF; printf("WiFi_Wait(0x%02x): timeout!\n", status); return 0; } } TIM6->CR1 &=~ TIM_CR1_CEN; // 关闭定时器 return 1; } /* 写寄存器, 返回写入后寄存器的实际内容 */ uint8_t WiFi_Write(uint8_t func, uint32_t addr, uint8_t value) { WiFi_SendCMD52(func, addr, value, CMD52_WRITE | CMD52_READAFTERWRITE); if (SDIO->STA & SDIO_STA_CMDREND) { SDIO->ICR = SDIO_ICR_CMDRENDC; return SDIO->RESP1 & 0xff; } else { printf("WiFi_Write failed, SDIO->STA=0x%08x!\n", SDIO->STA); return 0; } } /* 发送数据 */ // count为要发送的字节数或块数, bufsize为data缓冲区的大小 uint8_t WiFi_WriteData(uint8_t func, uint32_t addr, const void *data, uint16_t count, uint32_t bufsize, uint32_t flags) { uint32_t dctrl = SDIO->DCTRL & ~SDIO_DCTRL_DTDIR; uint32_t len; // 实际要发送的字节数 #ifndef WIFI_USEDMA const uint32_t *p = data; #endif if (flags & CMD53_BLOCKMODE) { // 块传输模式 (DTMODE=0) dctrl &= ~SDIO_DCTRL_DTMODE; len = count * WiFi_GetSDIOBlockSize(); // 块数*块大小 } else { // 多字节传输模式 (DTMODE=1, SDIO的频率低于16MHz时才支持) dctrl |= SDIO_DCTRL_DTMODE; len = count; if (len % 4 != 0) { len += 4 - len % 4; // WiFi模块要求写入的字节数必须为4的整数倍 count = len; // 传入CMD53的数据大小参数也应该是4的倍数 } } if (bufsize < len) printf("WiFi_WriteData: a buffer of at least %d bytes is required! bufsize=%d\n", len, bufsize); // 只读缓冲区越界不会影响数据传输, 所以这只是一个警告 WiFi_SendCMD53(func, addr, count, flags | CMD53_WRITE); while (SDIO->STA & SDIO_STA_CMDACT); if ((SDIO->STA & SDIO_STA_CMDREND) == 0) { printf("WiFi_WriteData: CMD53 no response!\n"); return 0; } SDIO->ICR = SDIO_ICR_CMDRENDC; // 开始发送数据 #ifdef WIFI_USEDMA DMA2_Channel4->CMAR = (uint32_t)data; DMA2_Channel4->CPAR = (uint32_t)&SDIO->FIFO; DMA2_Channel4->CNDTR = len / 4; DMA2_Channel4->CCR = DMA_CCR4_PL | DMA_CCR4_MSIZE_1 | DMA_CCR4_PSIZE_1 | DMA_CCR4_MINC | DMA_CCR4_DIR | DMA_CCR4_EN; #endif SDIO->DLEN = len; SDIO->DCTRL = dctrl | SDIO_DCTRL_DTEN; #ifdef WIFI_USEDMA while ((DMA2->ISR & DMA_ISR_TCIF4) == 0); DMA2->IFCR = DMA_IFCR_CTCIF4; DMA2_Channel4->CCR &= ~DMA_CCR4_EN; #else while (len) { len -= 4; SDIO->FIFO = *p++; // 向FIFO送入4字节数据 while (SDIO->STA & SDIO_STA_TXFIFOF); // 如果FIFO已满则等待 } #endif while (SDIO->STA & SDIO_STA_TXACT); // 等待发送完毕 SDIO->DCTRL &= ~SDIO_DCTRL_DTEN; // 数据传输完毕后DTEN应及时清零, 防止后续对DCTRL寄存器操作后误启动数据传输导致超时或CRC校验错误 // 清除相关标志位 SDIO->ICR = SDIO_ICR_DATAENDC; if (flags & CMD53_BLOCKMODE) SDIO->ICR = SDIO_ICR_DBCKENDC; SDIO->STA; // 读状态寄存器后标志位才能真正清除 return SDIO_SUCCEEDED(); } /* 向WiFi缓冲区发送数据 */ uint8_t WiFi_WritePort(const void *data, uint16_t size, uint32_t bufsize) { uint16_t block_num, block_size; block_size = WiFi_GetSDIOBlockSize(); WiFi_Wait(0); // 发送CMD53前必须IOReady=1 #ifndef WIFI_HIGHSPEED if (size > 512 || size % block_size == 0) // 若size为数据块大小的整数倍, 则采用数据块方式发送 { #endif block_num = size / block_size; if (size % block_size != 0) block_num++; // 大于512字节时不能采用字节流方式发送(如712字节), 仍采用数据块方式 return WiFi_WriteData(1, io_addr, data, block_num, bufsize, CMD53_BLOCKMODE); #ifndef WIFI_HIGHSPEED } else return WiFi_WriteData(1, io_addr, data, size, bufsize, 0); // 否则采用字节流方式发送 #endif }【httptest.c】
#include <string.h> #include "lwip/tcp.h" // 一般情况下需要包含的头文件 #define STR_AND_SIZE(str) (str), strlen(str) static 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, "<meta charset=\"gb2312\"><title>获取网页名称</title><div style=\"font-family:Arial\"><b>请求的网页文件名称是: </b>%s</div>", 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); } else tcp_close(tpcb); return ERR_OK; } static 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); }
在工程的所在文件夹创建一个lwip文件夹。然后在lwip的官方网站下载lwip-2.0.2.zip,打开压缩包中的lwip-2.0.2/src文件夹,解压以下文件夹到工程的lwip目录下。
core/
core/ipv4
include/lwip
include/netif
netif/ethernet.c
netif/ethernetif.c
解压后,将里面的c文件都添加到工程的lwip分组下。
具体添加的文件请看下图:
接下来,创建lwip/include/arch/cc.h文件,内容如下:
#define PACK_STRUCT_BEGIN __packed // struct前的__packed
创建lwip/include/lwipopts.h文件,内容如下:
#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 // 不进行临界区保护 (在中断中调用lwip函数时要小心)
打开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 <[email protected]> * */ /* * 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 // 允许编译器编译该文件 #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 <string.h> // memcpy函数所在的头文件 #include "WiFi.h" // WiFi模块驱动程序头文件 /** * 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 */ WiFi_MACAddr(netif->hwaddr, WIFI_ACT_GET); // 获取网卡的默认MAC地址 printf("MAC Addr: %02X:%02X:%02X:%02X:%02X:%02X\n", netif->hwaddr[0], netif->hwaddr[1], netif->hwaddr[2], netif->hwaddr[3], netif->hwaddr[4], netif->hwaddr[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. */ } /** * 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[1792]; WiFi_DataTx *packet = (WiFi_DataTx *)buffer; uint8_t *bufptr = packet->payload; //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); memcpy(bufptr, q->payload, q->len); // 复制数据包内容 bufptr += q->len; // 指针前移 } //signal that packet should be sent(); WiFi_SendPacket(packet, bufptr - packet->payload, sizeof(buffer)); // 发送数据包 // 打印数据包内容 printf("[Send] size=%d\n", packet->tx_packet_length); //dump_data(packet->payload, packet->tx_packet_length); 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; // 添加的变量 uint8_t buffer[1792]; // 由于WiFi模块本身不支持使用多个CMD53命令读取数据包, 所以必须建立一个缓冲区, 一次性读取完 WiFi_DataRx *packet = (WiFi_DataRx *)buffer; uint8_t *bufptr = packet->payload; /* Obtain the size of the packet and put it into the "len" variable. */ // 读取整个数据包 if (!WiFi_ReceivePacket(buffer, sizeof(buffer))) { // 若读取失败, 则不分配pbuf, 退出本函数 p = NULL; goto after_alloc; } len = packet->rx_packet_length; // 获取数据包的大小 // 打印数据包内容 printf("[Recv] size=%d\n", len); //dump_data(packet->payload, 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); after_alloc: // 添加此标记 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); memcpy(q->payload, bufptr, q->len); // 复制数据包内容 bufptr += 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_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 /*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 */
在项目属性中,设置Include Paths为:.;.\lwip\include
其中点表示工程根目录。
另外,为了在程序中使用printf函数,Target选项卡下的Use MicroLIB复选框也要勾选上。
编译并下载程序。
【电脑连接WiFi模块创建的带WEP密码的热点】
WiFi_SetWEP(WIFI_ACT_ADD, "Hello, World!"); // 设置热点WEP密码 (若不想设置密码请删除掉这句话) WiFi_StartADHOC("Marvell 88W8686"); // 创建Ad-Hoc热点
此时电脑的IP地址要填写192.168.43.1,子网掩码为255.255.255.0,网关为空(电脑作网关)。
此时可用浏览器访问WiFi模块上的网页:
程序运行结果:
Command response received: CMD63, RESP_90ff8000 Command response received: CMD63, RESP_90300000 Number of I/O Functions: 1 Memory Present: 0 Relative card address: 0x0001 Card selected! status=0x00001e00 SDIO Clock: 24MHz -------------- CIS of Function 0 ---------------- Pointer to Function 0 Card Information Structure (CIS): 0x00008000 [CIS Tuple 0x15] addr=0x00008000 size=31 01004D617276656C6C003830322E3131205344494F2049443A2030420000FF Marvell 802.11 SDIO ID: 0B [CIS Tuple 0x20] addr=0x00008021 size=4 DF020391 SDIO Card manufacturer code: 0x02df manufacturer information (Part Number and/or Revision): 0x9103 [CIS Tuple 0x21] addr=0x00008027 size=2 0C00 Card function code: 0x0c System initialization bit mask: 0x00 [CIS Tuple 0x22] addr=0x0000802b size=4 00000132 maximum block size: 256 maximum transfer rate code: 0x32 -------------------------------------------------- -------------- CIS of Function 1 ---------------- Pointer to Function 1 Card Information Structure (CIS): 0x00008080 [CIS Tuple 0x21] addr=0x00008080 size=2 0C00 Card function code: 0x0c System initialization bit mask: 0x00 [CIS Tuple 0x22] addr=0x00008084 size=28 01011000000000000000000000010000000000000000000000000000 maximum block size: 256 -------------------------------------------------- Firmware is successfully downloaded! WiFi_Wait(0x0a): timeout! Resend WiFi command 0x0028! size=16 SSID 'Marvell 88W8686', MAC 02:22:E2:81:E9:80, RSSI 50, Channel 1 Capability: 0x0012 (Security: WEP, Mode: Ad-Hoc) SSID 'TP-LINK_ORANGE', MAC B0:95:8E:05:82:CA, RSSI 56, Channel 1 Capability: 0x0431 (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 71, Channel 6 Capability: 0x0431 (Security: WPA2, Mode: Infrastructure) SSID 'TP-LINK_PLC', MAC 30:FC:68:38:6E:2C, RSSI 72, Channel 6 Capability: 0x0431 (Security: WPA2, Mode: Infrastructure) SSID '10507', MAC B0:95:8E:04:2A:06, RSSI 49, Channel 11 Capability: 0x0431 (Security: WPA2, Mode: Infrastructure) SSID 'CMCC-Young', MAC D6:14:4B:6F:A6:0F, RSSI 68, Channel 11 Capability: 0x0621 (Security: unsecured, Mode: Infrastructure) SSID 'CMCC-EDU', MAC D6:14:4B:6F:A6:0E, RSSI 68, Channel 11 Capability: 0x0621 (Security: unsecured, Mode: Infrastructure) MAC Addr: 00:1A:6B:A4:AA:B4 [Send] size=42 [Event 30] size=8 [Event 4] size=8 The number of stations in this ad hoc newtork has changed! [Event 17] size=8 All other stations have been away from this ad hoc network! [Event 4] size=8 The number of stations in this ad hoc newtork has changed! [Recv] size=42 [Recv] size=42 [Recv] size=42 [Recv] size=42 [Recv] size=42 [Recv] size=42 [Recv] size=42 [Recv] size=42 [Recv] size=42 [Recv] size=42 [Recv] size=42 [Recv] size=42 [Event 17] size=8 All other stations have been away from this ad hoc network! [Event 4] size=8 The number of stations in this ad hoc newtork has changed! [Event 17] size=8 All other stations have been away from this ad hoc network! [Recv] size=42 [Event 4] size=8 The number of stations in this ad hoc newtork has changed! [Recv] size=42 [Recv] size=42 [Event 17] size=8 All other stations have been away from this ad hoc network! [Recv] size=42 [Recv] size=42 [Event 4] size=8 The number of stations in this ad hoc newtork has changed! [Recv] size=42 [Recv] size=42 [Recv] size=42 [Recv] size=42 [Recv] size=42【电脑和WiFi模块同时连接手机开的热点】
WiFi_Connect("vivo Y29L", BSS_INFRASTRUCTURE); // 连接手机开的没有密码的AP热点
此时开热点的手机为网关,电脑的IP地址为自动获取。用电脑查看一下获取到的IP地址和网关,确保网关和程序里面设置的是一致的。
笔者的手机IP地址为192.168.43.1,因此程序里面网关就写的是192.168.43.1,IP地址前三个要一样,最后一个数随便写。
配置完成后,即可用电脑或手机上的浏览器访问开发板上面的HTTP服务器。
Command response received: CMD63, RESP_90ff8000 Command response received: CMD63, RESP_90300000 Number of I/O Functions: 1 Memory Present: 0 Relative card address: 0x0001 Card selected! status=0x00001e00 SDIO Clock: 24MHz -------------- CIS of Function 0 ---------------- Pointer to Function 0 Card Information Structure (CIS): 0x00008000 [CIS Tuple 0x15] addr=0x00008000 size=31 01004D617276656C6C003830322E3131205344494F2049443A2030420000FF Marvell 802.11 SDIO ID: 0B [CIS Tuple 0x20] addr=0x00008021 size=4 DF020391 SDIO Card manufacturer code: 0x02df manufacturer information (Part Number and/or Revision): 0x9103 [CIS Tuple 0x21] addr=0x00008027 size=2 0C00 Card function code: 0x0c System initialization bit mask: 0x00 [CIS Tuple 0x22] addr=0x0000802b size=4 00000132 maximum block size: 256 maximum transfer rate code: 0x32 -------------------------------------------------- -------------- CIS of Function 1 ---------------- Pointer to Function 1 Card Information Structure (CIS): 0x00008080 [CIS Tuple 0x21] addr=0x00008080 size=2 0C00 Card function code: 0x0c System initialization bit mask: 0x00 [CIS Tuple 0x22] addr=0x00008084 size=28 01011000000000000000000000010000000000000000000000000000 maximum block size: 256 -------------------------------------------------- Firmware is successfully downloaded! WiFi_Wait(0x0a): timeout! Resend WiFi command 0x0028! size=16 SSID 'CMCC-Young', MAC E6:14:4B:57:40:00, RSSI 75, Channel 1 Capability: 0x0621 (Security: unsecured, Mode: Infrastructure) SSID 'TP-LINK_ORANGE', MAC B0:95:8E:05:82:CA, RSSI 50, Channel 1 Capability: 0x0431 (Security: WPA2, Mode: Infrastructure) SSID 'CMCC-EDU', MAC E6:14:4B:57:40:0F, RSSI 74, Channel 1 Capability: 0x0621 (Security: unsecured, Mode: Infrastructure) SSID 'CMCC-EDU', MAC 96:14:4B:6F:A5:DA, RSSI 73, Channel 1 Capability: 0x0621 (Security: unsecured, Mode: Infrastructure) SSID 'diansai505', MAC 50:FA:84:53:5B:8E, RSSI 75, Channel 2 Capability: 0x0411 (Security: WPA2, Mode: Infrastructure) SSID 'vivo Y29L', MAC F4:29:81:98:F3:78, RSSI 47, Channel 6 Capability: 0x8421 (Security: unsecured, Mode: Infrastructure) SSID 'TP-LINK_PLC', MAC 30:FC:68:38:6E:2C, RSSI 76, Channel 6 Capability: 0x0431 (Security: WPA2, Mode: Infrastructure) SSID 'xgxy666', MAC DC:FE:18:67:76:14, RSSI 73, Channel 6 Capability: 0x0431 (Security: WPA2, Mode: Infrastructure) SSID 'CDU_Free', MAC D4:61:FE:71:36:D0, RSSI 75, Channel 6 Capability: 0x8421 (Security: unsecured, Mode: Infrastructure) SSID 'CDU', MAC D4:61:FE:71:36:D1, RSSI 76, Channel 6 Capability: 0x0431 (Security: WPA2, Mode: Infrastructure) SSID 'CMCC-EDU', MAC D6:14:4B:6F:A6:0E, RSSI 69, Channel 11 Capability: 0x0621 (Security: unsecured, Mode: Infrastructure) SSID '10507', MAC B0:95:8E:04:2A:06, RSSI 47, Channel 11 Capability: 0x0431 (Security: WPA2, Mode: Infrastructure) SSID 'CMCC-Young', MAC D6:14:4B:6F:A6:0F, RSSI 69, Channel 11 Capability: 0x0621 (Security: unsecured, Mode: Infrastructure) Connected to vivo Y29L! MAC Addr: 00:1A:6B:A4:AA:B4 [Send] size=42 [Recv] size=42 [Send] size=42 [Recv] size=74 [Send] size=58 [Recv] size=74 [Send] size=58 [Recv] size=74 [Send] size=58 [Recv] size=54 [Recv] size=522 [Send] size=322 [Recv] size=54 [Recv] size=54 [Recv] size=54 [Recv] size=368 [Send] size=303 [Recv] size=54 [Recv] size=502 [Send] size=42 [Recv] size=502 [Recv] size=502 [Recv] size=42 [Send] size=54 [Send] size=322 [Recv] size=54 [Recv] size=368 [Send] size=303 [Recv] size=54 [Recv] size=502 [Send] size=322 [Recv] size=54 [Recv] size=368 [Send] size=42 [Recv] size=368 [Recv] size=368 [Recv] size=522 [Recv] size=42 [Send] size=54 [Recv] size=522 [Send] size=54 [Send] size=322 [Recv] size=54 [Send] size=303 [Recv] size=54 [Recv] size=368 [Send] size=42 [Recv] size=368 [Recv] size=42 [Send] size=54 [Recv] size=54 [Send] size=54 [Recv] size=54 [Send] size=42 [Recv] size=42 [Send] size=303 [Recv] size=54 [Event 8] size=16, reason=3, MAC: F4:29:81:98:F3:78 Deauthenticated!此外,程序还可以连接电脑创建的带有WEP密码的ad hoc热点。
//#define WIFI_HIGHSPEED // 采用SDIO高速模式 //#define WIFI_USEDMA // SDIO采用DMA方式收发数据(高速模式必须采用DMA)在main.c中,WiFi_Scan()扫描并显示附近的WiFi热点信息,可注释掉。WiFi.c中的WiFi_Init函数中调用的WiFi_ShowCIS函数用于显示WiFi模块的SDIO信息,也可以注释掉,减少屏幕显示内容。
#define WiFi_GetSDIOBlockSize() _BV(sdio_data.SDIO_DataBlockSize >> 4)【main.c(库函数版)】
#include <stdio.h> #include <stm32f10x.h> #include "lwip/init.h" // lwip_init函数所在的头文件 #include "lwip/timeouts.h" // sys_check_timeouts函数所在的头文件 #include "netif/ethernet.h" // ethernet_input函数所在头文件 #include "WiFi.h" // 这两个函数位于ethernetif.c中, 但没有头文件声明 err_t ethernetif_init(struct netif *netif); void ethernetif_input(struct netif *netif); // 一个很简单的http服务器 void init_http(void); // printf函数的实现, 必须在项目属性中勾选Use MicroLIB后才能用 int fputc(int ch, FILE *fp) { if (fp == stdout) { if (ch == '\n') { while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); USART_SendData(USART1, '\r'); } while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); USART_SendData(USART1, ch); } return ch; } // RTC时间转化为毫秒数 uint32_t sys_now(void) { uint32_t sec = RTC_GetCounter(); // 秒 uint32_t milli = (39999 - RTC_GetDivider()) * 1000 / 40000; // 毫秒 (DIV寄存器的范围是0~PRL-1) return sec * 1000 + milli; } // 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"); break; case 4: // Ad-Hoc网络中不止1个结点, 且连接数发生了变化 printf("The number of stations in this ad hoc newtork has changed!\n"); break; case 8: // 认证已解除 (例如手机关闭了热点, 或者是连接路由器后一直没有输入密码而自动断开连接) printf("Deauthenticated!\n"); break; case 9: printf("Disassociated!\n"); break; case 17: // Ad-Hoc网络中只剩本结点 printf("All other stations have been away from this ad hoc network!\n"); break; } if (event->header.length > sizeof(WiFi_Event)) dump_data(event + 1, event->header.length - sizeof(WiFi_Event)); } int main(void) { struct ip4_addr ipaddr, netmask, gw; struct netif wifi_88w8686; uint32_t last_check = 0; GPIO_InitTypeDef gpio; USART_InitTypeDef usart; // HCLK=PCLK2=72MHz, PCLK1=36MHz RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO, ENABLE); // 打开SDIO外设的时钟 #ifdef WIFI_USEDMA RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE); #endif RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_TIM6, ENABLE); // TIM6为延时用的定时器 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_USART1, ENABLE); // 串口1发送引脚PA9设为复用推挽50MHz输出, 接收引脚PA10为默认的浮空输入 gpio.GPIO_Mode = GPIO_Mode_AF_PP; gpio.GPIO_Pin = GPIO_Pin_9; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio); // PB12设为推挽50MHz输出 // WiFi模块的VCC引脚不可直接连接电源, 必须要串联一个PNP型三极管, 并把基极接到PB12上, 否则STM32复位时WiFi模块无法自动复位 // 复位时PB12输出高阻态, 三极管截止, WiFi模块断电, 配置完CRH寄存器后立即输出低电平, WiFi模块通电 gpio.GPIO_Mode = GPIO_Mode_Out_PP; gpio.GPIO_Pin = GPIO_Pin_12; GPIO_Init(GPIOB, &gpio); // PC8~11为SDIO的4位数据引脚, PC12为SDIO时钟引脚, 设为复用推挽50MHz输出 gpio.GPIO_Mode = GPIO_Mode_AF_PP; gpio.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12; GPIO_Init(GPIOC, &gpio); // PD2为SDIO命令引脚, 设为复用推挽50MHz输出 gpio.GPIO_Pin = GPIO_Pin_2; GPIO_Init(GPIOD, &gpio); // 打开串口1, 允许发送和接收 USART_StructInit(&usart); usart.USART_BaudRate = 115200; // 波特率 USART_Init(USART1, &usart); USART_Cmd(USART1, ENABLE); PWR_BackupAccessCmd(ENABLE); // 允许写后备寄存器(如RCC->BDCR) RCC_LSICmd(ENABLE); // 因为板上没有LSE晶振, 所以RTC时钟选LSI while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET); // 等待LSI启动 // 若RTC未打开, 则初始化RTC if ((RCC->BDCR & RCC_BDCR_RTCEN) == 0) // 没有库函数可以判断RTC是否已打开. 只能使用寄存器 { // 必须要先选择时钟, 然后再开启RTC时钟 RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); // 选LSI作为RTC时钟 RCC_RTCCLKCmd(ENABLE); // 开启RTC时钟, RTC开始走时 RTC_SetPrescaler(39999); // 设置分频系数: LSI晶振的频率是40kHz, 定时1s RTC_WaitForLastTask(); // 等待设置生效 //RTC_SetCounter(50); // 设置初始时间; STM32F1系列的RTC没有年月日、小时等寄存器, 只有一个32位的计数器, 要想实现日期和时间的功能必须调用C库<time.h>中的mktime函数, 用软件来实现这些功能 //RTC_WaitForLastTask(); } else RTC_WaitForSynchro(); // 等待RTC与APB1时钟同步 WiFi_Init(); WiFi_Scan(); // 显示附近的热点信息 (可以注释掉) WiFi_SetWEP(WIFI_ACT_ADD, "Hello, World!"); // 设置热点WEP密码 (若不想设置密码请删除掉这句话) WiFi_StartADHOC("Marvell 88W8686"); // 创建Ad-Hoc热点 //WiFi_SetWEP(WIFI_ACT_ADD, "48656c6c6f2c20576f726c6421"); // 设置热点WEP密码(第二种方式): Hello, World! //WiFi_Connect("Octopus-WEP", BSS_INDEPENDENT); // 连接Ad-Hoc热点 (Win7电脑可以创建WEP加密的Ad-Hoc型热点) //WiFi_Connect("10507", BSS_INFRASTRUCTURE); // 连接路由器建立的AP热点 //WiFi_Connect("vivo Y29L", BSS_INFRASTRUCTURE); // 连接手机开的没有密码的AP热点 lwip_init(); IP4_ADDR(&ipaddr, 192, 168, 43, 10); // 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); netif_set_default(&wifi_88w8686); // 设为默认网卡 netif_set_up(&wifi_88w8686); init_http(); while (1) { if (WiFi_PacketArrived()) ethernetif_input(&wifi_88w8686); // sys_check_timeouts函数千万不能调用的太频繁, 否则会出错!(例如开机后要等1~2分钟DHCP才能分配到IP地址) if (sys_now() - last_check > 200) { last_check = sys_now(); sys_check_timeouts(); } } }【WiFi.c(库函数版)】
#include <stdio.h> #include <stm32f10x.h> #include <string.h> #include "WiFi.h" extern const unsigned char firmware_helper_sd[2516]; extern const unsigned char firmware_sd8686[122916]; //const uint8_t wifi_mac_addr[] = {0x62, 0x1d, 0x2f, 0x00, 0x4e, 0x2d}; // MAC地址的第一个字节必须为偶数! 否则为多播地址 static uint16_t wifi_macctrl = WIFI_MACCTRL_ETHERNET2 | WIFI_MACCTRL_TX | WIFI_MACCTRL_RX; static uint16_t wifi_timeout = WIFI_DEFTIMEOUT; static uint32_t io_addr; static SDIO_CmdInitTypeDef sdio_cmd; static SDIO_DataInitTypeDef sdio_data; // 默认情况下startup_stm32f10x_hd.s设定的栈大小Stack_Size为0x400, 函数内最多只能创建1KB的变量 // 为了创建大缓冲区, 栈大小应该改大, 比如0x4000, 即16KB // 延时n毫秒 void delay(uint16_t nms) { timeout(nms); while (TIM_GetFlagStatus(TIM6, TIM_FLAG_Update) == RESET); // 等待定时器溢出 TIM_ClearFlag(TIM6, TIM_FLAG_Update); // 清除溢出标志 } // 显示数据内容 void dump_data(const void *data, uint32_t len) { const uint8_t *p = data; while (len--) printf("%02X", *p++); printf("\n"); } // 启动定时器, 超时时间设为n毫秒 void timeout(uint16_t nms) { TIM_TimeBaseInitTypeDef tim; TIM_SelectOnePulseMode(TIM6, TIM_OPMode_Single); // UIF置位后自动关闭定时器 TIM_UpdateRequestConfig(TIM6, TIM_UpdateSource_Regular); // 初始化时保持UIF=0 tim.TIM_ClockDivision = TIM_CKD_DIV1; tim.TIM_CounterMode = TIM_CounterMode_Up; tim.TIM_Period = 10 * nms - 1; tim.TIM_Prescaler = 7199; // APB1总线上的定时器时钟为72MHz TIM_TimeBaseInit(TIM6, &tim); TIM_Cmd(TIM6, ENABLE); // 开始计时 } /* 关联一个热点 */ uint16_t WiFi_Associate(const char *ssid) { uint8_t buffer[2048]; WiFi_SSIDInfo info; WiFi_CmdRequest_Associate *cmd = (WiFi_CmdRequest_Associate *)buffer; WiFi_CmdResponse_Associate *resp = (WiFi_CmdResponse_Associate *)buffer; MrvlIETypes_PhyParamDSSet_t *ds; MrvlIETypes_CfParamSet_t *cf; MrvlIETypes_AuthType_t *auth; MrvlIETypes_RsnParamSet_t *rsn; if (!WiFi_ScanSSID(ssid, &info, buffer, sizeof(buffer))) { printf("Cannot find AP: %s!\n", ssid); return WIFI_ASSOCIATION_NOTFOUND; } memcpy(cmd->peer_sta_addr, info.mac_addr, sizeof(info.mac_addr)); cmd->cap_info = info.cap_info; cmd->listen_interval = 10; cmd->bcn_period = info.bcn_period; cmd->dtim_period = 1; memcpy(cmd + 1, &info.ssid, TLV_STRUCTLEN(info.ssid)); ds = (MrvlIETypes_PhyParamDSSet_t *)((uint8_t *)(cmd + 1) + TLV_STRUCTLEN(info.ssid)); ds->header.type = MRVLIETYPES_DSPARAMSET; ds->header.length = 1; ds->channel = info.channel; cf = (MrvlIETypes_CfParamSet_t *)(ds + 1); memset(cf, 0, sizeof(MrvlIETypes_CfParamSet_t)); cf->header.type = MRVLIETYPES_CFPARAMSET; cf->header.length = TLV_PAYLOADLEN(*cf); memcpy(cf + 1, &info.rates, TLV_STRUCTLEN(info.rates)); auth = (MrvlIETypes_AuthType_t *)((uint8_t *)(cf + 1) + TLV_STRUCTLEN(info.rates)); auth->header.type = MRVLIETYPES_AUTHTYPE; auth->header.length = TLV_PAYLOADLEN(*auth); auth->auth_type = AUTH_MODE_OPEN; rsn = (MrvlIETypes_RsnParamSet_t *)(auth + 1); if (info.rsn.header.type) { // WPA2网络必须在命令中加入RSN参数才能成功连接 memcpy(rsn, &info.rsn, TLV_STRUCTLEN(info.rsn)); WiFi_SendCommand(CMD_802_11_ASSOCIATE, buffer, (uint8_t *)rsn - buffer + TLV_STRUCTLEN(info.rsn), sizeof(buffer)); } else WiFi_SendCommand(CMD_802_11_ASSOCIATE, buffer, (uint8_t *)rsn - buffer, sizeof(buffer)); // 其余网络不需要RSN参数 if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) { printf("Association with %s failed!\n", ssid); return WIFI_ASSOCIATION_ERROR; } //printf("capability=0x%04x, status_code=0x%04x, aid=0x%04x\n", resp->capability, resp->status_code, resp->association_id); if (resp->association_id == 0xffff) return ((-resp->capability) << 8) | resp->status_code; return WIFI_ASSOCIATION_SUCCESS; } /* 连接WiFi热点, 超时自动重连 */ uint16_t WiFi_Connect(const char *ssid, uint8_t type) { uint16_t ret; do { if (type == BSS_INDEPENDENT) // 连接Ad-Hoc型热点 ret = WiFi_JoinADHOC(ssid); else if (type == BSS_INFRASTRUCTURE) // 连接普通热点 ret = WiFi_Associate(ssid); else { printf("WiFi_Connect: incorrect network type!\n"); return WIFI_ASSOCIATION_ERROR; } if (ret != WIFI_ASSOCIATION_SUCCESS) { printf("%s returned 0x%04x\n", (type == BSS_INDEPENDENT) ? "WiFi_JoinADHOC" : "WiFi_Associate", ret); delay(2000); // 等待一段时间后重连 } } while (WIFI_ASSOCIATION_TIMEOUT(ret) || ret == WIFI_ASSOCIATION_NOTFOUND); // 若连接超时, 或未扫描到热点, 则重连 if (ret != WIFI_ASSOCIATION_SUCCESS) return ret; printf("Connected to %s!\n", ssid); return ret; } /* 下载固件 */ // 参考文档: marvell-88w8686-固件下载程序说明.doc uint8_t WiFi_DownloadFirmware(void) { uint8_t helper_buf[64]; const uint8_t *data; uint16_t size; uint32_t len; // 块大小设为32 sdio_data.SDIO_DataBlockSize = SDIO_DataBlockSize_32b; WiFi_SetBlockSize(1); // 应用到Function 1 // 下载helper io_addr = WiFi_Read(1, WIFI_IOPORT0) | (WiFi_Read(1, WIFI_IOPORT1) << 8) | (WiFi_Read(1, WIFI_IOPORT2) << 16); data = firmware_helper_sd; len = sizeof(firmware_helper_sd); while (len) { // 每次下载64字节, 其中前4字节为本次下载的数据量 size = (len > 60) ? 60 : len; *(uint32_t *)helper_buf = size; memcpy(helper_buf + 4, data, size); WiFi_Wait(WIFI_CARDSTATUS_DNLDCARDRDY); WiFi_WritePort(helper_buf, sizeof(helper_buf), sizeof(helper_buf)); len -= size; data += size; } *(uint32_t *)helper_buf = 0; WiFi_WritePort(helper_buf, sizeof(helper_buf), sizeof(helper_buf)); // 以空数据包结束 // 下载固件 data = firmware_sd8686; len = sizeof(firmware_sd8686); while (len) { WiFi_Wait(WIFI_CARDSTATUS_DNLDCARDRDY); while ((size = WiFi_Read(1, WIFI_SQREADBASEADDR0) | (WiFi_Read(1, WIFI_SQREADBASEADDR1) << 8)) == 0); // 获取本次下载的字节数 //printf("Required: %d bytes, Remaining: %d bytes\n", size, len); if (size & 1) { // 若size为奇数(如17), 则说明接收端有CRC校验错误, 应重新传送上一次的内容(这部分代码省略) printf("Error: an odd size is invalid!\n"); return 0; } if (size > len) size = len; // len为缓冲区剩余大小 #ifdef WIFI_HIGHSPEED // 高速模式下不能使用多字节传输模式, 只能使用块传输模式, 因此缓冲区要足够大 if (len < 32) { // 若缓冲区空间不足一个数据块, 则借用helper_buf memcpy(helper_buf, data, size); WiFi_WritePort(helper_buf, size, sizeof(helper_buf)); } else #endif WiFi_WritePort(data, size, len); if (!SDIO_SUCCEEDED()) { printf("Data transfer error! SDIO->STA=0x%08x\n", SDIO->STA); return 0; } len -= size; data += size; } // 等待Firmware启动 while (WiFi_GetPacketLength() == 0xfedc); printf("Firmware is successfully downloaded!\n"); return 1; } /* 获取数据帧大小 */ uint16_t WiFi_GetPacketLength(void) { // 读Scratch pad 4寄存器的低16位 return WiFi_Read(1, WIFI_SCRATCHPAD4_0) | (WiFi_Read(1, WIFI_SCRATCHPAD4_1) << 8); } /* 初始化WiFi模块 */ // SDIO Simplified Specification Version 3.00: 3. SDIO Card Initialization void WiFi_Init(void) { uint16_t rca; // 虽然SDIO标准规定SDIO接口上可以接多张SD卡, 但是STM32的SDIO接口只能接一张卡(芯片手册上有说明), 软件片选RCA无意义 uint32_t resp; SDIO_InitTypeDef sdio; SDIO_SetPowerState(SDIO_PowerState_ON); // 打开SDIO外设 SDIO_StructInit(&sdio); sdio.SDIO_BusWide = SDIO_BusWide_4b; sdio.SDIO_ClockDiv = 178; // 初始化时最高允许的频率: 72MHz/(178+2)=400kHz SDIO_Init(&sdio); SDIO_ClockCmd(ENABLE); SDIO_SetSDIOOperation(ENABLE); // 开启SDIO模式 delay(10); // 延时可防止CMD5重发 // 不需要发送CMD0, 因为SD I/O card的初始化命令是CMD52 // An I/O only card or the I/O portion of a combo card is NOT reset by CMD0. (See 4.4 Reset for SDIO) /* 发送CMD5: IO_SEND_OP_COND */ sdio_cmd.SDIO_Argument = 0; sdio_cmd.SDIO_CmdIndex = 5; sdio_cmd.SDIO_CPSM = SDIO_CPSM_Enable; sdio_cmd.SDIO_Response = SDIO_Response_Short; sdio_cmd.SDIO_Wait = SDIO_Wait_No; SDIO_SendCommand(&sdio_cmd); while (SDIO_GetFlagStatus(SDIO_FLAG_CMDACT) == SET); while (SDIO_GetFlagStatus(SDIO_FLAG_CTIMEOUT) == SET) { // 为了保险起见还是要检查一下是否要重发命令 SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT); // 清除标志 printf("Timeout! Resend CMD%d\n", sdio_cmd.SDIO_CmdIndex); delay(5); SDIO_SendCommand(&sdio_cmd); // 重发 while (SDIO_GetFlagStatus(SDIO_FLAG_CMDACT) == SET); } if (SDIO_GetFlagStatus(SDIO_FLAG_CMDREND) == SET) { SDIO_ClearFlag(SDIO_FLAG_CMDREND); printf("Command response received: CMD%d, RESP_%08x\n", SDIO_GetCommandResponse(), SDIO_GetResponse(SDIO_RESP1)); } /* 设置参数VDD Voltage Window: 3.2~3.4V, 并再次发送CMD5 */ sdio_cmd.SDIO_Argument = 0x300000; SDIO_SendCommand(&sdio_cmd); while (SDIO_GetFlagStatus(SDIO_FLAG_CMDACT) == SET); if (SDIO_GetFlagStatus(SDIO_FLAG_CMDREND) == SET) { SDIO_ClearFlag(SDIO_FLAG_CMDREND); resp = SDIO_GetResponse(SDIO_RESP1); printf("Command response received: CMD%d, RESP_%08x\n", SDIO_GetCommandResponse(), resp); if (resp & _BV(31)) { // Card is ready to operate after initialization printf("Number of I/O Functions: %d\n", (resp >> 28) & 7); printf("Memory Present: %d\n", (resp & _BV(27)) != 0); } } /* 获取WiFi模块地址 (CMD3: SEND_RELATIVE_ADDR, Ask the card to publish a new relative address (RCA)) */ sdio_cmd.SDIO_Argument = 0; sdio_cmd.SDIO_CmdIndex = 3; SDIO_SendCommand(&sdio_cmd); while (SDIO_GetFlagStatus(SDIO_FLAG_CMDACT) == SET); if (SDIO_GetFlagStatus(SDIO_FLAG_CMDREND) == SET) { SDIO_ClearFlag(SDIO_FLAG_CMDREND); rca = SDIO_GetResponse(SDIO_RESP1) >> 16; printf("Relative card address: 0x%04x\n", rca); } /* 选中WiFi模块 (CMD7: SELECT/DESELECT_CARD) */ sdio_cmd.SDIO_Argument = rca << 16; sdio_cmd.SDIO_CmdIndex = 7; SDIO_SendCommand(&sdio_cmd); while (SDIO_GetFlagStatus(SDIO_FLAG_CMDACT) == SET); if (SDIO_GetFlagStatus(SDIO_FLAG_CMDREND) == SET) { SDIO_ClearFlag(SDIO_FLAG_CMDREND); printf("Card selected! status=0x%08x\n", SDIO_GetResponse(SDIO_RESP1)); } /* 提高时钟频率, 超时时间为0.1s */ #ifdef WIFI_HIGHSPEED sdio.SDIO_ClockDiv = 1; // 72MHz/(1+2)=24MHz SDIO_Init(&sdio); sdio_data.SDIO_DataTimeOut = 2400000; printf("SDIO Clock: 24MHz\n"); #else sdio.SDIO_ClockDiv = 70; // 72MHz/(70+2)=1MHz SDIO_Init(&sdio); sdio_data.SDIO_DataTimeOut = 100000; printf("SDIO Clock: 1MHz\n"); #endif /* 选择总线宽度 (Wide Bus Selection) */ // For an SDIO card a write to the CCCR using CMD52 is used to select bus width. (See 4.5 Bus Width) // CMD52: IO_RW_DIRECT, CCCR: Card Common Control Registers WiFi_Write(0, SDIO_CCCR_BUSIFCTRL, WiFi_Read(0, SDIO_CCCR_BUSIFCTRL) | SDIO_CCCR_BUSIFCTRL_BUSWID_4Bit); // Bus Width: 4-bit bus // 初始化Function 1 WiFi_Write(0, SDIO_CCCR_IOEN, SDIO_CCCR_IOEN_IOE1); // IOE1=1 (Enable Function) while ((WiFi_Read(0, SDIO_CCCR_IORDY) & SDIO_CCCR_IORDY_IOR1) == 0); // 等到IOR1=1 (I/O Function Ready) WiFi_Write(0, SDIO_CCCR_INTEN, SDIO_CCCR_INTEN_IENM | SDIO_CCCR_INTEN_IEN1); // 打开SDIO中断请求 WiFi_Write(1, WIFI_INTMASK, WIFI_INTMASK_HOSTINTMASK); // 利用中断标志位来判定是否有数据要读取, 可靠性更高 // 显示CIS信息(SDIO卡信息) WiFi_ShowCIS(0); WiFi_ShowCIS(1); #ifdef WIFI_USEDMA // 必须在DPSM禁用的时候开关DMA请求 SDIO_DMACmd(ENABLE); #endif // 下载固件 if (!WiFi_DownloadFirmware()) while (1); // 设置数据块大小为256字节 sdio_data.SDIO_DataBlockSize = SDIO_DataBlockSize_256b; WiFi_SetBlockSize(0); WiFi_SetBlockSize(1); // 允许发送和接收 // 不使用带有LLC子层的802.2SNAP帧格式, 这样LLC和SNAP这8个字节的内容就不会夹在数据链路层的源地址字段与数据字段之间 WiFi_MACControl(wifi_macctrl); } /* 加入Ad-Hoc网络 */ uint16_t WiFi_JoinADHOC(const char *ssid) { uint8_t buffer[2048]; WiFi_SSIDInfo info; WiFi_CmdRequest_ADHOCJoin *cmd = (WiFi_CmdRequest_ADHOCJoin *)buffer; if (!WiFi_ScanSSID(ssid, &info, buffer, sizeof(buffer))) { printf("Cannot find AP: %s!\n", ssid); return WIFI_ASSOCIATION_NOTFOUND; } memcpy(cmd->bssid, info.mac_addr, sizeof(cmd->bssid)); memset(cmd->ssid, 0, sizeof(cmd->ssid)); strncpy((char *)cmd->ssid, ssid, sizeof(cmd->ssid)); cmd->bss_type = BSS_ANY; cmd->bcn_period = info.bcn_period; cmd->dtim_period = 1; memset(cmd->timestamp, 0, sizeof(cmd->timestamp) + sizeof(cmd->start_ts)); cmd->ds_param_set.header.type = MRVLIETYPES_DSPARAMSET; cmd->ds_param_set.header.length = TLV_PAYLOADLEN(cmd->ds_param_set); cmd->ds_param_set.channel = info.channel; cmd->reserved1 = 0; cmd->ibss_param_set.header.type = MRVLIETYPES_IBSSPARAMSET; cmd->ibss_param_set.header.length = TLV_PAYLOADLEN(cmd->ibss_param_set); cmd->ibss_param_set.atim_window = 0; cmd->reserved2 = 0; cmd->cap_info = info.cap_info; memcpy(cmd->data_rates, info.rates.rates, sizeof(cmd->data_rates)); cmd->reserved3 = 0; WiFi_SendCommand(CMD_802_11_AD_HOC_JOIN, buffer, sizeof(WiFi_CmdRequest_ADHOCJoin), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) return WIFI_ASSOCIATION_ERROR; return cmd->header.result; } /* 获取或设置密钥 */ uint16_t WiFi_KeyMaterial(MrvlIETypes_KeyParamSet_t *keys, uint16_t size, uint8_t action) { uint8_t buffer[WIFI_DEFBUFSIZE]; uint8_t ret_size; WiFi_Cmd_KeyMaterial *cmd = (WiFi_Cmd_KeyMaterial *)buffer; cmd->action = action; if (action == WIFI_ACT_SET) { memcpy(cmd + 1, keys, size); WiFi_SendCommand(CMD_802_11_KEY_MATERIAL, buffer, sizeof(WiFi_Cmd_KeyMaterial) + size, sizeof(buffer)); } else WiFi_SendCommand(CMD_802_11_KEY_MATERIAL, buffer, sizeof(WiFi_Cmd_KeyMaterial), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) return (action == WIFI_ACT_GET) ? 0 : CMD_STATUS_ERROR; if (action == WIFI_ACT_GET) { ret_size = cmd->header.size - sizeof(cmd->header) - sizeof(cmd->action); if (ret_size <= size) memcpy(keys, cmd + 1, ret_size); else printf("WiFi_KeyMaterial: Buffer size is too small! %d bytes required!\n", ret_size); return ret_size; // action=get时返回读取的数据大小 } else return cmd->header.result; // action=set时返回命令执行结果值 } /* 获取或设置MAC地址 */ uint16_t WiFi_MACAddr(uint8_t addr[6], uint8_t action) { uint8_t buffer[WIFI_DEFBUFSIZE]; WiFi_Cmd_MACAddr *cmd = (WiFi_Cmd_MACAddr *)buffer; cmd->action = action; if (action == WIFI_ACT_SET) memcpy(cmd->mac_addr, addr, 6); WiFi_SendCommand(CMD_802_11_MAC_ADDR, buffer, sizeof(WiFi_Cmd_MACAddr), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) return CMD_STATUS_ERROR; if (action == WIFI_ACT_GET) memcpy(addr, cmd->mac_addr, 6); return cmd->header.result; } /* 配置MAC */ uint16_t WiFi_MACControl(uint16_t action) { uint8_t buffer[WIFI_DEFBUFSIZE]; WiFi_Cmd_MACCtrl *cmd = (WiFi_Cmd_MACCtrl *)buffer; cmd->action = action; cmd->reserved = 0; WiFi_SendCommand(CMD_MAC_CONTROL, buffer, sizeof(WiFi_Cmd_MACCtrl), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) return CMD_STATUS_ERROR; return cmd->header.result; } /* 判断是否有新数据包到来 */ uint8_t WiFi_PacketArrived(void) { //return (WiFi_Read(1, WIFI_CARDSTATUS) & WIFI_CARDSTATUS_UPLDCARDRDY) != 0; // 可靠性差, 标志位会被CMD53命令清除, 不建议使用 return (WiFi_Read(1, WIFI_INTSTATUS) & WIFI_INTSTATUS_UPLD) != 0; // 可靠性高, 标志位需要手动清除, 必须打开中断后才能使用 } /* 读寄存器 */ uint8_t WiFi_Read(uint8_t func, uint32_t addr) { WiFi_SendCMD52(func, addr, NULL, NULL); if (SDIO_GetFlagStatus(SDIO_FLAG_CMDREND) == SET) { SDIO_ClearFlag(SDIO_FLAG_CMDREND); return SDIO_GetResponse(SDIO_RESP1) & 0xff; } else { printf("WiFi_Read failed, SDIO->STA=0x%08x!\n", SDIO->STA); return 0; } } /* 接收数据 */ // count为要接收的字节数或块数, bufsize为data缓冲区的大小 // bufsize=0时, 只读取数据不存入缓冲区 uint8_t WiFi_ReadData(uint8_t func, uint32_t addr, void *data, uint16_t count, uint16_t bufsize, uint32_t flags) { uint32_t len; // 实际要接收的字节数 #ifdef WIFI_USEDMA uint32_t temp; // 丢弃数据用的变量 DMA_InitTypeDef dma; #else uint32_t *p = data; #endif if (flags & CMD53_BLOCKMODE) { // 块传输模式 (DTMODE=0) sdio_data.SDIO_TransferMode = SDIO_TransferMode_Block; len = count * WiFi_GetSDIOBlockSize(); // 块数*块大小 } else { // 多字节传输模式 (DTMODE=1, SDIO的频率低于16MHz时才支持) sdio_data.SDIO_TransferMode = SDIO_TransferMode_Stream; len = count; if (len % 4 != 0) { len += 4 - len % 4; // WiFi模块要求字节数必须为4的整数倍 count = len; // 传入CMD53的数据大小参数也应该是4的倍数 } } if (bufsize > 0 && bufsize < len) { printf("WiFi_ReadData: a buffer of at least %d bytes is required! bufsize=%d\n", len, bufsize); return 0; } #ifdef WIFI_USEDMA dma.DMA_BufferSize = len / 4; dma.DMA_DIR = DMA_DIR_PeripheralSRC; dma.DMA_M2M = DMA_M2M_Disable; dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; dma.DMA_Mode = DMA_Mode_Normal; dma.DMA_PeripheralBaseAddr = (uint32_t)&SDIO->FIFO; dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable; dma.DMA_Priority = DMA_Priority_VeryHigh; if (bufsize > 0) { dma.DMA_MemoryBaseAddr = (uint32_t)data; dma.DMA_MemoryInc = DMA_MemoryInc_Enable; } else { dma.DMA_MemoryBaseAddr = (uint32_t)&temp; // 数据丢弃模式 dma.DMA_MemoryInc = DMA_MemoryInc_Disable; } DMA_Init(DMA2_Channel4, &dma); DMA_Cmd(DMA2_Channel4, ENABLE); #endif sdio_data.SDIO_DataLength = len; sdio_data.SDIO_DPSM = SDIO_DPSM_Enable; sdio_data.SDIO_TransferDir = SDIO_TransferDir_ToSDIO; SDIO_DataConfig(&sdio_data); WiFi_SendCMD53(func, addr, count, flags); #ifdef WIFI_USEDMA while (DMA_GetFlagStatus(DMA2_FLAG_TC4) == RESET); DMA_ClearFlag(DMA2_FLAG_TC4); DMA_Cmd(DMA2_Channel4, DISABLE); #else while (len) { // 如果有数据到来就读取数据 if (SDIO_GetFlagStatus(SDIO_FLAG_RXDAVL) == SET) { len -= 4; if (bufsize > 0) *p++ = SDIO_ReadData(); else SDIO_ReadData(); // 读寄存器, 但不保存数据 } if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) == SET) { printf("Data timeout!\n"); break; } else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) == SET) { printf("Data CRC check failed! %d bytes are lost\n", len); break; } } #endif while (SDIO_GetFlagStatus(SDIO_FLAG_CMDACT) == SET || SDIO_GetFlagStatus(SDIO_FLAG_RXACT) == SET); SDIO_ClearFlag(SDIO_FLAG_CMDREND | SDIO_FLAG_DATAEND); if (flags & CMD53_BLOCKMODE) SDIO_ClearFlag(SDIO_FLAG_DBCKEND); return SDIO_SUCCEEDED(); } uint8_t WiFi_ReadPort(void *data, uint16_t size, uint16_t bufsize) { uint16_t block_num, block_size; block_size = WiFi_GetSDIOBlockSize(); WiFi_Wait(0); // 发送CMD53前必须IOReady=1 WiFi_Write(1, WIFI_INTSTATUS, WiFi_Read(1, WIFI_INTSTATUS) & ~WIFI_INTSTATUS_UPLD); // 读端口前先清除UPLD中断标志位 #ifndef WIFI_HIGHSPEED if (size > 512 || size % block_size == 0) // 若size为数据块大小的整数倍, 则采用数据块方式接收 { #endif block_num = size / block_size; if (size % block_size != 0) block_num++; // 大于512字节时不能采用字节流方式接收(如712字节), 仍采用数据块方式 return WiFi_ReadData(1, io_addr, data, block_num, bufsize, CMD53_BLOCKMODE); #ifndef WIFI_HIGHSPEED } else return WiFi_ReadData(1, io_addr, data, size, bufsize, 0); // 否则采用字节流方式接收 #endif } /* 接收数据帧, 返回是否成功 */ uint8_t WiFi_ReceivePacket(void *buf, uint16_t bufsize) { uint8_t ret; uint16_t size = WiFi_GetPacketLength(); WiFi_SDIOFrameHeader *hdr = (WiFi_SDIOFrameHeader *)buf; ret = WiFi_ReadPort(buf, size, bufsize); if (ret && hdr->type != WIFI_SDIOFRAME_DATA) { if (WIFI_SDIOFRAME_EVENT) WiFi_EventHandler(buf); ret = 0; } return ret; } /* 接收WiFi命令的回应 */ uint8_t WiFi_ReceiveResponse(void *buf, uint16_t bufsize) { uint8_t ret; uint8_t wait = 0; uint16_t size; WiFi_CommandHeader *cmd = (WiFi_CommandHeader *)buf; // 等待数据准备好 // 固件下载完后发送的第一个WiFi命令偶尔会收不到回应 // 为了保证系统的可靠性, 命令的超时重传非常重要 while (!WiFi_Wait(WIFI_CARDSTATUS_UPLDCARDRDY)) { // 若WiFi_Wait返回0, 则说明超时 wait++; if (wait >= 5) { printf("No response!\n"); return 0; } if (cmd->frame_header.type == WIFI_SDIOFRAME_COMMAND) { // 若超时后还没收到数据, 则重发命令, 然后再次执行WiFi_Wait printf("Resend WiFi command 0x%04x! size=%d\n", cmd->cmd_code, cmd->frame_header.length); WiFi_ResendCommand(buf, bufsize); } else return 0; // 若buf中的内容不是命令, 则不重发直接退出 } size = WiFi_GetPacketLength(); ret = WiFi_ReadPort(buf, size, bufsize); if (ret && cmd->frame_header.type != WIFI_SDIOFRAME_COMMAND) { ret = 0; // buf中的内容已被接收到的数据覆盖, 无法重发命令 if (cmd->frame_header.type == WIFI_SDIOFRAME_EVENT) WiFi_EventHandler(buf); } return ret; } /* 扫描全部热点 (仅显示) */ void WiFi_Scan(void) { // 必须把STM32启动文件*.s中的Stack_Size改大, 否则函数中无法创建大数组 uint8_t buffer[2048]; // 用于接收返回的WiFi接入点信息, 需要较大的内存空间来存放 uint8_t i, j, n; uint8_t ssid[33], channel, wpa; uint16_t ie_size; WiFi_CmdRequest_Scan *cmd = (WiFi_CmdRequest_Scan *)buffer; // 用buffer空间来存放要发送的命令 MrvlIETypes_ChanListParamSet_t *chanlist = (MrvlIETypes_ChanListParamSet_t *)(cmd + 1); // 这里的+1指的是前进sizeof(指针类型)个地址单元, 而非只前进1个地址单元 WiFi_CmdResponse_Scan *resp = (WiFi_CmdResponse_Scan *)buffer; WiFi_BssDescSet *bss_desc_set; WiFi_Vendor *vendor; IEEEType *ie_params; //MrvlIETypes_TsfTimestamp_t *tft_table; // 分4次扫描14个通道 for (i = 0; i < 4; i++) { cmd->bss_type = BSS_ANY; memset(cmd->bss_id, 0, sizeof(cmd->bss_id)); // 通道的基本参数 n = (i == 3) ? 2 : 4; // 本次要扫描的通道数 chanlist->header.type = MRVLIETYPES_CHANLISTPARAMSET; chanlist->header.length = n * sizeof(chanlist->channels); for (j = 0; j < n; j++) { chanlist->channels[j].band_config_type = 0; chanlist->channels[j].chan_number = 4 * i + j + 1; // 通道号 chanlist->channels[j].scan_type = 0; chanlist->channels[j].min_scan_time = 0; chanlist->channels[j].max_scan_time = 100; } // 发送命令并接收数据 WiFi_SendCommand(CMD_802_11_SCAN, buffer, sizeof(WiFi_CmdRequest_Scan) + TLV_STRUCTLEN(*chanlist), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) // 接收的数据会将cmd和chanlist中的内容覆盖掉 { printf("WiFi_Scan: no response!\n"); continue; } // 显示热点信息, num_of_set为热点数 if (resp->num_of_set > 0) { bss_desc_set = (WiFi_BssDescSet *)(resp + 1); for (j = 0; j < resp->num_of_set; j++) { wpa = 0; ie_params = &bss_desc_set->ie_parameters; ie_size = bss_desc_set->ie_length - (sizeof(WiFi_BssDescSet) - sizeof(bss_desc_set->ie_length) - sizeof(bss_desc_set->ie_parameters)); while (ie_size > 0) { switch (ie_params->header.type) { case MRVLIETYPES_SSIDPARAMSET: // SSID名称 memcpy(ssid, ie_params->data, ie_params->header.length); ssid[ie_params->header.length] = '\0'; break; case MRVLIETYPES_DSPARAMSET: // 通道号 channel = ie_params->data[0]; break; case MRVLIETYPES_RSNPARAMSET: wpa = 2; // 表示WPA版本号(0表示没有使用WPA) break; case MRVLIETYPES_VENDORPARAMSET: if (wpa == 0) { vendor = (WiFi_Vendor *)ie_params->data; if (vendor->oui[0] == 0x00 && vendor->oui[1] == 0x50 && vendor->oui[2] == 0xf2 && vendor->oui_type == 0x01) wpa = 1; } break; } ie_size -= TLV_STRUCTLEN(*ie_params); ie_params = (IEEEType *)TLV_NEXT(ie_params); } if (ie_size != 0) printf("ie_parameters error!\n"); printf("SSID '%s', ", ssid); // 热点名称 printf("MAC %02X:%02X:%02X:%02X:%02X:%02X, ", bss_desc_set->bssid[0], bss_desc_set->bssid[1], bss_desc_set->bssid[2], bss_desc_set->bssid[3], bss_desc_set->bssid[4], bss_desc_set->bssid[5]); // MAC地址 printf("RSSI %d, Channel %d\n", bss_desc_set->rssi, channel); // 信号强度和通道号 //printf(" Timestamp %lld, Beacon interval %d\n", bss_desc_set->pkt_time_stamp, bss_desc_set->bcn_interval); printf(" Capability: 0x%04x (Security: ", bss_desc_set->cap_info); if (bss_desc_set->cap_info & WIFI_CAPABILITY_PRIVACY) { if (wpa == 1) printf("WPA"); else if (wpa == 2) printf("WPA2"); else printf("WEP"); } else printf("unsecured"); printf(", Mode: "); if (bss_desc_set->cap_info & WIFI_CAPABILITY_IBSS) printf("Ad-Hoc"); else printf("Infrastructure"); printf(")\n"); // 转向下一个热点信息 bss_desc_set = (WiFi_BssDescSet *)((uint8_t *)bss_desc_set + sizeof(bss_desc_set->ie_length) + bss_desc_set->ie_length); } // resp->buf_size就是bss_desc_set的总大小 // 因此tft_table == buffer + sizeof(WiFi_CmdResponse_Scan) + resp->buf_size /* tft_table = (MrvlIETypes_TsfTimestamp_t *)bss_desc_set; if (tft_table->header.type == MRVLIETYPES_TSFTIMESTAMP && tft_table->header.length == resp->num_of_set * sizeof(uint64_t)) { printf("Timestamps: "); for (j = 0; j < resp->num_of_set; j++) printf("%lld ", tft_table->tsf_table[j]); printf("\n"); } */ // TSF timestamp table是整个数据的末尾, 后面没有Channel/band table //if (((uint8_t *)tft_table - buffer) + TLV_STRUCTLEN(*tft_table) == cmd->header.frame_header.length) // printf("data end!\n"); } } } /* 获取指定名称的热点的信息 */ // buffer用来存放返回的全部结果, 因此bufsize应该足够大 // info用来存放从buffer中提取出来的一些常用信息 uint8_t WiFi_ScanSSID(const char *ssid, WiFi_SSIDInfo *info, uint8_t *buffer, uint16_t bufsize) { uint8_t i, ret; uint16_t ie_size; WiFi_CmdRequest_Scan *cmd = (WiFi_CmdRequest_Scan *)buffer; WiFi_CmdResponse_Scan *resp = (WiFi_CmdResponse_Scan *)buffer; WiFi_BssDescSet *bss_desc_set = (WiFi_BssDescSet *)(resp + 1); MrvlIETypes_ChanListParamSet_t *chan_list; IEEEType *ie_params; WiFi_Vendor *vendor; cmd->bss_type = BSS_ANY; memset(cmd->bss_id, 0, sizeof(cmd->bss_id)); // 添加ssid参数 info->ssid.header.type = MRVLIETYPES_SSIDPARAMSET; info->ssid.header.length = strlen(ssid); memcpy(info->ssid.ssid, ssid, info->ssid.header.length); memcpy(cmd + 1, &info->ssid, TLV_STRUCTLEN(info->ssid)); chan_list = (MrvlIETypes_ChanListParamSet_t *)((uint8_t *)(cmd + 1) + TLV_STRUCTLEN(info->ssid)); chan_list->header.type = MRVLIETYPES_CHANLISTPARAMSET; chan_list->header.length = 14 * sizeof(chan_list->channels); // 一次性扫描14个通道 for (i = 0; i < 14; i++) { chan_list->channels[i].band_config_type = 0; chan_list->channels[i].chan_number = i + 1; chan_list->channels[i].scan_type = 0; chan_list->channels[i].min_scan_time = 0; chan_list->channels[i].max_scan_time = 100; } WiFi_SendCommand(CMD_802_11_SCAN, buffer, ((uint8_t *)chan_list - buffer) + TLV_STRUCTLEN(*chan_list), bufsize); wifi_timeout = 3000; // 延长超时时间 ret = WiFi_ReceiveResponse(buffer, bufsize); wifi_timeout = WIFI_DEFTIMEOUT; if (!ret || resp->num_of_set == 0) return 0; // 失败 // bss_desc_set以扫描到的第一个信息项为准 memcpy(info->mac_addr, bss_desc_set->bssid, sizeof(info->mac_addr)); info->cap_info = bss_desc_set->cap_info; info->bcn_period = bss_desc_set->bcn_interval; // 若type=0, 则表明没有该项的信息 (除SSID结构体外, 因为SSID的type=MRVLIETYPES_SSIDPARAMSET=0) info->rates.header.type = 0; info->rsn.header.type = 0; info->wpa.header.type = 0; info->wwm.header.type = 0; info->wps.header.type = 0; ie_params = &bss_desc_set->ie_parameters; ie_size = bss_desc_set->ie_length - (sizeof(WiFi_BssDescSet) - sizeof(bss_desc_set->ie_length) - sizeof(bss_desc_set->ie_parameters)); // 所有IEEE_Type数据的总大小 while (ie_size > 0) { switch (ie_params->header.type) { case MRVLIETYPES_RATESPARAMSET: // 速率 WiFi_TranslateTLV((MrvlIEType *)&info->rates, ie_params, sizeof(info->rates.rates)); break; case MRVLIETYPES_DSPARAMSET: // 通道号 info->channel = ie_params->data[0]; break; case MRVLIETYPES_RSNPARAMSET: // 通常只有一个RSN信息 (与WPA2相关) // printf("RSN len=%d\n", ie_params->length); WiFi_TranslateTLV((MrvlIEType *)&info->rsn, ie_params, sizeof(info->rsn.rsn)); break; case MRVLIETYPES_VENDORPARAMSET: // 通常会有多项VENDOR信息 (与WPA相关) vendor = (WiFi_Vendor *)ie_params->data; if (vendor->oui[0] == 0x00 && vendor->oui[1] == 0x50 && vendor->oui[2] == 0xf2) { switch (vendor->oui_type) { case 0x01: // wpa_oui WiFi_TranslateTLV((MrvlIEType *)&info->wpa, ie_params, sizeof(info->wpa.vendor)); break; case 0x02: // wmm_oui if (ie_params->header.length == 24) // 合法大小 WiFi_TranslateTLV((MrvlIEType *)&info->wwm, ie_params, sizeof(info->wwm.vendor)); break; case 0x04: // wps_oui WiFi_TranslateTLV((MrvlIEType *)&info->wps, ie_params, sizeof(info->wps.vendor)); break; } } break; } // 转向下一个TLV ie_size -= TLV_STRUCTLEN(*ie_params); ie_params = (IEEEType *)TLV_NEXT(ie_params); } return 1; // 成功 } void WiFi_SendCMD52(uint8_t func, uint32_t addr, uint8_t data, uint32_t flags) { sdio_cmd.SDIO_Argument = (func << 28) | (addr << 9) | data | flags; sdio_cmd.SDIO_CmdIndex = 52; SDIO_SendCommand(&sdio_cmd); while (SDIO_GetFlagStatus(SDIO_FLAG_CMDACT) == SET); } void WiFi_SendCMD53(uint8_t func, uint32_t addr, uint16_t count, uint32_t flags) { // 当count=512时, 和0x1ff相与后为0, 符合要求 sdio_cmd.SDIO_Argument = (func << 28) | (addr << 9) | (count & 0x1ff) | flags; sdio_cmd.SDIO_CmdIndex = 53; SDIO_SendCommand(&sdio_cmd); } /* 发送WiFi命令 */ uint8_t WiFi_SendCommand(uint16_t com_code, void *data, uint16_t size, uint16_t bufsize) { static uint16_t seq_num = 0; WiFi_CommandHeader *cmdhdr = (WiFi_CommandHeader *)data; if (size != 0) { cmdhdr->frame_header.length = size; cmdhdr->frame_header.type = WIFI_SDIOFRAME_COMMAND; cmdhdr->cmd_code = com_code; cmdhdr->size = size - sizeof(WiFi_SDIOFrameHeader); // 命令大小包括命令头部, 但不包括SDIO帧头部 cmdhdr->seq_num = seq_num++; cmdhdr->result = 0; } else size = cmdhdr->frame_header.length; // 重发命令时不填写cmdhdr return WiFi_WritePort(data, size, bufsize); } /* 发送数据帧 */ uint8_t WiFi_SendPacket(WiFi_DataTx *packet, uint16_t packet_len, uint16_t bufsize) { uint8_t ret; // 有关发送数据包的细节, 请参考Firmware Specification PDF的Chapter 3: Data Path packet->header.length = sizeof(WiFi_DataTx) - sizeof(packet->payload) + packet_len; packet->header.type = WIFI_SDIOFRAME_DATA; packet->reserved1 = 0; packet->tx_control = 0; // 控制信息的格式请参考3.2.1 Per-Packet Settings packet->tx_packet_offset = sizeof(WiFi_DataTx) - sizeof(packet->payload) - sizeof(packet->header); // 不包括SDIOFrameHeader packet->tx_packet_length = packet_len; memcpy((void *)&packet->tx_dest_addr_high, packet->payload, 6); packet->priority = 0; packet->flags = 0; packet->pkt_delay_2ms = 0; packet->reserved2 = 0; ret = WiFi_WritePort(packet, packet->header.length, bufsize); if (ret) ret &= WiFi_Wait(WIFI_CARDSTATUS_DNLDCARDRDY); // WiFi模块收到数据后, 会将该位置1 return ret; } /* 将SDIO->DCTRL中的块大小信息应用到WiFi模块指定的功能区上 */ void WiFi_SetBlockSize(uint8_t func) { // Part E1: 6.9 Card Common Control Registers (CCCR), 6.10 Function Basic Registers (FBR) uint16_t size = WiFi_GetSDIOBlockSize(); WiFi_Write(0, (func << 8) | 0x10, size & 0xff); WiFi_Write(0, (func << 8) | 0x11, size >> 8); } /* 设置密钥 */ uint8_t WiFi_SetKeyMaterial(uint16_t type, uint16_t info, const uint8_t *key, uint16_t len) { MrvlIETypes_KeyParamSet_t key_param; if (len > sizeof(key_param.key)) return CMD_STATUS_ERROR; key_param.header.type = MRVLIETYPES_KEYPARAMSET; key_param.header.length = (key_param.key - (uint8_t *)&key_param) - sizeof(key_param.header) + len; key_param.key_type_id = type; key_param.key_info = info; key_param.key_len = len; // 当len=0时可保留key的值, 只更新key_info if (len) memcpy(key_param.key, key, len); return WiFi_KeyMaterial(&key_param, TLV_STRUCTLEN(key_param), WIFI_ACT_SET); } /* 设置WEP密钥 (长度必须为5或13个字符) */ // action: WIFI_ACT_ADD / WIFI_ACT_REMOVE uint16_t WiFi_SetWEP(uint8_t action, const char *key) { uint8_t buffer[WIFI_DEFBUFSIZE]; uint8_t i, len; uint16_t ret; uint32_t temp; WiFi_Cmd_SetWEP *cmd = (WiFi_Cmd_SetWEP *)buffer; cmd->action = action; cmd->tx_key_index = 0; memset(cmd->wep_types, 0, sizeof(cmd->wep_types) + sizeof(cmd->keys)); if (action == WIFI_ACT_ADD) { len = strlen(key); if (len == 5 || len == 13) { // 5个或13个ASCII密钥字符 if (len == 5) cmd->wep_types[0] = WIFI_WEPKEYTYPE_40BIT; else if (len == 13) cmd->wep_types[0] = WIFI_WEPKEYTYPE_104BIT; memcpy(cmd->keys[0], key, len); } else if (len == 10 || len == 26) { // 10个或26个16进制密钥字符 if (len == 10) cmd->wep_types[0] = WIFI_WEPKEYTYPE_40BIT; else if (len == 26) cmd->wep_types[0] = WIFI_WEPKEYTYPE_104BIT; for (i = 0; i < len; i++) { if (!(key[i] >= '0' && key[i] <= '9') && !(key[i] >= 'a' && key[i] <= 'f') && !(key[i] >= 'A' && key[i] <= 'F')) { printf("WiFi_SetWEP: The hex key contains invalid characters!\n"); return 0xffff; } if (i % 2 == 0) { sscanf(key + i, "%02x", &temp); cmd->keys[0][i / 2] = temp; } } } else { printf("WiFi_SetWEP: The key length is invalid!\n"); return 0xffff; } wifi_macctrl |= WIFI_MACCTRL_WEP; } else wifi_macctrl &= ~WIFI_MACCTRL_WEP; ret = WiFi_MACControl(wifi_macctrl); if (ret != CMD_STATUS_SUCCESS) return ret; WiFi_SendCommand(CMD_802_11_SET_WEP, buffer, sizeof(WiFi_Cmd_SetWEP), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) return CMD_STATUS_ERROR; return cmd->header.result; } /* 显示WiFi模块信息 */ void WiFi_ShowCIS(uint8_t func) { uint8_t data[255]; uint8_t i, len; uint8_t tpl_code, tpl_link; // 16.2 Basic Tuple Format and Tuple Chain Structure uint32_t cis_ptr; printf("-------------- CIS of Function %d ----------------\n", func); // 获取CIS的地址 cis_ptr = (func << 8) | 0x9; cis_ptr = WiFi_Read(0, cis_ptr) | (WiFi_Read(0, cis_ptr + 1) << 8) | (WiFi_Read(0, cis_ptr + 2) << 16); printf("Pointer to Function %d Card Information Structure (CIS): 0x%08x\n", func, cis_ptr); // 遍历CIS, 直到尾节点 while ((tpl_code = WiFi_Read(0, cis_ptr++)) != CISTPL_END) { if (tpl_code == CISTPL_NULL) continue; tpl_link = WiFi_Read(0, cis_ptr++); // 本结点数据的大小 for (i = 0; i < tpl_link; i++) data[i] = WiFi_Read(0, cis_ptr + i); printf("[CIS Tuple 0x%02x] addr=0x%08x size=%d\n", tpl_code, cis_ptr - 2, tpl_link); dump_data(data, tpl_link); switch (tpl_code) { case CISTPL_VERS_1: i = 2; while (data[i] != 0xff) { len = strlen((char *)&data[i]); if (len != 0) printf("%s\n", data + i); i += len + 1; } break; case CISTPL_MANFID: // 16.6 CISTPL_MANFID: Manufacturer Identification String Tuple printf("SDIO Card manufacturer code: 0x%04x\n", *(uint16_t *)data); // TPLMID_MANF printf("manufacturer information (Part Number and/or Revision): 0x%04x\n", *(uint16_t *)(data + 2)); // TPLMID_CARD break; case CISTPL_FUNCID: // 16.7.1 CISTPL_FUNCID: Function Identification Tuple printf("Card function code: 0x%02x\n", data[0]); // TPLFID_FUNCTION printf("System initialization bit mask: 0x%02x\n", data[1]); // TPLFID_SYSINIT break; case CISTPL_FUNCE: // 16.7.2 CISTPL_FUNCE: Function Extension Tuple if (data[0] == 0) { // 16.7.3 CISTPL_FUNCE Tuple for Function 0 (Extended Data 00h) printf("maximum block size: %d\n", *(uint16_t *)(data + 1)); printf("maximum transfer rate code: 0x%02x\n", data[3]); } else { // 16.7.4 CISTPL_FUNCE Tuple for Function 1-7 (Extended Data 01h) printf("maximum block size: %d\n", *(uint16_t *)(data + 0x0c)); // TPLFE_MAX_BLK_SIZE } } cis_ptr += tpl_link; if (tpl_link == 0xff) break; // 当TPL_LINK为0xff时说明当前结点为尾节点 } printf("--------------------------------------------------\n"); } /* 显示所有密钥 */ void WiFi_ShowKeyMaterials(void) { uint8_t buffer[WIFI_DEFBUFSIZE]; uint16_t size; MrvlIETypes_KeyParamSet_t *key = (MrvlIETypes_KeyParamSet_t *)buffer; size = WiFi_KeyMaterial(key, sizeof(buffer), WIFI_ACT_GET); while (size) { printf("Type %d, Info 0x%04x, Len %d\n", key->key_type_id, key->key_info, key->key_len); dump_data(key->key, key->key_len); size -= TLV_STRUCTLEN(*key); key = (MrvlIETypes_KeyParamSet_t *)((uint8_t *)key + TLV_STRUCTLEN(*key)); } if (size == 0) printf("data end!\n"); } /* 创建一个Ad-Hoc型的WiFi热点 */ uint8_t WiFi_StartADHOC(const char *ssid) { uint8_t buffer[WIFI_DEFBUFSIZE] = {0}; WiFi_CmdRequest_ADHOCStart *cmd = (WiFi_CmdRequest_ADHOCStart *)buffer; strncpy((char *)cmd->ssid, ssid, sizeof(cmd->ssid)); cmd->bss_type = BSS_INDEPENDENT; cmd->bcn_period = 100; cmd->ibss_param_set.header.type = MRVLIETYPES_IBSSPARAMSET; cmd->ibss_param_set.header.length = TLV_PAYLOADLEN(cmd->ibss_param_set); cmd->ibss_param_set.atim_window = 0; cmd->ds_param_set.header.type = MRVLIETYPES_DSPARAMSET; cmd->ds_param_set.header.length = TLV_PAYLOADLEN(cmd->ds_param_set); cmd->ds_param_set.channel = 1; cmd->cap_info = WIFI_CAPABILITY_IBSS; if (wifi_macctrl & WIFI_MACCTRL_WEP) cmd->cap_info |= WIFI_CAPABILITY_PRIVACY; *(uint32_t *)cmd->data_rate = 0x968b8482; WiFi_SendCommand(CMD_802_11_AD_HOC_START, buffer, sizeof(WiFi_CmdRequest_ADHOCStart), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) return CMD_STATUS_ERROR; return cmd->header.result; } /* 设置WiFi事件 */ uint16_t WiFi_SubscribeEvent(uint16_t action, uint16_t events) { uint8_t buffer[WIFI_DEFBUFSIZE]; WiFi_Cmd_SubscribeEvent *cmd = (WiFi_Cmd_SubscribeEvent *)buffer; cmd->action = action; if (action == WIFI_ACT_GET) cmd->events = 0; else cmd->events = events; WiFi_SendCommand(CMD_802_11_SUBSCRIBE_EVENT, buffer, sizeof(WiFi_Cmd_SubscribeEvent), sizeof(buffer)); if (!WiFi_ReceiveResponse(buffer, sizeof(buffer))) return CMD_STATUS_ERROR; return cmd->events; } /* 将IEEE型的TLV转换成MrvlIE型的TLV */ uint8_t WiFi_TranslateTLV(MrvlIEType *mrvlie_tlv, const IEEEType *ieee_tlv, uint16_t mrvlie_payload_size) { mrvlie_tlv->header.type = ieee_tlv->header.type; if (ieee_tlv->header.length > mrvlie_payload_size) mrvlie_tlv->header.length = mrvlie_payload_size; // 若源数据大小超过缓冲区最大容量, 则丢弃剩余数据 else mrvlie_tlv->header.length = ieee_tlv->header.length; memcpy(mrvlie_tlv->data, ieee_tlv->data, mrvlie_tlv->header.length); // 复制数据 return mrvlie_tlv->header.length == ieee_tlv->header.length; // 返回值表示缓冲区大小是否足够 } /* 在规定的超时时间内, 等待指定的卡状态位置位(自动包括IO Ready位), 若成功则返回1 */ uint8_t WiFi_Wait(uint8_t status) { status |= WIFI_CARDSTATUS_IOREADY; timeout(wifi_timeout); while ((WiFi_Read(1, WIFI_CARDSTATUS) & status) != status) { if (TIM_GetFlagStatus(TIM6, TIM_FLAG_Update) == SET) { // 若超时时间已到 TIM_ClearFlag(TIM6, TIM_FLAG_Update); printf("WiFi_Wait(0x%02x): timeout!\n", status); return 0; } } TIM_Cmd(TIM6, DISABLE); // 关闭定时器 return 1; } /* 写寄存器, 返回写入后寄存器的实际内容 */ uint8_t WiFi_Write(uint8_t func, uint32_t addr, uint8_t value) { WiFi_SendCMD52(func, addr, value, CMD52_WRITE | CMD52_READAFTERWRITE); if (SDIO_GetFlagStatus(SDIO_FLAG_CMDREND) == SET) { SDIO_ClearFlag(SDIO_FLAG_CMDREND); return SDIO_GetResponse(SDIO_RESP1) & 0xff; } else { printf("WiFi_Write failed, SDIO->STA=0x%08x!\n", SDIO->STA); return 0; } } /* 发送数据 */ // count为要发送的字节数或块数, bufsize为data缓冲区的大小 uint8_t WiFi_WriteData(uint8_t func, uint32_t addr, const void *data, uint16_t count, uint32_t bufsize, uint32_t flags) { uint32_t len; // 实际要发送的字节数 #ifdef WIFI_USEDMA DMA_InitTypeDef dma; #else const uint32_t *p = data; #endif if (flags & CMD53_BLOCKMODE) { // 块传输模式 (DTMODE=0) sdio_data.SDIO_TransferMode = SDIO_TransferMode_Block; len = count * WiFi_GetSDIOBlockSize(); // 块数*块大小 } else { // 多字节传输模式 (DTMODE=1, SDIO的频率低于16MHz时才支持) sdio_data.SDIO_TransferMode = SDIO_TransferMode_Stream; len = count; if (len % 4 != 0) { len += 4 - len % 4; // WiFi模块要求写入的字节数必须为4的整数倍 count = len; // 传入CMD53的数据大小参数也应该是4的倍数 } } if (bufsize < len) printf("WiFi_WriteData: a buffer of at least %d bytes is required! bufsize=%d\n", len, bufsize); // 只读缓冲区越界不会影响数据传输, 所以这只是一个警告 WiFi_SendCMD53(func, addr, count, flags | CMD53_WRITE); while (SDIO_GetFlagStatus(SDIO_FLAG_CMDACT) == SET); if (SDIO_GetFlagStatus(SDIO_FLAG_CMDREND) == RESET) { printf("WiFi_WriteData: CMD53 no response!\n"); return 0; } SDIO_ClearFlag(SDIO_FLAG_CMDREND); // 开始发送数据 #ifdef WIFI_USEDMA dma.DMA_BufferSize = len / 4; dma.DMA_DIR = DMA_DIR_PeripheralDST; dma.DMA_M2M = DMA_M2M_Disable; dma.DMA_MemoryBaseAddr = (uint32_t)data; dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; dma.DMA_MemoryInc = DMA_MemoryInc_Enable; dma.DMA_Mode = DMA_Mode_Normal; dma.DMA_PeripheralBaseAddr = (uint32_t)&SDIO->FIFO; dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable; dma.DMA_Priority = DMA_Priority_VeryHigh; DMA_Init(DMA2_Channel4, &dma); DMA_Cmd(DMA2_Channel4, ENABLE); #endif sdio_data.SDIO_DataLength = len; sdio_data.SDIO_DPSM = SDIO_DPSM_Enable; sdio_data.SDIO_TransferDir = SDIO_TransferDir_ToCard; SDIO_DataConfig(&sdio_data); #ifdef WIFI_USEDMA while (DMA_GetFlagStatus(DMA2_FLAG_TC4) == RESET); DMA_ClearFlag(DMA2_FLAG_TC4); DMA_Cmd(DMA2_Channel4, DISABLE); #else while (len) { len -= 4; SDIO_WriteData(*p++); // 向FIFO送入4字节数据 while (SDIO_GetFlagStatus(SDIO_FLAG_TXFIFOF) == SET); // 如果FIFO已满则等待 } #endif while (SDIO_GetFlagStatus(SDIO_FLAG_TXACT) == SET); // 等待发送完毕 // 清除相关标志位 SDIO_ClearFlag(SDIO_FLAG_DATAEND); if (flags & CMD53_BLOCKMODE) SDIO_ClearFlag(SDIO_FLAG_DBCKEND); return SDIO_SUCCEEDED(); } /* 向WiFi缓冲区发送数据 */ uint8_t WiFi_WritePort(const void *data, uint16_t size, uint32_t bufsize) { uint16_t block_num, block_size; block_size = WiFi_GetSDIOBlockSize(); WiFi_Wait(0); // 发送CMD53前必须IOReady=1 #ifndef WIFI_HIGHSPEED if (size > 512 || size % block_size == 0) // 若size为数据块大小的整数倍, 则采用数据块方式发送 { #endif block_num = size / block_size; if (size % block_size != 0) block_num++; // 大于512字节时不能采用字节流方式发送(如712字节), 仍采用数据块方式 return WiFi_WriteData(1, io_addr, data, block_num, bufsize, CMD53_BLOCKMODE); #ifndef WIFI_HIGHSPEED } else return WiFi_WriteData(1, io_addr, data, size, bufsize, 0); // 否则采用字节流方式发送 #endif }