【程序】STM32H743ZI用SDMMC2驱动88W8801 WiFi模块

该程序是旧版本!最新版本为20220213版:
https://blog.csdn.net/ZLK1214/article/details/122915474

本程序所用的单片机型号为:STM32H743ZI
本程序基于:Marvell 88W8801 WiFi模块创建或连接热点,并使用lwip2.1.2建立http服务器(20200208版)
Keil工程下载链接:STM32H743ZI用SDMMC2驱动88W8801_20220112.zip-网络设备文档类资源-CSDN下载
晶振:25MHz HSE
接口:SDMMC2

【开发板】

自制88W8801 WiFi模块:

88W8801原理图、PCB文件和封装库下载链接:百度网盘 请输入提取码(提取码:my2n)
请注意U2的左右顺序。从模块正面看,中间左边是5脚(OUT),接的是88W8801芯片,右边是2脚(IN),接的是天线。其余四个脚都是GND。

【接线方法】

引脚I/O口复用功能布线长度
PDNPE6
SDMMC2_D0PB14AF93150mil
SDMMC2_D1PB15AF93150mil
SDMMC2_D2PB3AF93150mil
SDMMC2_D3PB4AF93150mil
SDMMC2_CKPD6AF116300mil
SDMMC2_CMDPD7AF113150mil

【PCB布线规则】
命令线CMD和数据线D0~3等长布线,时钟线CK为数据线长度的两倍。绕蛇形线等长,线宽为3.5mil。

 【程序运行截图】

1. 串口输出

2. ping IP地址和计算机名

3. 访问http服务器

4. 将固件烧写到单片机Flash固定区域的程序(用于节约主程序烧写时间) 

5. 创建的热点

6. 测速器上位机(V1.4版)

7. 在路由器管理页面中看到的设备名字

【关于SDIO 1线模式下Stream模式写数据卡死的问题】

关于STM32H743ZI SDMMC 1线模式下Stream模式写数据卡死的问题_ZLK1214的专栏-CSDN博客https://blog.csdn.net/ZLK1214/article/details/122222803本文的程序没有修改stm32h7xx_ll_sdmmc.h头文件,而是直接在wifi_lowlevel.c里面重新定义一个新的WiFi_LowLevel_SDMMC_ConfigData函数,以便支持SDMMC_TRANSFER_MODE_MULTIBYTE模式。

【测速结果】

STM32F103RE和STM32F407VE单片机用lwip 2.1.2驱动88W8801 WiFi模块,如何提高TCP和UDP的传输速率_ZLK1214的专栏-CSDN博客_lwip提速https://blog.csdn.net/ZLK1214/article/details/112095017

自制88W8801 WiFi模块的测速结果:

【程序运行结果】

STM32H743ZI SDMMC2 88W8801
SystemCoreClock=480000000
[Clock] freq=400.0kHz, requested=400.0kHz, divider=60
RESPCMD63, RESP1_b0ff8000
RESPCMD63, RESP1_b0300000
Number of I/O Functions: 3
Memory Present: 0
Relative Card Address: 0x0001
Card selected! RESP1_00001e00
[Clock] freq=24000.0kHz, requested=25000.0kHz, divider=1
[CIS] func=0, ptr=0x00008000
Product Information: Marvell 802.11 SDIO ID: 48
Manufacturer Code: 0x02df
Manufacturer Information: 0x9138
Card Function Code: 0x0c
System Initialization Bit Mask: 0x00
Maximum Block Size: 256
Maximum Transfer Rate Code: 0x5a
[CIS] func=1, ptr=0x00008080
Manufacturer Code: 0x02df
Manufacturer Information: 0x9139
Card Function Code: 0x0c
System Initialization Bit Mask: 0x00
Maximum Block Size: 512
[CIS] func=2, ptr=0x00008100
[CIS] func=3, ptr=0x00008180
Firmware is successfully downloaded!
CMDRESP 0x804d at 18ms
MAC Addr: 08:EA:40:31:21:8B
IP_FORWARD is enabled!
LWIP_IPV6_FORWARD is enabled!
Join group FF02::1! bss=0x00
IPv6 link-local address: FE80::AEA:40FF:FE31:218B
Join group FF02::1! bss=0x10
IPv6 link-local address for micro AP: FE80::AEA:40FF:FE31:218B
IPv6 address for micro AP: FD20::1
Join group FF02::2! bss=0x10
[Send] len=86, bss=0x10, port=-1
[Send] len=86, bss=0x10, port=-1
CMDRESP 0x8006 at 436ms
SSID 'Oct1158-6', MAC 04:95:E6:E5:CD:A2, RSSI 27, Channel 1
  Timestamp 2691763851649, Beacon Interval 100, TSF timestamp: 200306
  Capability: 0x0511 (Security: WPA2, Mode: Infrastructure)
  Rates: 1.0Mbps 2.0Mbps 5.5Mbps 11.0Mbps 18.0Mbps 24.0Mbps 36.0Mbps 54.0Mbps
SSID 'ziroom904-1', MAC 80:12:DF:90:7F:5A, RSSI 73, Channel 1
  Timestamp 292636899391, Beacon Interval 100, TSF timestamp: 205378
  Capability: 0x0c11 (Security: WPA2, Mode: Infrastructure)
  Rates: 1.0Mbps 2.0Mbps 5.5Mbps 11.0Mbps 9.0Mbps 18.0Mbps 36.0Mbps 54.0Mbps
