本程序基于20220213版程序。原程序的wifi_lowlevel.c里面,DMA是用LL库写的,但是有些客户想要用HAL库写,不想用LL库。那可以在项目属性中去掉USE_FULL_LL_DRIVER的宏定义,然后用如下内容替换wifi_lowlevel.c文件。
程序下载链接:百度网盘 请输入提取码

【STM32F103RE】
wifi_lowlevel.c:

/* 定义与单片机寄存器操作和模块接口相关的函数, 方便在不同平台间移植 */
// 单片机: STM32F103RE, 模块接口: SDIO

#include <stdio.h>
#include <stdlib.h>
#include <stm32f1xx.h>
#include <string.h>
#include "wifi.h"

#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)
#define CMD53_TIMEOUT 10000000

static uint8_t WiFi_LowLevel_CalcClockDivider(uint32_t freq, uint32_t *preal);
static int WiFi_LowLevel_CheckError(const char *msg_title);
static uint16_t WiFi_LowLevel_GetBlockNum(uint8_t func, uint32_t *psize, uint32_t flags);
static void WiFi_LowLevel_GPIOInit(void);
static void WiFi_LowLevel_SDIOInit(void);
static void WiFi_LowLevel_SendCMD52(uint8_t func, uint32_t addr, uint8_t data, uint32_t flags);
static void WiFi_LowLevel_SendCMD53(uint8_t func, uint32_t addr, uint16_t count, uint32_t flags);
static int WiFi_LowLevel_SetSDIOBlockSize(uint32_t size);
#ifdef WIFI_FIRMWAREAREA_ADDR
static int WiFi_LowLevel_VerifyFirmware(void);
#endif
static void WiFi_LowLevel_WaitForResponse(const char *msg_title);
#if WIFI_USEDMA
static void WiFi_LowLevel_DMAComplete(DMA_HandleTypeDef *hdma);
static void WiFi_LowLevel_DMAError(DMA_HandleTypeDef *hdma);
#endif

CRC_HandleTypeDef hcrc;
#if WIFI_USEDMA
DMA_HandleTypeDef hdma24;
static volatile int8_t sdio_dma_complete;
static volatile uint32_t sdio_dma_error;
#endif
static uint8_t sdio_func_num = 0; // 功能区总数 (0号功能区除外)
static uint16_t sdio_block_size[2]; // 各功能区的块大小, 保存在此变量中避免每次都去发送CMD52命令读SDIO寄存器
static uint16_t sdio_rca; // RCA相对地址: 虽然SDIO标准规定SDIO接口上可以接多张SD卡, 但是STM32的SDIO接口只能接一张卡 (芯片手册上有说明)
static SDIO_CmdInitTypeDef sdio_cmd;
static SDIO_DataInitTypeDef sdio_data;

/* 计算SDIO时钟分频系数 */
static uint8_t WiFi_LowLevel_CalcClockDivider(uint32_t freq, uint32_t *preal)
{
  int divider;
  uint32_t sdioclk;
  
  sdioclk = HAL_RCC_GetHCLKFreq();
  if (freq == 0)
    freq = 1;
  
  divider = sdioclk / freq - 2;
  if (sdioclk % freq != 0)
    divider++; // 始终向上舍入, 保证实际频率不超过freq
  if (divider < 0)
    divider = 0;
  else if (divider > 255)
    divider = 255;
  
  *preal = sdioclk / (divider + 2);
  printf("[Clock] freq=%.1fkHz, requested=%.1fkHz, divider=%d\n", *preal / 1000.0f, freq / 1000.0f, divider);
  return divider & 0xff;
}

/* 检查并清除错误标志位 */
static int WiFi_LowLevel_CheckError(const char *msg_title)
{
  int err = 0;
  
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_CCRCFAIL) != RESET)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_CCRCFAIL);
    err++;
    printf("%s: CMD%d CRC failed!\n", msg_title, sdio_cmd.CmdIndex);
  }
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_CTIMEOUT) != RESET)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_CTIMEOUT);
    err++;
    printf("%s: CMD%d timeout!\n", msg_title, sdio_cmd.CmdIndex);
  }
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_DCRCFAIL) != RESET)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_DCRCFAIL);
    err++;
    printf("%s: data CRC failed!\n", msg_title);
  }
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_DTIMEOUT) != RESET)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_DTIMEOUT);
    err++;
    printf("%s: data timeout!\n", msg_title);
  }
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_STBITERR) != RESET)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_STBITERR);
    err++;
    printf("%s: start bit error!\n", msg_title);
  }
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_TXUNDERR) != RESET)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_TXUNDERR);
    err++;
    printf("%s: data underrun!\n", msg_title);
  }
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_RXOVERR) != RESET)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_RXOVERR);
    err++;
    printf("%s: data overrun!\n", msg_title);
  }
#if WIFI_USEDMA
  if (sdio_dma_error & HAL_DMA_ERROR_TE)
  {
    sdio_dma_error &= ~HAL_DMA_ERROR_TE;
    err++;
    printf("%s: DMA transfer error!\n", msg_title);
  }
#endif
  return err;
}

/* 延时n毫秒 */
void WiFi_LowLevel_Delay(int nms)
{
  HAL_Delay(nms);
}

/* 打印数据内容 */
void WiFi_LowLevel_Dump(const void *data, int len)
{
  const uint8_t *p = data;
  
  while (len--)
    printf("%02X", *p++);
  printf("\n");
}

/* 判断应该采用哪种方式传输数据 */
// 返回值: 0为多字节模式, 否则表示块传输模式的数据块数
// *psize的值会做适当调整, 有可能大于原值
static uint16_t WiFi_LowLevel_GetBlockNum(uint8_t func, uint32_t *psize, uint32_t flags)
{
  uint16_t block_num = 0;
  
  if ((flags & WIFI_RWDATA_ALLOWMULTIBYTE) == 0 || *psize > 512) // 大于512字节时必须用数据块方式传输
  {
    // 块传输模式 (DTMODE=0)
    WiFi_LowLevel_SetSDIOBlockSize(sdio_block_size[func]);
    
    block_num = *psize / sdio_block_size[func];
    if (*psize % sdio_block_size[func] != 0)
      block_num++;
    *psize = block_num * sdio_block_size[func]; // 块数*块大小
  }
  else
  {
    // 多字节传输模式 (DTMODE=1)
    *psize = (*psize + 3) & ~3; // WiFi模块要求写入的字节数必须为4的整数倍
  }
  
  return block_num;
}

/* 获取WiFi模块支持的SDIO功能区个数 (0号功能区除外) */
uint8_t WiFi_LowLevel_GetFunctionNum(void)
{
  return sdio_func_num;
}

/* 判断是否触发了网卡中断 */
int WiFi_LowLevel_GetITStatus(uint8_t clear)
{
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_SDIOIT) != RESET)
  {
    if (clear)
      __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_SDIOIT);
    return 1;
  }
  else
    return 0;
}

uint32_t WiFi_LowLevel_GetTicks(void)
{
  return HAL_GetTick();
}

