打印
[其他MCU]

Freescale KL26 SD卡读写程序分析

[复制链接]
4030|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Vitality1|  楼主 | 2015-2-9 17:09 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    先了解一下SD卡和Micro SD卡的基本常识:

    SD卡是SecureDigital Card卡的简称,直译成汉语就是“安全数字卡”,是1999年由日本松下公司、东芝公司和美国SANDISK公司共同开发研制的全新的存储卡产品。SD存储卡是一个完全开放的标准(系统),多用于MP3、数码摄像机、数码相机、电子图书、AV器材等等。
 MicroSD卡(这家伙原名叫TF卡(Trans-Flashcard),2004年改的名,原来不仅人名可以改,这玩意也改名~~),是一种极细小的快闪存储器卡,由著名的存储厂商闪迪(SanDisk)公司发明创立。这种卡主要于手机使用,但因它拥有体积极小的优点,随着不断提升的容量,它慢慢开始于GPS设备、便携式音乐播放器和一些快闪存储器盘中使用。
   那么这两种卡到底有什么区别呢?其实从这哥俩的名字就能看出一些端倪,MicroSD是在SD的前面加了一个Mirco,哦哦,Micro,那就是更小的SD喽。对的,其实SD卡与MicroSD卡在功能上没区别,MicroSD卡就是SD卡的微型版,也可以看作是升级版本,既然它们关系如此密切,所以在MicroSD卡外面加个卡套(专业一点是叫做adapter,适配器),就可以把MicroSD当作SD卡来使用了。如下图所示,将右侧MicroSD卡插入左侧卡套中,之后再插入电脑SD卡槽中,就可以使用把它当作U盘来使用了。



    再多说两句,你应该还听说过MMC卡,它的全称叫做Multimedia Card),翻译成中文为“多媒体卡”。在1997年由西门子及SanDisk共同开发的。这玩意和SD卡又有什么关联呢?SD卡的技术是基于MMC上发展而来的,它们区别在于初始化过程不同。
OK,下面看一下SD卡和Micro SD的引脚定义:
                             

     上图需要说明的是,SD卡和micro SD卡支持两种操作模式,分别为SD模式和SPI模式, SD模式速度快,安全性好,不过它需要引脚多而且主要是驱动起来复杂,当然有些MCU会自带SD模式的硬件接口,这样就方便多了,而SPI模式就方便许多了,虽然速度上没有SD模式快,不过很多MCU都自带SPI硬件资源,这样使用起来就比较方便,如果MCU不带SPI硬件接口的话,就需要使用IO口去模拟了。

相关帖子

沙发
Vitality1|  楼主 | 2015-2-9 17:10 | 只看该作者
上面两张图看着有点头晕,再来一张看起来简单一点的图:
   

  再来对比一下它们引脚之间的关系,

  可以发现Micro SD卡只是比SD卡少了一个VSS  引脚,这更加证明了这两个卡时如此的关系密切。。
  了解这些概念之后,我们就开始用MCU来控制它了。首先看一下SD卡硬件电路部分:


SPI下电路的连接非常简单,接上电源线Vdd和地线Vss,再接上SPI的CS,SCLK,DI(MOSI)和DO(MISO)就可以了,其他引脚可以放空。注意SD卡的电源和操作电压都为2.7-3.6V,如果使用5V的单片机要进行电平转换或串电阻限流。还有记得SD卡的CS,SCLK和DI要用10~100K的电阻上拉,下图是SD卡的一个连接示意图,对于MicroSD卡一样的接法,只是注意下引脚的不同就行了。

使用特权

