一、
见:
资料下载
文件列表:
MMCSDTimming.pdf
SD3.0_20090721.pdf-------详细介绍了SD、SDIO,标准的官方文档
SD_FAT文档.doc
Microsoft_fat32.doc
二、简易FAT32
FAT32.c
/********************************************************* SD卡,高位在前 SD卡在上电初期,卡主控通过检测引脚1(DAT3)来决定使用SD模式 还是SPI模式。当此脚接50KOhm上拉电阻时,卡进入SD模式; 当此脚为低电平,卡则工作于SPI模式。 *********************************************************/ #include <string.h> #include "FAT32.h" #include "stm32f10x_lib.h" #include "sdcard.h" //#include"LCM.h" #define SS 7 #define SS_H P2OUT |=(1<<SS); #define SS_L P2OUT &=~(1<<SS); unsigned char SD_SPC; //每簇扇区数(Sectors Per Cluster) unsigned long SD_SPF; //每FAT 扇区数(Sectors Per FAT) unsigned long SD_ROOT; //根目录所在扇区(编号为第二簇) unsigned long SD_FBG; //FAT表地址 unsigned long SD_FBG_BOOT;//FAT表基地址 unsigned char Fat32_DataBuffer[520]={0}; //512+两字节CRC+.... unsigned char DataBuffer[1024]={0}; //512+两字节CRC+.... unsigned long FileAddress =0; /*临时静态变量定义区*/ static unsigned long Temp0=0; static unsigned long Temp1=0; static unsigned long Temp2=0; static unsigned long Temp3=0; static unsigned char StatusInformation ; void delay(int t) //延时 { int x; for(;t>0;t--) //双重循环 for(x=0;x<1020;x++); } /**************************************************** 函数功能:块地址解析 输入: 输出:到指令中间的4个字节中 备注: *****************************************************/ void setblock(unsigned char cmd[],long block) //获取块地址 { cmd[1]=block>>15; cmd[2]=block>>7; cmd[3]=block<<1;//最低位为0 cmd[4]=0; } void Fat32_init(void)//得到SD卡信息:FAT位置,根目录位置,每簇扇区数,每FAT扇区数 { unsigned char Fat_Number; //FAT 数(Number of FAT) 该分区上FAT 的副本数。 #if 0 unsigned char cmd1[]={0,0,0,0,0,0};//CRC=0x95 unsigned char dbk; sdrst(); //SD复位, CMD0 dbk=1; cmd1[0]=INIT; while(dbk) //等待初始化, CMD1 { delay(50); dbk=SPI_SendCmd(cmd1,0); usendchar(dbk); } cmd1[0]=READ1; //读BPB,Read Single Block SPI_SendCmd(cmd1,515); //usendstr(Fat32_DataBuffer,515); usendchar(8); /* Fat_Number=bp->BPB_NF; //FAT 数(02) SD_SPC=bp->BPB_SPC; //每簇扇区数(Sectors Per Cluster)(02) SD_SPF=bp->BPB_SPF; //每FAT 扇区数(Sectors Per FAT)(000003c3=963) SD_FBG=bp->BPB_RS; //保留扇区(0022=34) SD_ROOT=Fat_Number*SD_SPF+SD_FBG; *///根目录扇区(编号为第二簇) #endif StatusInformation = SD_ReadBlock( 0, (u32 *)Fat32_DataBuffer, 512); //读取第0扇区 Fat_Number=Fat32_DataBuffer[16]; SD_SPC=Fat32_DataBuffer[13]; SD_SPF=(Fat32_DataBuffer[39]<<24)+(Fat32_DataBuffer[38]<<16)+(Fat32_DataBuffer[37]<<8)+Fat32_DataBuffer[36]; SD_FBG=Fat32_DataBuffer[14]+(Fat32_DataBuffer[15]<<8); SD_ROOT=Fat_Number*SD_SPF+SD_FBG; SD_FBG_BOOT=SD_FBG;//保存fat表基地址 //FAT32文件格式: 引导扇区 其余保留扇区 FAT1 FAT2表格(重复的) 根文件夹首簇 其他文件夹及所有文件 剩余扇区 } /**************************************************** 函数功能:读文件 输入:文件的簇地址 输出: 文件的数据存于全局变量Fat32_DataBuffer[1024]中;若 备注:若成功返回0,表面文件已经结束;若返回1,表明文 件还没有读完,文件的簇地址存在全局变量FileAddress中 *****************************************************/ unsigned char ReadFile(unsigned long cstbg) //起始簇号 { unsigned char cmdfo[]={READ1,0,0,0,0,0}; unsigned long cstnxt; //簇 unsigned long adrnxt; //扇区 //FAT_FAT *p=(FAT_FAT*)Fat32_DataBuffer; //FAT表指针 cstnxt=cstbg; //得到文件第一个簇 { adrnxt=SD_SPC*(cstnxt-2)+SD_ROOT; //减根目录簇号:2 //读一个簇 #if 0 setblock(cmdfo,adrnxt); SPI_SendCmd_2(cmdfo,515,0); setblock(cmdfo, (adrnxt+1) ); SPI_SendCmd_2(cmdfo,515,1); #endif SD_ReadMultiBlocks( (512*adrnxt),(u32 *)DataBuffer,512,2); /* for(k=0;k<512;k++) LCD_WriteData( *((unsigned int *)Fat32_DataBuffer) );*/ //usendstr(Fat32_DataBuffer,1023); /* if( cstnxt==128) { SD_FBG +=1; } if( cstnxt>=128) { cstnxt -=128; } */ SD_FBG =SD_FBG_BOOT+cstnxt / (0x80); cstnxt =cstnxt %(0x80); #if 0 setblock(cmdfo,SD_FBG); //读fat ???? SPI_SendCmd(cmdfo,515); #endif SD_ReadBlock(512*SD_FBG,(u32 *)Fat32_DataBuffer,512); //cstnxt=p->FAT_SEC[cstnxt]; //得下一个簇 Temp0=( (unsigned long)(Fat32_DataBuffer[4*cstnxt+3]) )<<8; Temp0=((unsigned long)(Temp0))<<8; Temp0=((unsigned long)(Temp0))<<8; Temp1=( (unsigned long)(Fat32_DataBuffer[4*cstnxt+2]) )<<8; Temp1=((unsigned long)(Temp1))<<8; Temp2=( (unsigned long)(Fat32_DataBuffer[4*cstnxt+1]) )<<8; Temp3=(unsigned long)(Fat32_DataBuffer[4*cstnxt]); //cstnxt=Fat32_DataBuffer[4*cstnxt]+ (Fat32_DataBuffer[(4*cstnxt)+1]<<8)+(Fat32_DataBuffer[(4*cstnxt)+2]<<16)+(Fat32_DataBuffer[(4*cstnxt)+3]<<24); cstnxt=Temp3+Temp2+Temp1+Temp0; FileAddress =cstnxt; if(cstnxt==0x0fffffff) //文件结束标志 { return 0; } else { return 1; } } } /**************************************************** 函数功能:在根目录下根据文件存放的序号寻找文件的首簇 输入: 文件的位置序号 输出: (隐形)文件的首簇地址赋值于全局变量FileAddress 备注:若索引成功返回1,失败返回0 *****************************************************/ unsigned char FindFileAdd_AccordingToNum(unsigned char id) //打开第id个文件 { unsigned char i,j=0,k,m,del,atr;//delete,attribute, unsigned char flag=0; unsigned int t1=0; unsigned int t2=0; unsigned char cmd[]={READ1,0,0,0,0,0};//CRC=0x95 for(m=0;m<200;m++)//xue 该成了从0开始 { setblock(cmd,SD_ROOT+m); //读根目录 SPI_SendCmd(cmd,515); for(i=0;i<16;i++) //找到第一个短文件 { for(k=0;k<32;k++) { if(Fat32_DataBuffer[32*i+k]) break; } if(k==32) return 0; del=Fat32_DataBuffer[32*i]; atr=Fat32_DataBuffer[32*i+11]; if((del!=0xe5)&&(atr!=0x0f)) j++; if(j==id) { flag=1; break; } } if(flag)break; } if(m==200)return 0; t1=Fat32_DataBuffer[32*i+20]+ (Fat32_DataBuffer[32*i+21] <<8); t2=Fat32_DataBuffer[32*i+26]+ (Fat32_DataBuffer[32*i+27] <<8); FileAddress =t1; FileAddress=(FileAddress<<16)|t2; return 1; //ReadFile(addr); } #define SINGLEFILE 0 #define MULTIFILE 1 //要把有关SD卡属性的信息都设成全局变量 /**************************************************** 函数功能:判断两个字符串是否相等 输入:读取的文件名首地址,被核对的文件名首地址 输出: 若相等返回1,否则返回0 备注: *****************************************************/ unsigned char CheckFileName(unsigned char * DiskFileName,unsigned char * CheckedFileName) { unsigned char i,j=0; for(i=0;i<8;i++) { if( (*DiskFileName) != (*CheckedFileName) ) { if( (0x20==*DiskFileName) && (0==*CheckedFileName) ) { return 1; } else return 0; } DiskFileName++;CheckedFileName++; } return 1; } /**************************************************** 函数功能:寻找文件的首簇 输入:文件的目录项簇号,文件名,文件类型 输出:文件的首簇地址 备注:若查找失败,返回NULL *****************************************************/ unsigned long FindFileAdd_AccordingToName(unsigned long FileListCluser,unsigned char *FileNameTemp,unsigned char SortTemp) { unsigned char i,j=0,k,m,del,atr;//delete,attribute, unsigned char flag=0; unsigned int t1=0; unsigned int t2=0; unsigned char cmd[]={READ1,0,0,0,0,0};//CRC=0x95 unsigned long SD_FileCluser=0; unsigned long addr=0; SD_FileCluser=SD_SPC*(FileListCluser-2)+SD_ROOT; for(m=0;m<2;m++)//xue 该成了从0开始,只搜索两个簇 { #if 0 setblock(cmd,SD_FileCluser+m); //读文件的目录 SPI_SendCmd(cmd,515); #endif SD_ReadBlock( 512*(SD_FileCluser+m) ,(u32 *)Fat32_DataBuffer,512); for(i=0;i<16;i++) //找到第一个短文件 { for(k=0;k<32;k++) { if(Fat32_DataBuffer[32*i+k]) break; } if(k==32) break; //搜索下一扇区 del=Fat32_DataBuffer[32*i]; atr=Fat32_DataBuffer[32*i+11]; if((del!=0xe5)&&(atr!=0x0f)) { //if( CheckFileName( (unsigned char*)(&Fat32_DataBuffer[32*i]),FileNameTemp) ) if( CheckFileName( &Fat32_DataBuffer[32*i],FileNameTemp) ) { if( (SortTemp==SINGLEFILE) && (Fat32_DataBuffer[32*i+11]!=0x10) )//文档 { flag=1; break; } if( (SortTemp==MULTIFILE) && (Fat32_DataBuffer[32*i+11]==0x10) )//文件夹 { flag=1; break;//;//会跳出包含它的最内层的循环语句(for,while)和switch,而且只跳出一层 } } } } if(flag)break; } if(m==2)return NULL; t1=Fat32_DataBuffer[32*i+20]+ (Fat32_DataBuffer[32*i+21] <<8); t2=Fat32_DataBuffer[32*i+26]+ (Fat32_DataBuffer[32*i+27] <<8); addr=t1; addr=addr<<16|t2; //文件起始簇号 /* FileAddress =t1; FileAddress=(FileAddress<<16)|t2;*/ return addr; } /**************************************************** 函数功能:寻找根目录下文档的首簇 输入: 文件名 输出: (隐形)文件的首簇地址赋值于全局变量FileAddress 备注:若索引成功返回1,失败返回0 *****************************************************/ unsigned char FindSingleFileAdd(unsigned char *FileName) { unsigned long FileAddressTemp=0; FileAddressTemp=FindFileAdd_AccordingToName(2,FileName,SINGLEFILE); if(NULL==FileAddressTemp) { return 0; } else { FileAddress =FileAddressTemp;//赋给全局变量 return 1; } } /**************************************************** 函数功能:寻找两重文件夹中文档的首簇 输入: 第一个文件夹的名称,第二个文件夹的名称,文档 的名称 输出: (隐形)文件的首簇地址赋值于全局变量FileAddress 备注:若索引成功返回1,失败返回0 *****************************************************/ unsigned char FindMultiFileAdd(unsigned char *FirstFileName,unsigned char *SecondFileName,unsigned char *FileName) { unsigned long FileAddressTemp =0; FileAddressTemp =FindFileAdd_AccordingToName(2,FirstFileName,MULTIFILE); if(NULL==FileAddressTemp) return 0; //没有找到第一个文件 FileAddressTemp=FindFileAdd_AccordingToName(FileAddressTemp,SecondFileName,MULTIFILE); if(NULL==FileAddressTemp) return 0; //没有找到第二个文件 FileAddressTemp=FindFileAdd_AccordingToName(FileAddressTemp,FileName,SINGLEFILE); if(NULL==FileAddressTemp) return 0; //没有找到文件 FileAddress =FileAddressTemp; ////赋给全局变量 return 1; }FAT32.h
/**************************** 0x40:复位 0x41:初始化 0x51:读单块 0x58:写单块 0x4a:CID read,16b 0x49:CSD read,16b ****************************/ #define INIT 0X41 #define READ1 0X51 #define WRITE1 0X58 #define RCID 0X4A #define RCSD 0X49 typedef struct { unsigned char BPB_NC1[0x0b]; //无用的 unsigned int BPB_BPS; //扇区字节数(Bytes Per Sector) unsigned char BPB_SPC; //每簇扇区数(Sectors Per Cluster) unsigned int BPB_RS; //保留扇区数(Reserved Sector) 0x0e //第一个FAT 开始之前的扇区数,包括引导扇区。本字段的十进制值一般为32 unsigned char BPB_NF; //FAT 数(Number of FAT) 该分区上FAT 的副本数。 0x0f //本字段的值一般为2 unsigned char BPB_NC2[0x0b]; //无用的 //2+2+1+2+2+2+++++ unsigned long BPB_HS; //隐藏扇区数(Hidden Sector)该分区上引导扇区 //之前的扇区数。在引导序列计算到根目录的数据区的绝对位移的过程中使用了该值 unsigned long BPB_LS; //总扇区数(Large Sector) 本字段包含FAT32 分区中总的扇区 unsigned long BPB_SPF; //每FAT 扇区数(Sectors PerFAT)(只被FAT32 使用)该分区 0x1e //每个FAT 所占的扇区数。计算机利用这个数和 FAT 数以及隐藏扇区数 //(本表中所描述的)来决定根目录从哪里开始。 unsigned int BPB_EF; //扩展标志(Extended Flag)(只被FAT32 使用) unsigned int BPB_FV; //文件系统版本(File system Version)只供FAT32 使用,高 //字节是主要的修订号,而低字节是次要的修订号。 unsigned long BPB_RCN; //根目录簇号(Root Cluster Number) //(只供FAT32 使用) 根目录第一簇的簇号。本字段的值一般为2,但不总是如此 unsigned int BPB_FSI; //文件系统信息扇区号(File System Information //SectorNumber)(只供FAT32 使用) unsigned int BPB_BS; //备份引导扇区(只供FAT32 使用) 为一个非零值,这个非零 //值表示该分区保存引导扇区的副本的保留区中的扇区号。 }FAT_BPB; typedef struct { unsigned long FAT_SEC[128];//四字节为一簇 }FAT_FAT; //文件分配表:file allocation table typedef struct { unsigned char SFILE_NAME1[8];//文件名 unsigned char SFILE_NAME2[3];//扩展名 unsigned char SFILE_ATTR; //attribute属性 unsigned char SFILE_NEC1; //系统保留 unsigned char SFILE_DAT1[7]; //日期等 unsigned int SFILE_ADDH; //文件起始簇号的高16 位 unsigned char SFILE_DAT2[4]; //日期等 unsigned int SFILE_ADDL; //文件起始簇号的低16 位 unsigned long SFILE_LEN; //表示文件的长度 }FAT_SFILE; //短文件名文件(32)字节 typedef struct { unsigned char LFILE_ATTR; // unsigned char LFILE_NAME1[10]; // unsigned char LFILE_NEC1; //长文件名目录项标志,取值0FH unsigned char LFILE_NEC2; //系统保留 unsigned char LFILE_CHK; //校验值(根据短文件名计算得出) unsigned char LFILE_LNU1[12]; //长文件名unicode 码 unsigned int LFILE_ADD; //文件起始簇号(目前常置0) unsigned char LFILE_LNU2[4]; //长文件名unicode 码 }FAT_LFILE; //长文件名文件(32)字节 typedef struct { FAT_SFILE MENU[16]; }FAT_MENU; //目录项 // unsigned char FindFileAdd_AccordingToName(unsigned char id); extern unsigned char ReadFile(unsigned long cstbg); extern unsigned long FindFileAdd_AccordingToName(unsigned long FileListCluser,unsigned char *FileNameTemp,unsigned char SortTemp); extern unsigned char FindMultiFileAdd(unsigned char *FirstFileName,unsigned char *SecondFileName,unsigned char *FileName); extern unsigned char FindFileAdd_AccordingToName_Temp(unsigned char id); extern void usendstr(unsigned char s[],unsigned int size); extern void SdWrite(unsigned char datw[],unsigned long block);//要发送的命令,以及要写在哪一块 extern void delay(int t); /********************************************************************************************************************* *--------------------------------------2010年4月13日11:22:08 Fat32 外部函数 **********************************************************************************************************************/ extern void Fat32_init(void); extern unsigned char FindSingleFileAdd(unsigned char *FileName); extern unsigned char DataBuffer[1024]; extern unsigned long FileAddress;
三、FatFs移植示例。
支持格式化文件系统、读写、目录等。目前下面的代码只支持一个设备,如若想支持多个设备的文件系统,比如在SD卡、SPI FLASH中均实现文件系统,改动下面代码即可:
对每个Physical drive nmuber响应;底层的驱动只需要提供初始化(包括获取设备的容量)、读写即可。如果希望实现NFS、串口硬盘等,只需要把这几个接口通过通信链路(网络、串口等)映射过去。
/*-----------------------------------------------------------------------*/ /* Low level disk I/O module skeleton for FatFs (C)ChaN, 2007 */ /*-----------------------------------------------------------------------*/ /* This is a stub disk I/O module that acts as front end of the existing */ /* disk I/O modules and attach it to FatFs module with common interface. */ /*-----------------------------------------------------------------------*/ #include "diskio.h" #include "stm32f10x.h" #include "SD_drive.h" /*-----------------------------------------------------------------------*/ /* Correspondence between physical drive number and physical drive. */ #define ATA 0 #define MMC 1 #define USB 2 static int bus_in_sdio; /** * @brief 检查sd卡是否就绪 * @return 0:检测成功 * -1:检测失败 */ static int check_sdcard(void) { return check_maincard(); } /*-----------------------------------------------------------------------*/ /* Inidialize a Drive */ DSTATUS disk_initialize ( BYTE drv /* Physical drive nmuber (0..) */ ) { if( check_sdcard() == 0 ) { bus_in_sdio = 1; return RES_OK; } return RES_ERROR; } DSTATUS disk_unmount(BYTE drv) { bus_in_sdio = 0; return RES_OK; } DWORD get_fattime () { return 0; } /*-----------------------------------------------------------------------*/ /* Return Disk Status */ DSTATUS disk_status ( BYTE drv /* Physical drive nmuber (0..) */ ) { if(bus_in_sdio == 0) { return disk_initialize(drv); } //SD_GetStatus(); // == 0 ) //return RES_ERROR; return RES_OK; } /*-----------------------------------------------------------------------*/ /* Read Sector(s) */ DRESULT disk_read ( BYTE drv, /* Physical drive nmuber (0..) */ BYTE *buff, /* Data buffer to store read data */ DWORD sector, /* Sector address (LBA) */ BYTE count /* Number of sectors to read (1..255) */ ) { DRESULT res; res = (DRESULT)(read_card(sector, count, buff)); if( res != 0 ) return RES_ERROR; else return RES_OK; } /*-----------------------------------------------------------------------*/ /* Write Sector(s) */ #if _READONLY == 0 DRESULT disk_write ( BYTE drv, /* Physical drive nmuber (0..) */ const BYTE *buff, /* Data to be written */ DWORD sector, /* Sector address (LBA) */ BYTE count /* Number of sectors to write (1..255) */ ) { DRESULT res; res = (DRESULT)(write_card(sector, count, (unsigned char*)buff)); if( res == 0) return RES_OK; else return RES_ERROR; } #endif /* _READONLY */ /*-----------------------------------------------------------------------*/ /* Miscellaneous Functions */ // 返回磁盘状态 DRESULT disk_ioctl ( BYTE drv, /* Physical drive nmuber (0..) */ BYTE ctrl, /* Control code */ void *buff /* Buffer to send/receive control data */ ) { DRESULT res; if (drv) { return RES_PARERR; //仅支持单磁盘操作,否则返回参数错误 } //FATFS目前版本仅需处理CTRL_SYNC,GET_SECTOR_COUNT,GET_BLOCK_SIZ三个命令 switch(ctrl) { case CTRL_SYNC: res = RES_OK; break; case GET_BLOCK_SIZE: *(WORD*)buff = 1; res = RES_OK; break; case GET_SECTOR_COUNT: //读卡容量 *(DWORD*)buff = sd_state.capacity; break; default: res = RES_PARERR; break; } return res; }