SSID 'ChinaNet-sec9', MAC 1C:FF:59:6F:AC:36, RSSI 73, Channel 4
  Timestamp 411767490008, Beacon Interval 100, TSF timestamp: 519271
  Capability: 0x0411 (Security: WPA2, Mode: Infrastructure)
  Rates: 1.0Mbps 2.0Mbps 5.5Mbps 11.0Mbps 18.0Mbps 24.0Mbps 36.0Mbps 54.0Mbps
CMDRESP 0x8006 at 434ms
SSID '??WIFI', MAC 1C:D5:E2:72:98:60, RSSI 44, Channel 5
  Timestamp 71707314440, Beacon Interval 100, TSF timestamp: 636295
  Capability: 0x0411 (Security: WPA2, Mode: Infrastructure)
  Rates: 1.0Mbps 2.0Mbps 5.5Mbps 11.0Mbps 6.0Mbps 9.0Mbps 12.0Mbps 18.0Mbps
SSID 'TP-LINK_53C4', MAC 58:41:20:B0:53:C4, RSSI 61, Channel 6
  Timestamp 5880689950200, Beacon Interval 100, TSF timestamp: 746060
  Capability: 0x1411 (Security: WPA2, Mode: Infrastructure)
  Rates: 1.0Mbps 2.0Mbps 5.5Mbps 11.0Mbps 9.0Mbps 18.0Mbps 36.0Mbps 54.0Mbps
SSID 'HUAWEI', MAC 2C:C5:46:C9:7E:80, RSSI 77, Channel 6
  Timestamp 2227753197723, Beacon Interval 100, TSF timestamp: 760477
  Capability: 0x1431 (Security: WPA2, Mode: Infrastructure)
  Rates: 1.0Mbps 2.0Mbps 5.5Mbps 6.0Mbps 9.0Mbps 11.0Mbps 12.0Mbps 18.0Mbps
CMDRESP 0x8006 at 436ms
SSID 'Oct1158-2', MAC FC:D7:33:FE:D6:02, RSSI 40, Channel 11
  Timestamp 2881626271062, Beacon Interval 100, TSF timestamp: 1285332
  Capability: 0x0431 (Security: WPA2, Mode: Infrastructure)
  Rates: 1.0Mbps 2.0Mbps 5.5Mbps 11.0Mbps 6.0Mbps 9.0Mbps 12.0Mbps 18.0Mbps
CMDRESP 0x8006 at 220ms
SSID 'CMCC-t2gW', MAC 74:E1:9A:B7:09:77, RSSI 78, Channel 13
  Timestamp 3020600062583, Beacon Interval 100, TSF timestamp: 1508386
  Capability: 0x0411 (Security: WPA2, Mode: Infrastructure)
  Rates: 1.0Mbps 2.0Mbps 5.5Mbps 11.0Mbps 9.0Mbps 18.0Mbps 36.0Mbps 54.0Mbps
