STM32外设SDIO和SD卡的配置

本文详细介绍了SD卡的类型、容量标准、速度等级及其内部结构,包括存储单元、电源检测、卡及接口控制器和接口驱动器。还解析了SD卡的PIN脚定义、总线协议、命令格式和响应类型。接着,文章探讨了STM32的SDIO接口,包括其结构、时钟管理、数据传输模式以及数据路径状态机。最后,讲解了SD卡的热插拔检测、初始化过程和读写操作,包括普通读写和DMA读写。
摘要由CSDN通过智能技术生成

01 SD Card简介

SD卡(SD Card,Secure Digital Memory Card),是一种安全数码卡。现在已经被TF卡(MicroSD)取代。TF卡已经成为最广泛应用于数码、存储等便携设备上的新型快闪存储卡。SD卡和TF卡有不同的容量标准和速度等级。

在这里插入图片描述

容量标准

SD卡和TF卡的容量标准如下表所示。

在这里插入图片描述

SD容量标准TF容量标准容量范围磁盘格式
SDMicroSD最大至2GBFAT12、FAT16
SDHCmicroSDHC2GB至32GBFAT32
SDXCmicroSDXC32GB至2TBexFAT
SDUCmicroSDXC2TB至128TBexFAT

速度等级

速度等级如下表所示。

在这里插入图片描述

SD卡的结构

SD卡的结构组成包括存储单元、存储单元的接口、电源检测、卡及接口控制器和接口驱动器。

  • 存储单元

是存储数据的部件,存储单元通过存储单元接口与卡控制单元进行数据的传输;

  • 电源检测单元

保证SD卡工作在合适的电压下,如果出现掉电或者异常状态时,它会使控制单元和存储单元接口复位;

  • 卡及接口控制单元

控制SD卡的运行状态,包含几个特殊寄存器;

  • 接口驱动器

控制SD卡引脚的输入和输出。

在这里插入图片描述

PIN脚定义

标准SD卡有九个对外的触点,SD模式和SPI模式下的引脚定义不同。

  • DAT线和CMD线需要接上拉电阻。
  • CD线在power up后能够作为SD模式的“卡检测”端口或SPI模式的片选端。
  • RESERVED引脚也需外接上拉电阻,避免浮空输入导致功耗的增加。

img

SD Mode
序号引脚功能
1CD/DAT3Card detect / Data line[bit3]
2CMDCommand/Response
3VSS1Supply voltage ground
4VDDSupply voltage
5CLKClock
6VSS2Supply voltage ground
7DAT0Data line[bit0]
8DAT1Data line[bit1]
9DAT2Data line[bit2]
SPI Mode
序号引脚功能
1CSChip Select(active low)
2DataIn(MOSI)Host-to-card Commands and Data
3VSS1Supply voltage ground
4VDDSupply voltage
5CLKClock
6VSS2Supply voltage ground
7DataOut(MISO)Card-to-host Data and Status
8RSVReserved
9RSVReserved
存储单元

读写SD卡数据的基本单位是1 byte,所有数据都是以block的形式来传输的。SDHC标准卡的数据块长度为512 bytes。每个sector扇区的block大小是固定的,定义在CSD寄存器中。

block长度可以通过CSD寄存器自定义

在这里插入图片描述

SD卡的寄存器

NameWidth(bit)Description
CID128卡标识号
RCA16卡相对地址,在初始化的时候确定
CSD128卡描述数据,卡操作条件的信息
SCR64SD卡配置寄存器,SD卡的特殊信息
OCR32操作条件寄存器
SSR512SD卡状态寄存器
CSR32卡状态寄存器
OCR

Operation Conditions Register (OCR),操作条件寄存器,存储着SD卡的VDD电压配置文件,寄存器结构如下。

img

CID

Card Identification Register (CID),卡标识寄存器,长度共16字节,包含唯一的卡识别码。寄存器结构如下图所示。

在这里插入图片描述

CSD

