基于stm32f103zet6的FAT16文件系统学习0(读SD卡扇区)

发布者:谁与争锋1最新更新时间:2017-09-06 来源: eefocus关键字:stm32f103zet6  FAT16  文件系统学  SD卡扇区 手机看文章 扫描二维码
随时随地手机看文章

SD卡已经看了两天了,主要是因为测试出来的卡容量不对,所以一直找原因,最终还是发现了,总比不过是单位上面出现了问题,或许是之前没有接触到SD的缘故吧,所以对其中的一些寄存器很不了解,一切都是重新开始,对照这寄存器手册,理解程序,修改程序。一步步还是总结一下!

首先关于SD卡的协议是有必要了解的,我今天花了一上午的课堂时间来理解这个SD卡的协议,就是基于这个文档的,这个文档很适合入门SD协议的(个人认为)。http://download.csdn.net/detail/king_bingge/5218183

初识SD之后,就可以开始正式学习SD卡了!

一、要使用SD卡,那么首先肯定得对SD卡进行初始化,那么如何进行初始化呢?(命令的参数暂且不提)

1、这里涉及到很多指令了。协议规定了在给SD卡上电之后需要给出至少74个时钟脉冲后,才能进行相关的SD初始化工作,虽然是这么说,但是我不给74个时钟,他照样能初始化,看看。


  1. for(i=0;i<10;i++)SD_SPI_ReadWriteByte(0XF);  


但是,或许为了能够更加成功的初始化吧,所以有这个规定所以,我们还是规规矩矩的好,给它74个时钟,没关系的嘛!