/* 初始化WiFi模块有关的所有GPIO引脚 */
static void WiFi_LowLevel_GPIOInit(void)
{
  GPIO_InitTypeDef gpio;
  
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  
  // 要使用PA15引脚, 必须关闭JTAG (不用PA15可以去掉)
  __HAL_RCC_AFIO_CLK_ENABLE();
  __HAL_AFIO_REMAP_SWJ_NOJTAG();
  
  // 使Wi-Fi模块复位信号(PDN)有效
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);
  gpio.Mode = GPIO_MODE_OUTPUT_PP; // PA15设为推挽输出, 并立即输出低电平
  gpio.Pin = GPIO_PIN_15;
  gpio.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &gpio);
  
  // 撤销Wi-Fi模块的复位信号
  WiFi_LowLevel_Delay(100); // 延时一段时间, 使WiFi模块能够正确复位
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);
  
  // SDIO相关引脚
  // PC8~11: SDIO_D0~3, PC12: SDIO_CK, 设为复用推挽输出
  gpio.Mode = GPIO_MODE_AF_PP;
  gpio.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;
  gpio.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOC, &gpio);
  
  // PD2: SDIO_CMD, 设为复用推挽输出
  gpio.Pin = GPIO_PIN_2;
  HAL_GPIO_Init(GPIOD, &gpio);
}

void WiFi_LowLevel_Init(void)
{
  // 在此处打开WiFi模块所需要的除GPIO和SDIO外所有其他外设的时钟
  __HAL_RCC_CRC_CLK_ENABLE();
  
  hcrc.Instance = CRC;
  HAL_CRC_Init(&hcrc);
  
  // 检查Flash中保存的固件内容是否已被破坏
#ifdef WIFI_FIRMWAREAREA_ADDR
  if (!WiFi_LowLevel_VerifyFirmware())
  {
    printf("Error: The firmware stored in flash memory is corrupted!\n");
    printf("Either run flash_saver program, or remove the definition of WIFI_FIRMWAREAREA_ADDR in WiFi.h\n");
    abort();
  }
#endif
  
  WiFi_LowLevel_GPIOInit();
  WiFi_LowLevel_SDIOInit();
}

/* 接收数据, 自动判断采用哪种传输模式 */
// size为要接收的字节数, bufsize为data缓冲区的大小
// 若bufsize=0, 则只读取数据, 但不保存到data中, 此时data可以为NULL
int WiFi_LowLevel_ReadData(uint8_t func, uint32_t addr, void *data, uint32_t size, uint32_t bufsize, uint32_t flags)
{
  int i, err = 0;
  uint16_t block_num; // 数据块个数
  uint32_t cmd53_flags = 0;
#if WIFI_USEDMA
  uint32_t temp; // 丢弃数据用的变量
#else
  uint32_t *p = data;
#endif
  
  if ((uintptr_t)data & 3)
  {
    // DMA每次传输多个字节时, 内存和外设地址必须要对齐, 否则将不能正确传输且不会提示错误
    printf("%s: data must be 4-byte aligned!\n", __FUNCTION__);
    return -2; // 不重试
  }
  if (size == 0)
  {
    printf("%s: size cannot be 0!\n", __FUNCTION__);
    return -2;
  }
  
  block_num = WiFi_LowLevel_GetBlockNum(func, &size, flags);
  if (bufsize != 0 && bufsize < size)
  {
    printf("%s: a buffer of at least %d bytes is required! bufsize=%d\n", __FUNCTION__, size, bufsize);
    return -2;
  }
  
#if WIFI_USEDMA
  hdma24.Init.Direction = DMA_PERIPH_TO_MEMORY;
  hdma24.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
  if (bufsize > 0)
    hdma24.Init.MemInc = DMA_MINC_ENABLE;
  else
    hdma24.Init.MemInc = DMA_MINC_DISABLE; // 数据丢弃模式
  hdma24.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  hdma24.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma24.Init.Mode = DMA_NORMAL;
  hdma24.Init.Priority = DMA_PRIORITY_VERY_HIGH;
  HAL_DMA_Init(&hdma24);
  
  if (bufsize > 0)
    HAL_DMA_Start_IT(&hdma24, (uint32_t)&SDIO->FIFO, (uint32_t)data, size / 4);
  else
    HAL_DMA_Start_IT(&hdma24, (uint32_t)&SDIO->FIFO, (uint32_t)&temp, size / 4);
#endif
  
  if (flags & WIFI_RWDATA_ADDRINCREMENT)
    cmd53_flags |= CMD53_INCREMENTING;
  if (block_num)
  {
    sdio_data.TransferMode = SDIO_TRANSFER_MODE_BLOCK;
    WiFi_LowLevel_SendCMD53(func, addr, block_num, cmd53_flags | CMD53_BLOCKMODE);
  }
  else
  {
    sdio_data.TransferMode = SDIO_TRANSFER_MODE_STREAM;
    WiFi_LowLevel_SendCMD53(func, addr, size, cmd53_flags);
  }
  
  sdio_data.DataLength = size;
  sdio_data.DPSM = SDIO_DPSM_ENABLE;
  sdio_data.TransferDir = SDIO_TRANSFER_DIR_TO_SDIO;
  SDIO_ConfigData(SDIO, &sdio_data);
  
#if !WIFI_USEDMA
  while (size > 0)
  {
    if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_RXDAVL) != RESET)
    {
      // 如果有数据到来就读取数据
      size -= 4;
      if (bufsize > 0)
        *p++ = SDIO_ReadFIFO(SDIO);
      else
        SDIO_ReadFIFO(SDIO); // 读寄存器, 但不保存数据
    }
    else
    {
      // 如果出现错误, 则退出循环
      err += WiFi_LowLevel_CheckError(__FUNCTION__);
      if (err)
        break;
    }
  }
#endif
  
  // 等待数据接收完毕
  i = 0;
  while (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_CMDACT) != RESET || __SDIO_GET_FLAG(SDIO, SDIO_FLAG_DATAEND) == RESET)
  {
    err += WiFi_LowLevel_CheckError(__FUNCTION__);
    if (err)
      break;
    
    i++;
    if (i == CMD53_TIMEOUT)
    {
      printf("%s: timeout!\n", __FUNCTION__);
      err++;
      break;
    }
  }
  sdio_data.DPSM = SDIO_DPSM_DISABLE;
  SDIO_ConfigData(SDIO, &sdio_data);
#if WIFI_USEDMA
  if (err == 0)
  {
    while (sdio_dma_complete == 0);
    sdio_dma_complete = 0;
  }
  else
  {
    // 请注意HAL库中Abort和Abort_IT的区别
    // Abort是以阻塞方式取消一个操作, 函数返回时操作已成功取消
    // Abort_IT是以中断方式取消一个操作, 函数返回时操作还没有取消成功, 直到XferAbortCallback回调函数被调用后, 才说明操作取消成功了
    if (sdio_dma_complete == 0)
      HAL_DMA_Abort(&hdma24);
    else
      sdio_dma_complete = 0;
  }
#endif
  
  // 清除相关标志位
  __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_CMDREND | SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND);
  err += WiFi_LowLevel_CheckError(__FUNCTION__);
  if (err != 0)
    return -1;
  return 0;
}

/* 读SDIO寄存器 */
uint8_t WiFi_LowLevel_ReadReg(uint8_t func, uint32_t addr)
{
  WiFi_LowLevel_SendCMD52(func, addr, 0, 0);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  return SDIO_GetResponse(SDIO, SDIO_RESP1) & 0xff;
}