Card Specific Data (CSD) Register,卡描述数据寄存器,用于获取SD卡的信息。具体结构大家可以查看官方手册。

RCA

Relative Card Address (RCA),卡相对地址寄存器,此地址用于卡识别程序后寻址的主机卡通信。

此外还有SSR和CSR等,而SPI模式下的寄存器基本一致,有些微小区别,具体可以查看官方手册。

SD总线

根据PIN脚定义可知,SD卡的MMC(MultiMediaCard)总线包括下图中的信号。

  • SD卡主从两个方向的传输都只以CLK 的上升沿有效。
  • SD卡的整个操作过程中会使用到两种不同频率的时钟来同步数据,一个是卡识别阶段的时钟频率FOD,最高为400kHz。另外一个是数据传输模式下的时钟频率FPP,默认最高为25MHz。如果通过相关寄存器配置使SD总线工作在高速模式,则最高频率可达到50MHz。

img

特点

1.通电后,默认情况下,SD卡只使用DAT0进行数据的传输
2.完成初始化后,SD总线允许动态配置数据线的数量

SPI总线

和普通SPI总线一样,带有SCK、CS、MOSI和MISO四个信号。SPI的相关SD配置,这里不展开,具体可以查看官方手册。

img

SD总线协议

SD 总线上的通信是基于命令和数据传输的。通信过程由一个起始位(“0”)启动,而由一个停止位(“1”) 终止。

SD总线的基本交互过程是命令和响应的交互,一般是主机向从机发送一个命令(Command),从设备在接收到命令后作出响应(Response),如果有数据的传递需求,则会有数据(Data)的传输参与到整个通信过程中。

在这里插入图片描述

命令格式

SD 命令格式固定为48bit,都是通过CMD 线连续传输的。

Bit position4746[45 : 40][39 : 8][7 : 1]0
Width(bits)1163271
Value‘0’‘1’xxx‘1’
DescriptionStart bitTransmission bitCommand indexArgumentCRC7End bit
命令类型

命令类型共有四种,分别是:

1.Broadcast(bc),无响应广播命令,发送到所有卡,不返回任务响应;

2.Broadcast w/Response(bcr),带响应广播命令,发送到所有卡,同时接收来自所有卡响应;

3.Addressed point-to-point(ac),寻址命令,发送到选定卡,DAT 线无数据传输;

4.Addressed point-to-point data transfer(adtc),寻址数据传输命令,发送到选定卡,DAT 线有数据传输。

另外还有两种通用命令,特定应用命令(ACMD) 和常规命令(GEN_CMD)。其中ACMD命令发送前,需要先发送CMD55 命令。