2、然后就是协议中说到当我们复位或者上电的时候,SD卡的SD控制寄存器处于卡识别模式中的空闲模式的,暂且这样称吧。本来我们是不需要发送复位命令了的,但是我们不知道我们的SD所支持的电压范围。所以,我们最好还是先给出一条复位指令,然后紧接着一条获取工作电压的指令,这样也是比较保险,如果多SD卡工作电压有疑问的,那么就得去看芯片手册了。有了这个知识,那下面的代码就不成问题了


  1. retry=20;  

  2.     do  

  3.     {  

  4.         r1=SD_SendCmd(CMD0,0,0x95);//进入IDLE状态  

  5.     }while((r1!=0X01) && retry--);  

  6.     SD_Type=0;//默认无卡  

  7.     if(r1==0X01)  

  8.     {  

  9.         if(SD_SendCmd(CMD8,0x1AA,0x87)==1)//SD V2.0  

  10.         {  

  11.             for(i=0;i<4;i++)buf[i]=SD_SPI_ReadWriteByte(0XFF);   //Get trailing return value of R7 resp  

  12.             if(buf[2]==0X01&&buf[3]==0XAA)//卡是否支持2.7~3.6V  

3、协议上还提到ACMD41命令的目的是给予 SD卡控制器一个识别 SD卡是否可以在所给Vdd 范围下工作的机制,如果 SD 卡无法在指定 Vdd 范围内工作,则它会进入非活动状态(Inactive state ),所以我们接下来需要发送这个命令,但是在发送这个命令之前,要知道这是一个应用型的命令,所以要加上CMD55命令,所以有了下面的代码。



  1. if(buf[2]==0X01&&buf[3]==0XAA)//卡是否支持2.7~3.6V  

  2.     {  

  3.         retry=0XFFFE;  

  4.         do  

  5.         {  

  6.             SD_SendCmd(CMD55,0,0X01);   //发送CMD55  

  7.             r1=SD_SendCmd(CMD41,0x40000000,0X01);//发送CMD41  

  8.         }while(r1&&retry--);  

  9.         if(retry&&SD_SendCmd(CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始        //获取供电状态  

  10.         {  

  11.             for(i=0;i<4;i++)buf[i]=SD_SPI_ReadWriteByte(0XFF);//得到OCR值  

  12.             if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;    //检查CCS  

  13.             else SD_Type=SD_TYPE_V2;     

  14.         }  


这样就获取了卡的类型了,至此卡的初始化基本完成,当然根据协议上,我们还可以在这里修改相对地址之类的。如果有必要的话,可以这样做!


二、初始化完SD卡,接下来如果你想查看我们SD的容量,可以这样做!

之前就是因为卡容量的问题,所以郁闷了好久,理解了个大概!注意这里函数名是读取扇区数,实际上返回的值是我们卡的容量,这里得注意了。

1、首先看代码


  1. u32 SD_GetSectorCount(void)  

  2. {  

  3.     u8 csd[16];  

  4.     u32 Capacity_KB,Capacity_MB ;  

  5.     u8 n;  

  6.         u16 csize;                            

  7.     //取CSD信息,如果期间出错,返回0  

  8.     if(SD_GetCSD(csd)!=0) return 0;       

  9.         n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;  

  10.         csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;  

  11.       

  12.         Capacity_KB= (u32)csize << (n - 10);//得到扇区数 ,这里的单位是KB  

  13.         Capacity_MB = Capacity_KB/1024;  

  14.     return Capacity_MB;  

  15. }  


这个计算的问题必须得看SD卡的手册,也就是128位的CSD寄存器。这里我把我分析的过程贴出来,我不得不说比较乱,或许只有我自己能看懂了,懒得整理了,仅供参考!


  1. My SD_Card  

  2. CSD寄存器中的值如下:  

  3. 00 7f ff 32    bit(127-96)          csd0 - csd3  

  4. 5f 59 83 cb    bit(95--64)          csd4 - csd7     0101 1111 0101 1001 1000 0011 1100 1011  

  5. 76 db df ff    bit(63--32)          csd8 - csd11        0111 0110 1101 1011 1101 1111 1111 1111  

  6. 96 40 00 97    bit(31---0)          csd12 -csd15  

  7.   

  8. csize  {62,73}  

  9. csize_muti{47,49}  

  10. read {80,83}  

  11. csize = 1111 0010 1101 = 3885  

  12. csize_muti = 111 = 7  

  13. read = 1001 = 9   

  14. 计算公式:  

  15. blocknr = (csize+1)*mult    =   

  16. mult = (csize_muti < 8)*(2^(csize_muti + 2))     

  17. block_len = (read < 12)*(2^(read))   

  18. capacity = blocknr * block_len = 13*4*3516*98304  

  19. 依据下面代码来计算我的容量:  

  20.         n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;  

  21.         csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;  

  22.               

  23.         Capacity_KB= (u32)csize << (n - 10);//得到扇区数 ,这里的单位是KB  // 00 7f ff 32 5f 59 83 cb  76 db df ff 96 40 00 97  

  24.         Capacity_MB = Capacity_KB/1024;  

  25.           

  26.           

  27.         1、(csd[8] >> 6) 得到的是 bit62和bit63的值                去掉2位  

  28.         2、((u16)csd[7] << 2)得到的是bit64--bit69的值            去掉6位  

  29.         3、((u16)(csd[6] & 3) << 10)得到的是bit70--bit73的值   

其实我的问题还是出现在单位上面!


这样我们就能看到显示的容量值了,我的是1G的。打印出来是971M,和windows下面的是一致的。其实我们可以通过读SD卡的引导扇区,从而把相关的信息读取出来,而不需要使用那些个寄存器。那么现在我们的计算公式就是(这只是我自己信手写的,如果想要理解,你必须得看扇区的内容咯,我就是对照着那个MBR来写的)


  1. x=(((buf_read[34])*64*1024+(buf_read[33])*256+(buf_read[32])))*512/1024/1024;   //打印大小  

  2. printf("\n SD Sector Size:%d  Mb\n",x);  


虽然不怎么雅观,但是能用就是了。



2、接下来看如何用SPI读一个扇区吧,先看代码


  1. u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt)  

  2. {  

  3.     u8 r1;  

  4.     if(SD_Type!=SD_TYPE_V2HC)sector <<= 9;//转换为字节地址  

  5.     if(cnt==1)  

  6.     {  

  7.         r1=SD_SendCmd(CMD17,sector,0X01);//读命令  

  8.         if(r1==0)//指令发送成功  

  9.         {  

  10.             r1=SD_RecvData(buf,512);//接收512个字节       

  11.         }  

  12.     }else  

  13.     {  

  14.         r1=SD_SendCmd(CMD18,sector,0X01);//连续读命令  

  15.         do  

  16.         {  

  17.             r1=SD_RecvData(buf,512);//接收512个字节     

  18.             buf+=512;    

  19.         }while(--cnt && r1==0);       

  20.         SD_SendCmd(CMD12,0,0X01);   //发送停止命令  

  21.     }     

  22.     SD_DisSelect();//取消片选  

  23.     return r1;//  

  24. }  


这几行代码能实现单个和多个扇区的读写,跟踪进去可以能够看到这个函数


  1. //SPIx 读写一个字节  

  2. //TxData:要写入的字节  

  3. //返回值:读取到的字节  

  4. u8 SPIx_ReadWriteByte(u8 TxData)  

  5. {         

  6.     u8 retry=0;                   

  7.     while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位  

  8.         {  

  9.         retry++;  

  10.         if(retry>200)return 0;  

  11.         }               

  12.     SPI_I2S_SendData(SPI1, TxData);                                                              //通过外设SPIx发送一个数据  

  13.     retry=0;  

  14.   

  15.     while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); //检查指定的SPI标志位设置与否:接受缓存非空标志位  

  16.         {  

  17.         retry++;  

  18.         if(retry>200)return 0;  

  19.         }                                 

  20.     return SPI_I2S_ReceiveData(SPI1);                                                               //返回通过SPIx最近接收的数据                         

  21. }  


这个函数的功能实现了既可以进行发送又可以进行读取数据,现在来总结一下!


读一个扇区的过程

1、先发命令r1=SD_SendCmd(CMD17,sector,0X01);//读单个扇区的命令


  1. u8 SD_RecvData(u8*buf,u16 len)  

  2. {                   

  3.     if(SD_GetResponse(0xFE))return 1;//等待SD卡发回数据起始令牌0xFE  

  4.     while(len--)//开始接收数据  

  5.     {  

  6.         *buf=SPIx_ReadWriteByte(0xFF);  

  7.         buf++;  

  8.     }  

  9.     //下面是2个伪CRC(dummy CRC)  

  10.     SD_SPI_ReadWriteByte(0xFF);  

  11.     SD_SPI_ReadWriteByte(0xFF);                                                           

  12.     return 0;//读取成功  

  13. }  

那么对应的写扇区也类似的


1、先发写单个扇区的命令 r1=SD_SendCmd(CMD24,sector,0X01);//写命令

2、将Buffer里面的内容写到对应的扇区里面去


  1. u8 SD_SendBlock(u8*buf,u8 cmd)  

  2. {     

  3.     u16 t;              

  4.     if(SD_WaitReady())return 1;//等待准备失效  

  5.     SD_SPI_ReadWriteByte(cmd);  

  6.     if(cmd!=0XFD)//不是结束指令  

  7.     {  

  8.         for(t=0;t<512;t++)SPIx_ReadWriteByte(buf[t]);//提高速度,减少函数传参时间  

  9.         SD_SPI_ReadWriteByte(0xFF);//忽略crc  

  10.         SD_SPI_ReadWriteByte(0xFF);  

  11.         t=SD_SPI_ReadWriteByte(0xFF);//接收响应  

  12.         if((t&0x1F)!=0x05)return 2;//响应错误                                                             

  13.     }                                                                                     

  14.     return 0;//写入成功  

  15. }  


到这里,读写扇区就完成了,下一步就是,使用文件系统来进行操作了。


关键字:stm32f103zet6  FAT16  文件系统学  SD卡扇区 引用地址:基于stm32f103zet6的FAT16文件系统学习0(读SD卡扇区)

上一篇:基于stm32f103zet6的FAT16文件系统学习3(初步分析ff9a)
下一篇:基于stm32f103zet6的FAT16文件系统学习1(初识FAT16)

小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved