本程序所用的单片机型号为:STM32F103RE
PB12端口为外接的WiFi模块电源开关,当PB12输出低电平时接通电源。WiFi模块的电源引脚VCC不可直接连接到电源上,必须要串联一组PNP三极管(或场效应管),并把基极接到PB12端口上,基极要接限流电阻。
注意:WM-G-MR-09模块的芯片组(Chip Set)就是Marvell 88W8686。
该程序目前暂不支持WPA和WPA2加密方式!
88W8686 WiFi模块的固件(Firmware)内容:
【勘误】
2018年2月3日:while (WiFi_GetPacketLength() == 0xfedc);中的==应该改为!=
注意必须把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 <adam@sics.se>
*
*/
/*
* 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热点。
WiFi.h中的两个重要选项:
//#define WIFI_HIGHSPEED // 采用SDIO高速模式
//#define WIFI_USEDMA // SDIO采用DMA方式收发数据(高速模式必须采用DMA)
在main.c中,WiFi_Scan()扫描并显示附近的WiFi热点信息,可注释掉。WiFi.c中的WiFi_Init函数中调用的WiFi_ShowCIS函数用于显示WiFi模块的SDIO信息,也可以注释掉,减少屏幕显示内容。
以下为STM32标准库函数版本的程序。
【WiFi.h(库函数版)】
把寄存器版中的WiFi_GetSDIOBlockSize的定义改为:
#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
}
【扩展阅读】
将Marvell 88W8686 WiFi模块的固件内容写入到STM32F103RE单片机Flash末尾的固定位置,减少程序下载的时间: