注意:SPI方式驱动88W8801的完整代码已于2019年12月12日发布,支持F1和F4单片机,分为标准库和HAL库两个版本。下载地址为https://pan.baidu.com/s/1myogwFS687nCnsPEzU9ySQ
笔者所用的单片机为:STM32F407VE
程序中没有用STM32F4的SPI外设,而是直接用GPIO产生SPI时序。
目前程序完成了:88W8801 WiFi模块的初始化,以及显示CIS模块信息。
Keil4工程下载地址:https://pan.baidu.com/s/1T-845yKJ3uHKy3nTxeOYNg
注意不要在工程属性里面勾选“Use MicroLIB”!
【模块连线】
模块引脚名称 | SPI引脚名称 | I/O口 |
---|---|---|
D3 | CS(或NSS) | PC11 |
CLK | SCK | PC12 |
D0 | MISO | PC8 |
CMD | MOSI | PD2 |
D1 | INT | PC9 |
PDN | PD14 | |
RESET | 悬空 | |
VCC | 3.3V |
【笔者所用的开发板和WiFi模块】
在笔者所用的开发板上,Wi-Fi模块的VCC不是直接接到电源上的,而是串联了一个场效应管,当PA15为低电平时Wi-Fi模块通电。
【Keil4工程截图】
【程序运行结果】
【主要代码】
WiFi_LowLevel.c:
// 定义与单片机寄存器操作相关的函数, 方便在不同平台间移植
#include <stdio.h>
#include <stm32f4xx.h>
#include "common.h"
#include "WiFi.h"
#define CS_0 GPIO_WriteBit(GPIOC, GPIO_Pin_11, Bit_RESET)
#define CS_1 GPIO_WriteBit(GPIOC, GPIO_Pin_11, Bit_SET)
#define SCK_0 GPIO_WriteBit(GPIOC, GPIO_Pin_12, Bit_RESET)
#define SCK_1 GPIO_WriteBit(GPIOC, GPIO_Pin_12, Bit_SET)
#define MISO (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_8) == Bit_SET)
#define MOSI_0 GPIO_WriteBit(GPIOD, GPIO_Pin_2, Bit_RESET)
#define MOSI_1 GPIO_WriteBit(GPIOD, GPIO_Pin_2, Bit_SET)
#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)
static uint8_t WiFi_LowLevel_CalcCRC7(const void *data, uint16_t len);
static void WiFi_LowLevel_GPIOInit(void);
static void WiFi_LowLevel_ReceiveResponse(uint8_t *resp, uint8_t resp_len);
static void WiFi_LowLevel_SDIOInit(void);
static void WiFi_LowLevel_SendCMD52(uint8_t func, uint32_t addr, uint8_t data, uint32_t flags, uint8_t *resp, uint8_t resp_len);
static void WiFi_LowLevel_Send(uint8_t *data, uint16_t len);
static uint8_t WiFi_LowLevel_SendByte(uint8_t data);
static void WiFi_LowLevel_SendCommand(uint8_t index, uint32_t argument, uint8_t *resp, uint8_t resp_len);
static uint8_t sdio_func_num = 0; // 功能区总数 (0号功能区除外)
/* 计算CRC7校验码 */
static uint8_t WiFi_LowLevel_CalcCRC7(const void *data, uint16_t len)
{
const uint8_t *p = data;
const uint8_t polynomial = 0x89;
uint8_t j;
uint16_t temp = p[0] << 8;
uint16_t i;
for (i = 1; i <= len; i++)
{
if (i != len)
temp |= p[i];
for (j = 0; j < 8; j++)
{
if (temp & 0x8000)
temp ^= polynomial << 8;
temp <<= 1;
}
}
return temp >> 9;
}
/* 获取WiFi模块支持SDIO功能区个数 (0号功能区除外) */
uint8_t WiFi_LowLevel_GetFunctionNum(void)
{
return sdio_func_num;
}
/* 判断是否触发了网卡中断 */
uint8_t WiFi_LowLevel_GetITStatus(uint8_t clear)
{
if (EXTI_GetFlagStatus(EXTI_Line9) == SET)
{
if (clear)
EXTI_ClearFlag(EXTI_Line9);
return 1;
}
else
return 0;
}
/* 初始化WiFi模块有关的所有GPIO引脚 */
static void WiFi_LowLevel_GPIOInit(void)
{
EXTI_InitTypeDef exti;
GPIO_InitTypeDef gpio;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
// 使Wi-Fi模块复位信号(PDN)有效
gpio.GPIO_Mode = GPIO_Mode_OUT;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_Pin = GPIO_Pin_14;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio.GPIO_Speed = GPIO_Low_Speed;
GPIO_Init(GPIOD, &gpio);
GPIO_WriteBit(GPIOD, GPIO_Pin_14, Bit_RESET);
// PA15为MOS管控制的Wi-Fi模块电源开关, 低电平时Wi-Fi模块通电
gpio.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOA, &gpio);
GPIO_WriteBit(GPIOA, GPIO_Pin_15, Bit_RESET);
// 撤销Wi-Fi模块的复位信号
delay(10); // 延时一段时间, 使WiFi模块能够正确复位
GPIO_WriteBit(GPIOD, GPIO_Pin_14, Bit_SET);
// SDIO相关引脚
// PC8: MISO, 设为浮空输入
gpio.GPIO_Mode = GPIO_Mode_IN;
gpio.GPIO_Pin = GPIO_Pin_8;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOC, &gpio);
// PC9: INT, 设为带上拉电阻的输入
gpio.GPIO_Mode = GPIO_Mode_IN;
gpio.GPIO_Pin = GPIO_Pin_9;
gpio.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOC, &gpio);
// PC11: CS, PC12: SCK, 设为推挽输出
CS_1;
gpio.GPIO_Mode = GPIO_Mode_OUT;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio.GPIO_Speed = GPIO_High_Speed;
GPIO_Init(GPIOC, &gpio);
// PD2: MOSI, 设为推挽输出
gpio.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOD, &gpio);
// 配置外部中断
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource9);
exti.EXTI_Line = EXTI_Line9;
exti.EXTI_LineCmd = ENABLE;
exti.EXTI_Mode = EXTI_Mode_Interrupt;
exti.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&exti);
}
void WiFi_LowLevel_Init(void)
{
WiFi_LowLevel_GPIOInit();
WiFi_LowLevel_SDIOInit();
}
/* 读SDIO寄存器 */
uint8_t WiFi_LowLevel_ReadReg(uint8_t func, uint32_t addr)
{
uint8_t resp[2];
WiFi_LowLevel_SendCMD52(func, addr, 0, 0, resp, sizeof(resp));
return resp[1];
}
/* 接收指定长度的SDIO命令回应 */
// 7.3.2.1 Format R1
// 7.5.1.1 Host Command to Card Response - Card is ready
// 7.5.4 Timing Values
static void WiFi_LowLevel_ReceiveResponse(uint8_t *resp, uint8_t resp_len)
{
uint8_t i, temp;
// 等待回应期间 (NCR), MISO始终为高电平
// Modified R1回应的最高位为0, 当接收到的字节的最高位为0时退出循环
do
{
temp = WiFi_LowLevel_SendByte(0xff);
} while (temp & 0x80);
resp[0] = temp;
for (i = 1; i < resp_len; i++)
resp[i] = WiFi_LowLevel_SendByte(0xff);
}
static void WiFi_LowLevel_SDIOInit(void)
{
uint8_t resp[5];
// 延时可防止CMD5重发 (6.4 Power Scheme)
delay(100);
WiFi_LowLevel_SendByte(0xff); // 在CS=1的情况下发送8个空时钟
/* 切换到SPI模式 (CMD0: Used to change from SD to SPI mode) */
WiFi_LowLevel_SendCommand(0, 0, resp, 1);
printf("CMD0, R1_%02X\n", resp[0]);
/* 发送CMD5: IO_SEND_OP_COND */
WiFi_LowLevel_SendCommand(5, 0, resp, sizeof(resp));
printf("CMD5, R1_%02X, RESP1_%02x%02x%02x%02x\n", resp[0], resp[1], resp[2], resp[3], resp[4]);
/* 设置参数VDD Voltage Window: 3.2~3.4V, 并再次发送CMD5 */
WiFi_LowLevel_SendCommand(5, 0x300000, resp, sizeof(resp));
printf("CMD5, R1_%02X, RESP1_%02x%02x%02x%02x\n", resp[0], resp[1], resp[2], resp[3], resp[4]);
if (resp[1] & _BV(7))
{
// Card is ready to operate after initialization
sdio_func_num = (resp[1] >> 4) & 7;
printf("Number of I/O Functions: %d\n", sdio_func_num);
printf("Memory Present: %d\n", (resp[1] & _BV(3)) != 0);
}
// SPI模式不支持CMD3和CMD7, 所以不需要发送这两个命令
}
static void WiFi_LowLevel_Send(uint8_t *data, uint16_t len)
{
uint16_t i;
for (i = 0; i < len; i++)
data[i] = WiFi_LowLevel_SendByte(data[i]);
}
static uint8_t WiFi_LowLevel_SendByte(uint8_t data)
{
uint8_t i;
uint8_t recved = 0;
for (i = 0; i < 8; i++)
{
if (data & 0x80)
MOSI_1;
else
MOSI_0;
data <<= 1;
SCK_1;
recved <<= 1;
if (MISO)
recved |= 1;
SCK_0;
}
return recved;
}
static void WiFi_LowLevel_SendCMD52(uint8_t func, uint32_t addr, uint8_t data, uint32_t flags, uint8_t *resp, uint8_t resp_len)
{
uint32_t arg = (func << 28) | (addr << 9) | data | flags;
WiFi_LowLevel_SendCommand(52, arg, resp, resp_len);
}
static void WiFi_LowLevel_SendCommand(uint8_t index, uint32_t argument, uint8_t *resp, uint8_t resp_len)
{
uint8_t crc;
uint8_t data[6];
data[0] = index | 0x40; // 最高位为起始位, 始终为0, SD卡从起始位开始接收命令
data[1] = argument >> 24;
data[2] = argument >> 16;
data[3] = argument >> 8;
data[4] = argument;
crc = WiFi_LowLevel_CalcCRC7(data, 5);
data[5] = (crc << 1) | 1;
CS_0;
WiFi_LowLevel_Send(data, sizeof(data));
WiFi_LowLevel_ReceiveResponse(resp, resp_len);
CS_1;
}
/* 写寄存器, 返回写入后寄存器的实际内容 */
uint8_t WiFi_LowLevel_WriteReg(uint8_t func, uint32_t addr, uint8_t value)
{
uint8_t resp[2];
WiFi_LowLevel_SendCMD52(func, addr, value, CMD52_WRITE | CMD52_READAFTERWRITE, resp, sizeof(resp));
return resp[1];
}