/* 初始化SDIO外设并完成WiFi模块的枚举 */
// SDIO Simplified Specification Version 3.00: 3. SDIO Card Initialization
static void WiFi_LowLevel_SDIOInit(void)
{
  SDIO_InitTypeDef sdio = {0};
  uint8_t value;
  uint32_t freq, resp;
  
  // SDIO外设拥有两个时钟: SDIOCLK=HCLK=72MHz (分频后用于产生SDIO_CK=PC12引脚时钟), AHB bus clock=HCLK/2=36MHz (用于访问寄存器)
  __HAL_RCC_SDIO_CLK_ENABLE();
#if WIFI_USEDMA
  __HAL_RCC_DMA2_CLK_ENABLE();
#endif
  
  SDIO_PowerState_ON(SDIO); // 打开SDIO外设
  sdio.ClockDiv = WiFi_LowLevel_CalcClockDivider(400000, &freq); // 初始化时最高允许的频率: 400kHz
  SDIO_Init(SDIO, sdio);
  __SDIO_ENABLE(SDIO);
  
  __SDIO_OPERATION_ENABLE(SDIO); // 设为SDIO模式
#if WIFI_USEDMA
  __SDIO_DMA_ENABLE(SDIO); // 设为DMA传输模式
  
  hdma24.Instance = DMA2_Channel4;
  HAL_DMA_Init(&hdma24);
  HAL_DMA_RegisterCallback(&hdma24, HAL_DMA_XFER_CPLT_CB_ID, WiFi_LowLevel_DMAComplete);
  HAL_DMA_RegisterCallback(&hdma24, HAL_DMA_XFER_ERROR_CB_ID, WiFi_LowLevel_DMAError);
  HAL_NVIC_EnableIRQ(DMA2_Channel4_5_IRQn);
#endif
  
  // 不需要发送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)
  WiFi_LowLevel_Delay(10); // 延时可防止CMD5重发
  
  /* 发送CMD5: IO_SEND_OP_COND */
  sdio_cmd.Argument = 0;
  sdio_cmd.CmdIndex = 5;
  sdio_cmd.CPSM = SDIO_CPSM_ENABLE;
  sdio_cmd.Response = SDIO_RESPONSE_SHORT; // 接收短回应
  sdio_cmd.WaitForInterrupt = SDIO_WAIT_NO;
  SDIO_SendCommand(SDIO, &sdio_cmd);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  printf("RESPCMD%d, RESP1_%08x\n", SDIO_GetCommandResponse(SDIO), SDIO_GetResponse(SDIO, SDIO_RESP1));
  
  /* 设置参数VDD Voltage Window: 3.2~3.4V, 并再次发送CMD5 */
  sdio_cmd.Argument = 0x300000;
  SDIO_SendCommand(SDIO, &sdio_cmd);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  resp = SDIO_GetResponse(SDIO, SDIO_RESP1);
  printf("RESPCMD%d, RESP1_%08x\n", SDIO_GetCommandResponse(SDIO), resp);
  if (resp & _BV(31))
  {
    // Card is ready to operate after initialization
    sdio_func_num = (resp >> 28) & 7;
    printf("Number of I/O Functions: %d\n", sdio_func_num);
    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.Argument = 0;
  sdio_cmd.CmdIndex = 3;
  SDIO_SendCommand(SDIO, &sdio_cmd);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  sdio_rca = SDIO_GetResponse(SDIO, SDIO_RESP1) >> 16;
  printf("Relative Card Address: 0x%04x\n", sdio_rca);
  
  /* 选中WiFi模块 (CMD7: SELECT/DESELECT_CARD) */
  sdio_cmd.Argument = sdio_rca << 16;
  sdio_cmd.CmdIndex = 7;
  SDIO_SendCommand(SDIO, &sdio_cmd);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  printf("Card selected! RESP1_%08x\n", SDIO_GetResponse(SDIO, SDIO_RESP1));
  
  /* 提高时钟频率, 并设置数据超时时间为0.1s */
  value = WiFi_LowLevel_ReadReg(0, SDIO_CCCR_HS);
  if (value & SDIO_CCCR_HS_SHS)
    WiFi_LowLevel_WriteReg(0, SDIO_CCCR_HS, value | SDIO_CCCR_HS_EHS); // 开启SDIO高速模式, 支持25~50MHz范围的时钟频率
  sdio.ClockDiv = WiFi_LowLevel_CalcClockDivider(WIFI_CLOCK_FREQ, &freq);
  sdio_data.DataTimeOut = freq / 10;
  
  /* SDIO外设的总线宽度设为4位 */
  sdio.BusWide = SDIO_BUS_WIDE_4B;
  SDIO_Init(SDIO, sdio);
  WiFi_LowLevel_WriteReg(0, SDIO_CCCR_BUSIFCTRL, WiFi_LowLevel_ReadReg(0, SDIO_CCCR_BUSIFCTRL) | SDIO_CCCR_BUSIFCTRL_BUSWID_4Bit);
}

static void WiFi_LowLevel_SendCMD52(uint8_t func, uint32_t addr, uint8_t data, uint32_t flags)
{
  sdio_cmd.Argument = (func << 28) | (addr << 9) | data | flags;
  sdio_cmd.CmdIndex = 52;
  sdio_cmd.CPSM = SDIO_CPSM_ENABLE;
  sdio_cmd.Response = SDIO_RESPONSE_SHORT;
  sdio_cmd.WaitForInterrupt = SDIO_WAIT_NO;
  SDIO_SendCommand(SDIO, &sdio_cmd);
}

static void WiFi_LowLevel_SendCMD53(uint8_t func, uint32_t addr, uint16_t count, uint32_t flags)
{
  // 当count=512时, 和0x1ff相与后为0, 符合SDIO标准
  sdio_cmd.Argument = (func << 28) | (addr << 9) | (count & 0x1ff) | flags;
  sdio_cmd.CmdIndex = 53;
  sdio_cmd.CPSM = SDIO_CPSM_ENABLE;
  sdio_cmd.Response = SDIO_RESPONSE_SHORT;
  sdio_cmd.WaitForInterrupt = SDIO_WAIT_NO;
  SDIO_SendCommand(SDIO, &sdio_cmd);
}

/* 设置WiFi模块功能区的数据块大小 */
int WiFi_LowLevel_SetBlockSize(uint8_t func, uint32_t size)
{
  if (WiFi_LowLevel_SetSDIOBlockSize(size) == -1)
    return -1;
  
  sdio_block_size[func] = size;
  WiFi_LowLevel_WriteReg(0, (func << 8) | 0x10, size & 0xff);
  WiFi_LowLevel_WriteReg(0, (func << 8) | 0x11, size >> 8);
  return 0;
}

/* 设置SDIO外设的数据块大小 */
static int WiFi_LowLevel_SetSDIOBlockSize(uint32_t size)
{
  switch (size)
  {
    case 1:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_1B;
      break;
    case 2:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_2B;
      break;
    case 4:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_4B;
      break;
    case 8:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_8B;
      break;
    case 16:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_16B;
      break;
    case 32:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_32B;
      break;
    case 64:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_64B;
      break;
    case 128:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_128B;
      break;
    case 256:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_256B;
      break;
    case 512:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_512B;
      break;
    case 1024:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_1024B;
      break;
    case 2048:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_2048B;
      break;
    case 4096:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_4096B;
      break;
    case 8192:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_8192B;
      break;
    case 16384:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_16384B;
      break;
    default:
      return -1;
  }
  return 0;
}

/* 检查Flash中保存的固件内容是否完整 */
#ifdef WIFI_FIRMWAREAREA_ADDR
static int WiFi_LowLevel_VerifyFirmware(void)
{
  uint32_t crc, len;
  
  if (WIFI_FIRMWARE_SIZE > 300000)
    return 0; // 检验成功, 但检验结果为固件不完整, 所以不返回-1, 返回0
  
  len = WIFI_FIRMWARE_SIZE / 4 + 2; // 固件区(包括CRC)总大小的1/4
  crc = HAL_CRC_Calculate(&hcrc, (uint32_t *)WIFI_FIRMWAREAREA_ADDR, len);
  return crc == 0; // 返回1为完整, 0为不完整
}
#endif

/* 等待SDIO命令回应 */
static void WiFi_LowLevel_WaitForResponse(const char *msg_title)
{
  uint8_t i = 0;
  
  do
  {
    if (i == WIFI_LOWLEVEL_MAXRETRY)
    {
      WiFi_SetFaults(WIFI_FAULT_LOWLEVEL);
      return;
    }
    
    if (i != 0)
      SDIO_SendCommand(SDIO, &sdio_cmd); // 重发命令
    i++;
    
    while (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_CMDACT) != RESET); // 等待命令发送完毕
    WiFi_LowLevel_CheckError(msg_title);
  } while (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_CMDREND) == RESET); // 如果没有收到回应, 则重试
  __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_CMDREND);
}