Scan finished!
CMDRESP 0x80b0 at 1ms
[Event] code=0x002e, bss=0x10, size=85, reason=0, mac=08:EA:40:31:21:8B
AP is started!
Packet forwarding mode: by host
CMDRESP 0x80b1 at 6852ms
AP is created!
CMDRESP 0x8028 at 1ms
CMDRESP 0x8006 at 1671ms
CMDRESP 0x80c4 at 7634ms
[Event] code=0x002e, bss=0x10, size=85, reason=0, mac=08:EA:40:31:21:8B
AP is started!
Packet forwarding mode: by host
CMDRESP 0x8012 at 27ms
capability=0x0431, status_code=0x0000, aid=0xc003
Waiting for authentication!
[Event] code=0x0017, bss=0x00, size=77
WMM status change event occurred!
Synchronizing multicast filter! num=1
CMDRESP 0x8010 at 0ms
Multicast filter is synchronized! num=1
[Event] code=0x002b, bss=0x00, size=10
Authenticated!
[Send] len=350, bss=0x00, port=1
[Recv] len=42, bss=0x00, port=1
[Recv] len=42, bss=0x00, port=2
[Recv] len=42, bss=0x00, port=3
[Recv] len=60, bss=0x00, port=4
Join group FF02::1:FF31:218B! bss=0x00
[Send] len=86, bss=0x00, port=2
[Send] len=78, bss=0x00, port=3
Synchronizing multicast filter! num=2
CMDRESP 0x8010 at 0ms
Multicast filter is synchronized! num=2
[Send] len=86, bss=0x00, port=4
[Send] len=86, bss=0x00, port=5
[Send] len=350, bss=0x00, port=6
[Recv] len=590, bss=0x00, port=5
[Send] len=350, bss=0x00, port=7
[Recv] len=590, bss=0x00, port=6
[Send] len=42, bss=0x00, port=8
[Send] len=42, bss=0x00, port=9
[Send] len=42, bss=0x00, port=10
[Send] len=70, bss=0x00, port=11
-- Packet port 1 released at 3133ms
[Send] len=42, bss=0x00, port=1
DHCP supplied address!
IP address: 192.168.1.108
Subnet mask: 255.255.255.0
Default gateway: 192.168.1.1
DNS Server: 192.168.1.1
-- Packet port 2 released at 3021ms
[Send] len=42, bss=0x00, port=2
dns_test: IP of savannah.nongnu.org is not in cache!
Packet port 3 released at 3029ms
Packet port 4 released at 2838ms
Packet port 5 released at 2041ms
Packet port 6 released at 1544ms
Packet port 7 released at 1057ms
Packet port 8 released at 871ms
Packet port 9 released at 553ms
Packet port 10 released at 56ms
[Recv] len=42, bss=0x00, port=7
[Send] len=79, bss=0x00, port=3
[Recv] len=142, bss=0x00, port=8
[Send] len=86, bss=0x00, port=4
[Recv] len=95, bss=0x00, port=9
[Recv] len=86, bss=0x00, port=10
[Send] len=42, bss=0x00, port=5
[Send] len=99, bss=0x00, port=6
[Send] len=78, bss=0x00, port=7
[Recv] len=115, bss=0x00, port=11
DNS Found IP of savannah.nongnu.org: 209.51.188.72
TCP socket is connecting to 209.51.188.72...
[Send] len=62, bss=0x00, port=8
[Recv] len=62, bss=0x00, port=12
TCP socket is connected! err=0
[Send] len=115, bss=0x00, port=9
[Recv] len=54, bss=0x00, port=13
[Recv] len=1498, bss=0x00, port=14
1444 bytes received!
[Recv] len=1498, bss=0x00, port=15
1444 bytes received!
[Send] len=54, bss=0x00, port=10
[Recv] len=1498, bss=0x00, port=1
1444 bytes received!
[Recv] len=1498, bss=0x00, port=2
1444 bytes received!
-- Packet port 11 released at 1610ms
[Send] len=54, bss=0x00, port=11
Packet port 1 released at 1611ms
Packet port 2 released at 1596ms
Packet port 3 released at 1564ms
Packet port 4 released at 1561ms
Packet port 5 released at 632ms
Packet port 6 released at 632ms
Packet port 7 released at 632ms
Packet port 8 released at 620ms
Packet port 9 released at 352ms
[Recv] len=60, bss=0x00, port=3
[Recv] len=1498, bss=0x00, port=4
1444 bytes received!
[Recv] len=1498, bss=0x00, port=5
1444 bytes received!
[Send] len=54, bss=0x00, port=1
[Recv] len=1498, bss=0x00, port=6
1444 bytes received!
[Recv] len=1498, bss=0x00, port=7
1444 bytes received!
[Send] len=54, bss=0x00, port=2
[Send] len=42, bss=0x00, port=3
IPv6 address 1: 2409:8A62:321:6D60:AEA:40FF:FE31:218B
DNS Server: FE80::1
[Send] len=99, bss=0x00, port=4
dns_test: IP of savannah.nongnu.org is not in cache!
[Recv] len=127, bss=0x00, port=8
DNS Found IP of savannah.nongnu.org: 2001:470:142::72
TCP socket is connecting to 2001:470:142::72...
[Send] len=82, bss=0x00, port=5
[Send] len=86, bss=0x00, port=6
[Recv] len=1498, bss=0x00, port=9
1444 bytes received!
[Recv] len=1498, bss=0x00, port=10
1444 bytes received!
[Send] len=54, bss=0x00, port=7
[Recv] len=1498, bss=0x00, port=11
1444 bytes received!
[Recv] len=1498, bss=0x00, port=12
1444 bytes received!
[Send] len=54, bss=0x00, port=8
[Recv] len=86, bss=0x00, port=13
[Send] len=86, bss=0x00, port=9
[Recv] len=82, bss=0x00, port=14
TCP socket is connected! err=0
-- Packet port 10 released at 810ms
[Send] len=135, bss=0x00, port=10
Packet port 1 released at 527ms
Packet port 2 released at 511ms
Packet port 3 released at 423ms
Packet port 4 released at 417ms
Packet port 5 released at 398ms
Packet port 6 released at 331ms
Packet port 7 released at 262ms
Packet port 8 released at 247ms
Packet port 11 released at 823ms
[Recv] len=1498, bss=0x00, port=15
1444 bytes received!
[Recv] len=1498, bss=0x00, port=1
1444 bytes received!
[Send] len=54, bss=0x00, port=11
[Recv] len=1498, bss=0x00, port=2
1444 bytes received!
[Recv] len=1498, bss=0x00, port=3
1444 bytes received!
[Send] len=54, bss=0x00, port=1
[Recv] len=1498, bss=0x00, port=4
1444 bytes received!
[Recv] len=1498, bss=0x00, port=5
1444 bytes received!
[Send] len=54, bss=0x00, port=2
[Recv] len=1498, bss=0x00, port=6
1444 bytes received!
[Recv] len=1498, bss=0x00, port=7
1444 bytes received!
[Send] len=54, bss=0x00, port=3
[Recv] len=74, bss=0x00, port=8
[Recv] len=1498, bss=0x00, port=9
[Send] len=86, bss=0x00, port=4
[Recv] len=1498, bss=0x00, port=10
2848 bytes received!
[Send] len=74, bss=0x00, port=5
[Recv] len=1498, bss=0x00, port=11
[Send] len=86, bss=0x00, port=6
[Recv] len=1498, bss=0x00, port=12
2848 bytes received!
[Send] len=74, bss=0x00, port=7
[Recv] len=1498, bss=0x00, port=13
1444 bytes received!
[Recv] len=1498, bss=0x00, port=14
1444 bytes received!
[Send] len=54, bss=0x00, port=8
[Recv] len=1498, bss=0x00, port=15
1444 bytes received!
[Recv] len=697, bss=0x00, port=1
643 bytes received!
-- Packet port 9 released at 605ms
[Send] len=54, bss=0x00, port=9
-- Packet port 10 released at 598ms
[Send] len=42, bss=0x00, port=10
Packet port 1 released at 551ms
Packet port 2 released at 299ms
Packet port 3 released at 288ms
Packet port 4 released at 273ms
Packet port 5 released at 268ms
Packet port 6 released at 264ms
Packet port 7 released at 259ms
Packet port 11 released at 585ms
[Recv] len=1498, bss=0x00, port=2
1424 bytes received!
[Recv] len=1498, bss=0x00, port=3
1424 bytes received!
[Send] len=74, bss=0x00, port=11
[Recv] len=1498, bss=0x00, port=4
1424 bytes received!
[Recv] len=1498, bss=0x00, port=5
1424 bytes received!
[Send] len=74, bss=0x00, port=1
[Recv] len=1498, bss=0x00, port=6
1424 bytes received!
[Recv] len=1498, bss=0x00, port=7
1424 bytes received!
[Send] len=74, bss=0x00, port=2
[Recv] len=1498, bss=0x00, port=8
1424 bytes received!
[Recv] len=1498, bss=0x00, port=9
1424 bytes received!
[Send] len=74, bss=0x00, port=3
[Recv] len=1498, bss=0x00, port=10
1424 bytes received!
[Recv] len=1498, bss=0x00, port=11
1424 bytes received!
[Send] len=74, bss=0x00, port=4
[Recv] len=1498, bss=0x00, port=12
[Send] len=86, bss=0x00, port=5
[Recv] len=1498, bss=0x00, port=13
2848 bytes received!
[Send] len=74, bss=0x00, port=6
[Recv] len=1498, bss=0x00, port=14
1424 bytes received!
[Recv] len=1498, bss=0x00, port=15
1424 bytes received!
[Send] len=74, bss=0x00, port=7
Packet port 1 released at 990ms
Packet port 2 released at 675ms
Packet port 3 released at 664ms
Packet port 4 released at 348ms
Packet port 5 released at 344ms
Packet port 6 released at 339ms
Packet port 8 released at 1126ms
Packet port 9 released at 1113ms
Packet port 10 released at 1109ms
Packet port 11 released at 1032ms
[Recv] len=1498, bss=0x00, port=1
1424 bytes received!
[Recv] len=1498, bss=0x00, port=2
1424 bytes received!
[Send] len=74, bss=0x00, port=8
[Recv] len=1498, bss=0x00, port=3
1424 bytes received!
[Recv] len=1498, bss=0x00, port=4
1424 bytes received!
[Send] len=74, bss=0x00, port=9
[Recv] len=1498, bss=0x00, port=5
1424 bytes received!
[Recv] len=1177, bss=0x00, port=6
1103 bytes received!
[Send] len=74, bss=0x00, port=10
[Recv] len=86, bss=0x00, port=7
[Send] len=86, bss=0x00, port=11
[Recv] len=54, bss=0x00, port=8
TCP socket is closed! err=0, count=33855
[Send] len=54, bss=0x00, port=1
[Recv] len=54, bss=0x00, port=9
[Recv] len=74, bss=0x00, port=10
TCP socket is closed! err=0, count=33855
[Send] len=74, bss=0x00, port=2
[Send] len=86, bss=0x00, port=3
[Recv] len=78, bss=0x00, port=11
[Recv] len=74, bss=0x00, port=12
[Recv] len=42, bss=0x00, port=13
[Send] len=42, bss=0x00, port=4
[Recv] len=60, bss=0x00, port=14