基本命令(Class 0)
命令序号类型参数响应缩写描述
CMD0bc[31:0] stuff bitsGO_IDLE_STATE复位所有卡到idle状态
CMD2bcr[31:0] stuff bitsR2ALL_SEND_CID要求所有卡通过CMD返回CID值
CMD4bcr[31:0] stuff bitsR6SEND_RELATIVE_ADDR编程所有卡的DSR
CMD7ac[31:16] RCA [15:0] don’t careR1bSELECT/DESELECT_CARD选中/取消选中RCA地址对应的卡
CMD8bcr[31:12] RSV [7:0] VHSR7SEND_IF_COND发送卡接口条件,包括主机支持的电压等信息,并询问卡支持与否
CMD9ac[31:16] RCA [15:0] stuff bitsR2SEND_CSD通过CMD发送CSD内容到选定卡
CMD10ac[31:16] RCA [15:0] stuff bitsR2SEND_CID通过CMD发送CID内容到选定卡
CMD12ac[31:0] stuff bitsR1bSTOP_TRANSMISSION强制停止卡传输
CMD13ac[31:16] RCA [15:0] stuff bitsR1SEND_STATUS通过CMD发送选定卡的状态寄存器
CMD15ac[31:16] RCA [15:0] stuff bitsGO_INACTIVE_STATE使选定卡进入"inactive"状态
block读命令(Class 2)
命令序号类型参数响应缩写描述
CMD16ac[31:0] 块长度R1SET_BLOCK_LEN可设置标准SD卡的块长度,SDHC标准的SD卡命令长度固定为512bytes
CMD17adtc[31:0] 数据地址R1READ_SINGLE_BLOCK读取标准卡SEL_BLOCK_LEN的长度字节块,SDHC标准的SD卡固定读取512字节的block
CMD18adtc[31:0] 数据地址R1READ_MULTIPLE_BLOCK连续从SD卡读取数据block,直到被CMD12中断。其中块的长度和CMD17的相同
block写命令(Class 4)
命令序号类型参数响应缩写描述
CMD24adtc[31:0] 数据地址R1WRITE_BLOCK写入标准卡SEL_BLOCK_LEN的长度字节块,SDHC标准的SD卡固定读取512字节的block
CMD25adtc[31:0] 数据地址R1WRITE_MULTIPLE_BLOCK连续向SD卡写入数据block,直到被CMD12中断。其中块的长度和CMD17的相同
CMD27adtc[31:0] stuff bitsR1PROGRAM_CSD对CSD的可编程位进行编程
擦除命令(Class 5)
命令序号类型参数响应缩写描述
CMD32ac[31:0] 数据地址R1ERASE_WR_BLK_START设置擦除的起始块地址
CMD33ac[31:0] 数据地址R1ERASE_WR_BLK_END设置擦除的结束块地址
CMD38ac[31:0] stuff bitsR1bERASE擦除预先选定的块
锁卡命令(Class 7)
命令序号类型参数响应缩写描述
CMD32adtc[31:0] stuff bitsR1LOCK_UNLOCK加锁/解锁 SD卡
特定应用命令(Class 8)
命令序号类型参数响应缩写描述
CMD55ac[31:16] RCA [15:0] stuff bitsR1APP_CMD指定下个命令为特定应用命令,不是标准命令
CMD56adtc[31:1] stuff bits [0] RD/WRR1GEN_CMD在通用命令或特定应用命令中,用于传输一个数据块。最低位用于表示读数据或写数据
SD卡特定应用命令
命令序号类型参数响应缩写描述
ACMD6ac[31:2] stuff bits [1:0] Bus widthR1SET_BUS_WIDTH定义数据总线宽度 (‘00’ = 1bit,‘10’ = 4bits)
ACMD13adtc[31:0] stuff bitsR1SD_STATUS发送SD状态
ACMD41bcr[31:0] OCRR3SD_SEND_OP_COND要求卡发送它所支持的信息(HCS)和OCR寄存器内容
ACMD51adtc[31:0] stuff bitsR1SEND_SCR读取配置寄存器SCR
响应

响应由SD 卡通过CMD线向主机发出,有些命令要求响应,有些不要求,SDIO 总线共有7 种响应类型(R1 ~ R7)。

SD 卡不支持R4和R5 类型的响应

在这里插入图片描述

R1标准响应

如果有传输到卡的信号,那么在数据线上可能有busy的信号(R1b)出现

Bit position4746[45 : 40][39 : 8][7 : 1]0
Width(bits)1163271
Value‘0’‘0’xxx‘1’
DescriptionStart bitTransmission bitCommand indexCard statusCRC7End bit
R2 CID,CSD寄存器响应

CID寄存器的值是CMD2和CMD10命令的响应,而CSD寄存器内容是CMD9命令的响应

Bit position135134[133 : 128][127 : 1]0
Width(bits)1161271
Value‘0’‘0’‘111111’x‘1’
DescriptionStart bitTransmission bitReservedCID or CSD寄存器的值End bit
R3 OCR寄存器响应

OCR寄存器的值是ACMD41命令的响应

Bit position4746[45 : 40][39 : 8][7 : 1]0
Width(bits)1163271
Value‘0’‘0’‘111111’x‘111111’‘1’
DescriptionStart bitTransmission bitReservedOCR RegisterReservedEnd bit
R6 发布的RCA寄存器响应

专用于CMD3的响应

