【程序】STM32F407VE单片机通过SPI接口初始化88W8801 WiFi模块并显示CIS信息

注意: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”!

【模块连线】

88W8801 SPI方式连线
模块引脚名称SPI引脚名称I/O口
D3CS(或NSS)PC11
CLKSCKPC12
D0MISOPC8
CMDMOSIPD2
D1INTPC9
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];
}

 

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值