How to Use MMC/SDC

数字安全记忆卡(Secure Digital Memory Card)(以下简称SDC)实际上是用于移动设备的标准记忆卡。SDC向下兼容多媒体卡(Multi Media Card)(以下简称MMC),因此,使用SDC的设备只需要经很小的改动便可使用MMC。而今又出现了外形尺寸更小的版本,例如RS-MMC,mini SD以及Micro SD,功能都是一样的。MMC/SDC内置微控制器,闪存控制操作(擦除、读、写以及错误控制)都是在记忆卡内完成的。数据以512字节大小的数据块的形式在存储卡和主控制器之间传输,所以,从上层来看,存储卡可以被看成是一个普通的硬盘。一般情况下,记忆卡使用的文件系统是带有FDISK分区规则的FAT12/FAT16文件系统(FDISK的解释见附注)。对于大容量存储卡(≥ 4GB),通常使用FAT32文件系统。

      本文讲述了笔者在嵌入式系统中使用MMC/SDC时所用到的基础知识以及各方面的细节问题。笔者相信,对于那些想要使用MMC/SDC的同志们来说,本文必是有用的入门笔记。

How to Use MMC/SDC_第1张图片

接口(Contact Interface)

       下面的图片说明了MMC/SDC的接口。MMC拥有7个触片,SDC有9个触片(比MMC多出两个附加的触片)。这些触片中有三个触片是为了存储卡的供电而设计的,所以,能用于传递有效信号的触片就剩下4个(MMC)或是6个(SDC)。故而,在主机和存储卡之间的数据传输是通过一个同步串行接口(Synchronous Serial Interface)进行的。

How to Use MMC/SDC_第2张图片

      SDC的工作电压范围可以从其内部的一个特殊寄存器中读出,并以此来确定之。然而,由于MMC/SDC的正常工作电压范围为2.7V~3.6V,所以,实际供电电压可以定为这个范围中的任意一个值。因MMC/SDC的工作电流可以达到十几毫安,故主机系统必须能提供至少100 mA的电流。

SPI(Serial Peripheral Interface 串行外设接口)模式

       SPI模式是另一种操作模式,是为了能在没有MMC/SDC默认主机接口(SDIO接口)的情况下使用存储卡而定义的。与MMC/SDC的默认接口通信协议相比,SPI协议要简单得多。MMC/SDC可以通过普通SPI接口或是GPIO口与大部分微控制器相连。因此,对于低成本的嵌入式应用来说,SPI模式操作MMC/SDC是绝佳选择。而且,价格低廉的微控制器并没有MMC/SDC的默认主机接口(SDIO接口)。对于SDC来说,“SPI Mode 0”即为其SPI模式;而对于MMC来说,它使用的不是SPI时序,在SCLK的上升沿,数据的移出和锁存操作同时被执行,但在SPI模式中的SPI Mode 0下,MMC也能正常工作。因此,对于MMC/SDC接口来说,使用SPI Mode 0(CPHA = 0,CPOL = 0)再合适不过了,但若使用SPI Mode 3,MMC/SDC在大部分情况下也能正常工作。

SPI Mode(SPI模式) Timing Diagram(时序图)
模式0:上升沿
数据先锁存,后移出
How to Use MMC/SDC_第3张图片
模式1:上升沿
数据先移出,后锁存
How to Use MMC/SDC_第4张图片
模式2:下降沿
数据先锁存,后移出
How to Use MMC/SDC_第5张图片
模式3:下降沿
数据先移出,后锁存
How to Use MMC/SDC_第6张图片


指令和响应

       在SPI模式下,信号线上的数据传输方向是固定的——面向字节的串行通信传输。从主机到存储卡的命令帧是一个固定长度的数据包(6个字节),见下图。当存储卡接受到命令帧时,对于命令帧的响应(R1,R2,R3)便可从存储卡中读出。数据的传输是由主机产生的串行时钟驱动的,主机必须不间断地读取字节——发送一个0xFF,然后接收响应数据,直到收到存储卡的有效响应。对于SDC来说,命令响应时间(Ncr)是0-8个时钟个数(单位为8个时钟周期),对于MMC来说,则是1-8个时钟个数。片选信号(CS)在对存储卡的一个执行过程(命令,响应以及可能存在的数据传输)中必须保持为低电平。在SPI模式中,CRC(循环冗余校验)是可选的,但是仍然需要一个位域以组成完整的命令帧。在读操作进行过程中,DI信号必须保持为高电平。

How to Use MMC/SDC_第7张图片 

