原创 万利板子上创建SD卡的FAT文件系统(一)

2008-7-28 21:43 5749 14 16 分类: MCU/ 嵌入式

万利的板子上创建SD卡文件系统 (一)<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />


                       ---SD卡读写


    之前在AVR上做过SDFAT文件系统,万利的EK-STM32F板子上有SD接口,正好可以做一个基于SDFAT文件系统,打算要做成支持FAT32FAT16,通过串口与PC连接,可以显示SD卡中的文件信息,也可以把文件读出来,例如,把指定文件名的txt文件通过串口显示到PC


 这是Keil3。22下的工程 点击下载


  


   我是去年过年以前决定开始做MP3,由于一直用AVR,所以自然就选用那个流传最广的方案——AVRSDVS1003,自己做了PCB,最后终于做成了,主要参考www.ourdev.cn上波仔的MEGA8版本的MP3(感谢波仔),其间花了大量时间学习SD卡读写和FAT文件系统的知识,现在正好用上。


   昨天晚上本来想用KEILSD卡工程,但是一直不成功,于是转到以前在AVR上的代码上来最后终于读出0扇区,而且与读卡器加WinHEX软件读出来的一致(有图为证)。我用的是Keil3.22,这个版本比较新,可以从Keil的中文网站上下载,最大的好处就是使用方便,已经开始支持JLink2了,万利的板子可以用Keil调试了,当然老版本的Keil可以先生成hex再用串口烧写到STM32


EKSTM32F板子上使用的是SPI1访问SD卡,还有一个SD卡的PowerCS线,另外一个PA2接到了SD卡插入检测,大家可以不理会,(万利不专业,原理图上没有画出来,板子背面写了)。


 


软件:Keil3.22建立的工程


 

硬件:


  SD     STM32


 SCK     PA5


 MISO    PA6


 MOSI    PA7


 POWER  PD10


  CS     PD9


具体的可以参考原理图,这个光盘里有,万利网站上有。


SPIGPIO的初始化如下。


void SPI_Config(void)


{


  GPIO_InitTypeDef  GPIO_InitStructure;


  SPI_InitTypeDef   SPI_InitStructure;


 


  /* GPIOA and GPIOD Periph clock enable */


  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD, ENABLE);


  /* SPI1 Periph clock enable */


  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);


 


  /* Configure SPI1 pins: SCK, MISO and MOSI */


  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;


  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;


  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;


  GPIO_Init(GPIOA, &GPIO_InitStructure);


 


  /* Configure PD9 pin: CS pin  ,PD10 : SD Power*/


  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;


  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;


  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;


  GPIO_Init(GPIOD, &GPIO_InitStructure);


 


  /* SPI1 Config */


  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;


  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;


  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;


  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;


  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;


  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;


  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;


  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;


  SPI_InitStructure.SPI_CRCPolynomial = 7;


  SPI_Init(SPI1, &SPI_InitStructure);


 


  /* SPI1 enable */


  SPI_Cmd(SPI1, ENABLE);


}


 


定义两个宏,分别是SD卡选中和不选中


#define   SPI_CS_Assert()     GPIO_ResetBits(GPIOD, GPIO_Pin_9)


/* Deselect MSD Card: ChipSelect pin high */


#define SPI_CS_Deassert()    GPIO_SetBits(GPIOD, GPIO_Pin_9)


 


对于万利的板子,还要一个上电的过程,该MOS管是的电平导通。


SPI初始化之后,我加了一句GPIO_ResetBits(GPIOD, GPIO_Pin_10);就是SD上电。注意上电之后,最好有一段延时,这样给SD内部初始化流充足的时间,避免初始化的失败。


初始化SD卡代码如下:


//sd卡复位          //reset sd card (software)


uint8 MMC_SD_Reset(void)


{


       uint8 i;


       uint8 retry;


       uint8 r1=0;


       retry = 0;


 


       SPI_Config();


    GPIO_ResetBits(GPIOD, GPIO_Pin_10);  // SD卡上电,打开MOS


    delay(1000);                         // 重要的延时


  /* MSD chip select high */


    SPI_CS_Deassert();


       do


       {


              for(i=0;i<10;i++) SPI_WriteByte(0xff);


              r1 = MMC_SD_SendCommand(0, 0);//idle命令    //send idle command


              retry++;


              if(retry>100) return 1;//超时退出            //time out


       } while(r1 != 0x01);   


 


 


       retry = 0;


       do


       {


              r1 = MMC_SD_SendCommand(1, 0);//active命令       //send active command


              retry++;


              if(retry>200) return 1;//超时退出            //time out


       } while(r1);


       //SPI_High();


       r1 = MMC_SD_SendCommand(59, 0);//crc           //disable CRC


 


       r1 = MMC_SD_SendCommand(16, 512);//设扇区大小512    //set sector size to 512


       return 0;//正常返回           //normal return


}


 