/* 发送数据, 自动判断采用哪种传输模式 */
// size为要发送的字节数, bufsize为data缓冲区的大小, bufsize=0时禁用缓冲区检查
int WiFi_LowLevel_WriteData(uint8_t func, uint32_t addr, const void *data, uint32_t size, uint32_t bufsize, uint32_t flags)
{
  int i, err = 0;
  uint16_t block_num; // 数据块个数
  uint32_t cmd53_flags = CMD53_WRITE;
#if !WIFI_USEDMA
  const uint32_t *p = data;
#endif
  
  if ((uintptr_t)data & 3)
  {
    printf("%s: data must be 4-byte aligned!\n", __FUNCTION__);
    return -2; // 不重试
  }
  if (size == 0)
  {
    printf("%s: size cannot be 0!\n", __FUNCTION__);
    return -2;
  }

  block_num = WiFi_LowLevel_GetBlockNum(func, &size, flags);
  if (bufsize != 0 && bufsize < size) // 只读缓冲区越界不会影响数据传输, 所以这只是一个警告
    printf("%s: a buffer of at least %d bytes is required! bufsize=%d\n", __FUNCTION__, size, bufsize);
  
  if (flags & WIFI_RWDATA_ADDRINCREMENT)
    cmd53_flags |= CMD53_INCREMENTING;
  if (block_num)
  {
    sdio_data.TransferMode = SDIO_TRANSFER_MODE_BLOCK;
    WiFi_LowLevel_SendCMD53(func, addr, block_num, cmd53_flags | CMD53_BLOCKMODE);
  }
  else
  {
    sdio_data.TransferMode = SDIO_TRANSFER_MODE_STREAM;
    WiFi_LowLevel_SendCMD53(func, addr, size, cmd53_flags);
  }
  WiFi_LowLevel_WaitForResponse(__FUNCTION__); // 必须要等到CMD53收到回应后才能开始发送数据
  
  // 开始发送数据
#if WIFI_USEDMA
  hdma24.Init.Direction = DMA_MEMORY_TO_PERIPH;
  hdma24.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
  hdma24.Init.MemInc = DMA_MINC_ENABLE;
  hdma24.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  hdma24.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma24.Init.Mode = DMA_NORMAL;
  hdma24.Init.Priority = DMA_PRIORITY_VERY_HIGH;
  HAL_DMA_Init(&hdma24);
#endif
  
  sdio_data.DataLength = size;
  sdio_data.DPSM = SDIO_DPSM_ENABLE;
  sdio_data.TransferDir = SDIO_TRANSFER_DIR_TO_CARD;
  SDIO_ConfigData(SDIO, &sdio_data);
  
#if !WIFI_USEDMA
  while (size > 0)
  {
    size -= 4;
    SDIO_WriteFIFO(SDIO, (uint32_t *)p); // 向FIFO送入4字节数据
    p++;
    while (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_TXFIFOF) != RESET); // 如果FIFO已满则等待
    
    // 如果出现错误, 则退出循环
    err += WiFi_LowLevel_CheckError(__FUNCTION__);
    if (err)
      break;
  }
#else
  HAL_DMA_Start_IT(&hdma24, (uint32_t)data, (uint32_t)&SDIO->FIFO, size / 4);
#endif
  
  // 等待发送完毕
  i = 0;
  while (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_DATAEND) == RESET)
  {
    err += WiFi_LowLevel_CheckError(__FUNCTION__);
    if (err)
      break;
    
    i++;
    if (i == CMD53_TIMEOUT)
    {
      printf("%s: timeout!\n", __FUNCTION__); // 用于跳出TXACT始终不清零, 也没有错误标志位置位的情况
      err++;
      break;
    }
  }
  sdio_data.DPSM = SDIO_DPSM_DISABLE;
  SDIO_ConfigData(SDIO, &sdio_data);
#if WIFI_USEDMA
  if (err == 0)
  {
    while (sdio_dma_complete == 0);
    sdio_dma_complete = 0;
  }
  else
  {
    // 请注意HAL库中Abort和Abort_IT的区别
    // Abort是以阻塞方式取消一个操作, 函数返回时操作已成功取消
    // Abort_IT是以中断方式取消一个操作, 函数返回时操作还没有取消成功, 直到XferAbortCallback回调函数被调用后, 才说明操作取消成功了
    if (sdio_dma_complete == 0)
      HAL_DMA_Abort(&hdma24);
    else
      sdio_dma_complete = 0;
  }
#endif
  
  // 清除相关标志位
  __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND);
  err += WiFi_LowLevel_CheckError(__FUNCTION__);
  if (err != 0)
    return -1;
  return 0;
}

/* 写寄存器, 返回写入后寄存器的实际内容 */
uint8_t WiFi_LowLevel_WriteReg(uint8_t func, uint32_t addr, uint8_t value)
{
  WiFi_LowLevel_SendCMD52(func, addr, value, CMD52_WRITE | CMD52_READAFTERWRITE);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  return SDIO_GetResponse(SDIO, SDIO_RESP1) & 0xff;
}

/* 处理DMA中断 */
#if WIFI_USEDMA
void DMA2_Channel4_5_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma24);
}

static void WiFi_LowLevel_DMAComplete(DMA_HandleTypeDef *hdma)
{
  sdio_dma_complete = 1;
  __HAL_DMA_DISABLE(hdma);
}

static void WiFi_LowLevel_DMAError(DMA_HandleTypeDef *hdma)
{
  sdio_dma_complete = -1;
  sdio_dma_error = HAL_DMA_GetError(hdma);
  __HAL_DMA_DISABLE(hdma);
}
#endif

【STM32F407ZG】
wifi_lowlevel.c:

/* 定义与单片机寄存器操作和模块接口相关的函数, 方便在不同平台间移植 */
// 单片机: STM32F407ZG, 模块接口: SDIO

#include <stdio.h>
#include <stdlib.h>
#include <stm32f4xx.h>
#include <string.h>
#include "wifi.h"

#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)
#define CMD53_TIMEOUT 10000000

