MapleadMCUDevelopmentBoard卡是日常生活中常见的一种电可擦除移动存储设备,因其存储量大、价格低廉而被广泛应用于数码相机、手机等数码产品中。SD卡支持两种总线方式:SD方式与SPI方式。其中SD方式采用6线制,使用CLK、CMD、DAT0、DAT1、DAT2、DAT3进行数据通信,其特点是数据位数宽(4位)、速度快。SPI方式采用4线制,使用CS、CLK、DataIn、DataOut这4个端口进行数据通信,其特点是速度要比SD方式慢,但总线简单、不需要进行CRC校验,因而比较适合单片机采用这种方式对SD卡进行读写操作。图21-1SD卡及其接口表21-1SD卡接口定义说明端口SD模式SPI模式1.DAT3/CSDAT3口。片选,低电平有效。2.CMD/DataIn命令/回应。数据输入端。3.VSS1电源地。电源地。4.VDD电源。电源。5.CLK/SCK同步时钟。同步时钟。6.VSS2电源地。电源地。7.DAT0/DataOutDAT0口。数据输出端。8.DAT1DAT1口。NC。9.DAT2DAT2口。NC。SD卡命令格式SD卡的命令字由6个字节组成,其组成结构见图21-2。MapleadMCUDevelopmentBoard个字节为命令号(CMD0、CMD1等),命令号的昀高位始终为0,次高位(位7)为1,表示命令是由主机向SD卡发送的命令。命令字的第2、3、4、5字节为命令参数表,传送随命令附带的参数,如地址信息等。命令字的昀后一个字节为CRC校验字节,其中该字节的高7位为CRC码,昀后一位为结束位,始终为1。Byte1*Byte2Byte3Byte4Byte5Byte601xxxxxx参数参数参数参数7位CRC1*X表示该位可以为“0”或者“1”。图21-2SD卡命令字结构当主机向SD卡发送一个命令后,SD卡会首先向主机发送一个相应码,如果指令没有错误SD卡接下来便会执行主机发来的命令。SD卡复位至SPI方式SD卡默认的读写方式为SD模式,要使用SPI模式对SD卡进行读写,需要在SD卡上电后对SD卡写入CMD0和CMD1命令。在两条命令写入SD卡后,如果一切正常,SD卡会进入SPI模式,从而可以方便地采用单片机对SD卡进行SPI方式的读写操作。图21-3对SD卡的复位操作时序当SD卡完成上电后,先对SD卡发送74个以上的同步时钟。然后向SD卡发送CMD0命令(因命令号的昀高位始终为0,次高位为1,因此发送给SD卡的命令是0与0x40进行或运算的结果)。命令字的第2、3、4、5字节皆为0x00。MapleadMCUDevelopmentBoard校验字节,固定为0x95。命令字发送完成后,发送若干个8位的同步时钟,直至SD卡发出响应字节0x01。接收完SD卡的响应字节后,CS线拉高,再发送8个空时钟。图21-4将SD卡初始化为SPI方式时序当SD卡执行完复位过程后,接着向SD卡发送初始化指令SMD1,以激活SD卡SPI方式。首先发送指令号CMD1(0x01|0x40=0x41),然后发送4个0x00字节,昀后发送CRC校验码,此处为0xFF,原因是此时SD卡已经工作在SPI模式,在SPI模式下,SD卡默认为不进行CRC校验,因此我们可以随意写入一个0xFF字节以填充满整个命令字的结构。当向SD卡发送完CMD1指令后,发送若干个8位的同步时钟,直至SD卡发出响应字节0x00。接收完SD卡的响应字节后,CS线拉高,再发送8个空时钟。至此,我们完成了SD卡的复位及进入到SPI方式的所有步骤。SD卡的SPI方式读写SD卡读写一次的数据量必须为512字节的整数倍,亦即,对SD卡读写操作的昀少数据量为512字节。我们也可以通过向SD卡写修改扇区大小的指令CMD16以使每次读写的数据量变为(n×512)字节(n≥1),本文中我们使用SD卡默认的一次操作512字节的模式对SD卡进行读写操作。MapleadMCUDevelopmentBoard卡的单数据块读操作:图21-5对SD卡进行单数据块读操作时序如图21-4所示,对SD卡读操作的时序为:(1)写入读单数据块命令CMD17(0x11|0x40=0x51)。(2)写4个地址参数,4个字节凑成一个32位的地址值,第一个字节是32位地址值的昀高8位数据,第4个字节是32位地址值的昀低8位数据。(3)写CRC校验位0xFF。(4)写若干个0xFF的空操作。(5)SD卡发送0x00响应。(6)写若干个0xFF的空操作。(7)SD卡发送0xFE数据头。(8)SD卡发送指令指定地址的512字节数据块。(9)SD卡发送两字节的CRC校验码,由于SPI模式默认不需要CRC校验,因此这两个字节的数据可丢弃不用。(10)拉高CS,发送8个空时钟。至此,我们完成了对SD卡指定地址数据块的读操作。MapleadMCUDevelopmentBoard卡的单数据块写操作:图21-6对SD卡进行单数据块写操作时序如图21-4所示,对SD卡读操作的时序为:(1)写入读单数据块命令CMD24(0x18|0x40=0x58)。(2)写4个地址参数,4个字节凑成一个32位的地址值,第一个字节是32位地址值的昀低8位数据,第4个字节是32位地址值的昀高8位数据。(3)写CRC校验位0xFF。(4)写若干个0xFF的空操作。(5)SD卡发送0x00响应。(6)写若干个0xFF的空操作。(7)写0xFE数据头,表示接下来要发送512字节的数据块。(8)写512字节数据块。(9)写两字节的0xFF作为CRC校验码的填充字节。(10)SD卡发送xxx00101B响应码。(11)在SD卡将512字节数据向指定地址写完之前,其数据输出断时钟被拉低。(12)SD卡释放数据输出线DataOut。MapleadMCUDevelopmentBoard(13)拉高CS,发送8个空时钟。至此,我们完成了对SD卡指定地址数据块的写操作。图21-7SD卡电路连接原理图SD卡电路连接实物图图21-8SD卡电路连接实物图用杜邦线将P3.4口连接至SD模块的CLK端口;用杜邦线将P3.5口连接至SD模块的DI端口;MapleadMCUDevelopmentBoard端口;用杜邦线将P3.7口连接至SD模块的CS端口;将一张2G容量以下(2G以上没有测试过)的SD卡插入SD卡槽就可以进行该实验了。SD卡SPI方式读写演示程序例程21-1SD卡SPI方式读写/*******************************************************************************说明:*(1)晶振12MHz调试通过。*(2)2G以下SD卡SPI方式读写驱动程序。*(3)对SD卡操作要以块为单位进行操作(地址为512的整数倍;数据量为512的整*数倍)。*(4)向SD卡中写数据时,尽量避开卡开始部分的地址(本程序数据写入点为51200*地址以后的空间),不然可能会造成SD卡无法识别、无法格式化等问题(可用SD*专用格式化软件解决)!*(5)请确保您的SD卡内没有个人资料,不然数据会丢失!!!!!!!!!!!!!*(6)建议采用价格便宜的工厂用测试卡运行本程序,对于运行本程序有可能造成的*SD卡损坏等情况,本程序作者及开发板生产销售商不承担任何责任!!!!!!*(7)如果单片机内部的RAM不够大,读入的数据块将会部分丢失,但不影响程序的*演示(例如STC89C52RC内部的xdata空间容量为256Byte,当所取数据块超过*256字节时内存溢出,数据丢失。如果超出存储空间读数据,取出的数据都为*0xFF)。*(8)测试时如果SD卡中途无反应,彻底关闭开发板电源,重新启动。*(9)测试时,先用杜邦线将单片机输出端口与对应的SD卡SPI接口相连:*P3.4----SD卡SPI接口的CLK*P3.5----SD卡SPI接口的DI*P3.6----SD卡SPI接口的DO*P3.7----SD卡SPI接口的CS*作者:ShenneyJohn*******************************************************************************Copyright(C)2010byShenneyJohn,AllRightsReserved.******************************************************************************/#includereg52.hsbitCLK=P3^4;//时钟线sbitDI=P3^5;//数据输入线(相对SD卡为输入,单片机刚好相反)sbitDO=P3^6;//数据输出线(相对SD卡为输出,单片机刚好相反)sbitCS=P3^7;//片选线voidDisplay(unsignedchar);voidSPI_W(unsignedchar);unsignedcharSPI_R(void);unsignedcharSD_Response(void);voidSD_Cmd(unsignedchar,unsignedlong,unsignedchar);unsignedcharSD_Init(void);unsignedcharSD_Block_W(unsignedchar,unsignedlong,unsignedint);unsignedchar*SD_Block_R(unsignedlong,unsignedint);MapleadMCUDevelopmentBoard[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};//数码管段码unsignedcharcodeLED_Bit[]={0x00,0x01};//数码管位码unsignedcharxdata*DATA;//指向数据块的指针unsignedcharcodeTest_1[]={//测试数据块10X00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0X0B,0X0C,0X0D,0X0E,0X0F,0X10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0X1B,0X1C,0X1D,0X1E,0X1F,0X20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0X2B,0X2C,0X2D,0X2E,0X2F,0X30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0X3B,0X3C,0X3D,0X3E,0X3F,0X40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0X4B,0X4C,0X4D,0X4E,0X4F,0X50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x5