SDMMC
SDMMC是STM32手册中对于整个SD/SDIO/MMC接口的统称。
然后明白这里SD/SDIO/MMC的概念
- MMC:MultiMediaCard ,即多媒体卡,容量大,耗电少,速率快,广泛用于消费类电子产品中。
- SD卡:Secure Digital Mermory Card ,即安全数码卡,他是在MMC的基础上发展而来的,主要强调安全性,可以设定存储的使用权限,传输速率比MMC快,同时向前兼容了MMC卡。
- SDIO卡:SDIO是在SD标准上定义了一种外设接口,它和SD卡规范间的一个重要区别是增加了低速标准。在SDIO卡只需要SPI和1位SD传输模式。低速卡的目标应用是以最小的硬件开销支持低速IO能力。
- MCI:Multimedia Card Interface ,即多媒体卡接口,上面说的不同卡的接口都属于MCI接口。
STM32中的SDMMC接口目前可以支持下面的协议:
- MMC 4.2版本,并且向前兼容 ,支持3种模式:1-bit,4-bit,8-bit
- SD卡 2.0版本
- SDIO卡 2.0版本,支持:1-bit,4-bit
目前的SDMMC接口在8-bit模式下最快支持50Mhz的数据传输
SDMMC的总线传输协议基本上是分为两个部分,一个是命令/回应的传输,一个是数据块/流的传输
SDMMC中的D0接口被拉低,表示Busy状态,这个时候,SDMMC是不会发送任何数据到总线上的。
使用SDIO接口的外围设备:
- Wi-Fi Card
- CMOS Sensor Card
- GPS Card
- GSM/GPRS Modem Card
- Bluetooth Card
SDMMC主要包含两部分,一个是SDMMC适配器,其包含了时钟、命令、数据传输模块,另一个就是APB2接口,通过SDMMC寄存器产生中断和DMA请求信号。
3.31以上版本的MMC卡可以支持1位、4位、8位总线用来数据传输,但是低于这个版本的只能用1位传输,而且初始化命令时必须要用开漏模式。
SD卡和SDIO卡可以用1位或者4位,数据线应该使用推挽模式。
再看一下各种输出模式的特性
- 推挽输出:导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载抽取电流。推拉式输出级既提高电路的负载能力,又提高开关速度。
- 开漏输出:一般来说,开漏是用来连接不同电平的器件,匹配电平用的,适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内),但是有上升沿延时。
其中所使用的时钟必须满足下面的要求:
PCLK2 > 3 x 总线宽度 ⁄ 32 × SDMMC_CK
我们所使用的是:
PCLK2(108) > 3 x 总线宽度(4) ⁄ 32 × SDMMC_CK(48)
SDMMC适配器又包含了5个子模块:
- 寄存器
- 控制单元
- 命令路径
- 数据路径
- 数据队列
其中数据队列(DaTa FIFO)需要详细了解一下。
FIFO 是 32-bit宽 32字的深度
同时这个FIFO是被APB2的总线时钟来控制的,而不是SDMMCCLK。
SDMMC的DMA
SDMMC如何使用DMA,其配置也是手册上说明的
-
使能DMA2,清除所有DMA2相关中断
-
根据读写配置DMA2_Stream3或者DMA2_Stream6的通道4。
-
DMA传输的目标地址是SDMMC_FIFO的寄存器地址。
-
内存是自增模式,外设非自增,外设和源宽度都是一个字
-
配置硬件流控制(F4版本的不同)
-
配置突发传输量是4节拍,使能DMA,完成配置
如果SDMMC主机要使用DMA必须配置成外设控制流的模式,而且SDMMC只会产生突发的传输,所以外设方面必须要配置成自增突发模式
卡的识别过程
对于不同的卡,识别的过程并不一样。
我们用的是SDHC卡,其实就是高容量的SD卡,在SD 2.0中,规定SDHC是符合新的规范、且容量大于2GB小于等于32GB的SD卡。
所以卡的识别过程中 CMD引脚应该用的就是推挽模式。
- 激活总线,广播SD_APP_OP_COND (ACMD41)命令
- 卡会根据其内容进行响应,不兼容的卡会被挂起
- 继续广播ALL_SEND_CID (CMD2)命令给所有激活的卡
- 卡会发送他们自己的CIDs,并且进入识别状态
- SDMMC发送SET_RELATIVE_ADDR (CMD3)命令给某个激活卡,给了他们一个关联地址,RCA,RCA地址比CID要短,并且通过RCA地址进行寻址操作,有了地址的卡变成了待机状态。关联地址可以修改,寻址地址为最后接收到的地址。
- 如果有多块卡的情况下,重复5的步骤,完成全部的卡关联。
CID:Card Identification Number CSD:Card Specific Data
基础操作
块写入
写入块的结尾会附加CRC校验,块的大小取决于WRITE_BL_LEN的配置,如果CRC校验失败了,已传输数据不会被写入,SDMMC_D引脚会拉低,如果多块写入,则剩下的块数据都会被忽略。
有一些卡写一块数据,可能需要很长或者是无法预测的时间。
总体来说写入分为两个部分,一个是SDMMC把数据通过DMA发送出去,一个是SD卡接收到数据以后,也要先完成CRC校验,然后开始写入到卡内部块中去,如果写缓冲满了或者是无法接收新块的数据,这个时候SDMMC_D 是拉低的。
主控可以通过SEND_STATUS command (CMD13)命令 来获取当前卡的状态信息,READY_FOR_DATA会显示是否可以接收新数据。卡写入的时候,可以释放片选,这不会中断写入操作,重新选择卡的时候也是会用SDMMC_D 拉低与否来显示当前是否在写入。
块读取
通过CMD17 (READ_SINGLE_BLOCK)命令来初始化单块读取,然后卡会转变为Transfer状态。 CMD18 (READ_MULTIPLE_BLOCK)命令来多块读取。如果出现了任何错误,会停止数据传输,这时需要主机发送停止传输命令,错误的状态会显示在Response中
流模式只能用于MMC卡,其他卡无法使用。
块擦除
MMC使用的是组擦除,SD使用的是什么并没有说明。
ERASE_GROUP_START(CMD35) 用来设置开始擦除地址 ERASE_GROUP_END (CMD36) 设置擦除结束地址 ERASE (CMD38)开始擦除
擦除时也是使用 SDMMC_D 拉低与否来显示当前是否在擦除。
修改总线宽度
通过 SET_BUS_WIDTH (ACMD6)命令可以修改总线宽度。在SDMMC启动以后或者使用GO_IDLE_STATE (CMD0)命令 可以使得总线宽度为1位
片选
因为可能包含多个卡,所以可以通过SELECT/DESELECT_CARD (CMD7)命令来片选。
写保护
写保护模式,可以通过CSD中的写保护来设置永久写保护或者临时写保护。当然可以设置部分扇区写 保护,详细的参考CSD中的组写保护相关设置
还有一种是机械写保护模式,直接拨动SD卡的写保护开关即可。
还有密码保护模式,密码保护模式下可以reset卡,初始化,选卡,查询卡状态,就是不能传输数据。 密码保护的细节见数据手册
状态寄存器
一共有两个状态寄存器,一个是卡状态寄存器,一个是SD状态寄存器。
卡状态寄存器
命令的响应会包括一个32位的卡状态寄存器,没有特殊说明的情况下,卡状态寄存器总是与上一次的命令相关。
SD状态寄存器
SD状态寄存器主要是和SD存储卡相关的,512位大小,通过发送 CMD13来获取该寄存器内容。
这里存储的信息多属于SD卡的硬件信息相关,虽然很大,但是有很多内容都是保留,有用的位相对较少。
命令和响应
命令
SDMMC提供了两种命令,ACMD和GEN_CMD。其中ACMD是特定命令,相当于是系统级别命令,而GEN_CMD相当于是普通命令。
要使用特殊命令,需要先发APP_CMD (CMD55),APP_CMD就会被置位,然后发送所需要的ACMD,卡就会将接收到的命令解释为ACMD。如果发的是普通命令,那么APP_CMD就会被清零,执行的也是普通命令。
具体的命令又分为了四种:
- 广播不响应命令
- 广播响应命令,接收所有卡的响应
- 点到点命令
- 点到点数据传输命令
具体命令详见手册中的命令表
响应
响应一共有五种:
- R1 正常响应命令,带CRC校验
- R1b 类似与R1 但是带有一个可选的Busy的传输信号,可作用于传输的引脚上,应该指的是SDMMC_D0引脚
- R2 是用来返回CID和CSD寄存器内容的,对应CMD2和CMD10
- R3 是用来返回OCR寄存器内容的,对应CMD1
- R4 包含了RCA地址,用来寻址,返回寄存器内容
- R4b 类似于R4,但是仅SDIO可以使用
- R5 中断请求
- R6 仅SDIO可用,对CMD3的响应,主要也是对
硬件流控制
硬件流控制,主要是为了防止FIFO溢出的,当要溢出时,他会停止SDMMC_CK并且冻结SDMMC状态机。 冻结的前提是状态机的时钟来源是SDMMC_CK
感觉看完了F7手册里关于SDMMC的介绍,还是不是很理解到底要怎么做,虽然有些配置是知道了, 但是还是有很多东西不明白,还要参考其他文档。
SD卡协议
SD卡初始流程
初始化时使用 1bit 位宽进行初始化 先进行管脚设置 然后配置收发的DMA 设置为busy状态,开始sd卡的参数初始化 本质上就算之前没有配置1bit位宽在sd卡初始化HAL_SD_InitCard的时候也会被强制成1bit进行初始化
SDMMC: 先关闭时钟,开启SDMMC电源,然后开启SDMMC时钟,开启后稍微等待几个时钟周期
SD_PowerON,获取SD卡的操作电压,操作时钟等信息 检测卡的协议类型
初始化结束时 使用 4bit 位宽,进行正常读写操作
库中明确说明了在读写操作之前,应该通过get cardstatus来判断是否可以进行读写操作
SD卡状态
一旦数据传输完成,卡将退出数据写状态并进入Programming State(传输成功)或Transfer State(传输失败)。 如果一个快写操作停止,而且最后一块块长度和CRC是有效的,那么数据可以被操作(programmed)。 卡可能提供块写缓冲。这意味着在前一块数据被操作时,下一块数据可以传送给卡。如果所有卡写缓冲已满,只要卡在Programming State,DAT0将保持低电平(BUSY)。
也就是说判断是否可以传输不仅仅是看Programming State,还是取决于Transfer State 在卡被编程(programming)时,禁止参数设置命令。参数设置命令包括:设置块长度(CMD16),擦除块开始(CMD32)和擦除块结束(CMD33)
如图所示,SD卡的状态机是这样的,如果在programming状态以后必然会回到Transfer,所以只需要在Transfer判断是否可以继续传输即可
出错处理
SD卡在超时以后的处理
/* Clear all the static flags */
__HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
hsd->ErrorCode |= errorstate;
hsd->State = HAL_SD_STATE_READY;
return HAL_ERROR;
/* Send stop transmission command in case of multiblock read */
if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DATAEND) && (NumberOfBlocks > 1U))
{
if(hsd->SdCard.CardType != CARD_SECURED)
{
/* Send stop transmission command */
errorstate = SDMMC_CmdStopTransfer(hsd->Instance);
if(errorstate != HAL_SD_ERROR_NONE)
{
/* Clear all the static flags */
__HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
hsd->ErrorCode |= errorstate;
hsd->State = HAL_SD_STATE_READY;
return HAL_ERROR;
}
}
}
/* Empty FIFO if there is still any data */
while ((__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXDAVL)))
{
*tempbuff = SDMMC_ReadFIFO(hsd->Instance);
tempbuff++;
if((Timeout == 0U)||((HAL_GetTick()-tickstart) >= Timeout))
{
/* Clear all the static flags */
__HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
hsd->ErrorCode |= HAL_SD_ERROR_TIMEOUT;
hsd->State= HAL_SD_STATE_READY;
return HAL_ERROR;
}
}