static uint8_t WiFi_LowLevel_CalcClockDivider(uint32_t freq, uint32_t *preal);
static int WiFi_LowLevel_CheckError(const char *msg_title);
static uint16_t WiFi_LowLevel_GetBlockNum(uint8_t func, uint32_t *psize, uint32_t flags);
static void WiFi_LowLevel_GPIOInit(void);
static void WiFi_LowLevel_SDIOInit(void);
static void WiFi_LowLevel_SendCMD52(uint8_t func, uint32_t addr, uint8_t data, uint32_t flags);
static void WiFi_LowLevel_SendCMD53(uint8_t func, uint32_t addr, uint16_t count, uint32_t flags);
static int WiFi_LowLevel_SetSDIOBlockSize(uint32_t size);
#ifdef WIFI_FIRMWAREAREA_ADDR
static int WiFi_LowLevel_VerifyFirmware(void);
#endif
static void WiFi_LowLevel_WaitForResponse(const char *msg_title);
#if WIFI_USEDMA
static void WiFi_LowLevel_DMAComplete(DMA_HandleTypeDef *hdma);
static void WiFi_LowLevel_DMAError(DMA_HandleTypeDef *hdma);
#endif

CRC_HandleTypeDef hcrc;
#if WIFI_USEDMA
DMA_HandleTypeDef hdma23;
static volatile int8_t sdio_dma_complete;
static volatile uint32_t sdio_dma_error;
#endif
static uint8_t sdio_func_num = 0; // 功能区总数 (0号功能区除外)
static uint16_t sdio_block_size[2]; // 各功能区的块大小, 保存在此变量中避免每次都去发送CMD52命令读SDIO寄存器
static uint16_t sdio_rca; // RCA相对地址: 虽然SDIO标准规定SDIO接口上可以接多张SD卡, 但是STM32的SDIO接口只能接一张卡 (芯片手册上有说明)
static SDIO_CmdInitTypeDef sdio_cmd;
static SDIO_DataInitTypeDef sdio_data;

/* 计算SDIO时钟分频系数 */
static uint8_t WiFi_LowLevel_CalcClockDivider(uint32_t freq, uint32_t *preal)
{
  int divider;
  uint32_t sdioclk;
  uint64_t pll;
  RCC_OscInitTypeDef osc;
  
  HAL_RCC_GetOscConfig(&osc);
  if (osc.PLL.PLLSource == RCC_PLLSOURCE_HSE)
    pll = HSE_VALUE;
  else
    pll = HSI_VALUE;
  
  sdioclk = (pll * osc.PLL.PLLN) / (osc.PLL.PLLM * osc.PLL.PLLQ);
  if (freq == 0)
    freq = 1;
  
  divider = sdioclk / freq - 2;
  if (sdioclk % freq != 0)
    divider++; // 始终向上舍入, 保证实际频率不超过freq
  if (divider < 0)
    divider = 0;
  else if (divider > 255)
    divider = 255;
  
  *preal = sdioclk / (divider + 2);
  printf("[Clock] freq=%.1fkHz, requested=%.1fkHz, divider=%d\n", *preal / 1000.0f, freq / 1000.0f, divider);
  return divider & 0xff;
}

/* 检查并清除错误标志位 */
static int WiFi_LowLevel_CheckError(const char *msg_title)
{
  int err = 0;
  
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_CCRCFAIL) != RESET)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_CCRCFAIL);
    err++;
    printf("%s: CMD%d CRC failed!\n", msg_title, sdio_cmd.CmdIndex);
  }
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_CTIMEOUT) != RESET)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_CTIMEOUT);
    err++;
    printf("%s: CMD%d timeout!\n", msg_title, sdio_cmd.CmdIndex);
  }
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_DCRCFAIL) != RESET)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_DCRCFAIL);
    err++;
    printf("%s: data CRC failed!\n", msg_title);
  }
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_DTIMEOUT) != RESET)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_DTIMEOUT);
    err++;
    printf("%s: data timeout!\n", msg_title);
  }
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_STBITERR) != RESET)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_STBITERR);
    err++;
    printf("%s: start bit error!\n", msg_title);
  }
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_TXUNDERR) != RESET)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_TXUNDERR);
    err++;
    printf("%s: data underrun!\n", msg_title);
  }
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_RXOVERR) != RESET)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_RXOVERR);
    err++;
    printf("%s: data overrun!\n", msg_title);
  }
#if WIFI_USEDMA
  if (sdio_dma_error & HAL_DMA_ERROR_TE)
  {
    sdio_dma_error &= ~HAL_DMA_ERROR_TE;
    err++;
    printf("%s: DMA transfer error!\n", msg_title);
  }
  if (sdio_dma_error & HAL_DMA_ERROR_DME)
  {
    sdio_dma_error &= ~HAL_DMA_ERROR_DME;
    err++;
    printf("%s: DMA direct mode error!\n", msg_title);
  }
  // FIFO error错误可忽略
#endif
  return err;
}

/* 延时n毫秒 */
void WiFi_LowLevel_Delay(int nms)
{
  HAL_Delay(nms);
}

/* 打印数据内容 */
void WiFi_LowLevel_Dump(const void *data, int len)
{
  const uint8_t *p = data;
  
  while (len--)
    printf("%02X", *p++);
  printf("\n");
}

/* 判断应该采用哪种方式传输数据 */
// 返回值: 0为多字节模式, 否则表示块传输模式的数据块数
// *psize的值会做适当调整, 有可能大于原值
static uint16_t WiFi_LowLevel_GetBlockNum(uint8_t func, uint32_t *psize, uint32_t flags)
{
  uint16_t block_num = 0;
  
  if ((flags & WIFI_RWDATA_ALLOWMULTIBYTE) == 0 || *psize > 512) // 大于512字节时必须用数据块方式传输
  {
    // 块传输模式 (DTMODE=0)
    WiFi_LowLevel_SetSDIOBlockSize(sdio_block_size[func]);
    
    block_num = *psize / sdio_block_size[func];
    if (*psize % sdio_block_size[func] != 0)
      block_num++;
    *psize = block_num * sdio_block_size[func]; // 块数*块大小
  }
  else
  {
    // 多字节传输模式 (DTMODE=1)
    *psize = (*psize + 3) & ~3; // WiFi模块要求写入的字节数必须为4的整数倍
  }
  
  return block_num;
}

/* 获取WiFi模块支持的SDIO功能区个数 (0号功能区除外) */
uint8_t WiFi_LowLevel_GetFunctionNum(void)
{
  return sdio_func_num;
}

/* 判断是否触发了网卡中断 */
int WiFi_LowLevel_GetITStatus(uint8_t clear)
{
  if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_SDIOIT) != RESET)
  {
    if (clear)
      __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_SDIOIT);
    return 1;
  }
  else
    return 0;
}

uint32_t WiFi_LowLevel_GetTicks(void)
{
  return HAL_GetTick();
}

/* 初始化WiFi模块有关的所有GPIO引脚 */
static void WiFi_LowLevel_GPIOInit(void)
{
  GPIO_InitTypeDef gpio;
  
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  
  // 使Wi-Fi模块复位信号(PDN)有效
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);
  gpio.Mode = GPIO_MODE_OUTPUT_PP; // PA15设为推挽输出, 并立即输出低电平
  gpio.Pin = GPIO_PIN_15;
  gpio.Pull = GPIO_NOPULL;
  gpio.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &gpio);
  
  // 撤销Wi-Fi模块的复位信号
  WiFi_LowLevel_Delay(100); // 延时一段时间, 使WiFi模块能够正确复位
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);
  
  // SDIO相关引脚
  // PC8~11: SDIO_D0~3, PC12: SDIO_CK, 设为复用推挽输出
  gpio.Alternate = GPIO_AF12_SDIO;
  gpio.Mode = GPIO_MODE_AF_PP;
  gpio.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;
  gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIOC, &gpio);
  
  // PD2: SDIO_CMD, 设为复用推挽输出
  gpio.Pin = GPIO_PIN_2;
  HAL_GPIO_Init(GPIOD, &gpio);
}