SPI指令集

       每条指令都以简略形式表达,例如GO_IDLE_STATE或者CMDD<n>,<n>是指令索引号,可以是0~63中的任意值。下面的表格仅给出了通常的读/写操作以及存储卡初始化时所使用的指令。对于所有指令的详细阐述,请参考MMCA和SDCA的规格说明书。

命令索引 参数 响应 数据 缩写 描述
CMD0 R1 GO_IDLE_STATE   软件复位
CMD1 R1 SEND_OP_COND   执行初始化进程
ACMD41(*1) *2 R1 APP_SEND_OP_COND   仅针对SDC,执行初始化进程
CMD8 *3 R7 SEND_IF_COND   仅针对SDC Version 2。检查电压范围
CMD9 R1 SEND_CSD   读取CSD寄存器中的内容
CMD10 R1 SEND_CID   读取CID寄存器中的内容
CMD12 R1b STOP_TRANSMISSION   停止读数据
CMD16 块长度[31:0] R1 SET_BLOCKLEN   改变 读/写 数据块大小
CMD17 地址[31:0] R1 READ_SINGLE_BLOCK   读取单个块
CMD18 地址[31:0] R1 READ_MULTIPLE_BLOCK   读取多个块
CMD23 块的个数[15:0] R1 SET_BLOCK_COUNT   仅针对MMC,定义在下一个多块读写指令中的块的个数
ACMD23(*1) 块的个数[22:0] R1 SET_WR_BLOCK_ERASE_COUNT   仅针对SDC,定义在下一个多块写指令中要预先擦除的块的个数
CMD24 地址[31:0] R1 WRITE_BLOCK   写一个块
CMD25 地址[31:0] R1 WRITE_MULTIPLE_BLOCK   写多个块
CMD55(*1) R1 APP_CMD   ACMD<n>指令的先行指令
CMD58 R3 READ_OCR   读取OCR寄存器中的内容

*1:ACMD<n> 表示CMD55-CMD<n>中的一个指令队列;
*2: Rsv(0)[31], HCS[30], Rsv(0)[29:0]
*3: Rsv(0)[31:12], Supply Voltage(1)[11:8], Check Pattern(0xAA)[7:0]

SPI响应

       对于不同的指令索引,一共有三种响应格式:R1,R2和R3,对于大多数指令来说,存储卡经常返回一个字节长度的R1响应,下图给出了R1响应中的位定义,值0x00表示命令执行成功,如果出现错误,R1响应中对应的状态位将会被置位。只有执行CMD58时,才会有R3响应(由R1及后面的32位的OCR组成)。

       有一些指令的执行时间比指令反应时间还要长,这些指令返回响应R1b。响应R1b是由R1响应和“忙标志”组成的(在内部进程执行过程中,引脚DO的电平必须始终保持为低电平)。主机需要等待这个过程结束——直到收到0xFF。

How to Use MMC/SDC_第8张图片

SPI模式下的初始化过程

       在上电复位后,MMC/SDC进入其默认操作模式。要进入SPI模式,必须执行以下步骤。

上电(存储卡插入)

       当供电电压达到2.2V时,至少需要等待1毫秒。保持引脚DI和CS为高电平至少74个时钟周期(这里的时钟是SCLK),然后存储卡才可以接收默认指令。

软件复位

       设置SPI时钟速率在100KHz~400KHz之间,然后发送CMD0指令,同时拉低片选(CS)引脚以复位存储卡。当存储卡接收到CMD0命令时,存储卡会自行检测引脚CS的电平,若CS电平为低,则卡片进入SPI模式。由于CMD0指令必须作为默认指令发出,所以指令中的CRC域必须填充一个有效值。一旦存储卡进入SPI模式,CRC校验功能被禁止,此后在指令执行时,存储卡不会检查CRC。

       so that command transmission routine can be written with the hardcorded CRC value that valid for only CMD0 and CMD8

       所以,把硬件记录的CRC值用于填充CMD命令中的CRC域,这样的“例行”命令只对CMD0和CMD8有效(这句翻译有问题,原文没太理解,不知是不是这个意思)。一旦CMD0命令被成功执行,存储卡会进入空闲状态,并向主机回复带有空闲状态标志的R1响应(0x01)。CRC校验功能也可以被命令CMD59打开。

