01 SD Card简介
SD卡(SD Card,Secure Digital Memory Card),它是一种安全数码卡。现在它已经被使用了。TF卡(MicroSD)取代。TF该卡已成为数字、存储等便携设备中应用最广泛的新型快闪存储卡。SD卡和TF卡有不同的容量标准和速度等级。
容量标准
SD卡和TF卡的容量标准如下表所示。
SD | MicroSD | 最大至2GB | FAT12、FAT16 |
SDHC | microSDHC | 2GB至32GB | FAT32 |
SDXC | microSDXC | 32GB至2TB | exFAT |
SDUC | microSDXC | 2TB至128TB | exFAT |
速度等级
速度等级如下表所示。
SD卡的结构
SD卡的结构组成包括接口、电源检测、接口控制器和接口驱动器。
- 存储单元
存储单元通过存储单元接口和卡控制单元传输数据口和卡控制单元传输数据;
- 电源检测单元
保证SD如果卡在适当的电压下工作,如果有电源故障或异常状态,它将重置控制单元和存储单元的接口;
- 卡及接口控制单元
控制SD卡的运行状态包括几个特殊的寄存器;
- 接口驱动器
控制SD卡引脚的输入和输出。
PIN脚定义
标准SD卡有九个外部触点,SD模式和SPI引脚定义在模式下不同。
- DAT线和CMD线路需要连接拉电阻。
- CD线在power up后能够作为SD卡检测端口或模式SPI模型的片选端。
- RESERVED引脚还需要外部电阻,以避免浮空输入导致功耗增加。
SD Mode
1 | CD/DAT3 | Card detect / Data line[bit3] |
2 | CMD | Command/Response |
3 | VSS1 | Supply voltage ground |
4 | VDD | Supply voltage |
5 | CLK | Clock |
6 | VSS2 | Supply voltage ground |
7 | DAT0 | Data line[bit0] |
8 | DAT1 | Data line[bit1] |
9 | DAT2 | Data line[bit2] |
SPI Mode
1 | CS | Chip Select(active low) |
2 | DataIn(MOSI) | Host-to-card Commands and Data |
3 | VSS1 | Supply voltage ground |
4 | VDD | Supply voltage |
5 | CLK | Clock |
6 | VSS2 | Supply voltage ground |
7 | DataOut(MISO) | Card-to-host Data an Status |
8 | RSV | Reserved |
9 | RSV | Reserved |
存储单元
读写SD卡数据的基本单位是1 byte,所有数据都是以block的形式来传输的。SDHC标准卡的数据块长度为512 bytes。每个sector扇区的block大小是固定的,定义在CSD寄存器中。
SD卡的寄存器
CID | 128 | 卡标识号 |
RCA | 16 | 卡相对地址,在初始化的时候确定 |
CSD | 128 | 卡描述数据,卡操作条件的信息 |
SCR | 64 | SD卡配置寄存器,SD卡的特殊信息 |
OCR | 32 | 操作条件寄存器 |
SSR | 512 | SD卡状态寄存器 |
CSR | 32 | 卡状态寄存器 |
OCR
Operation Conditions Register (OCR),操作条件寄存器,存储着SD卡的VDD电压配置文件,寄存器结构如下。
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。
特点
1.通电后,默认情况下,SD卡只使用DAT0进行数据的传输 2.完成初始化后,SD总线允许动态配置数据线的数量
SPI总线
和普通SPI总线一样,带有SCK、CS、MOSI和MISO四个信号。SPI的相关SD配置,这里不展开,具体可以查看官方手册。
SD总线协议
SD 总线上的通信是基于命令和数据传输的。通信过程由一个起始位(“0”)启动,而由一个停止位(“1”) 终止。
SD总线的基本交互过程是命令和响应的交互,一般是主机向从机发送一个命令(Command),从设备在接收到命令后作出响应(Response),如果有数据的传递需求,则会有数据(Data)的传输参与到整个通信过程中。
命令格式
SD 命令格式固定为48bit,都是通过CMD 线连续传输的。
47 | 46 | [45 : 40] | [39 : 8] | [7 : 1] | 0 | |
---|---|---|---|---|---|---|
1 | 1 | 6 | 32 | 7 | 1 | |
‘0’ | ‘1’ | x | x | x | ‘1’ | |
Start bit | Transmission bit | Command index | Argument | CRC7 | End 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)
CMD0 | bc | [31:0] stuff bits | – | GO_IDLE_STATE | 复位所有卡到idle状态 |
CMD2 | bcr | [31:0] stuff bits | R2 | ALL_SEND_CID | 要求所有卡通过CMD返回CID值 |
CMD4 | bcr | [31:0] stuff bits | R6 | SEND_RELATIVE_ADDR | 编程所有卡的DSR |
CMD7 | ac | [31:16] RCA [15:0] don’t care | R1b | SELECT/DESELECT_CARD | 选中/取消选中RCA地址对应的卡 |
CMD8 | bcr | [31:12] RSV [7:0] VHS | R7 | SEND_IF_COND | 发送卡接口条件,包括主机支持的电压等信息,并询问卡支持与否 |
CMD9 | ac | [31:16] RCA [15:0] stuff bits | R2 | SEND_CSD | 通过CMD发送CSD内容到选定卡 |
CMD10 | ac | [31:16] RCA [15:0] stuff bits | R2 | SEND_CID | 通过CMD发送CID内容到选定卡 |
CMD12 | ac | [31:0] stuff bits | R1b | STOP_TRANSMISSION | 强制停止卡传输 |
CMD13 | ac | [31:16] RCA [15:0] stuff bits | R1 | SEND_STATUS | 通过CMD发送选定卡的状态寄存器 |
CMD15 | ac | [31:16] RCA [15:0] stuff bits | – | GO_INACTIVE_STATE | 使选定卡进入"inactive"状态 |
block读命令(Class 2)
CMD16 | ac | [31:0] 块长度 | R1 | SET_BLOCK_LEN | 可设置标准SD卡的块长度,SDHC标准的SD卡命令长度固定为512bytes |
CMD17 | adtc | [31:0] 数据地址 | R1 | READ_SINGLE_BLOCK | 读取标准卡SEL_BLOCK_LEN的长度字节块,SDHC标准的SD卡固定读取512字节的block |
CMD18 | adtc | [31:0] 数据地址 | R1 | READ_MULTIPLE_BLOCK | 连续从SD卡读取数据block,直到被CMD12中断。其中块的长度和CMD17的相同 |
block写命令(Class 4)
CMD24 | adtc | [31:0] 数据地址 | R1 | WRITE_BLOCK | 写入标准卡SEL_BLOCK_LEN的长度字节块,SDHC标准的SD卡固定读取512字节的block |
CMD25 | adtc | [31:0] 数据地址 | R1 | WRITE_MULTIPLE_BLOCK | 连续向SD卡写入数据block,直到被CMD12中断。其中块的长度和CMD17的相同 |
CMD27 | adtc | [31:0] stuff bits | R1 | PROGRAM_CSD | 对CSD的可编程位进行编程 |
擦除命令(Class 5)
CMD32 | ac | [31:0] 数据地址 | R1 | ERASE_WR_BLK_START | 设置擦除的起始块地址 |
CMD33 | ac | [31:0] 数据地址 | R1 | ERASE_WR_BLK_END | 设置擦除的结束块地址 |
CMD38 | ac | [31:0] stuff bits | R1b | ERASE | 擦除预先选定的块 |
锁卡命令(Class 7)
CMD32 | adtc | [31:0] stuff bits | R1 | LOCK_UNLOCK | 加锁/解锁 SD卡 |
特定应用命令(Class 8)
CMD55 | ac | [31:16] RCA [15:0] stuff bits | R1 | APP_CMD | 指定下个命令为特定应用命令,不是标准命令 |
CMD56 | adtc | [31:1] stuff bits [0] RD/WR | R1 | GEN_CMD | 在通用命令或特定应用命令中,用于传输一个数据块。最低位用于表示读数据或写数据 |
SD卡特定应用命令
ACMD6 | ac | [31:2] stuff bits [1:0] Bus width | R1 | SET_BUS_WIDTH | 定义数据总线宽度 (‘00’ = 1bit,‘10’ = 4bits) |
ACMD13 | adtc | [31:0] stuff bits | R1 | SD_STATUS | 发送SD状态 |
ACMD41 | bcr | [31:0] OCR | R3 | SD_SEND_OP_COND | 要求卡发送它所支持的信息(HCS)和OCR寄存器内容 |
ACMD51 | adtc | [31:0] stuff bits | R1 | SEND_SCR | 读取配置寄存器SCR |
响应
响应由SD 卡通过CMD线向主机发出,有些命令要求响应,有些不要求,SDIO 总线共有7 种响应类型(R1 ~ R7)。
SD 卡不支持R4和R5 类型的响应
R1标准响应
如果有传输到卡的信号,那么在数据线上可能有busy的信号(R1b)出现
47 | 46 | [45 : 40] | [39 : 8] | [7 : 1] | 0 | |
---|---|---|---|---|---|---|
1 | 1 | 6 | 32 | 7 | 1 | |
‘0’ | ‘0’ | x | x | x | ‘1’ | |
Start bit | Transmission bit | Command index | Card status | CRC7 | End bit |
R2 CID,CSD寄存器响应
CID寄存器的值是CMD2和CMD10命令的响应,而CSD寄存器内容是CMD9命令的响应
135 | 134 | [133 : 128] | [127 : 1] | 0 | |
---|---|---|---|---|---|
1 | 1 | 6 | 127 | 1 | |
‘0’ | ‘0’ | ‘111111’ | x | ‘1’ | |
Start bit | Transmission bit | Reserved | CID or CSD寄存器的值 | End bit |
R3 OCR寄存器响应
OCR寄存器的值是ACMD41命令的响应
47 | 46 | [45 : 40] | [39 : 8] | [7 : 1] | 0 | |
---|---|---|---|---|---|---|
1 | 1 | 6 | 32 | 7 | 1 | |
‘0’ | ‘0’ | ‘111111’ | x | ‘111111’ | ‘1’ | |
Start bit | Transmission bit | Reserved | OCR Register | Reserved | End bit |
R6 发布的RCA寄存器响应
专用于CMD3的响应
R7 发布的RCA寄存器响应
专用于CMD8命令的响应,返回卡所支持的电压范围和检测模式
47 | 46 | [45 : 40] | [39 : 20] | [19 : 16] | [15 : 8] | [7 : 1] | |
---|---|---|---|---|---|---|---|
1 | 1 | 6 | 20 | 4 | 8 | 7 | |
‘0’ | ‘0’ | ‘001000’ | ‘00000h’ | x | x | x | |
Start bit | Transmission bit | CMD8 | Reserved | Rev Voltage | Check Mode | CRC7 |
数据包
使用所有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命令用来选中和取消选中卡
02 STM32的SDIO接口
以下内容以STM32F407xx为例。
SDIO的结构
STM32的SDIO由SDIO适配器和APB2接口组成。其中,SDIO适配器提供主机功能,包括SD时钟、发送命令和数据传输等。而APB2接口用于控制SDIO适配器的寄存器,并可产生中断和DMA请求。
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 信号。
命令路径
命令路径单元向卡发送命令并从卡接收响应。而命令的格式可以看上述“”章节。
命令路径以半双工模式运行,因此CMD线可以发送或者接收命令和响应
命令路径状态机
当发送命令和接收响应时,会启动CPSM状态机。
命令超时为64个SDIO_CK 时钟周期的固定值
响应
SDIO 支持两种响应类型,以适配SD卡的响应。两种类型均使用 CRC 错误校验。包括:
- 136位长响应
- 48位短响应
响应的格式可以看上述“”章节。
数据路径
数据路径子单元负责主机与卡相互传输数据。如果使能了 4 位宽度的总线模式,则 使用所有四个数据信号线 (SDIO_D[3:0]) 在每个时钟周期内传输 4 个数据位,8位宽度则是8个数据位。数据包的格式可以看上述“”章节。
未使能宽总线模式时,则每个时钟周期使用 SDIO_D0传输一位数据位
数据路径状态机
DPSM 以 SDIO_CK 的频率运行,卡总线信号上的数据与 SDIO_CK 的上升沿保持同步。
数据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初始化”、“上电”、“获取卡信息”和“设置数据传输模式”四个过程。具体过程可以查看上述“”章节。
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》