void WiFi_LowLevel_Init(void)
{
  // 在此处打开WiFi模块所需要的除GPIO和SDIO外所有其他外设的时钟
  __HAL_RCC_CRC_CLK_ENABLE();
  
  hcrc.Instance = CRC;
  HAL_CRC_Init(&hcrc);
  
  // 检查Flash中保存的固件内容是否已被破坏
#ifdef WIFI_FIRMWAREAREA_ADDR
  if (!WiFi_LowLevel_VerifyFirmware())
  {
    printf("Error: The firmware stored in flash memory is corrupted!\n");
    printf("Either run flash_saver program, or remove the definition of WIFI_FIRMWAREAREA_ADDR in WiFi.h\n");
    abort();
  }
#endif
  
  WiFi_LowLevel_GPIOInit();
  WiFi_LowLevel_SDIOInit();
}

/* 接收数据, 自动判断采用哪种传输模式 */
// size为要接收的字节数, bufsize为data缓冲区的大小
// 若bufsize=0, 则只读取数据, 但不保存到data中, 此时data可以为NULL
int WiFi_LowLevel_ReadData(uint8_t func, uint32_t addr, void *data, uint32_t size, uint32_t bufsize, uint32_t flags)
{
  int i, err = 0;
  uint16_t block_num; // 数据块个数
  uint32_t cmd53_flags = 0;
#if WIFI_USEDMA
  uint32_t temp; // 丢弃数据用的变量
#else
  uint32_t *p = data;
#endif
  
  if ((uintptr_t)data & 3)
  {
    // DMA每次传输多个字节时, 内存和外设地址必须要对齐, 否则将不能正确传输且不会提示错误
    printf("%s: data must be 4-byte aligned!\n", __FUNCTION__);
    return -2; // 不重试
  }
  if (size == 0)
  {
    printf("%s: size cannot be 0!\n", __FUNCTION__);
    return -2;
  }
  
  block_num = WiFi_LowLevel_GetBlockNum(func, &size, flags);
  if (bufsize != 0 && bufsize < size)
  {
    printf("%s: a buffer of at least %d bytes is required! bufsize=%d\n", __FUNCTION__, size, bufsize);
    return -2;
  }
  
#if WIFI_USEDMA
  hdma23.Init.Channel = DMA_CHANNEL_4;
  hdma23.Init.Direction = DMA_PERIPH_TO_MEMORY;
  hdma23.Init.FIFOMode = DMA_FIFOMODE_ENABLE; // 必须使用FIFO模式
  hdma23.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
  hdma23.Init.MemBurst = DMA_MBURST_INC4;
  hdma23.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
  if (bufsize > 0)
    hdma23.Init.MemInc = DMA_MINC_ENABLE;
  else
    hdma23.Init.MemInc = DMA_MINC_DISABLE; // 数据丢弃模式
  hdma23.Init.Mode = DMA_PFCTRL; // 必须由SDIO控制DMA传输的数据量
  hdma23.Init.PeriphBurst = DMA_PBURST_INC4;
  hdma23.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  hdma23.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma23.Init.Priority = DMA_PRIORITY_VERY_HIGH;
  HAL_DMA_Init(&hdma23);
  
  if (bufsize > 0)
    HAL_DMA_Start_IT(&hdma23, (uint32_t)&SDIO->FIFO, (uint32_t)data, 1); // 由SDIO控制流量时, DataLength无效 (HTIF3也不会置位)
  else
    HAL_DMA_Start_IT(&hdma23, (uint32_t)&SDIO->FIFO, (uint32_t)&temp, 1);
#endif
  
  if (flags & WIFI_RWDATA_ADDRINCREMENT)
    cmd53_flags |= CMD53_INCREMENTING;
  if (block_num)
  {
    sdio_data.TransferMode = SDIO_TRANSFER_MODE_BLOCK;
    WiFi_LowLevel_SendCMD53(func, addr, block_num, cmd53_flags | CMD53_BLOCKMODE);
  }
  else
  {
    sdio_data.TransferMode = SDIO_TRANSFER_MODE_STREAM;
    WiFi_LowLevel_SendCMD53(func, addr, size, cmd53_flags);
  }
  
  sdio_data.DataLength = size;
  sdio_data.DPSM = SDIO_DPSM_ENABLE;
  sdio_data.TransferDir = SDIO_TRANSFER_DIR_TO_SDIO;
  SDIO_ConfigData(SDIO, &sdio_data);
  
#if !WIFI_USEDMA
  while (size > 0)
  {
    if (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_RXDAVL) != RESET)
    {
      // 如果有数据到来就读取数据
      size -= 4;
      if (bufsize > 0)
        *p++ = SDIO_ReadFIFO(SDIO);
      else
        SDIO_ReadFIFO(SDIO); // 读寄存器, 但不保存数据
    }
    else
    {
      // 如果出现错误, 则退出循环
      err += WiFi_LowLevel_CheckError(__FUNCTION__);
      if (err)
        break;
    }
  }
#endif
  
  // 等待数据接收完毕
  i = 0;
  while (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_CMDACT) != RESET || __SDIO_GET_FLAG(SDIO, SDIO_FLAG_DATAEND) == RESET)
  {
    err += WiFi_LowLevel_CheckError(__FUNCTION__);
    if (err)
      break;
    
    i++;
    if (i == CMD53_TIMEOUT)
    {
      printf("%s: timeout!\n", __FUNCTION__);
      err++;
      break;
    }
  }
  sdio_data.DPSM = SDIO_DPSM_DISABLE;
  SDIO_ConfigData(SDIO, &sdio_data);
#if WIFI_USEDMA
  if (err == 0)
  {
    while (sdio_dma_complete == 0);
    sdio_dma_complete = 0;
  }
  else
  {
    // 请注意HAL库中Abort和Abort_IT的区别
    // Abort是以阻塞方式取消一个操作, 函数返回时操作已成功取消
    // Abort_IT是以中断方式取消一个操作, 函数返回时操作还没有取消成功, 直到XferAbortCallback回调函数被调用后, 才说明操作取消成功了
    if (sdio_dma_complete == 0)
      HAL_DMA_Abort(&hdma23);
    else
      sdio_dma_complete = 0;
  }
#endif
  
  // 清除相关标志位
  __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_CMDREND | SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND);
  err += WiFi_LowLevel_CheckError(__FUNCTION__);
  if (err != 0)
    return -1;
  return 0;
}

/* 读SDIO寄存器 */
uint8_t WiFi_LowLevel_ReadReg(uint8_t func, uint32_t addr)
{
  WiFi_LowLevel_SendCMD52(func, addr, 0, 0);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  return SDIO_GetResponse(SDIO, SDIO_RESP1) & 0xff;
}