在这里插入图片描述

R7 发布的RCA寄存器响应

专用于CMD8命令的响应,返回卡所支持的电压范围和检测模式

Bit position4746[45 : 40][39 : 20][19 : 16][15 : 8][7 : 1]
Width(bits)11620487
Value‘0’‘0’‘001000’‘00000h’xxx
DescriptionStart bitTransmission bitCMD8ReservedRev VoltageCheck ModeCRC7
数据包

使用所有wide bus时,每一次数据传输4个bit,每条线路的CRC是独立计算和校验的。SD卡只在DAT0线路上向主机发送CRC状态响应和Standard busy信号。下图中CMD53命令和ACMD13命令的两个数据包传输例子。

在这里插入图片描述

在这里插入图片描述

卡状态类型和操作模式

在这里插入图片描述

卡识别模式

在该模式下,主机会对所有处于“卡识别模式”的SD 卡进行复位操作,然后确认其工作电压范围,识别SD卡类型,并且获取SD 卡的相对地址(RCA)。下图是该模式下的SD卡状态转换图。

1.主机也可以发送 CMD0让所有卡软复位进入空闲状态,但当前处于Inactive State的卡不会复位
2.主机在与卡开始通信前,需要先确认双方在互相支持的电压范围内。SD 卡内的寄存器有一个电压支持范围,主机当前的电压必须在该范围内才能与卡正常通信,CMD8可以用于该操作。
3.ACMD41命令可以识别或拒绝不匹配主机电压范围的SD卡
4.CMD2命令要求所有卡返回CID
5.CMD3命令是让卡推荐一个RCA,RCA是16bit的地址,相对于使用CID(128bit)进行通信会更简化

在这里插入图片描述

数据传输模式

SD 卡系统处于数据传输模式下,主机才可以对其进行数据读写。数据传输模式下可以将主机SD

时钟频率设置为FPP,默认最高为25MHz。

1.数据传输模式下,主机和目标卡通信都是通过RCA来点对点通信的
2.CMD7命令用来选中和取消选中卡

img

02 STM32的SDIO接口

以下内容以STM32F407xx为例。

SDIO的结构

STM32的SDIO由SDIO适配器和APB2接口组成。其中,SDIO适配器提供主机功能,包括SD时钟、发送命令和数据传输等。而APB2接口用于控制SDIO适配器的寄存器,并可产生中断和DMA请求。

img

SDIO的时钟
SDIOCLK

是SDIO适配器的时钟,频率为48MHz。

PCLK2

是APB2总线的时钟。

SDIO_CK

是SDIO接口与SD卡同步的时钟,时钟源是SDIOCLK = 48MHz。当使能BYPASS模式时,SDIO_CK = SDIOCLK = HCLK。而禁止BYPASS时,SDIO_CK = SDIOCLK / (2 + CLKDIV)。

注意
1.配置时钟时,要留意不能超过25MHz
2.PCLK >= (3 / 8) x SDIO_CK

SDIO适配器

SDIO 适配器是SD卡系统中的主机端。共由五个单元组成,分别是:

  • 控制单元
  • 适配器寄存器单元
  • 命令路径单元
  • 数据路径单元
  • 数据FIFO

1.适配器寄存器和 FIFO 使用 APB2 总线时钟域 (PCLK2)
2.控制单元、命令路径和数据路径使用 SDIO 适配器时钟域 (SDIOCLK)

在这里插入图片描述

控制单元

控制单元由电源管理子单元和时钟管理子单元组成。电源管理子单元会在断电阶段和上电阶段中禁止卡总线输出信号。而时钟管理子单元负责生成和控制 SDIO_CK 信号。

在这里插入图片描述

命令路径

命令路径单元向卡发送命令并从卡接收响应。而命令的格式可以看上述“SD总线协议 - 命令格式”章节。

命令路径以半双工模式运行,因此CMD线可以发送或者接收命令和响应

在这里插入图片描述

命令路径状态机

当发送命令和接收响应时,会启动CPSM状态机。

