该程序是旧版本!最新版本为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口 | 复用功能 | 布线长度 |
---|---|---|---|
PDN | PE6 | ||
SDMMC2_D0 | PB14 | AF9 | 3150mil |
SDMMC2_D1 | PB15 | AF9 | 3150mil |
SDMMC2_D2 | PB3 | AF9 | 3150mil |
SDMMC2_D3 | PB4 | AF9 | 3150mil |
SDMMC2_CK | PD6 | AF11 | 6300mil |
SDMMC2_CMD | PD7 | AF11 | 3150mil |
【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模式。
【测速结果】
自制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;
}