初始化

       在空闲状态中,存储卡只接受命令CMD0(软件复位)、CMD1(初始化进程)、ACMD41(仅针对SDC的初始化)和CMD58(读取OCR寄存器),除了这些命令之外的其他命令无效。在空闲状态中,使用命令CMD58读取OCR寄存器中的内容,检查OCR寄存器指示的工作电压范围,万一实际供电电压超出了工作电压范围,存储卡必须被“弹出”,值得注意的是,所有存储卡的工作电压范围为2.7V~3.6V。当存储卡收到命令CMD1时,便开始执行初始化过程。主机在发送CMD1命令后必须不断检查卡片的响应以判断卡片的初始化是否已经完成。当卡片成功初始化时,空闲状态下R1响应中的空闲状态位被清零(R1响应由0x01变成0x00)。卡片的初始化过程可能需要几百个毫秒(卡的容量越大,初始化需要的时间也越长)所以我们需要考虑超时的问题。在空闲状态位被清零后,就可以执行普通的 读/写 命令了。

       因为对于SDC来说,建议采用ACMD41而不是CMD1作为卡片的初始化命令,所以我们可以先尝试ACMD41命令,若无效则再尝试CMD1命令,这样就可以完美支持这两种存储卡(MMC/SDC)。

       为了优化读/写卡片的性能,SPI总线的时钟速率应该尽可能的快,CSD寄存器中的 TRAN_SPEED 域表明了卡片可以接受的最快时钟速率。对于MMC来说,最快时钟速率为20MHz,对于SDC来说,大多数情况下,最快时钟速率则为25MHz。

      Note that the clock rate will able to be fixed to 20/25MHz in SPI mode because there is no open-drain condition that restricts the clock rate.

       注意,在SPI模式中,时钟速率可以被固定在20/25MHz,这是因为没有开漏条件可以限制时钟速率。(此话有点不理解,不知这样翻译是否恰当)

       对于2GB或更大容量的存储卡来说,初始的块长度可能会超过512,所以,如果需要,可以通过CMD16命令来改变 读/写 的块长度。

如何支持第二版本的SDC和高容量的存储卡

       在使用CMD0命令使卡片进入空闲状态后,发送命令CMD8(参数设置为0x000001AA),在初始化过程之前改变CRC。如果CMD8命令被拒——不合法的命令错误(0x05),那么存储卡为第一代的SDC或是MMCC。但若CMD8命令被接受,卡片会返回R7响应(由R1响应<0x01>和32位的数据位组成)。返回值0x1AA的低12位表明该卡为第二代存储卡,并能工作在2.7V~3.6V的电压范围之中,反之,卡片必须被“弹出”。CMD8命令成功执行后,则使用命令ACMD41(参数为第30位的HCS)执行初始化进程。在初始化进程结束后,主机需要读取OCR寄存器中的第30位(CCS),若该位被置位,则下面要阐述的数据读/写操作命令需要的参数是块的地址而非字节地址,一般情况下,块的大小为512字节。

数据传输

数据包和数据响应

       在数据传输处理中,在响应了主机的读写命令后,存储卡便会开始发送或接收一个或多个数据块。数据块是以数据包形式传输的,而数据包则由数据标志、数据块和CRC校验域组成。数据包的格式如下图所示,可以看到,数据标志有三种,其中,“停止传输数据标志”意味着多块读写操作结束,这个标志是作为字节单独使用的——不包含数据块以及CRC。

How to Use MMC/SDC_第9张图片

读单个块

How to Use MMC/SDC_第10张图片

        读命令CMD17中的参数指定了读取字节或块的起始地址。

       The sector address specified by upper layer must be scaled properly.
       上层(软件)必须合理指定扇区地址(这句话是否该这样理解)?

若CMD17命令成功执行,存储卡将会执行读操作,将读到的数据块发送给主机。在检测到有效数据标志后,主机会接收到数据域和两个字节的CRC校验域。

      The CRC bytes must be flushed even if it is not needed.
       尽管不需要,还是必须得刷新CRC域(flush到底该如何理解)。

       如果在读操作中出现错误,存储卡将会返回错误标志而不是数据包。

读多个块

How to Use MMC/SDC_第11张图片

     多块读的指令可以从指定地址中连续读取多个块。如果在多块读指令之前没有指定需要读取的块的数量,读进程将会以“开始—结束”的多块读形式执行,读操作将会持续到存储卡收到CMD12命令为止。在收到命令CMD12之后的字节是废弃字节,主机必须在收到命令CMD12的回应之前把该废弃字节丢弃掉。

写单个块

How to Use MMC/SDC_第12张图片

       当存储卡接收到写命令时,主机在收到命令响应后的1个时钟单元(1个单元为八个SCLK时钟周期)后向存储卡发送一个数据包。数据包格式和读数据命令中的数据包格式是一样的。除非CRC功能被开启,否则数据包中的CRC域可以被任何值填充。当数据包发出以后,存储卡会在收到数据包后立即反馈一个数据响应给主机。数据响应后紧跟着一个忙碌标志以执行写操作。大部分卡片无法修改写数据的块的大小,通常情况下写数据的块的大小为512。

       在SPI模式中,原则上,在一个操作过程中,片选信号(CS)必须始终保持为低电平,然而,这个规则还是有个例外的。当存储卡处于忙碌状态时,主机可以解除片选信号以释放SPI总线,从而去处理其他SPI设备。如果在存储卡处理内部进程时,片选信号再次有效,存储卡会将引脚DO的电平拉低。

