SD_STM32_SPI驱动+FatFs文件系统

一、

见:

资料下载

文件列表:

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;
}


  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值