评论回复
板凳
Vitality1|  楼主 | 2015-2-9 17:10 | 只看该作者
由于本实验是在YL-KL26开发板上进行的,该板子是优龙公司做的,链接如下:http://www.ucdragon.cn/product/showproduct.php?lang=cn&id=14,淘宝网址为:
http://item.taobao.com/item.htm?spm=a1z10.1.w137644-2186920978.41.JbpFWl&id=36763794750  ,售价99RMB,还挺便宜,记得我大学那会买了一个51开发板还一百多元呢。
所以让我们来看一下此开发板上Micro SD卡部分的原理图:

   
这张图有一点困惑了我老半天,那就是9脚nCD是什么玩意?刚开始我一直以为J5是一个将TF卡转换为SD卡的卡套,因为看到左侧有9个引脚,就想当然的这么认为了,结果怎么也搞不明白9脚nCD是什么功能?后来才恍然明白,J5只不是一个TF卡的卡座而已,直接上图,这玩意如下:
你如果数一数它最下侧引脚数目的话,会发现有9个引脚,这是怎么回事?之前明明说TF卡只有8个引脚,那么多出来的这个引脚是怎么回事?原来多出来的这个CD引脚具有这样的功能,平时卡座不插卡的时候,它和GND脚是断开的,当插入卡时,它就和GND连在一起了。CD原来是CardDetection的意思。可以通过这个这个脚来检测有没有卡插入。
这个卡座的引脚定义如下,pin1到pin8的定义和TF卡的引脚定义是一样的,就是多了9脚.

使用特权

评论回复
地板
Vitality1|  楼主 | 2015-2-9 17:12 | 只看该作者
硬件明白之后,下面就开始看软件部分了。
下面就来具体分析一下如何初始化及读写TF卡。
还是得先补充一点基础知识,以前欠下不会的,都得还回来啊,^^
SD卡的命令格式如下:


   它由6 个字节组成,字节1 的最高2 位固定为01,低6 位为命令号(比如CMD16,为10000,完整的CMD16,第一个字节为01010000)。字节2~5 为命令参数,共4个字节,并不是所有命令都有参数,没有参数的话该位一般就置0。最后一个字节由7bit CRC校验位和1bit停止位(该位固定为1)组成。在SPI模式下,CRC是被忽略的,可以都置1或置0.但是发送CMD0时要记得加上CRC,即最后1字节为0x95(因为发送CMD0时还未进入SPI模式,[url=]PS[/url]:CMD8也要,但一般大家都把发送CMD8省略了)。
每次发送完一次命令后,SD卡都会有回应。SD卡的回应有多种格式,1字节的R1,2字节的R2等,不过一般在SPI模式中我们只用到R1,下面介绍R1的格式:

向SD卡写入一个CMD或者ACMD指令的过程是这样的: 首先使CS为低电平,SD卡使能;其次在SD卡的DI写入指令;写入指令后还要附加8个填充时钟,使SD卡完成内部操作;之后在SD卡的DO上接受回应;回应接受完毕使CS为高电平,再附加8个填充时钟。
初始化的典型流程如下:
1、初始化与 SD 卡连接的硬件条件(MCU 的 SPI 配置,IO 口配置);
2、拉高片选,上电延时(>74 个 CLK);
3、选择片选,发送CMD0命令 ,进入 IDLE状态;
4、发送 CMD8,检查是否支持 2.0 协议;
5、根据不同协议检查 SD 卡(命令包括:CMD55、CMD41、CMD58 和 CMD1 等);
6、取消片选,发多 8 个 CLK,结束初始化。
拉高CS,发送至少74个clk周期是为了使SD卡达到正常工作电压和进行同步,原因是SD卡内部有个供电电压上升时间,大概为64个CLK,剩下的10个CLK用于SD卡同步,之后才能开始CMD0的操作,在卡初始化的时候,CLK时钟最大不能超过400Khz!
发送CMD0,需要收到回应0x01表示成功进入idle状态,这由上述R1的格式可以看出来。
上述2,3过程可用下图来表示:


对于4,发送CMD8,它的返回格式如下:


接收到的第一个字节,如果为0x01,表示SD卡响应了CMD8命令,此卡为V2.0版本,如果不为0x01(一般会是0x05或者0x09),表示SD卡不支持CMD8命令,此卡为V1.0卡或者MMC卡。对于V2.0版本,后续还会收到4个字节,这里只需要关注最后两字节,8-11表示SD卡支持的电压范围,此处应该得到0001,最后一字节在发送CMD8指令时发来的数据,在程序中我们调用r1= SDSendCmd(CMD8,0x000001aa,0x87);这里应该收到aa.
如果CMD8收到的第一个字节不是0x01的话,就不用再读后续的三个字节了。

   对于5,如果是V2.0卡那么流程如下:不断发送CMD55+ACMD41,先发送CMD55,在收到0x01之后,再发送ACMD41,直到收到0x00表示V2.0初始化成功,进入Ready状态。再发送CMD58命令来判断是HCSD还是SCSD,到此SD2.0卡初始化成功。SD卡按容量的大小可以分为SC、HC 、XC三种类型,如下表所示:


      需要注意的是:SD卡和SDHC 卡协议基本兼容,但是SDXC 卡,同这两者区别就比较大了,我们讨论的主要是SD/SDHC 卡 。
对于5, 如果CMD8返回错误,表示不支持V2.0卡,则进一步判断为1.0卡还是MMC卡,方法循环发送CMD55+ACMD41,返回无错误,则为SD1.0卡,到此SD1.0卡初始化成功,如果在一定的循环次数下,返回为错误,则进一步发送CMD1进行初始化,如果返回无错误,则确定为MMC卡,如果在一定的次数下,返回为错误,则不能识别该卡,初始结束。

总结:一个完整的驱动是应该包括上述所有过程的,CMD0是必须要发送的,CMD8用来区分是否为V2.0卡,如果是V2.0,再发送CMD55+ACMD41 来确认初始化是否成功,CMD58用来区分卡容量大小。所以对于V2.0卡,完整的驱动应该包括CMD0,CMD8,CMD55+ACMD41,CMD58 这几个命令。对于V1.0卡,需要CMD0,CMD8,CMD55+ACMD41命令。对于MMC卡,需要CMD0,CMD1命令。

使用特权

评论回复
5
Vitality1|  楼主 | 2015-2-9 17:12 | 只看该作者
初始化完成之后,就可以进行读写操作了。
SD卡读单块和多块的命令分别为CMD17和CMD18,它们的参数即要读的区域的开始地址。因为考虑到一般SD卡的读写要求地址对齐,所以一般我们都将地址转为块,并以扇区(块)(512Byte)为单位进行读写,比如读扇区0参数就为0,读扇区1参数就为1<<9(即地址512),读扇区2参数就为2<<9(即地址1024),依此类推。
   读单块方法:
   1.发送CMD17,收到0x00表示成功
   2.连续读直到读到开始字节0xFE
   3.读512个字节
   4.读两个CRC字节
   读单块时序图:
读多块方法:
   1.发送CMD18读,收到0x00表示成功
   2.连续读直到读到开始字节0xFE
   3.读512字节
   4.读两个CRC字节
   5.如果还想读下一扇区,重复2-4
   6.发送CMD12来停止读多块操作

写单块和多块:
   SD卡用CMD24和CMD25来写单块和多块,参数的定义和读操作是一样的。
   写单块方法:
   1.发送CMD24,收到0x00表示成功
   2.发送若干时钟
   3.发送写单块开始字节0xFE
   4.发送512个字节数据
   5.发送2字节CRC(可以均为0xff)
   6.连续读直到读到XXX00101表示数据写入成功
   7.继续读进行忙检测(读到0x00表示SD卡正忙),当读到0xff表示写操作完成
写单块时序图:


写多块方法:
   1.发送CMD25,收到0x00表示成功
   2.发送若干时钟
   3.发送写多块开始字节0xFC
   4.发送512字节数据
   5.发送两个CRC(可以均为0xff)
   6.连续读直到读到XXX00101表示数据写入成功
   7.继续读进行忙检测,直到读到0xFF表示写操作完成
   8.如果想读下一扇区重复2-7步骤
   9.发送写多块停止字节0xFD来停止写操作
   10.进行忙检测直到读到0xFF