/* 初始化SDIO外设并完成WiFi模块的枚举 */
// SDIO Simplified Specification Version 3.00: 3. SDIO Card Initialization
static void WiFi_LowLevel_SDIOInit(void)
{
  SDIO_InitTypeDef sdio = {0};
  uint8_t value;
  uint32_t freq, resp;
  
  // 在F4单片机中, 必须要开启PLL才能使用SDIO外设
  if (__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET)
  {
    __HAL_RCC_PLL_ENABLE();
    while (__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET);
    printf("PLL is enabled!\n");
  }
  
  // SDIO外设拥有两个时钟: SDIOCLK=48MHz (分频后用于产生SDIO_CK=PC12引脚时钟), APB2 bus clock=PCLK2=84MHz (用于访问寄存器)
  __HAL_RCC_SDIO_CLK_ENABLE();
#if WIFI_USEDMA
  __HAL_RCC_DMA2_CLK_ENABLE();
#endif
  
  SDIO_PowerState_ON(SDIO); // 打开SDIO外设
  sdio.ClockDiv = WiFi_LowLevel_CalcClockDivider(400000, &freq); // 初始化时最高允许的频率: 400kHz
  SDIO_Init(SDIO, sdio);
  __SDIO_ENABLE(SDIO);
  
  __SDIO_OPERATION_ENABLE(SDIO); // 设为SDIO模式
#if WIFI_USEDMA
  WiFi_LowLevel_Delay(1); // 在F4上打开SDIO DMA前必须延时, 否则会失败
  __SDIO_DMA_ENABLE(SDIO); // 设为DMA传输模式
  
  hdma23.Instance = DMA2_Stream3;
  HAL_DMA_Init(&hdma23);
  HAL_DMA_RegisterCallback(&hdma23, HAL_DMA_XFER_CPLT_CB_ID, WiFi_LowLevel_DMAComplete);
  HAL_DMA_RegisterCallback(&hdma23, HAL_DMA_XFER_ERROR_CB_ID, WiFi_LowLevel_DMAError);
  HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
#endif
  
  // 不需要发送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)
  WiFi_LowLevel_Delay(10); // 延时可防止CMD5重发
  
  /* 发送CMD5: IO_SEND_OP_COND */
  sdio_cmd.Argument = 0;
  sdio_cmd.CmdIndex = 5;
  sdio_cmd.CPSM = SDIO_CPSM_ENABLE;
  sdio_cmd.Response = SDIO_RESPONSE_SHORT; // 接收短回应
  sdio_cmd.WaitForInterrupt = SDIO_WAIT_NO;
  SDIO_SendCommand(SDIO, &sdio_cmd);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  printf("RESPCMD%d, RESP1_%08x\n", SDIO_GetCommandResponse(SDIO), SDIO_GetResponse(SDIO, SDIO_RESP1));
  
  /* 设置参数VDD Voltage Window: 3.2~3.4V, 并再次发送CMD5 */
  sdio_cmd.Argument = 0x300000;
  SDIO_SendCommand(SDIO, &sdio_cmd);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  resp = SDIO_GetResponse(SDIO, SDIO_RESP1);
  printf("RESPCMD%d, RESP1_%08x\n", SDIO_GetCommandResponse(SDIO), resp);
  if (resp & _BV(31))
  {
    // Card is ready to operate after initialization
    sdio_func_num = (resp >> 28) & 7;
    printf("Number of I/O Functions: %d\n", sdio_func_num);
    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.Argument = 0;
  sdio_cmd.CmdIndex = 3;
  SDIO_SendCommand(SDIO, &sdio_cmd);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  sdio_rca = SDIO_GetResponse(SDIO, SDIO_RESP1) >> 16;
  printf("Relative Card Address: 0x%04x\n", sdio_rca);
  
  /* 选中WiFi模块 (CMD7: SELECT/DESELECT_CARD) */
  sdio_cmd.Argument = sdio_rca << 16;
  sdio_cmd.CmdIndex = 7;
  SDIO_SendCommand(SDIO, &sdio_cmd);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  printf("Card selected! RESP1_%08x\n", SDIO_GetResponse(SDIO, SDIO_RESP1));
  
  /* 提高时钟频率, 并设置数据超时时间为0.1s */
  value = WiFi_LowLevel_ReadReg(0, SDIO_CCCR_HS);
  if (value & SDIO_CCCR_HS_SHS)
    WiFi_LowLevel_WriteReg(0, SDIO_CCCR_HS, value | SDIO_CCCR_HS_EHS); // 开启SDIO高速模式, 支持25~50MHz范围的时钟频率
  sdio.ClockDiv = WiFi_LowLevel_CalcClockDivider(WIFI_CLOCK_FREQ, &freq);
  sdio_data.DataTimeOut = freq / 10;
  
  /* SDIO外设的总线宽度设为4位 */
  sdio.BusWide = SDIO_BUS_WIDE_4B;
  SDIO_Init(SDIO, sdio);
  WiFi_LowLevel_WriteReg(0, SDIO_CCCR_BUSIFCTRL, WiFi_LowLevel_ReadReg(0, SDIO_CCCR_BUSIFCTRL) | SDIO_CCCR_BUSIFCTRL_BUSWID_4Bit);
}

static void WiFi_LowLevel_SendCMD52(uint8_t func, uint32_t addr, uint8_t data, uint32_t flags)
{
  sdio_cmd.Argument = (func << 28) | (addr << 9) | data | flags;
  sdio_cmd.CmdIndex = 52;
  sdio_cmd.CPSM = SDIO_CPSM_ENABLE;
  sdio_cmd.Response = SDIO_RESPONSE_SHORT;
  sdio_cmd.WaitForInterrupt = SDIO_WAIT_NO;
  SDIO_SendCommand(SDIO, &sdio_cmd);
}

static void WiFi_LowLevel_SendCMD53(uint8_t func, uint32_t addr, uint16_t count, uint32_t flags)
{
  // 当count=512时, 和0x1ff相与后为0, 符合SDIO标准
  sdio_cmd.Argument = (func << 28) | (addr << 9) | (count & 0x1ff) | flags;
  sdio_cmd.CmdIndex = 53;
  sdio_cmd.CPSM = SDIO_CPSM_ENABLE;
  sdio_cmd.Response = SDIO_RESPONSE_SHORT;
  sdio_cmd.WaitForInterrupt = SDIO_WAIT_NO;
  SDIO_SendCommand(SDIO, &sdio_cmd);
}

/* 设置WiFi模块功能区的数据块大小 */
int WiFi_LowLevel_SetBlockSize(uint8_t func, uint32_t size)
{
  if (WiFi_LowLevel_SetSDIOBlockSize(size) == -1)
    return -1;
  
  sdio_block_size[func] = size;
  WiFi_LowLevel_WriteReg(0, (func << 8) | 0x10, size & 0xff);
  WiFi_LowLevel_WriteReg(0, (func << 8) | 0x11, size >> 8);
  return 0;
}

/* 设置SDIO外设的数据块大小 */
static int WiFi_LowLevel_SetSDIOBlockSize(uint32_t size)
{
  switch (size)
  {
    case 1:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_1B;
      break;
    case 2:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_2B;
      break;
    case 4:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_4B;
      break;
    case 8:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_8B;
      break;
    case 16:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_16B;
      break;
    case 32:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_32B;
      break;
    case 64:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_64B;
      break;
    case 128:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_128B;
      break;
    case 256:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_256B;
      break;
    case 512:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_512B;
      break;
    case 1024:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_1024B;
      break;
    case 2048:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_2048B;
      break;
    case 4096:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_4096B;
      break;
    case 8192:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_8192B;
      break;
    case 16384:
      sdio_data.DataBlockSize = SDIO_DATABLOCK_SIZE_16384B;
      break;
    default:
      return -1;
  }
  return 0;
}

/* 检查Flash中保存的固件内容是否完整 */
#ifdef WIFI_FIRMWAREAREA_ADDR
static int WiFi_LowLevel_VerifyFirmware(void)
{
  uint32_t crc, len;
  
  if (WIFI_FIRMWARE_SIZE > 300000)
    return 0; // 检验成功, 但检验结果为固件不完整, 所以不返回-1, 返回0
  
  len = WIFI_FIRMWARE_SIZE / 4 + 2; // 固件区(包括CRC)总大小的1/4
  crc = HAL_CRC_Calculate(&hcrc, (uint32_t *)WIFI_FIRMWAREAREA_ADDR, len);
  return crc == 0; // 返回1为完整, 0为不完整
}
#endif

/* 等待SDIO命令回应 */
static void WiFi_LowLevel_WaitForResponse(const char *msg_title)
{
  uint8_t i = 0;
  
  do
  {
    if (i == WIFI_LOWLEVEL_MAXRETRY)
    {
      WiFi_SetFaults(WIFI_FAULT_LOWLEVEL);
      return;
    }
    
    if (i != 0)
      SDIO_SendCommand(SDIO, &sdio_cmd); // 重发命令
    i++;
    
    while (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_CMDACT) != RESET); // 等待命令发送完毕
    WiFi_LowLevel_CheckError(msg_title);
  } while (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_CMDREND) == RESET); // 如果没有收到回应, 则重试
  __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_CMDREND);
}

