文章转自STM32F103正点原子学习笔记系列——SD卡 - 知乎 (zhihu.com)
1.SD卡介绍
SD卡,Secure Digital Card,成为安全数字卡(安全数码卡)。本质:nand flash+控制芯片。
SD卡系列主要为三种:SD卡(full size)、Mini SD卡以及MicroSD卡。
SD卡
特点:容量大、高安全性、体积小、传输速度快、接口简单。
SD卡存储容量等级分为四个:SDSC(Secure Digital Standard Capacity)、SDHC(Secure Digital High Capacity)、SDXC(Secure Digital eXtended Capacity)、SDUC(Secure Digital Ultra Capacity)。
SD卡分类
文件系统:存储设备上组织文件的方法。
注意:STM32最大支持32GB SD卡。
假如对SD卡的操作跟EEPROM或者NOR FLASH操作一样,读写数据并验证数据的正确性,不需要FAT文件系统。
SD卡经常用在Windows操作系统上存放数据,就得使用操作系统支持的FAT文件系统。
SD卡速度等级分为:Speed Class、USH Speed Class和Video Speed Class。
SD卡速度分类
SD卡驱动方式
微处理器对SD卡进行操作,可通过SPI接口、SDIO接口。不同接口,SD卡引脚功能不一样。
SD卡引脚
SDIO接口通信线:CLK/CMD/DAT0-3。
CLK:时钟线,SDIO主机产生,由STM32微控制器的SDIO外设输出;
CMD:命令线,SDIO主机通过该线发送命令控制SD卡,若命令要求SD卡响应,SD卡也是通过该线传输响应信息。(类似IIC的应答信号)
DAT0-3:数据线,用于接收或发送数据;SD卡可将DAT0拉低表示处于忙状态。(SPI接口的MISO也有该特性)
SPI接口通信线:CS/CLK/MOSI/MISO。
TF卡
TF卡只比SD卡少一个电源引脚VSS2,其他引脚功能类似。
TF卡引脚
SD卡和TF卡只有引脚和形状大小不同,内部结构类似,操作时序完全相同,可用完全相同的驱动代码。
SD卡寄存器
SD卡有8个寄存器,但不能直接进行读写操作,需要通过命令来控制。SD卡协议定义了一些命令用于实现某一特定功能,SD卡根据收到的命令要求对内部寄存器进行修改。
SD卡寄存器
OCR寄存器
OCR寄存器
CSD、SCR寄存器
CSD、SCR寄存器
SDIO模式
SD总线上的通信基于命令和数据位流传输。
SD卡命令
命令:应用相关命令(ACMD)和通用命令(CMD),通过命令线CMD传输,固定长度48位。
响应:SD卡接收到命令,会有一个响应,用来反应SD卡状态。有2种响应类型:短响应(48位,格式和命令一样)和长响应(136位)。
数据:主机发送的数据/SD发送的数据。SD数据是以块(Block)形式传输,SDHC卡数据块长度一般为512字节。数据块需要CRC保证数据传输成功。
SD卡命令格式
SD卡的命令格式由6个字节组成,发送数据时高位在前,SD卡的写入命令格式如下:
SD卡命令格式
Byte1:命令字的第一个字节为命令号(如CMD0、CMD1等),格式为“0 1 x x x x x x”;
Byte2-Byte5:命令参数,有些命令参数是保留位,没有定义参数的内容,保留位应设置为0;
Byte6:用于校验命令传输内容正确性,前7位为CRC(循环冗余校验)校验位,最后一位为停止位0。
注意:使用SDIO接口驱动,CRC7校验值必须正确;而SPI接口驱动,CRC7校验默认关闭,即伪CRC。
SDIO模式和SPI模式,可使用的命令和特定类支持的命令有所不同。
SD卡系统的命令被分成多个类:
SD卡命令分类
其中,基本命令Class0、面向块读取命令Class2、面向块写入命令Class4、擦除命令Class5、加锁命令Class7、特定于应用命令Class8是必须有的,其余则是非必须支持的命令。
SDIO模式常用命令
SDIO常用命令
SD卡响应
SD卡和单片机的通信采用发送应答机制。
每发送一个命令,SD卡都会给出应答,告知主机该命令执行情况,或返回主机需要获取的数据。使用SDIO接口,响应会通过CMD线传输。
SD卡响应因使用接口不同,格式也不同。具体响应有R1、R1b、R2、R3、R7。
响应内容大小可分为短响应48bit和长响应136bit。
SD卡响应
R1响应:如果有传输到卡的数据,那么数据线0有busy信号(R1b)。
R1响应
可通过响应内容的command index获知响应哪个命令。
R2响应:CID寄存器内容作为CMD2和CMD10响应,CSM寄存器内容作为CMD9响应。
R2响应
R3响应:OCR寄存器值作为ACMD41的响应。
R3响应
R7响应:专用于命令CMD8的响应,返回卡支持电压范围和检测模式。
R7响应
R6响应:专用于命令CMD3的响应(RCA响应)。
R6响应
这里要注意SPI模式没有RCA寄存器,也不支持CMD3命令,没有R6响应。
SD卡操作模式
在SD卡系统(主机和SD卡)定义了两种操作模式:卡识别模式和数据传输模式。
系统复位后,主机和SD卡都处于卡识别模式,主机在总线上找设备;SD卡被主机识别后,SD卡进入到数据传输模式,而主机在总线上所有卡都被识别后也进入数据传输模式。
卡识别模式:识别总线上SD卡类型;
数据传输模式:读写操作。
SD卡模式与状态
卡识别模式
SD卡卡识别模式
这里就是上电从空闲状态出发,先发送CMD8,主机给卡看电压,有响应就发ACMD41,看SD卡的OCR寄存器,主机看SD卡的电压是否匹配;进入准备状态后发送CMD2看SD卡的CID寄存器,看到SD卡的信息,进入识别状态;发送CMD3,获取SD卡的RCA寄存器,卡会请求新的RCA值,就进入了待机状态。
SDIO模式下SD卡初始化
SD卡SDIO初始化
需要区分4类卡:SDHC卡、SDSC卡(这两个是SD2.0卡)、SD1.x卡、MMC卡。
数据传输模式
SD卡数据传输模式
SD卡单块数据读取流程
- 发送CMD16命令,设置数据块大小
- 等待CMD16响应(R1)
- 发送CMD17指令,开始读数据
- 等待CMD17响应(R1)
- 读一个数据块数据
注意:CMD16设置的数据块大小,一般为512字节,此设置直接决定SD卡的块大小,SD卡默认的块大小自动失效。
SD卡多块数据读取流程
- 发送CMD16命令,设置数据块大小
- 等待CMD16响应(R1)
- 等待CMD18命令,开始读数据块
- 等待CMD18响应(R1)
- 读一个数据块数据
- 读第二个数据块数据
- ……
- 发送CMD12命令,结束数据块读取
- 等待CMD12响应(R1)
- 结束多块数据块读取
SD卡单块数据写入流程
- 发送CMD16命令,设置数据块大小
- 发送CMD13命令,查询卡状态
- 等待READY_FOR_DATA位=1
- 发送CMD24命令,开始写入数据
- 写入一个数据块数据
SD卡多块数据写入流程
- 发送CMD16命令,设置数据块大小
- 发送ACMD23命令,预擦除数据块(ACMD指令仅对SD卡有效,另外需要先发送CMD55指令)
- 发送CMD25命令,开始写入数据
- 写入第一个数据块
- 写入第二个数据块
- ……
- 发送CMD12命令,结束数据块写入
- 发送CMD13命令,查询卡状态
- 等待SD卡写入过程
- 完成多块数据块写入
SPI模式就直接跳过了,是针对Mini开发板的,我的是精英板,支持SDIO外设。
2.SDIO外设介绍
SDIO接口简介
SDIO,全程Secure Digital Input and Outpt,即安全数字输入输出接口。
SDIO外设
STM32F10x和F4xx系列控制器只支持SD卡规范版本2.0,即只支持标准容量SD卡和高容量SDHC卡,不支持超大容量SDXC标准卡,所以可支持最高卡容量为32G。
SDIO框图介绍
SDIO外设由SDIO适配器和AHC(F1)/APB2(F4)接口两部分组成:
SDIO框图
SDIO外设的三个时钟:
SDIO_CK:由SDIO适配器中的时钟产生器在外部引脚输出的通信时钟信号
不同总线协议,最高时钟频率不同;每个时钟脉冲传输的是命令或数据。
SDIOCLK:SDIO适配器的工作时钟——48MHz,来自主PLL的独立输出,和PLLCLK独立
计算公式:SDIO_CK = SDIOCLK / (2 + CLKDIV)。注意:SD卡初始化,SDIO_CK不能超过400kHz;初始化完成后可设为最大(不可超过SD卡最大操作频率25MHz)并可更改数据总线宽度(默认只用SDIO_D0进行初始化)。
HCLK/2或PCLK2:CPU以此时钟访问SDIO外设寄存器,AHB总线接口时钟(HCLK/2)是36MHz,APB2总线接口时钟(PCLK2)是84MHz(F407) / 90(F429)MHz。
SDIO适配器:提供SD卡特有的功能:产生时钟、发送命令、接收响应、双向传输数据。
SDIO适配器
命令通道进入空闲至少8个SDIO_CK后,CPSM进入空闲状态
命令通道状态机CPSM
- 置位发送使能,写命令寄存器,命令开始发送
- 不需要响应,进入空闲状态
- 如果还需响应,进入等待状态,启动命令定时器
- 超时未接收到响应,置位超时标志,进入空闲状态
- 接收响应完成,根据CRC设置状态寄存器
命令发送完成后设置状态标志:
CMDREND:响应CRC正常
CCRCFAIL:响应CRC失败
CMDSENT:发送不需要响应的命令
CTIMEOUT:响应超时
CMDACT:正在进行命令传输
数据通道状态机(DPSM)
数据通道状态机DPSM
数据FIFO:包括32个字的数据缓冲和发送与接收电路;TXACT和RXACT标志分别表明处于发送还是接收状态,有数据通道子单元对这两个标志互斥的置位。
发送FIFO:SDIO发送数据,由数据通道单元置位TXACT,通过APB2接口(F4)/AHB接口(F1)将数据写入发送FIFO;相关状态——TXFIFOF、TXFIFOE、TXFIFOHE、TXDAVL、TXUNDERRUN。
UNDERRUN就是下溢出,读空FIFO后继续读导致下溢。
接收FIFO:SDIO接收数据,由数据单元置位RXACT,通过接收FIFO存放从数据通道接收到的数据;相关状态——RXFIFOF、RXFIFOE、RXFIFOHF、RXDAVL、RXOVERR。OVERR就是上溢,写满FIFO后继续写就导致上溢。
以上状态均可通过SDIO_STA状态寄存器查询,同时上溢和下溢可由硬件流控解决。
命令格式
命令是用于开始一项操作。命令在CMD线上串行发送,属于半双工模式,命令和响应可分别发送和接收。命令固定长度为48位。
SDIO命令
SDIO块数据传输
SDIO和SD卡通信一般以数据块的形式进行传输。
SDIO数据传输
SD卡在收到主机相关命令后,开始发送数据块到主机,所有数据块都带CRC校验(由硬件自动处理);单块数据块读时,在收到1个数据块就停止,不需要发送停止命令。
多块数据块读时,SD卡将一直发送数据给主机,直到接到主机发送的STOP命令。
SDIO多块数据传输
数据块写操作同数据块读操作基本类似,只是数据块写时,多了一个繁忙判断,新的数据块必须在SD卡非繁忙时发送。
这里的繁忙信号由SD卡拉低SDIO_D0表示,SDIO硬件自动控制,不需要软件处理。
SDIO寄存器介绍
SDIO寄存器
SDIO电源控制寄存器(SDIO_POWER)
SDIO_POWER
STM32复位后,PWRCTRL=00,处于掉电状态。首先要给SDIO上电,PWRCTRL设置为11。
SDIO时钟控制寄存器(SDIO_CLKCR)
SDIO_CLKCR
注意:SDIO_CK频率过快可能导致SD卡通信失败,此时可以降低SDIO频率。
SDIO参数寄存器(SDIO_ARG)
SDIO_ARG
这个寄存器就是设置48位短命令时,配置[39:8]位数据的寄存器。
SDIO命令寄存器(SDIO_CMD)
SDIO_CMD
首先要CPSMEN置1,然后根据命令需要配置WAITRESP,最后配置命令索引。
SDIO命令响应寄存器(SDIO_RESPCMD)
SDIO_RESPCMD
通过这个寄存器知道响应的是什么命令,而且这个寄存器是只读的。响应内容是另一个寄存器。
SDIO响应1~4寄存器(SDIO_RESP1~4)
SDIO_RESP1~4
短响应只需要看SDIO_RESP1,长响应需要读四个寄存器并重新组合。
SDIO数据定时器寄存器(SDIO_DTIMER)
SDIO_DTIMER
写入数据控制寄存器进行数据传输前,必须先写入数据定时器寄存器和数据长度寄存器!
SDIO数据长度寄存器(SDIO_DLEN)
SDIO_DLEN
对于块数据,该寄存器值必须是数据块长度整数倍。
SDIO数据控制寄存器(SDIO_DCTRL)
SDIO_DCTRL
这里就是选择块传输以及控制器至卡。
SDIO状态寄存器(SDIO_STA)
SDIO_STA
主要关注以上圈出的位,来判断数据的传输状态。
SDIO数据FIFO寄存器(SDIO_FIFO)
SDIO_FIFO
收发的FIFO是32位宽度的读/写寄存器,在连续的32个地址上包含32个寄存器,CPU可以使用FIFO读写多个操作数。必须是以4字节对齐的内存进行操作,否则将导致出错。
SDIO相关HAL库驱动
HAL库相关
用于发送SDIO_SendCommand函数,用于接收响应sdio_GetResponse函数。
相关结构体:SD_HandleTypeDef(SD_InitTypeDef)、SDIO_CmdInitTypeDef、SDIO_DataInitTypeDef、HAL_SD_CardInfoTypeDef。
SD卡HAL库驱动文件:stm32f1xx_hal_sd.c和stm32f1xx_ll_sdmmc.c。(前者封装了初始化、读写、擦除等驱动接口;后者对接SDIO寄存器与SD卡通讯)。
SDIO_HandleTypeDef
SDIO_HandleTypeDef
SDIO_CmdInitTypeDef
SDIO_CmdInitTypeDef
SDIO_DataInitTypeDef
SDIO_DataInitTypeDef
SDIO_DataInitTypeDef关联的函数SDIO_ConfigData,只有发送/收到数据块才会使用。
注意:SD卡操作是用数据块,MMC卡才是数据流。
HAL_SD_CardInfoTypeDef
HAL_SD_CardInfoTypeDef
通过HAL_SDGetCardInfo函数获取卡信息。
初始化SD卡函数:HAL_SD_Init()
- 初始化GPIO功能,SDIO时钟不超过400kHz
- 初始化SDIO外设接口(默认配置) (SDIO_Init)
- 开启SDIO电源,为卡提供时钟,并延时两毫秒(SDIO_PowerState_ON)
- 发送CMD0复位命令,卡到空闲状态
- 发送CMD8主机支持电压,询问卡是否支持
- 根据响应判断SD卡版本
- 反复发送ACMD41,等待OCR回应卡上电完成和卡容量类型(以上为SD_PowerON)
- 检查电源状态,进入准备状态
- 发送CMD2,获得卡信息(CID)
- 发送CMD3,获得卡相对地址(RCA)
- 发送CMD9,获得卡数据(CSD)
- 发送CMD7,选择卡,切换传输状态(以上为SD_InitCard)
第一步是HAL_SD_MspInit中;之后所有都是HAL_SD_InitCard。
读写函数:
HAL库读写函数
3.编程实战
驱动步骤:
- 初始化SD卡相关GPIO口——需要设置为复用推挽模式
- SD卡初始化——HAL_SD_Init
- 重新设置SDIO总线位宽——HAL_SD_WideBusOperation_config
- 数据块写入——HAL_SD_WriteBlocks
- 数据块读取——HAL_SD_ReadBlocks
SD卡原理图
TF卡原理图