命令超时为64个SDIO_CK 时钟周期的固定值

img

响应

SDIO 支持两种响应类型,以适配SD卡的响应。两种类型均使用 CRC 错误校验。包括:

  • 136位长响应
  • 48位短响应

响应的格式可以看上述“SD总线协议 - 响应”章节。

数据路径

数据路径子单元负责主机与卡相互传输数据。如果使能了 4 位宽度的总线模式,则 使用所有四个数据信号线 (SDIO_D[3:0]) 在每个时钟周期内传输 4 个数据位,8位宽度则是8个数据位。数据包的格式可以看上述“SD总线协议 - 数据包”章节。

未使能宽总线模式时,则每个时钟周期使用 SDIO_D0传输一位数据位

img

数据路径状态机

DPSM 以 SDIO_CK 的频率运行,卡总线信号上的数据与 SDIO_CK 的上升沿保持同步。

img

数据FIFO

数据 FIFO子单元是一个数据缓冲器,带有发送和接收单元。传输 FIFO和接收FIFO是互斥关系的。传输或接收的标志可以触发中断或DMA请求。

数据 FIFO是在 APB2 时钟域 (PCLK2) 中运行的,所以来自 SDIO 时钟域 (SDIOCLK) 子单元中的所有信号都将要重新同步

03 SD卡的配置和应用

SD卡热插拔检测

SD卡的热插拔可以通过CD/DAT3信号脚来检测,实现的步骤如下。

1.将CD/DAT3 信号连在MCU中断引脚上,并通过510K电阻下拉,而MCU配置为高电平触发中断;

2.在没有SD卡插入时,该信号为低电平,一但有SD卡插入,SD卡内部通过50KΩ把 DATA3 信号拉高至高电平,MCU随即产生一个中断,再在中断服务函数里写处理逻辑,从而实现热插拔的功能。

SD卡的初始化

SD卡的初始化一般分为“SDIO初始化”、“上电”、“获取卡信息”和“设置数据传输模式”四个过程。具体过程可以查看上述“SD总线协议 - 卡识别模式”章节。

img

SDIO初始化

初始化SDIO为1位总线宽度,频率不超过400KHz。

/* SDIO Initialization Frequency (400KHz max) */
#define SDIO_INIT_CLK_DIV     ((uint8_t)0x76)    /* 48MHz / (SDMMC_INIT_CLK_DIV + 2) < 400KHz */

SD_InitTypeDef Init;

/* Default SDIO peripheral configuration for SD card initialization */
Init.ClockEdge           = SDIO_CLOCK_EDGE_RISING;
Init.ClockBypass         = SDIO_CLOCK_BYPASS_DISABLE;
Init.ClockPowerSave      = SDIO_CLOCK_POWER_SAVE_DISABLE;
Init.BusWide             = SDIO_BUS_WIDE_1B;
Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
Init.ClockDiv            = SDIO_INIT_CLK_DIV;

/* Initialize SDIO peripheral interface with default configuration */
SDIO_Init(hsd->Instance, Init);
SD卡上电

SD卡的上电过程如下图所示。注意上电后要给74个clock,给足够长的时间SD卡准备。

在这里插入图片描述

/* Disable SDIO Clock */
__HAL_SD_DISABLE(hsd);

/* Set Power State to ON */
(void)SDIO_PowerState_ON(hsd->Instance);

/* Enable SDIO Clock */
__HAL_SD_ENABLE(hsd);

/* Identify card operating voltage */
errorstate = SD_PowerON(hsd);
获取卡信息
/* Card initialization */
SD_InitCard(hsd);
设置SDIO工作在数据传输模式
/* Configure SD Bus width (4 bits mode selected) */
HAL_SD_ConfigWideBusOperation(&sdHandle, SDIO_BUS_WIDE_4B);

SD卡的读写操作