/* 发送数据, 自动判断采用哪种传输模式 */
// size为要发送的字节数, bufsize为data缓冲区的大小, bufsize=0时禁用缓冲区检查
int WiFi_LowLevel_WriteData(uint8_t func, uint32_t addr, const void *data, uint32_t size, uint32_t bufsize, uint32_t flags)
{
  int i, err = 0;
  uint16_t block_num; // 数据块个数
  uint32_t cmd53_flags = CMD53_WRITE;
#if !WIFI_USEDMA
  const uint32_t *p = data;
#endif
  
  if ((uintptr_t)data & 3)
  {
    printf("%s: data must be 4-byte aligned!\n", __FUNCTION__);
    return -2; // 不重试
  }
  if (size == 0)
  {
    printf("%s: size cannot be 0!\n", __FUNCTION__);
    return -2;
  }

  block_num = WiFi_LowLevel_GetBlockNum(func, &size, flags);
  if (bufsize != 0 && bufsize < size) // 只读缓冲区越界不会影响数据传输, 所以这只是一个警告
    printf("%s: a buffer of at least %d bytes is required! bufsize=%d\n", __FUNCTION__, size, bufsize);
  
  if (flags & WIFI_RWDATA_ADDRINCREMENT)
    cmd53_flags |= CMD53_INCREMENTING;
  if (block_num)
  {
    sdio_data.TransferMode = SDIO_TRANSFER_MODE_BLOCK;
    WiFi_LowLevel_SendCMD53(func, addr, block_num, cmd53_flags | CMD53_BLOCKMODE);
  }
  else
  {
    sdio_data.TransferMode = SDIO_TRANSFER_MODE_STREAM;
    WiFi_LowLevel_SendCMD53(func, addr, size, cmd53_flags);
  }
  WiFi_LowLevel_WaitForResponse(__FUNCTION__); // 必须要等到CMD53收到回应后才能开始发送数据
  
  // 开始发送数据
#if WIFI_USEDMA
  hdma23.Init.Channel = DMA_CHANNEL_4;
  hdma23.Init.Direction = DMA_MEMORY_TO_PERIPH;
  hdma23.Init.FIFOMode = DMA_FIFOMODE_ENABLE; // 必须使用FIFO模式
  hdma23.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
  hdma23.Init.MemBurst = DMA_MBURST_INC4;
  hdma23.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
  hdma23.Init.MemInc = DMA_MINC_ENABLE;
  hdma23.Init.Mode = DMA_PFCTRL; // 必须由SDIO控制DMA传输的数据量
  hdma23.Init.PeriphBurst = DMA_PBURST_INC4;
  hdma23.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  hdma23.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma23.Init.Priority = DMA_PRIORITY_VERY_HIGH;
  HAL_DMA_Init(&hdma23);
#endif
  
  sdio_data.DataLength = size;
  sdio_data.DPSM = SDIO_DPSM_ENABLE;
  sdio_data.TransferDir = SDIO_TRANSFER_DIR_TO_CARD;
  SDIO_ConfigData(SDIO, &sdio_data);
  
#if !WIFI_USEDMA
  while (size > 0)
  {
    size -= 4;
    SDIO_WriteFIFO(SDIO, (uint32_t *)p); // 向FIFO送入4字节数据
    p++;
    while (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_TXFIFOF) != RESET); // 如果FIFO已满则等待
    
    // 如果出现错误, 则退出循环
    err += WiFi_LowLevel_CheckError(__FUNCTION__);
    if (err)
      break;
  }
#else
  HAL_DMA_Start_IT(&hdma23, (uint32_t)data, (uint32_t)&SDIO->FIFO, 1); // 由SDIO控制流量时, DataLength无效 (HTIF3也不会置位)
#endif
  
  // 等待发送完毕
  i = 0;
  while (__SDIO_GET_FLAG(SDIO, SDIO_FLAG_DATAEND) == RESET)
  {
    err += WiFi_LowLevel_CheckError(__FUNCTION__);
    if (err)
      break;
    
    i++;
    if (i == CMD53_TIMEOUT)
    {
      printf("%s: timeout!\n", __FUNCTION__); // 用于跳出TXACT始终不清零, 也没有错误标志位置位的情况
      err++;
      break;
    }
  }
  sdio_data.DPSM = SDIO_DPSM_DISABLE;
  SDIO_ConfigData(SDIO, &sdio_data);
#if WIFI_USEDMA
  if (err == 0)
  {
    while (sdio_dma_complete == 0);
    sdio_dma_complete = 0;
  }
  else
  {
    // 请注意HAL库中Abort和Abort_IT的区别
    // Abort是以阻塞方式取消一个操作, 函数返回时操作已成功取消
    // Abort_IT是以中断方式取消一个操作, 函数返回时操作还没有取消成功, 直到XferAbortCallback回调函数被调用后, 才说明操作取消成功了
    if (sdio_dma_complete == 0)
      HAL_DMA_Abort(&hdma23);
    else
      sdio_dma_complete = 0;
  }
#endif
  
  // 清除相关标志位
  __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND);
  err += WiFi_LowLevel_CheckError(__FUNCTION__);
  if (err != 0)
    return -1;
  return 0;
}

/* 写寄存器, 返回写入后寄存器的实际内容 */
uint8_t WiFi_LowLevel_WriteReg(uint8_t func, uint32_t addr, uint8_t value)
{
  WiFi_LowLevel_SendCMD52(func, addr, value, CMD52_WRITE | CMD52_READAFTERWRITE);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  return SDIO_GetResponse(SDIO, SDIO_RESP1) & 0xff;
}

/* 处理DMA中断 */
#if WIFI_USEDMA
void DMA2_Stream3_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma23);
}

static void WiFi_LowLevel_DMAComplete(DMA_HandleTypeDef *hdma)
{
  sdio_dma_complete = 1;
}

static void WiFi_LowLevel_DMAError(DMA_HandleTypeDef *hdma)
{
  sdio_dma_error = HAL_DMA_GetError(hdma);
  if (sdio_dma_error & HAL_DMA_ERROR_TE)
    sdio_dma_complete = -1;
}
#endif
07-10 06:49