使用特权

评论回复
6
Vitality1|  楼主 | 2015-2-9 17:13 | 只看该作者
OK,SD卡驱动部分基本讲完了,下面动手跑一跑代码吧。代码的下载地址为:
,下载后可以直接download到YL-KL26中。
本代码实现的功能很简单,就是将Buffer1中的数据先写入SD卡,然后再读出来存放到Buffer2中并打印出来。实验结果如下:


使用特权

评论回复
7
Vitality1|  楼主 | 2015-2-9 17:14 | 只看该作者
附上:sd_Driver.c的代码

[cpp] view plaincopy


  • /*
  • ** File Name:               SD_Driver.c
  • ** Last modified date:     2014/11/7   
  • ** Modified by: Wang Wenxue
  • ** Last version:            V1.0
  • ** Description:              
  • */  
  •   
  • #include "SD_Drv.h"  
  • #include "includes.h"  
  •   
  •   
  •   
  • //set CS low  
  • void CS_Enable()  
  • {  
  •     //set CS low  
  •     FGPIOC_PCOR |= 1 << 4;  
  • }  
  •   
  • //set CS high and send 8 clocks  
  • void CS_Disable()  
  • {  
  •     //set CS high  
  •     FGPIOC_PSOR |= 1 << 4;   
  •     //send 8 clocks  
  •     SDWriteByte(0xff);  
  • }  
  •   
  •   
  • //write a byte  
  • void SDWriteByte(INT8U data)  
  • {  
  •     INT8U ucTemp;  
  •   while((SPI0_S & SPI_S_SPTEF_MASK) != SPI_S_SPTEF_MASK);              
  •   SPI0_DL = data;  
  •       
  •      
  •   while((SPI0_S & SPI_S_SPRF_MASK) != SPI_S_SPRF_MASK);                 
  •   ucTemp = SPI0_DL;                                                   
  •   ucTemp = ucTemp;  
  • }  
  •   
  •   
  • //read a byte  
  • INT8U SDReadByte()  
  • {  
  •     INT8U data = 0x00;  
  •   
  •     //read 8 bit(MSB)  
  •     while((SPI0_S & SPI_S_SPTEF_MASK) != SPI_S_SPTEF_MASK);              
  •   SPI0_DL = 0xff;                                                     
  •    
  •   while((SPI0_S & SPI_S_SPRF_MASK) != SPI_S_SPRF_MASK);                  
  •   data = SPI0_DL;                                                     
  •     return data;                                                   
  • }  
  •   
  • //send a command and send back the response  
  • INT8U SDSendCmd(INT8U cmd,INT32U arg,INT8U crc)  
  • {  
  •     INT8U r1,time = 0;  
  •   
  •     //send the command,arguments and CRC  
  •     SDWriteByte((cmd & 0x3f) | 0x40);  
  •     SDWriteByte(arg >> 24);  
  •     SDWriteByte(arg >> 16);  
  •     SDWriteByte(arg >> 8);  
  •     SDWriteByte(arg);  
  •     SDWriteByte(crc);  
  •   
  • //read the respond until responds is not '0xff' or timeout  
  •     do   
  • {  
  •   r1 = Send_Byte( 0xff ); // ??????  
  •   time++;  
  •     //if time out,return  
  •     if(time > 254) break;  
  • } while ( r1==0xff);  
  • return r1;  
  •   
  • }  
  •   
  •   
  •   
  •   
  • /******************************************************************************************************************************
  • //Function Name : SD_Init
  • //description   :  
  • //Input         : None
  • //Output        : None
  • //Return        : 0:success
  • //                1:Failure
  • ******************************************************************************************************************************/  
  • INT8U SD_Init( void )  
  • {  
  •     INT16U i = 0;  
  •     while(SDReset() != 0){        
  •         i ++;  
  •         if(i >= 20)   
  •         {  
  •             printf("SDReset Fail!\n\r");   
  •             return 1;  
  •         }  
  •     }  
  •     printf("SDReset success!\n\r");   
  •     i = 0;  
  •     while(SDInit() != 0){      
  •         i ++;  
  •         if(i >= 20)   
  •         {  
  •             printf("SDInit Fail!\n\r");   
  •             return 1;  
  •         }  
  •     }  
  •     printf("SDInit success!\n\r");   
  •     return 0;  
  •   
  • }  
  •   
  •   
  • //reset SD card  
  • INT8U SDReset( void )  
  • {  
  •     INT8U i,r1,time = 0;  
  •   
  •     //set CS high  
  •     CS_Disable();  
  •    
  •     //send 128 clocks  
  •     for(i = 0;i < 16;i ++)  
  •     {  
  •         SDWriteByte(0xff);  
  •     }  
  •   
  •     //set CS low  
  •     CS_Enable();  
  •   
  •     //send CMD0 till the response is 0x01  
  •     do{  
  •         r1 = SDSendCmd(CMD0,0,0x95);  
  •         time ++;  
  •         //if time out,set CS high and return r1  
  •         if(time > 254)  
  •         {  
  •             //set CS high and send 8 clocks  
  •             CS_Disable();  
  •             return r1;  
  •         }  
  •     }while(r1 != 0x01);  
  •   
  •     //set CS high and send 8 clocks  
  •     CS_Disable();  
  •   
  •     return 0;  
  • }  
  •   
  •   
  • //initial SD card(send CMD55+ACMD41 or CMD1)  
  • INT8U SDInit()  
  • {  
  •     INT8U r1,time = 0;  
  •   
  •     //set CS low  
  •     CS_Enable();  
  •   
  •     //check interface operating condition  
  •     r1 = SDSendCmd(CMD8,0x000001aa,0x87);  
  •       
  •     //if support Ver1.x,but do not support Ver2.0,set CS high and return r1  
  •     if(r1 == 0x05)  
  •     {  
  •         //set CS high and send 8 clocks  
  •         CS_Disable();  
  •         return r1;  
  •     }  
  •     //read the other 4 bytes of response(the response of CMD8 is 5 bytes)  
  •     r1=SDReadByte();  
  •     r1=SDReadByte();  
  •     r1=SDReadByte();  
  •     r1=SDReadByte();  
  •   
  •     CS_Disable();  
  •     CS_Enable();  
  •     do{  
  •         //send CMD55+ACMD41 to initial SD card  
  •         do{  
  •             r1 = SDSendCmd(CMD55,0,0xff);  
  •             time ++;  
  •             //if time out,set CS high and return r1  
  •             if(time > 254)  
  •             {  
  •                 //set CS high and send 8 clocks  
  •                 CS_Disable();  
  •                 return r1;  
  •             }  
  •         }while(r1 != 0x01);  
  •          
  •      CS_Disable();  
  •      CS_Enable();  
  •   
  •         r1 = SDSendCmd(ACMD41,0x40000000,0xff);  
  •   
  •         //send CMD1 to initial SD card  
  •         //r1 = SDSendCmd(CMD1,0x00ffc000,0xff);  
  •         time ++;  
  •   
  •         //if time out,set CS high and return r1  
  •         if(time > 254)  
  •         {  
  •             //set CS high and send 8 clocks  
  •             CS_Disable();  
  •             return r1;  
  •         }  
  •     }while(r1 != 0x00);  
  •   
  •     //set CS high and send 8 clocks  
  •     CS_Disable();  
  •   
  •     return 0;  
  • }  
  •   
  •   