普通读写
/* Read SD block*/
uint8_t BSP_SD_ReadBlocks(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout)
{
  uint8_t sd_state = MSD_OK;

  if (HAL_SD_ReadBlocks(&sdHandle, (uint8_t *)pData, ReadAddr, NumOfBlocks, Timeout) != HAL_OK)
  {
    sd_state = MSD_ERROR;
  }

  return sd_state;
}

/* Write SD block*/
uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout)
{
  uint8_t sd_state = MSD_OK;

  if (HAL_SD_WriteBlocks(&sdHandle, (uint8_t *)pData, WriteAddr, NumOfBlocks, Timeout) != HAL_OK)
  {
    sd_state = MSD_ERROR;
  }

  return sd_state;
}
DMA读写

使用DMA读写前要先配置DMA通道,一个用于发送,另一个用于接收。

#define SDIO_DMA_ENABLE()                       __HAL_RCC_DMA2_CLK_ENABLE()
#define SDIO_TX_DMA_CHANNEL                     DMA_CHANNEL_4
#define SDIO_RX_DMA_CHANNEL                     DMA_CHANNEL_4
#define SDIO_TX_DMA_STREAM                      DMA2_Stream6
#define SDIO_RX_DMA_STREAM                      DMA2_Stream3
#define SDIO_TX_DMA_IRQ                         DMA2_Stream6_IRQn
#define SDIO_RX_DMA_IRQ                         DMA2_Stream3_IRQn

/* SDIO_RX Init */
hdma_sdio_rx.Instance = SDIO_RX_DMA_STREAM;
hdma_sdio_rx.Init.Channel = SDIO_RX_DMA_CHANNEL;
hdma_sdio_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_sdio_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_sdio_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_sdio_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_sdio_rx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_sdio_rx.Init.Mode = DMA_PFCTRL;
hdma_sdio_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_sdio_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_sdio_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_sdio_rx.Init.MemBurst = DMA_MBURST_INC4;
hdma_sdio_rx.Init.PeriphBurst = DMA_PBURST_INC4;
if (HAL_DMA_Init(&hdma_sdio_rx) != HAL_OK)
{
    Error_Handler();
}

__HAL_LINKDMA(hsd,hdmarx,hdma_sdio_rx);

/* SDIO_TX Init */
hdma_sdio_tx.Instance = SDIO_TX_DMA_STREAM;
hdma_sdio_tx.Init.Channel = SDIO_TX_DMA_CHANNEL;
hdma_sdio_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_sdio_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_sdio_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_sdio_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_sdio_tx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_sdio_tx.Init.Mode = DMA_PFCTRL;
hdma_sdio_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_sdio_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_sdio_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_sdio_tx.Init.MemBurst = DMA_MBURST_INC4;
hdma_sdio_tx.Init.PeriphBurst = DMA_PBURST_INC4;
if (HAL_DMA_Init(&hdma_sdio_tx) != HAL_OK)
{
    Error_Handler();
}

__HAL_LINKDMA(hsd,hdmatx,hdma_sdio_tx);

DMA读写

/* Read SD block*/
uint8_t BSP_SD_ReadBlocks_DMA(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks)
{
  uint8_t sd_state = MSD_OK;

  /* Read block(s) in DMA transfer mode */
  if (HAL_SD_ReadBlocks_DMA(&sdHandle, (uint8_t *)pData, ReadAddr, NumOfBlocks) != HAL_OK)
  {
    sd_state = MSD_ERROR;
  }

  return sd_state;
}

/* Write SD block*/
uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks)
{
  uint8_t sd_state = MSD_OK;

  /* Write block(s) in DMA transfer mode */
  if (HAL_SD_WriteBlocks_DMA(&sdHandle, (uint8_t *)pData, WriteAddr, NumOfBlocks) != HAL_OK)
  {
    sd_state = MSD_ERROR;
  }

  return sd_state;
}

参考文献

1.SD Card association

2.《Sandiskmanual-SecureDigital2.2》

3.《SD Specifications Part 1 Physical Layer Simplified Specification》

4.《RM0090》

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

MorroMaker

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

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

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

打赏作者

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

抵扣说明:

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

余额充值