还有两个基本函数,即写和读SPI(读写合二为一)


unsigned char SPI_WriteByte(unsigned char data)


{


 uint8 Data = 0;


 


  /* Wait until the transmit buffer is empty */


  while (SPI_GetFlagStatus(SPI1, SPI_FLAG_TXE) == RESET);


  /* Send the byte */


  SPI_SendData(SPI1, data);


 


  /* Wait until a data is received */


  while (SPI_GetFlagStatus(SPI1, SPI_FLAG_RXNE) == RESET);


  /* Get the received data */


  Data = SPI_ReceiveData(SPI1);


 


  /* Return the shifted data */


  return Data;


}


 


SD卡中写一个命令的函数


 


//sd卡写命令             //sd send command


uint8 MMC_SD_SendCommand(uint8 cmd, uint32 arg)


{


       uint8 r1;


       uint8 retry="0";


      


       SPI_WriteByte(0xff);


       SPI_CS_Assert();


      


       SPI_WriteByte(cmd | 0x40);//分别写入命令 //send command


       SPI_WriteByte(arg>>24);


       SPI_WriteByte(arg>>16);


       SPI_WriteByte(arg>>8);


       SPI_WriteByte(arg);


       SPI_WriteByte(0x95);


      


       while((r1 = SPI_WriteByte(0xff)) == 0xff)//等待响应,     //wait response


              if(retry++ > 200) break;//超时退出                             //time out error


 


       SPI_CS_Deassert();


 


       return r1;//返回状态值                                  //return state


}


 


有了这些函数之后就可以读写SD卡了,主要是读一个扇区及写一个扇区,注意SD卡只能是读写扇区,即512 个字节。具体代码见我的工程文件的SD_MMC.c uint8 MMC_SD_ReadSingleBlock(uint32 sector, uint8* buffer)  读第sector扇区到Bufferuint8 MMC_SD_WriteSingleBlock(uint32 sector, uint8* buffer)是写扇区。


{


 


为了调试方便,我把串口1接上了(最左边哪个口),初始化成功后,把0扇区的512个字节读出来,大部分为0,只有前后的一些字节不为0,调试助手接到的字符为


3|P P­ <t<uuL[1]t<t<tV ^|[1]W_s3Ou}=Uu|Invalid partition tableError loading operating systemMissing operating system

在串口助手和Winhex软件读出来的分别是下图:

点击开大图 

点击开大图



每个人的电脑和SD卡不同,这些数据会有小差异,但基本相同,如果你的试验也是这些数据,那么,恭喜你,你也做成SD卡读写试验了,没成功也不要紧,检查SD卡插紧了没有。跳线接了没有,量量电压对不对,等等,一定能做成。


还有很多人用IAR,可以把我的几个文件拷到你的IAR工程,编译,调试,不难的,我开始也用的IAR


 


   好了,第一课就上到这里,下一次开始介绍FAT了,谢谢你的关注,如果你参考我的例子做成了,或者认为我的代码和经验有帮助,就顶我一下啊。祝你好运。

文章评论2条评论)

登录后参与讨论

用户377235 2014-1-4 00:19

你这个是199元还是399元的板子呢? 我万利199元的板子SD卡的SPI接口没有接上拉电阻,所以我做SD卡MP3实验的时候没成功,接上上拉就好了。IO配置和你一样,都是复用输出

用户312603 2010-12-12 17:22

用单片机采在SD卡上的数都是16进制的,怎么才能直接用读卡器得到想要的10进制呢?不然建这个fat系统有什么用?盼复!
相关推荐阅读
用户1407470 2012-11-04 15:12
MEGa128驱动ENC28J60
ENC28J60调试心得: (1)网线要通,建议两根直连网线,将ENC28j60模块、MEGA128各自分别接到路由器,使用前确保网线是好的。不建议使用交叉网线将模块直接连电脑,我先这样没调通...
用户1407470 2012-11-04 15:05
MEGA128驱动超声波测距模块US-100
AVRstudio6下写的,IDE自带GCC。使用Timer1的输入捕获,晶振8M.   #include <stdio.h> #define F_CPU 8000000...
用户1407470 2012-11-03 21:47
MEGa128驱动ENC28J60
MEGa128驱动ENC28J60  ...
用户1407470 2012-11-03 21:47
MEGa128驱动ENC28J60
MEGa128驱动ENC28J60  ...
用户1407470 2009-07-17 21:06
把我的AT91SAM7X256转CAN开源了
https://static.assets-stash.eet-china.com/album/old-resources/2009/7/17/4dc7a64c-c7ff-4d73-a706-d410...
我要评论
2
14
关闭 站长推荐上一条 /2 下一条