Therefore a preceding busy check (wait ready immediataly before command and data packet) instead of post wait can eliminate waste wait time.
       因此,提前检查卡片忙碌状态(在发送命令和数据包之前就开始)而不是轮询等待,可以利用浪费的等待时间(这个话有点模棱两可)。

       此外,数据响应发出1个时钟单元后,卡片开始执行内部操作。换而言之,存储卡需要8个时钟周期以执行内部写操作。在此期间,不需要考虑片选信号的状态,从而可以释放SPI总线去执行其他工作。The state of CS signal during the eight clocks is negligible so that it can done by bus release process described below.——这句话的后半句怎么理解呢?

写多个块

How to Use MMC/SDC_第13张图片

       多块写命令从指定地址开始连续写多个块,若在多块写命令之前没有制定传输数据块的数量,默认将以“开始—停止”的方式执行该操作,即只有当数据包中的数据标志为停止传输数据标志时,写多个块的操作才会停止。此时,在停止传输数据标志后,引脚DO上会出现忙碌标志。对于SDC,

the multiple block write transaction must be terminated with a Stop Tran token independent of the transfer type, pre-defined or open-ended.
多块写操作需要“停止传输标志”来终止,该标志与数据传输类型无关,可以预先定义或采用“开始—停止”方式。(这句话该如何理解?)

读寄存器CSD和CID

      这些操作和读单个块的操作是类似的,只是不再需要定义数据块的长度了。CSD和CID寄存器中的内容以16字节数据块的形式发送给主机。对于该CMD命令的细节,寄存器CID和OCR的相关知识,请参加MMC/SDC的说明书。

总线浮动和热插入的考虑

       所有可能浮动的信号必须通过电阻拉成高电平抑或是低电平,对于MOS器件来说,这是一个很普通的设计规则。由于引脚DI和引脚DO一般情况下是高电平,需要拉高。SDC/MMC的规格书建议采用50KΩ~100KΩ的上拉电阻。然而,在SDC/MMC的规格书中并未设计到时钟信号的处理,这是因为时钟信号是由主机产生的,若时钟信号产生浮动,应该将之拉低。(这是为什么?)

       MMC/SDC支持热插拔,但需要注意主机电路的一些设计细节,以避免误操作。例如,如果系统供电直接和卡座的电源相连,那么在存储卡被插入卡座时,存储卡的内置电容(与电源引脚相连的电容)会被充电,从而产生充电电流,该电流会导致Vcc电压下滑。下图中,图A表示了在存储卡插入时,Vcc电压会下滑约600mV——这可能会出发单片机的BOD(Brown Out Dectect——欠压<低电压>检测)功能。图B表示了在电路中加入电感以阻止电流突变时,Vcc电压会下滑约200mV。需要使用低ESR(Equivalent Series Resistance——等效串联电阻)的电容,如OS-CON(固态有机半导体电容器——铝电解电容器),能够消除Vcc电压下滑,如图C所示。然而,低ESR的电容器也会导致LDO(Low Dropout Regulator——低压差线性稳压器)的不稳定。(可以采用开关电源稳压器)

总线浮动和热插入

 多从机配置的考虑

       在SPI总线中,每个从机都被独立的片选信号(CS)选中,因而,SPI总线可以连接多个从机设备。普通SPI从机通过片选信号驱动或释放其DO信号来共享SPI总线。

      However MMC/SDC drives/releases DO signal in synchronising to the SCLK.
       然而,MMC/SDC驱动或者释放DO信号是与SCLK同步的。

       这意味着MMC/SDC和其他连接到SPI总线的从机有可能产生总线上的冲突,下图表明了MMC/SDC驱动或释放的时序(引脚DO被拉到 Vcc/2 以看出总线状态)。因此,为了让MMC/SDC释放DO引脚信号,主机设备必须在释放CS信号后向从机发送一个字节。