【程序代码】

WiFi_LowLevel.c:

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

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

#define FLASH_ALIGN_SIZE(size) (((size) + 31) & ~31)
#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 uint16_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_SDMMCInit(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_SetSDMMCBlockSize(uint32_t size);
#ifdef WIFI_FIRMWAREAREA_ADDR
static int WiFi_LowLevel_VerifyFirmware(void);
#endif
static void WiFi_LowLevel_WaitForResponse(const char *msg_title);

#define WIFI_SDMMC_TRANSFER_MODE_MULTIBYTE SDMMC_DCTRL_DTMODE_0
#define WIFI_IS_SDMMC_TRANSFER_MODE(MODE) (((MODE) == SDMMC_TRANSFER_MODE_BLOCK) || \
                                           ((MODE) == WIFI_SDMMC_TRANSFER_MODE_MULTIBYTE) || \
                                           ((MODE) == SDMMC_TRANSFER_MODE_STREAM))
static HAL_StatusTypeDef WiFi_LowLevel_SDMMC_ConfigData(SDMMC_TypeDef *SDMMCx, SDMMC_DataInitTypeDef *Data);

CRC_HandleTypeDef hcrc;
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的SDMMC接口只能接一张卡 (芯片手册上有说明)
static SDMMC_CmdInitTypeDef sdmmc_cmd;
static SDMMC_DataInitTypeDef sdmmc_data;

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