使用特权

评论回复
8
Vitality1|  楼主 | 2015-2-9 17:15 | 只看该作者
  •   
  •   
  • //read a single sector  
  • INT8U SDReadSector(INT32U addr,INT8U * buffer)  
  • {  
  •     INT8U r1;  
  •     INT16U i,time = 0;  
  •   
  •     //set CS low  
  •     CS_Enable();  
  •   
  •     //send CMD17 for single block read  
  •     r1 = SDSendCmd(CMD17,addr << 9,0x55);  
  •     //if CMD17 fail,return  
  •     if(r1 != 0x00)  
  •     {  
  •         //set CS high and send 8 clocks  
  •         CS_Disable();  
  •         return r1;  
  •     }  
  •   
  •     //continually read till get the start byte 0xfe  
  •     do{  
  •         r1 = SDReadByte();  
  •         time ++;  
  •         //if time out,set CS high and return r1  
  •         if(time > 30000)  
  •         {  
  •             //set CS high and send 8 clocks  
  •             CS_Disable();  
  •             return r1;  
  •         }  
  •     }while(r1 != 0xfe);  
  •   
  •     //read 512 Bits of data  
  •     for(i = 0;i < 512;i ++)  
  •     {  
  •         buffer = SDReadByte();  
  •     }  
  •   
  •     //read two bits of CRC  
  •     SDReadByte();  
  •     SDReadByte();  
  •   
  •     //set CS high and send 8 clocks  
  •     CS_Disable();  
  •   
  •     return 0;  
  • }  
  •   
  •   
  •   
  • //write a single sector  
  • INT8U SDWriteSector(INT32U addr,INT8U * buffer)  
  • {  
  •     INT16U i,time = 0;  
  •     INT8U r1;  
  •   
  •     //set CS low  
  •     CS_Enable();  
  •   
  •     do{  
  •         do{  
  •             //send CMD24 for single block write  
  •             r1 = SDSendCmd(CMD24,addr << 9,0xff);  
  •             time ++;  
  •             //if time out,set CS high and return r1  
  •             if(time > 254)  
  •             {  
  •                 //set CS high and send 8 clocks  
  •                 CS_Disable();  
  •                 return r1;  
  •             }  
  •         }while(r1 != 0x00);  
  •         time = 0;  
  •   
  •         //send some dummy clocks  
  •         for(i = 0;i < 5;i ++)  
  •         {  
  •             SDWriteByte(0xff);  
  •         }  
  •   
  •         //write start byte  
  •         SDWriteByte(0xfe);  
  •   
  •         //write 512 bytes of data  
  •         for(i = 0;i < 512;i ++)  
  •         {  
  •             SDWriteByte(buffer);  
  •         }  
  •   
  •         //write 2 bytes of CRC  
  •         SDWriteByte(0xff);  
  •         SDWriteByte(0xff);  
  •   
  •         //read response  
  •         r1 = SDReadByte();  
  •         time ++;  
  •         //if time out,set CS high and return r1  
  •         if(time > 254)  
  •         {  
  •             //set CS high and send 8 clocks  
  •             CS_Disable();  
  •             return r1;  
  •         }  
  •     }while((r1 & 0x1f)!= 0x05);  
  •     time = 0;  
  •   
  •     //check busy  
  •     do{  
  •         r1 = SDReadByte();  
  •         time ++;  
  •         //if time out,set CS high and return r1  
  •         if(time > 60000)  
  •         {  
  •             //set CS high and send 8 clocks  
  •             CS_Disable();  
  •             return r1;  
  •         }  
  •     }while(r1 != 0xff);  
  •   
  •     //set CS high and send 8 clocks  
  •     CS_Disable();  
  •   
  •     return 0;  
  • }  

使用特权

评论回复
9
FSL_TICS_Jeremy| | 2015-2-10 09:28 | 只看该作者
赞!

使用特权

评论回复
10
hello_zkp| | 2015-12-7 16:23 | 只看该作者
总结的非常好,对于入门SD卡很有帮助

使用特权

评论回复
11
大苏牙| | 2015-12-7 21:03 | 只看该作者
写sd卡和写内部flash有什么区别呢?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

81

主题

421

帖子

9

粉丝