In the SPI bus, each slave device is selected with separated CS signals, and plural devices can be attached to an SPI bus. Generic SPI slave device drives/releases its DO signal by CS signal asynchronously to share an SPI bus. However MMC/SDC drives/releases DO signal in synchronising to the SCLK. This means there is a posibility of bus conflict with MMC/SDC and any other SPI slaves that attached to an SPI bus. Right image shows the drive/release timing of the MMC/SDC (the DO signal is pulled to 1/2 vcc to see the bus state). Therefore to make MMC/SDC release DO signal, the master device must send a byte after CS signal is deasserted.

这段话的理解有困难,翻译不太准确。

多从机设计

优化写操作性能

      大部分MMC/SDC采用 NAND 闪存作为存储阵列。NAND闪存性价比较高且能够快速读/写大量数据。但是,其缺点是在重写某个区域的数据时效率很低。一般来说,闪存需要在写新数据之前擦除已有的数据,而且擦除操作的最小单位(称为擦除块)比写操作的数据块要大。典型的NAND闪存对于写/擦除操作的块大小为512Bytes/16KBytes。而且,当前的高速卡采用了“大容量块操作”芯片(2KB/128KB)。

      This means that rewriting entire data in the erase block is done in the card even if write only a sector (512 bytes).
       这表明,在NAND介质存储卡中,即使只写一个扇区的数据(512Bytes)也需要在擦除的块内重写整个数据。(这句话究竟想要表达什么)

标准

       现在假设某个嵌入式系统的存储空间是有限的,那么读写SDC/MMC的性能到底如何呢?笔者使用便宜的Atmega64(9.2MHz)进行了测试。考虑到存储空间有限,write( ) 和 read( ) 函数一次仅读取2048字节。测试结果是,在容量为128MB的SDC上,写速度为77KB/sec,读速度为328KB/sec。

       对于512MB的SDC,写速度低得可怜——只有128MB SDC写速度的1/3。一般来说,大容量存储设备的读/写速度与其面记录密度成正比。然而,对于存储卡来说,这个规则有时是相反的。对于MMC来说,看上去其读写速度好像是SDC的几倍,性能并不坏。笔者测试了不同厂家生产的SDC,发现PQI(劲永)厂家的SDC性能优于Hitach(日立)的MMC,而松下和东芝的存储卡产品性能较差。

擦除块的大小

      为了分析写操作的细节,

busy time (number of polling cycles) after sent a write data is typed out to console in the low level disk write function.
在发送一个写数据包后的忙碌时间(轮训时间)——这句话到底应该怎么理解。
Multiple numbers on a line indicates data blocks and a Stop Tran token that issued by a multiple block write transaction.
——这句话完全不理解。

      分析结果表明,对于128MB的SDC和512MB的SDC,其内部进程是有区别的。128MB的SDC是在多块写操作完成后重写整个擦除块。而512MB的SDC似乎内置了4KB的数据缓存,所以512MB的SDC以4KB为界重写擦除块,因此两者不能直接作比较。但重写擦除块的时间可以检测出,对于128MB的SDC,这个时间为3800(单位是什么),而512MB的SDC,这个时间则是30000——几乎是128MB SDC的8倍。从分析结果判断,似乎128MB的SDC采用了小容量块的主控芯片,而512MB的SDC则采用了大容量块或是MLC(Multi=Level Cell—多层单元)主控芯片。当然,块尺寸越大,重写小部分块的性能越低。对于512MB的SDC,只有存储介质顶端的512KB的空间重写速度较快。这段时间可以通过函数close( )中的写时间读出。可能该空间是为了快速FAT访问而进行了特别处理。

提升写速度

       为了避免上述提及的瓶颈,提高SDC/MMC的写速度,一次尽可能写入足够多的块(能与擦除块对齐是最好的)能提升写数据的性能。换而言之,就是分配一个大的数据缓冲区并将之传递给函数 fwrite( ) 。对于低级存储卡的写功能,必须预先告诉存储卡要写的扇区数以提高写处理的效率。这被称为“预定义多块写”。然而,这里的预定义指令与MMC(CMD23)及SDC(ACMD23)是不同的。

       当然,对于只有几KB RAM的廉价MCU来说,这些提升SDC写速度的方法可能是徒劳的。与SDC相比,CF(CompactFlash)卡的性能是其十倍。若你需要较高的写速度,CF或MMC比SDC更合适。

       存储卡出厂时被预先分区、格式化,以使分配单元与擦除块对齐。若不小心使用某个设备将MMC/SDC重新格式化或分区时造成与MMC/SDC的不兼容。这种优化会被破坏,写速度可能会降低。笔者曾经尝试过使用PC的FAT32重新格式化512MB的SDC,在文件复制时的传输速度下降了不少。因此,重新格式化存储卡需要使用与MMC/SDC兼容的设备而不是PC。

你可能感兴趣的:(How to Use MMC/SDC)