/* 检查并清除错误标志位 */
static int WiFi_LowLevel_CheckError(const char *msg_title)
{
  int err = 0;
  
  if (__SDMMC_GET_FLAG(SDMMC2, SDMMC_FLAG_CCRCFAIL) != RESET)
  {
    __SDMMC_CLEAR_FLAG(SDMMC2, SDMMC_FLAG_CCRCFAIL);
    err++;
    printf("%s: CMD%d CRC failed!\n", msg_title, sdmmc_cmd.CmdIndex);
  }
  if (__SDMMC_GET_FLAG(SDMMC2, SDMMC_FLAG_CTIMEOUT) != RESET)
  {
    __SDMMC_CLEAR_FLAG(SDMMC2, SDMMC_FLAG_CTIMEOUT);
    err++;
    printf("%s: CMD%d timeout!\n", msg_title, sdmmc_cmd.CmdIndex);
  }
  if (__SDMMC_GET_FLAG(SDMMC2, SDMMC_FLAG_DCRCFAIL) != RESET)
  {
    __SDMMC_CLEAR_FLAG(SDMMC2, SDMMC_FLAG_DCRCFAIL);
    err++;
    printf("%s: data CRC failed!\n", msg_title);
  }
  if (__SDMMC_GET_FLAG(SDMMC2, SDMMC_FLAG_DTIMEOUT) != RESET)
  {
    __SDMMC_CLEAR_FLAG(SDMMC2, SDMMC_FLAG_DTIMEOUT);
    err++;
    printf("%s: data timeout!\n", msg_title);
  }
  if (__SDMMC_GET_FLAG(SDMMC2, SDMMC_FLAG_TXUNDERR) != RESET)
  {
    __SDMMC_CLEAR_FLAG(SDMMC2, SDMMC_FLAG_TXUNDERR);
    err++;
    printf("%s: data underrun!\n", msg_title);
  }
  if (__SDMMC_GET_FLAG(SDMMC2, SDMMC_FLAG_RXOVERR) != RESET)
  {
    __SDMMC_CLEAR_FLAG(SDMMC2, SDMMC_FLAG_RXOVERR);
    err++;
    printf("%s: data overrun!\n", msg_title);
  }
#if WIFI_USEDMA
  if (__SDMMC_GET_FLAG(SDMMC2, SDMMC_FLAG_IDMATE) != RESET)
  {
    __SDMMC_CLEAR_FLAG(SDMMC2, SDMMC_FLAG_IDMATE);
    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_SetSDMMCBlockSize(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 (__SDMMC_GET_FLAG(SDMMC2, SDMMC_FLAG_SDIOIT) != RESET)
  {
    if (clear)
      __SDMMC_CLEAR_FLAG(SDMMC2, SDMMC_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 = {0};
  
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();
  
  // 使Wi-Fi模块复位信号(PDN)有效
  gpio.Mode = GPIO_MODE_OUTPUT_PP; // PE6设为推挽输出, 并立即输出低电平
  gpio.Pin = GPIO_PIN_6;
  gpio.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOE, &gpio);
  
  // 撤销Wi-Fi模块的复位信号
  WiFi_LowLevel_Delay(100); // 延时一段时间, 使WiFi模块能够正确复位
  HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_SET);
  
  // SDMMC相关引脚
  // PB3~4: SDMMC2_D2~3, PB14~15: SDMMC2_D0~1, 设为复用推挽输出
  gpio.Alternate = GPIO_AF9_SDMMC2;
  gpio.Mode = GPIO_MODE_AF_PP;
  gpio.Pin = GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_14 | GPIO_PIN_15;
  gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIOB, &gpio);
  
  // PD6: SDMMC2_CK, PD7: SDMMC2_CMD, 设为复用推挽输出
  gpio.Alternate = GPIO_AF11_SDMMC2;
  gpio.Pin = GPIO_PIN_6 | GPIO_PIN_7;
  HAL_GPIO_Init(GPIOD, &gpio);
}

void WiFi_LowLevel_Init(void)
{
  // 在此处打开WiFi模块所需要的除GPIO和SDMMC外所有其他外设的时钟
  __HAL_RCC_CRC_CLK_ENABLE();
  
  hcrc.Instance = CRC;
  hcrc.Init.CRCLength = HAL_CRC_LENGTH_32B;
  hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_WORDS;
  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_SDMMCInit();
}

/* 接收数据, 自动判断采用哪种传输模式 */
// 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
  void *mem = NULL; // 丢弃数据用的内存
#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
  if (bufsize > 0)
  {
    // 正常接收模式
    SDMMC2->IDMABASE0 = (uint32_t)data;
  }
  else
  {
    // 数据丢弃模式
    mem = malloc(size);
    if (mem == NULL)
    {
      printf("%s: out of memory!\n", __FUNCTION__);
      return -2;
    }
    SDMMC2->IDMABASE0 = (uint32_t)mem;
  }
  SDMMC2->IDMACTRL = SDMMC_ENABLE_IDMA_SINGLE_BUFF; // 设为单缓冲区模式, 并打开IDMA
#endif
  
  if (flags & WIFI_RWDATA_ADDRINCREMENT)
    cmd53_flags |= CMD53_INCREMENTING;
  if (block_num)
  {
    sdmmc_data.TransferMode = SDMMC_TRANSFER_MODE_BLOCK;
    WiFi_LowLevel_SendCMD53(func, addr, block_num, cmd53_flags | CMD53_BLOCKMODE);
  }
  else
  {
    sdmmc_data.TransferMode = WIFI_SDMMC_TRANSFER_MODE_MULTIBYTE;
    WiFi_LowLevel_SendCMD53(func, addr, size, cmd53_flags);
  }
  
  sdmmc_data.DataLength = size;
  sdmmc_data.DPSM = SDMMC_DPSM_ENABLE;
  sdmmc_data.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC;
  WiFi_LowLevel_SDMMC_ConfigData(SDMMC2, &sdmmc_data);
  
#if !WIFI_USEDMA
  while (size > 0)
  {
    if (__SDMMC_GET_FLAG(SDMMC2, SDMMC_FLAG_RXFIFOE) == RESET)
    {
      // 如果有数据到来就读取数据
      size -= 4;
      if (bufsize > 0)
        *p++ = SDMMC_ReadFIFO(SDMMC2);
      else
        SDMMC_ReadFIFO(SDMMC2); // 读寄存器, 但不保存数据
    }
    else
    {
      // 如果出现错误, 则退出循环
      err += WiFi_LowLevel_CheckError(__FUNCTION__);
      if (err)
        break;
    }
  }
#endif
  
  // 等待数据接收完毕
  i = 0;
  while (__SDMMC_GET_FLAG(SDMMC2, SDMMC_FLAG_CMDACT) != RESET || __SDMMC_GET_FLAG(SDMMC2, SDMMC_FLAG_DATAEND) == RESET)
  {
    err += WiFi_LowLevel_CheckError(__FUNCTION__);
    if (err)
      break;
    
    i++;
    if (i == CMD53_TIMEOUT)
    {
      printf("%s: timeout!\n", __FUNCTION__);
      err++;
      break;
    }
  }
  sdmmc_data.DPSM = SDMMC_DPSM_DISABLE;
  WiFi_LowLevel_SDMMC_ConfigData(SDMMC2, &sdmmc_data);
#if WIFI_USEDMA
  if (mem != NULL)
    free(mem);
  __SDMMC_CLEAR_FLAG(SDMMC2, SDMMC_FLAG_IDMABTC); // 清除IDMA传输完成标志位
  SDMMC2->IDMACTRL = SDMMC_DISABLE_IDMA; // 关闭IDMA
#endif
  
  // 清除相关标志位
  __SDMMC_CLEAR_FLAG(SDMMC2, SDMMC_FLAG_CMDREND | SDMMC_FLAG_DATAEND | SDMMC_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 SDMMC_GetResponse(SDMMC2, SDMMC_RESP1) & 0xff;
}

/* 重定义SDMMC_ConfigData函数 */
// STM32H7 SDMMC外设的SDMMC_DCTRL_DTMODE一共定义了4种传输模式:
//   00: Block data transfer ending on block count.
//   01: SDIO multibyte data transfer.
//   10: eMMC Stream data transfer. (WIDBUS shall select 1-bit wide bus mode)
//   11: Block data transfer ending with STOP_TRANSMISSION command (not to be used with DTEN initiated data transfers).
// 然而, V1.9.0版本的STM32H7 HAL库只定义了其中两种模式:
//   00: SDMMC_TRANSFER_MODE_BLOCK
//   10: SDMMC_TRANSFER_MODE_STREAM
// SDMMC_TRANSFER_MODE_STREAM是eMMC专用的流模式, 不能用于SDIO 1线模式下CMD53发送数据, 否则会卡死在while循环中
// 如果强行设置sdmmc_data.TransferMode=SDMMC_DCTRL_DTMODE_0选01模式, SDMMC_ConfigData库函数会报assertion failed错误
// 因此在这里重定义一个新的SDMMC_ConfigData函数
static HAL_StatusTypeDef WiFi_LowLevel_SDMMC_ConfigData(SDMMC_TypeDef *SDMMCx, SDMMC_DataInitTypeDef *Data)
{
  uint32_t tmpreg = 0;

  /* Check the parameters */
  assert_param(IS_SDMMC_DATA_LENGTH(Data->DataLength));
  assert_param(IS_SDMMC_BLOCK_SIZE(Data->DataBlockSize));
  assert_param(IS_SDMMC_TRANSFER_DIR(Data->TransferDir));
  assert_param(WIFI_IS_SDMMC_TRANSFER_MODE(Data->TransferMode));
  assert_param(IS_SDMMC_DPSM(Data->DPSM));

  /* Set the SDMMC Data TimeOut value */
  SDMMCx->DTIMER = Data->DataTimeOut;

  /* Set the SDMMC DataLength value */
  SDMMCx->DLEN = Data->DataLength;

  /* Set the SDMMC data configuration parameters */
  tmpreg |= (uint32_t)(Data->DataBlockSize | \
                       Data->TransferDir   | \
                       Data->TransferMode  | \
                       Data->DPSM);

  /* Write to SDMMC DCTRL */
  MODIFY_REG(SDMMCx->DCTRL, DCTRL_CLEAR_MASK, tmpreg);

  return HAL_OK;
}

/* 初始化SDMMC外设并完成WiFi模块的枚举 */
// SDIO Simplified Specification Version 3.00: 3. SDIO Card Initialization
static void WiFi_LowLevel_SDMMCInit(void)
{
  SDMMC_InitTypeDef sdmmc = {0};
  uint32_t freq, resp;
  
  // 在H7单片机中, 必须要开启PLL才能使用SDMMC外设
  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");
  }
  
  // SDMMC外设的时钟由RCC_D1CCIPR_SDMMCSEL位选择, 默认是PLL1_Q
  __HAL_RCC_SDMMC2_CLK_ENABLE();
  
  SDMMC_PowerState_ON(SDMMC2); // 打开SDMMC外设
  sdmmc.ClockDiv = WiFi_LowLevel_CalcClockDivider(400000, &freq); // 初始化时最高允许的频率: 400kHz
  SDMMC_Init(SDMMC2, sdmmc);
  
  __SDMMC_OPERATION_ENABLE(SDMMC2); // 设为SDIO模式
  
  // 不需要发送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 */
  sdmmc_cmd.Argument = 0;
  sdmmc_cmd.CmdIndex = 5;
  sdmmc_cmd.CPSM = SDMMC_CPSM_ENABLE;
  sdmmc_cmd.Response = SDMMC_RESPONSE_SHORT; // 接收短回应
  sdmmc_cmd.WaitForInterrupt = SDMMC_WAIT_NO;
  SDMMC_SendCommand(SDMMC2, &sdmmc_cmd);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  printf("RESPCMD%d, RESP1_%08x\n", SDMMC_GetCommandResponse(SDMMC2), SDMMC_GetResponse(SDMMC2, SDMMC_RESP1));
  
  /* 设置参数VDD Voltage Window: 3.2~3.4V, 并再次发送CMD5 */
  sdmmc_cmd.Argument = 0x300000;
  SDMMC_SendCommand(SDMMC2, &sdmmc_cmd);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  resp = SDMMC_GetResponse(SDMMC2, SDMMC_RESP1);
  printf("RESPCMD%d, RESP1_%08x\n", SDMMC_GetCommandResponse(SDMMC2), 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)) */
  sdmmc_cmd.Argument = 0;
  sdmmc_cmd.CmdIndex = 3;
  SDMMC_SendCommand(SDMMC2, &sdmmc_cmd);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  sdio_rca = SDMMC_GetResponse(SDMMC2, SDMMC_RESP1) >> 16;
  printf("Relative Card Address: 0x%04x\n", sdio_rca);
  
  /* 选中WiFi模块 (CMD7: SELECT/DESELECT_CARD) */
  sdmmc_cmd.Argument = sdio_rca << 16;
  sdmmc_cmd.CmdIndex = 7;
  SDMMC_SendCommand(SDMMC2, &sdmmc_cmd);
  WiFi_LowLevel_WaitForResponse(__FUNCTION__);
  printf("Card selected! RESP1_%08x\n", SDMMC_GetResponse(SDMMC2, SDMMC_RESP1));
  
  /* 提高时钟频率, 并设置数据超时时间为0.1s */
  sdmmc.ClockDiv = WiFi_LowLevel_CalcClockDivider(WIFI_CLOCK_FREQ, &freq);
  sdmmc_data.DataTimeOut = freq / 10;
  
  /* SDMMC外设的总线宽度设为4位 */
  sdmmc.BusWide = SDMMC_BUS_WIDE_4B;
  SDMMC_Init(SDMMC2, sdmmc);
  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)
{
  sdmmc_cmd.Argument = (func << 28) | (addr << 9) | data | flags;
  sdmmc_cmd.CmdIndex = 52;
  sdmmc_cmd.CPSM = SDMMC_CPSM_ENABLE;
  sdmmc_cmd.Response = SDMMC_RESPONSE_SHORT;
  sdmmc_cmd.WaitForInterrupt = SDMMC_WAIT_NO;
  SDMMC_SendCommand(SDMMC2, &sdmmc_cmd);
}

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

/* 设置WiFi模块功能区的数据块大小 */
int WiFi_LowLevel_SetBlockSize(uint8_t func, uint32_t size)
{
  if (WiFi_LowLevel_SetSDMMCBlockSize(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;
}

/* 设置SDMMC外设的数据块大小 */
static int WiFi_LowLevel_SetSDMMCBlockSize(uint32_t size)
{
  switch (size)
  {
    case 1:
      sdmmc_data.DataBlockSize = SDMMC_DATABLOCK_SIZE_1B;
      break;
    case 2:
      sdmmc_data.DataBlockSize = SDMMC_DATABLOCK_SIZE_2B;
      break;
    case 4:
      sdmmc_data.DataBlockSize = SDMMC_DATABLOCK_SIZE_4B;
      break;
    case 8:
      sdmmc_data.DataBlockSize = SDMMC_DATABLOCK_SIZE_8B;
      break;
    case 16:
      sdmmc_data.DataBlockSize = SDMMC_DATABLOCK_SIZE_16B;
      break;
    case 32:
      sdmmc_data.DataBlockSize = SDMMC_DATABLOCK_SIZE_32B;
      break;
    case 64:
      sdmmc_data.DataBlockSize = SDMMC_DATABLOCK_SIZE_64B;
      break;
    case 128:
      sdmmc_data.DataBlockSize = SDMMC_DATABLOCK_SIZE_128B;
      break;
    case 256:
      sdmmc_data.DataBlockSize = SDMMC_DATABLOCK_SIZE_256B;
      break;
    case 512:
      sdmmc_data.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B;
      break;
    case 1024:
      sdmmc_data.DataBlockSize = SDMMC_DATABLOCK_SIZE_1024B;
      break;
    case 2048:
      sdmmc_data.DataBlockSize = SDMMC_DATABLOCK_SIZE_2048B;
      break;
    case 4096:
      sdmmc_data.DataBlockSize = SDMMC_DATABLOCK_SIZE_4096B;
      break;
    case 8192:
      sdmmc_data.DataBlockSize = SDMMC_DATABLOCK_SIZE_8192B;
      break;
    case 16384:
      sdmmc_data.DataBlockSize = SDMMC_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 != 255536)
    return 0; // 检验成功, 但检验结果为固件不完整, 所以不返回-1, 返回0
  
  len = 2 * FLASH_ALIGN_SIZE(4) + FLASH_ALIGN_SIZE(WIFI_FIRMWARE_SIZE);
  crc = HAL_CRC_Calculate(&hcrc, (uint32_t *)WIFI_FIRMWAREAREA_ADDR, len / 4);
  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)
      abort();
    
    if (i != 0)
      SDMMC_SendCommand(SDMMC2, &sdmmc_cmd); // 重发命令
    i++;
    
    while (__SDMMC_GET_FLAG(SDMMC2, SDMMC_FLAG_CMDACT) != RESET); // 等待命令发送完毕
    WiFi_LowLevel_CheckError(msg_title);
  } while (__SDMMC_GET_FLAG(SDMMC2, SDMMC_FLAG_CMDREND) == RESET); // 如果没有收到回应, 则重试
  __SDMMC_CLEAR_FLAG(SDMMC2, SDMMC_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)
  {
    sdmmc_data.TransferMode = SDMMC_TRANSFER_MODE_BLOCK;
    WiFi_LowLevel_SendCMD53(func, addr, block_num, cmd53_flags | CMD53_BLOCKMODE);
  }
  else
  {
    sdmmc_data.TransferMode = WIFI_SDMMC_TRANSFER_MODE_MULTIBYTE;
    WiFi_LowLevel_SendCMD53(func, addr, size, cmd53_flags);
  }
  WiFi_LowLevel_WaitForResponse(__FUNCTION__); // 必须要等到CMD53收到回应后才能开始发送数据
  
  // 开始发送数据
#if WIFI_USEDMA
  SDMMC2->IDMABASE0 = (uint32_t)data;
  SDMMC2->IDMACTRL = SDMMC_ENABLE_IDMA_SINGLE_BUFF; // 设为单缓冲区模式, 并打开IDMA
#endif
  
  sdmmc_data.DataLength = size;
  sdmmc_data.DPSM = SDMMC_DPSM_ENABLE;
  sdmmc_data.TransferDir = SDMMC_TRANSFER_DIR_TO_CARD;
  WiFi_LowLevel_SDMMC_ConfigData(SDMMC2, &sdmmc_data);
  
#if !WIFI_USEDMA
  while (size > 0)
  {
    size -= 4;
    SDMMC_WriteFIFO(SDMMC2, (uint32_t *)p); // 向FIFO送入4字节数据
    p++;
    while (__SDMMC_GET_FLAG(SDMMC2, SDMMC_FLAG_TXFIFOF) != RESET); // 如果FIFO已满则等待
    
    // 如果出现错误, 则退出循环
    err += WiFi_LowLevel_CheckError(__FUNCTION__);
    if (err)
      break;
  }
#endif
  
  // 等待发送完毕
  i = 0;
  while (__SDMMC_GET_FLAG(SDMMC2, SDMMC_FLAG_DATAEND) == RESET)
  {
    err += WiFi_LowLevel_CheckError(__FUNCTION__);
    if (err)
      break;
    
    i++;
    if (i == CMD53_TIMEOUT)
    {
      printf("%s: timeout!\n", __FUNCTION__); // 用于跳出TXACT始终不清零, 也没有错误标志位置位的情况
      err++;
      break;
    }
  }
  sdmmc_data.DPSM = SDMMC_DPSM_DISABLE;
  WiFi_LowLevel_SDMMC_ConfigData(SDMMC2, &sdmmc_data);
#if WIFI_USEDMA
  __SDMMC_CLEAR_FLAG(SDMMC2, SDMMC_FLAG_IDMABTC); // 清除IDMA传输完成标志位
  SDMMC2->IDMACTRL = SDMMC_DISABLE_IDMA; // 关闭IDMA
#endif
  
  // 清除相关标志位
  __SDMMC_CLEAR_FLAG(SDMMC2, SDMMC_FLAG_DATAEND | SDMMC_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 SDMMC_GetResponse(SDMMC2, SDMMC_RESP1) & 0xff;
}

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
STM32H743是一款高性能的微控制器,它内置了SDMMC控制器,可以用于SD卡和MMC卡的读写操作。下面是使用STM32CubeMX和RT-Thread Studio进行SDMMC配置的详细过程: 1. 打开STM32CubeMX,选择对应的芯片型号,打开SDMMC功能。 2. 配置SDMMC时钟,使其与SD卡或MMC卡的时钟匹配。 3. 配置SDMMC的数据线和命令线,使其与SD卡或MMC卡的接口匹配。 4. 生成代码并导入到RT-Thread Studio中。 5. 在RT-Thread Studio中编写SDMMC读写操作的代码,例如: ```c #include "stm32h7xx_hal.h" #include "drv_sdmmc.h" #define SDMMC_BLOCK_SIZE 512 SD_HandleTypeDef hsd1; int sdmmc_init(void) { hsd1.Instance = SDMMC1; hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING; hsd1.Init.ClockBypass = SDMMC_CLOCK_BYPASS_DISABLE; hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE; hsd1.Init.BusWide = SDMMC_BUS_WIDE_1B; hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; hsd1.Init.ClockDiv = 2; if (HAL_SD_Init(&hsd1) != HAL_OK) { return -1; } return 0; } int sdmmc_read(uint32_t block_addr, uint8_t *buf, uint32_t block_cnt) { if (HAL_SD_ReadBlocks(&hsd1, buf, block_addr, block_cnt, SDMMC_TIMEOUT) != HAL_OK) { return -1; } return 0; } int sdmmc_write(uint32_t block_addr, uint8_t *buf, uint32_t block_cnt) { if (HAL_SD_WriteBlocks(&hsd1, buf, block_addr, block_cnt, SDMMC_TIMEOUT) != HAL_OK) { return -1; } return 0; } ``` 以上代码中,sdmmc_init()函数用于初始化SDMMC控制器,sdmmc_read()函数用于读取SD卡或MMC卡中的数据,sdmmc_write()函数用于向SD卡或MMC卡中写入数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

巨大八爪鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值