使用AVR单片机读取SD卡文件(FAT32文件系统)-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 10212|回复: 6

使用AVR单片机读取SD卡文件(FAT32文件系统)

[复制链接]
发表于 2016-6-30 20:02 | 显示全部楼层 |阅读模式
    参考为删减的FAT32文件系统,删改大部分函数,形成了在ICCAVR下编译只有7519字节的hex文件。大家可以参考本文的文件系统来实现文件读取,文件定位,可以用来编写MP3解码和电子书解码。本程序在ICCAVR7.2下编译通过,采用Atmega 328p芯片,片外16Mhz晶振。本程序作为示例程序,仅供参考。以下是部分程序,完整见附件。

//------------------------main.c文件-----------------------------------
/********************************************************************************
功能:基于FAT32文件系统的SD卡、TF卡打开文件、读取数据。
Atmega328P 8位单片机,片外16Mhz晶体振荡器。串口波特率19200
CS片选选PB2     
参考:于振南FAT32文件系统
                                            2016/3/22 ChenYanan
*********************************************************************************/
#include<iom328pv.h>
#include<AVRdef.h>
#include<string.h>
#include "SdInit.h"
#include "FAT32.h"  //文件系统信息结构体
#include "main.h"

//全局变量


FAT32_Init_Args Init_Args ,*pInit_Args; //初始化参数集合
FileInfo fileinfo;                      //文件信息集合


//设备初始化---------------------------------------------------------------
void Init_Devices(void)
{Init_Clock();
Init_Timer0();
Init_Port();
Init_USART0();
}


//主函数=====================================================================
void main(void)
{ uint08 u8temp;
  uint16 len;
  uint08 FAT32_ReceBuffer[512];
  CLI();                                        //禁中断
  Init_Devices();                               //初始化设备
  //SEI();                                      //中断使能

//======================================================  
SCI_Send_Str("Start to init SD...\r\n");
u8temp=FAT32_Device_Init();                    //SD首次初始化时间较长
if(u8temp)                                     //返回0说明初始化成功
{ SCI_Send_Str("SD Init ERROR!\r\n");
   SCI_Put_Inf("Error Code:",(uint32)u8temp);
}
pInit_Args=&Init_Args;                         //将FAT32的初始化参数集合指针指向设备的初始化参数集

Init_watchdog();                              //初始化完成后开启看门狗

SCI_Put_Inf("Total Sector:",SD_GetTotalSec()); //获取SD卡的总扇区数
SCI_Send_Str("=================\r\n");
//=========================================================
  SCI_Send_Str("Start to Init FAT32...\r\n");
   u8temp=(uint08)FAT32_Init();                 //初始化文件系统
if(!u8temp)                                    //文件系统初始化成功
{
   SCI_Put_Inf("BPB_Sector_No:",Init_Args.BPB_Sector_No);   
   SCI_Put_Inf("Total_SizeKB:",Init_Args.Total_SizeKB);
   SCI_Put_Inf("BytesPerSector:",Init_Args.BytesPerSector);
   SCI_Put_Inf("FATsectors:",Init_Args.FATsectors);  
   SCI_Put_Inf("SectorsPerClust:",Init_Args.SectorsPerClust);
   SCI_Put_Inf("FirstFATSector:",Init_Args.FirstFATSector);
   SCI_Put_Inf("FirstDirSector:",Init_Args.FirstDirSector);
   SCI_Put_Inf("FSsec:",Init_Args.FSINFO_Sec);
   SCI_Put_Inf("Next_Free_Cluster:",Init_Args.Next_Free_Cluster);
   SCI_Put_Inf("FreenCluster:",Init_Args.Free_nCluster);
} else
   SCI_Put_Inf("File System inits fail, Err Code:",u8temp);  
//=============================================================
//文件打开
  u8temp=FAT32_Open_File(&fileinfo,(sint08*)"/test1.txt",0,1);
  if(!u8temp)                                          //打开文件成功
  {
  SCI_Send_Str("Succeed to open file.\r\n");
  SCI_Send_Str("====================\n");
  SCI_Send_Str("File_Name(Short 8.3):");               //8.3格式文件名,不支持中文文件名
  SCI_Send_Str((uint08*)fileinfo.File_Name);
  SCI_Put_Inf("File_Size:",fileinfo.File_Size);
         
  SCI_Send_Str("File_CDate:");
  SCI_Put_Num(fileinfo.File_CDate.year); SCI_Send_Str("/");
  SCI_Put_Num(fileinfo.File_CDate.month);SCI_Send_Str("/");
  SCI_Put_Num(fileinfo.File_CDate.day);  SCI_Send_Str("  ");
  SCI_Put_Num(fileinfo.File_CTime.hour); SCI_Send_Str(":");
  SCI_Put_Num(fileinfo.File_CTime.min);  SCI_Send_Str(":");
  SCI_Put_Num(fileinfo.File_CTime.sec);  SCI_Send_Str("\r\n");
         
  SCI_Put_Inf("File_StartClust:",fileinfo.File_StartClust);
  SCI_Put_Inf("File_CurClust:",fileinfo.File_CurClust);
  SCI_Put_Inf("File_CurSec:",fileinfo.File_CurSec);
  SCI_Put_Inf("File_CurPos:",fileinfo.File_CurPos);
  SCI_Put_Inf("File_CurOffset:",fileinfo.File_CurOffset);
  SCI_Send_Str("==================\n");
  len=(uint16)FAT32_ReadData(&fileinfo,10,100,FAT32_ReceBuffer);  //从文件起始偏移10位置开始读取100个字节
  SCI_Put_Inf("Have Read data:",len);
  SCI_Send_Str("Data:\n");

  SCI_Send_Str(FAT32_ReceBuffer);                                //输出数据
  }
   else SCI_Put_Inf("Fail to Open file, Err Code:",u8temp);

   (void)FAT32_Close_File(&fileinfo); //必要步骤:关闭文件
//=========================================================================

  while(1)
  { WDR();                            //清看门狗

  }

}


//------------------------sdinit.c file---------------------
#include<iom328pv.h>
#include<AVRdef.h>
#include "SDInit.h"
#include "main.h"


//--------------------------------------------------------------
//片选端口         
#define SS_ON   PORTB&=0xFB                                  //低电平
#define SS_OFF  PORTB|=0x04                                  //高电平
uint08 SD1_Addr_Mode=0;
uint08 SD1_Ver=SD_VER_ERR;
//=============================================================================
//-----------------------------------------------------------------------------
void SPI_High_Rate(void)
{
  //最高操作速率不能高于25Mbps
  SPCR &= 0x5C;   //BR=busclk/(SPPR *SPR )=8M/4=2M ,page147
   //SPCR = (1<<SPE)|(1<<MSTR);              
}
//SPI主机初始化模式1,低速模式250k==========================================================
void SPI_Init_M1(void)
{                                          //SPI模式1
  DDRB|= (1<<DDB3)|(1<<DDB5);              //设置MOSI 和SCK 为输出,其他为输入
  SPCR = 0b01010001;                       //使能SPI 主机模式,时钟模式为下降沿,结束沿采样,设置时钟速率为fck/16
}
//SPI主机初始化模式3,低速模式250k==========================================================
void SPI_Init_M3(void)
{                                          //SPI模式3
  DDRB|= (1<<DDB3)|(1<<DDB5);              //设置MOSI 和SCK 为输出,其他为输入
  SPCR = 0b01011001;                       //使能SPI 主机模式,时钟模式为下降沿,结束沿采样,设置时钟速率为fck/16
}
//SPI主机初始化模式4,必须使用低速模式,62.5k==========================================================
void SPI_Init_M4(void)
{                                          //SPI模式4
  DDRB|= (1<<DDB3)|(1<<DDB5);              //设置MOSI 和SCK 为输出,其他为输入
  SPCR = 0b01011111;                       //使能SPI 主机模式,时钟模式为下降沿,结束沿采样,设置时钟速率为fck/128
}
//SPI数据发送,采用轮询=======================================================
void SPI_Send(uint08 data)
{
SPDR = data;                                //启动数据传输
while(!(SPSR & (1<<SPIF)));                 //等待传输结束                        
}
//SPI数据接收,采用轮询=======================================================
uint08 SPI_Rece(void)
{
SPDR = 0xff;                                //启动数据传输
while(!(SPSR & (1<<SPIF)));                 //等待传输结束
return SPDR;                                                 //接收字符
}

/******************************************************************
- 功能描述:向SD卡写命令
- 参数说明:SD卡的命令是6个字节,pcmd是指向命令字节序列的指针
- 返回说明:命令写入不成功,将返回0xff
******************************************************************/
uint08 SD_Write_Cmd(uint08 *pcmd)
{
uint08 r=0,time=0;
  SS_OFF;
(void)SPI_Rece();                //发送8个时钟,提高兼容性,如果没有这里,有些SD卡可能不支持   
  SS_ON;        
while(SPI_Rece()!=0xFF);        //等待SD卡准备好,再向其发送命令      
//_NOP();
//将6字节的命令序列写入SD卡
for(r=0;r<6;r++)
   SPI_Send(pcmd[r]);
           
if(pcmd[0]==0x1C) (void)SPI_Rece(); //如果是停止命令,跳过多余的字节

do
  {
   r=SPI_Rece();
   time++;
  }while((r==0xff)&&(time<TRY_TIME)); //等待响应,
  // _NOP();
   return r;
}
/******************************************************************
- 功能描述:SD卡初始化,针对于不同的SD卡,如MMC、SD或SDHC,初始化
             方法是不同的
- 参数说明:无
- 返回说明:调用成功,返回0x00,否则返回错误码
******************************************************************/
uint08 SD_Init(void)
{
uint08 time=0,r=0,i=0;        
uint08 rbuf[4]={0};        
uint08 pCMD0[6] ={0x40,0x00,0x00,0x00,0x00,0x95}; //CMD0,将SD卡从默认上电后的SD模式切换到SPI模式,使SD卡进入IDLE状态
uint08 pCMD1[6] ={0x41,0x00,0x00,0x00,0x00,0x01}; //CMD1,MMC卡使用CMD1命令进行初始化
uint08 pCMD8[6] ={0x48,0x00,0x00,0x01,0xAA,0x87}; //CMD8,用于鉴别SD卡的版本,并可从应答得知SD卡的工作电压
uint08 pCMD16[6]={0x50,0x00,0x00,0x02,0x00,0x01}; //CMD16,设置扇区大小为512字节,此命令用于在初始化完成之后进行试探性的操作,
                                                          //如果操作成功,说明初始化确实成功
uint08 pCMD55[6]={0x77,0x00,0x00,0x00,0x00,0x01}; //CMD55,用于告知SD卡后面是ACMD,即应用层命令 CMD55+ACMD41配合使用
                                                          //MMC卡使用CMD1来进行初始化,而SD卡则使用CMD55+ACMD41来进行初始化
uint08 pACMD41H[6]={0x69,0x40,0x00,0x00,0x00,0x01}; //ACMD41,此命令用于检测SD卡是否初始化完成,MMC卡,不适用此命令,针对2.0的SD卡
uint08 pACMD41S[6]={0x69,0x00,0x00,0x00,0x00,0x01}; //ACMD41,此命令用于检测SD卡是否初始化完成,MMC卡,不适用此命令,针对1.0的SD卡

uint08 pCMD58[6]={0x7A,0x00,0x00,0x00,0x00,0x01}; //CMD58,用于鉴别SD2.0到底是SDHC,还是普通的SD卡,二者对扇区地址的寻址方式不同


SPI_Init_M4();                                       // **关键步骤 SPI接口相关初始化

SS_OFF;                                                 //片选禁用
        
for(i=0;i<0x1f;i++)      //首先要发送最少31*8个时钟信号,这是必须的!激活SD卡
  (void)SPI_Rece();                           
do
{
   r=SD_Write_Cmd(pCMD0);                               //写入CMD0
   time++;
   if(time>=TRY_TIME) return(INIT_CMD0_ERROR);          //CMD0写入失败
  }while(r!=0x01);

  if(SD_Write_Cmd(pCMD8)==1)                             //写入CMD8,如果返回值为1,则SD卡版本为2.0
{
        rbuf[0]=SPI_Rece(); rbuf[1]=SPI_Rece(); //读取4个字节的R7回应,通过它可知此SD卡是否支持2.7~3.6V的工作电压
        rbuf[2]=SPI_Rece(); rbuf[3]=SPI_Rece();         
        if(rbuf[2]==0X01 && rbuf[3]==0XAA)                    //SD卡是否支持2.7~3.6V
        {               
         time=0;
         do
         {
          (void)SD_Write_Cmd(pCMD55);//写入CMD55
                r=SD_Write_Cmd(pACMD41H);//写入ACMD41,针对SD2.0
                time++;
    if(time>=TRY_TIME) return(INIT_SDV2_ACMD41_ERROR);//对SD2.0使用ACMD41进行初始化时产生错误

   }while(r!=0);        

   if(0==SD_Write_Cmd(pCMD58)) //写入CMD58,开始鉴别SD2.0
   {
          rbuf[0]=SPI_Rece(); rbuf[1]=SPI_Rece(); //读取4个字节的OCR,其中CCS指明了是SDHC还是普通的SD
          rbuf[2]=SPI_Rece(); rbuf[3]=SPI_Rece();        
    if(rbuf[0]&0x40)
                {
                 SD1_Ver=SD_VER_V2HC;    //SDHC卡        
                 SD1_Addr_Mode=1;        //SDHC卡的扇区寻址方式是扇区地址
                }        
    else SD1_Ver=SD_VER_V2; //普通的SD卡,2.0的卡包含SDHC和一些普通的卡                                
   }
  }
}
else                       //SD V1.0或MMC
{
        //SD卡使用ACMD41进行初始化,而MMC使用CMD1来进行初始化,依此来进一步判断是SD还是MMC
        (void)SD_Write_Cmd(pCMD55);//写入CMD55
        r=SD_Write_Cmd(pACMD41S);  //写入ACMD41,针对SD1.0

  if(r<=1)                  //检查返回值是否正确,如果正确,说明ACMD41命令被接受,即为SD卡
  {
         SD1_Ver=SD_VER_V1; //普通的SD1.0卡,一般来说容量不会超过2G
                        
         time=0;
         do
         {
                (void)SD_Write_Cmd(pCMD55);//写入CMD55
                r=SD_Write_Cmd(pACMD41S);//写入ACMD41,针对SD1.0
                time++;
    if(time>=TRY_TIME)
    {
     return(INIT_SDV1_ACMD41_ERROR);//对SD1.0使用ACMD41进行初始化时产生错误
    }
   }while(r!=0);                        
  }
  else                 //否则为MMC        
        {
         SD1_Ver=SD_VER_MMC; //MMC卡,它不支持ACMD41命令,而是使用CMD1进行初始化
                        
         time=0;
   do
   {
    r=SD_Write_Cmd(pCMD1);//写入CMD1
    time++;
    if(time>=TRY_TIME)
    {
     return(INIT_CMD1_ERROR);//MMC卡使用CMD1命令进行初始化中产生错误
    }
   }while(r!=0);                        
  }
}

if(0!=SD_Write_Cmd(pCMD16)) //SD卡的块大小必须为512字节
{
        SD1_Ver=SD_VER_ERR; //如果不成功,则此卡为无法识别的卡
        return INIT_ERROR;
}        

  SS_OFF;
(void)SPI_Rece(); //按照SD卡的操作时序在这里补8个时钟

SPI_High_Rate();                      //SPI切到高速           // **函数调用

return 0;//返回0,说明复位操作成功
}

/****************************************************************************
- 功能描述:读取addr扇区的512个字节到buffer指向的数据缓冲区
- 参数说明:addr:扇区地址
             buffer:指向数据缓冲区的指针
- 返回说明:调用成功,返回0x00,否则返回错误
****************************************************************************/
uint08 SD_Read_Sector(uint32 addr,uint08 *buffer)//从SD卡的指定扇区中读出512个字节,使用CMD17
{
uint16 i;
uint08 time,r;
uint08 pCMD16[]={0x51,0x00,0x00,0x00,0x00,0xFF}; //CMD16的字节序列

addr=addr<<=9;                     //sector = sector * 512将块地址(扇区地址)转为字节地址
        
pCMD16[1]=(uint08)((addr&0xFF000000)>>24);                //将字节地址写入到CMD17字节序列中
pCMD16[2]=(uint08)((addr&0x00ff0000)>>16);
pCMD16[3]=(uint08)((addr&0x0000ff00)>>8);
//pCMD17[4]=(uint08)addr;

time=0;
do
{  
  r=SD_Write_Cmd(pCMD16);                     //写入CMD17
  time++;
  if(time==TRY_TIME) return(READ_BLOCK_ERROR); //读块失败
}while(r!=0);
                           
                        
while(SPI_Rece()!= 0xfe); //一直读,当读到0xfe时,说明后面的是512字节的数据了

for(i=0;i<BufferSize;i++)                    //将数据写入到数据缓冲区中
   if(i<BufferSize) *(buffer++)=SPI_Rece();         


SPI_Rece();
SPI_Rece();       //两个字节的CRC校验码,不用关心
_NOP();
SS_OFF;
//(void)SPI_Trans_Byte(0xFF);      //按照SD1卡的操作时序在这里补8个时钟

return 0;
}
//============================================================================================
/****************************************************************************
- 功能描述:读取addr扇区开始的nsec个扇区的数据(★硬件多扇区读取)
- 参数说明:nsec:扇区数
             addr:开始扇区地址
             buffer:指向数据缓冲区的指针
- 返回说明:调用成功,返回0x00,否则返回错误码
- 注:SD卡初始化成功后,读写扇区时,尽量将SPI速度提上来,提高效率
****************************************************************************/
uint08 FAT32_Device_Read_nSector(uint32 nsec,uint32 addr,uint08 *buffer)
{
uint08 r,time;
uint32 i=0,j=0;
        
uint08 pCMD18[6]={0x52,0x00,0x00,0x00,0x00,0x01}; //CMD18的字节序列
uint08 pCMD12[6]={0x1C,0x00,0x00,0x00,0x00,0x01}; //CMD12,强制停止命令

if(!SD1_Addr_Mode) addr<<=9; //sector = sector * 512           将块地址(扇区地址)转为字节地址

pCMD18[1]=(uint08)(addr>>24); //将字节地址写入到CMD17字节序列中
pCMD18[2]=(uint08)(addr>>16);
pCMD18[3]=(uint08)(addr>>8);
pCMD18[4]=(uint08)addr;        

time=0;
do
{  
  r=SD_Write_Cmd(pCMD18); //写入CMD18
  time++;
  if(time==TRY_TIME)
  {
   return(READ_CMD18_ERROR); //写入CMD18失败
  }
}while(r!=0);

for(j=0;j<nsec;j++)
{  
  while(SPI_Rece()!= 0xFE); //一直读,当读到0xfe时,说明后面的是512字节的数据了

  for(i=0;i<512;i++)         //将数据写入到数据缓冲区中        
   *(buffer++)=SPI_Rece();

  for(i=0;i<2;i++)
(void)SPI_Rece(); //两个字节的CRC校验码,不用关心
}

(void)SD_Write_Cmd(pCMD12); //写入CMD12命令,停止数据读取

SS_OFF;
(void)SPI_Rece(); //按照SD卡的操作时序在这里补8个时钟

return 0;
}

/****************************************************************************
- 功能描述:获取SD卡的总扇区数(通过读取SD卡的CSD寄器组计算得到总扇区数)
- 参数说明:无
- 返回说明:返回SD卡的总扇区数
- 注:无
****************************************************************************/
uint32 SD_GetTotalSec(void)
{
uint08 pCSD[16];
uint32 Capacity;  
uint08 n,i;
uint16 csize;

uint08 pCMD9[6]={0x49,0x00,0x00,0x00,0x00,0x01}; //CMD9        

if(SD_Write_Cmd(pCMD9)!=0) //写入CMD9命令
{
        return GET_CSD_ERROR; //获取CSD时产生错误
}

while(SPI_Rece()!= 0xFE); //一直读,当读到0xfe时,说明后面的是16字节的CSD数据

for(i=0;i<16;i++) pCSD=SPI_Rece(); //读取CSD数据

for(i=0;i<2;i++)
(void)SPI_Rece(); //两个字节的CRC校验码,不用关心

  SS_OFF;
(void)SPI_Rece(); //按照SD卡的操作时序在这里补8个时钟
        
//如果为SDHC卡,即大容量卡,按照下面方式计算
if((pCSD[0]&0xC0)==0x40)          //SD2.0的卡
{        
        csize=pCSD[9]+(((uint16)(pCSD[8]))<<8)+1;
    Capacity=((uint32)csize)<<10;//得到扇区数                           
}
else //SD1.0的卡
{        
        n=(pCSD[5]&0x0F)+((pCSD[10]&0x80)>>7)+((pCSD[9]&0x03)<<1)+2;
        csize=(pCSD[8]>>6)+((uint16)pCSD[7]<<2)+((uint16)(pCSD[6]&0x03)<<0x0A)+1;
        Capacity=((uint32)csize)<<(n-9);//得到扇区数   
}
return Capacity;
}

//This is the end




//---------------------------fat32.h----------------------------------------------
//===========================================================
//基于AVR单片机FAT32文件系统程序
//创建时间:2016/3/22
//创建者 :Chen Yanan
//============================================================
#include "main.h"

#ifndef _FAT32_H_
#define _FAT32_H_


//FAT32的裁减宏:

//====================以下是FAT32的功能函数裁减宏,要使用到哪个功能函数,请将相应宏的注释去掉
//#define FAT32_FLUSH_FS  //刷新文件系统  如果没有使用RT_UPDATE_FSINFO,则在所有的文件操作完成之后,需要调用此函数

#define FAT32_OPEN_FILE  //打开文件
#define FAT32_CLOSE_FILE //关闭文件
#define FAT32_READDATA   //文件数据读取
//#define FAT32_READDATAX  //文件数据读取+重定向 此函数会将读取的每个字节送到处理函数,处理函数由使用者提供,请看宏Data_Redirect

//#define FAT32_CREATE_FILE //创建文件
//#define FAT32_DELETE_FILE //删除文件
//#define FAT32_WRITEDATA //写入数据,写入数据均是从文件的末尾追加数据
//#define FAT32_MODIFY_DATA //数据修改

//===================================================================================================================================

//#define TOV_CNT (200) //存储设备扇区读写重试次数,如果次数超过此值,仍然失败,则会导致物理设备的IO错误
//=================================================================================================================================

//CCCB,压缩簇链缓冲,是FAT32中所使用簇链缓冲算法,它可以极大的提高数据写入的速度
#define CCCB_LEN (8)   //簇链缓冲的长度,必须为偶数,且不小于4

//#define Data_Redirect UART_Send_Byte //数据重定向函数名定义  


/*========================文件函数关系=====================*/
//打开文件所用函数
#ifdef  FAT32_OPEN_FILE
#define FAT32_ENTER_DIR
#define GET_DIR_START_CLUSTER
#define CHECK_SFN_ILLEGAL_LENGTH
#define CHECK_SFN_DOT
#define CHECK_ILLEGAL_CHAR
#define CHECK_SFN_SPECIAL_CHAR
#define CHECK_SFN_ILLEGAL_LOWER
#define TO_FILE_NAME
#define SFN_MATCH
#define FINDSUBSTR
#define GET_NEXT_CLUSTER
#define IS_WILDFILENAME
#define ANALYSE_FDI
#endif
//读数据
#ifdef  FAT32_READDATA
#define FAT32_SEEK
#define GET_NEXT_CLUSTER
#define GET_NEXT_CLUSTER
#endif



//==========================================================================================
#define MBR_SECTOR (0)            //MBR扇区
#define DBR_MARK {0XEB,0X58,0X90} //DBR的标志码

//#define FDI_NBYTES (32)           //文件目录项所占字节数
#define NFDI_PER_SEC (16)         //每扇区的文件目录项数

#define FAT32_BUF_SIZE (512) //FAT32内部缓冲区大小
//-------------------------------------------------------------------------------------------
#define SOC(c)  (((c-2)*(pInit_Args->SectorsPerClust))+(pInit_Args->FirstDirSector)) //计算簇的开始扇区 Start Sector Of Cluster

#define NFATITEMBYTES   (4)                                                               //FAT表项所占用的字节数
#define NITEMSINFATSEC  (128)                                                             //FAT表一个扇区中包含的表项数

#define IS_END_CLU(cluster) ((0X0FFFFFFF)==(cluster))                                       //判断一个簇项的值是否是结束簇
#define IS_END_SEC_OF_CLU(sec,cluster) ((sec-SOC(cluster))==(pInit_Args->SectorsPerClust-1)) //判断是否是簇的最后一个扇区
#define LAST_SEC_OF_CLU(cluster) (SOC(cluster)+(pInit_Args->SectorsPerClust-1))              //簇的最后一个扇区的地址
#define IS_FAT32_TYPE(FST) (('F'==(FST[0])) && ('A'==(FST[1])) && ('T'==(FST[2])) && ('3'==(FST[3])) && ('2'==(FST[4]))) //检验文件系统是否FAT32

#define CHK_ATTR_FILE(attr) ((!((attr&0X10)!=0X00)) && (0X0F!=attr) && (0X00!=attr) && (0X08!=attr)) //属性字节第4位为0,同时不是长名属性0X0F,空项或卷标
#define CHK_ATTR_DIR(attr) ((attr&0X10)!=0X00)                                               //属性字节第4位为
#define Lower2Up(c) ((c>='a' && c<='z')?(c-32):c)
#define Upper2Low(C) ((C>='A' && C<='Z')?(C+32):C)

#define BOOL_TRUE     (1)
#define BOOL_FALSE    (0)
#define NUL_RET  (0)
//#define NUL_PTR  ((void *)0)


//=================错误码=======================
#define ERR_SUCC                  (0)
#define ERR_FAIL                  (1)
#define FSTYPE_NOT_FAT32          (2)
#define ERR_NO_FILE               (3)
#define ERR_NO_DIR                (4)
#define ERR_ILL_CHAR              (5)
#define ERR_SFN_ILL_LEN                  (6)


//================================================


//DPT:分区记录结构如下
typedef struct
{
uint08 Active;         //0x80表示此分区有效
uint08 StartHead;      //分区的开始磁头
uint08 StartSect;      //开始扇区
uint08 StartCyl;       //开始柱面
uint08 PartType;       //分区类型
uint08 EndHead;        //分区的结束头
uint08 EndSect;        //结束扇区
uint08 EndCyl;         //结束柱面
uint08 StartLBA[4];    //分区的第一个扇区
uint08 Size[4];        //分区的大小,总扇区数
}DPT_Item;
//MBR:分区扇区(绝对0扇区)定义如下
typedef struct
{
uint08   PartCode[446]; //MBR的引导程序
DPT_Item Part[4];       //4个分区记录
uint08   BootSectSig0;  //55
uint08   BootSectSig1;  //AA
}MBR;
//FAT32中对BPB的定义如下  一共占用90个字节
typedef struct
{uint08 BS_jmpBoot[3];     //跳转指令            offset:0
uint08 BS_OEMName[8];     //OEM名称             offset:3
uint08 BPB_BytesPerSec[2];//每扇区字节数        offset:11
uint08 BPB_SecPerClus;    //每簇扇区数          offset:13
uint08 BPB_RsvdSecCnt[2]; //保留扇区数目        offset:14
uint08 BPB_NumFATs;       //此卷中FAT表数       offset:16
uint08 BPB_RootEntCnt[2]; //FAT32为0            offset:17
uint08 BPB_TotSec16[2];   //FAT32为0            offset:19
uint08 BPB_Media;         //存储介质            offset:21
uint08 BPB_FATSz16[2];    //FAT32为0            offset:22
uint08 BPB_SecPerTrk[2];  //磁道扇区数          offset:24
uint08 BPB_NumHeads[2];   //磁头数              offset:26
uint08 BPB_HiddSec[4];    //FAT区前隐扇区数     offset:28
uint08 BPB_TotSec32[4];   //该卷总扇区数        offset:32
uint08 BPB_FATSz32[4];    //一个FAT表扇区数     offset:36
uint08 BPB_ExtFlags[2];   //FAT32特有           offset:40
uint08 BPB_FSVer[2];      //FAT32特有           offset:42
uint08 BPB_RootClus[4];   //根目录簇号          offset:44
uint08 FiSInfo[2];        //保留扇区FSINFO扇区数offset:48
uint08 BPB_BkBootSec[2];  //通常为6             offset:50
uint08 BPB_Reserved[12];  //扩展用              offset:52
uint08 BS_DrvNum;         //                    offset:64
uint08 BS_Reserved1;      //                    offset:65
uint08 BS_BootSig;        //                    offset:66
uint08 BS_VolID[4];       //                    offset:67
uint08 BS_FilSysType[11]; //                           offset:71
uint08 BS_FilSysType1[8]; //"FAT32"             offset:82
} DBR;

//FAT32中对文件目录项的定义如下一共占用32个字节
typedef struct
{
uint08 Name[8];        // 文件名,不足部分以空格补充
uint08 Extension[3];         // 扩展名,不足部分以空格补充
uint08 Attributes;           // 文件属性
uint08 LowerCase;            // 0
uint08 CTime10ms;             // 创建时间的10毫秒位
uint08 CTime[2];             // 创建时间
uint08 CDate[2];             // 创建日期
uint08 ADate[2];             // 访问日期
uint08 HighClust[2];   // 开始簇的高字
uint08 MTime[2];             // 最近的修改时间
uint08 MDate[2];             // 最近的修改日期
uint08 LowClust[2];           // 开始簇的低字
uint08 FileSize[4];      // 文件大小
} FDI;
//FAT32中对文件系统信息结构的定义
typedef  struct  
{
uint08 Head[4]; //"RRaA"
uint08 Resv1[480];
uint08 Sign[4]; //"rrAa"
uint08 Free_Cluster[4]; //剩余空簇数
uint08 Next_Free_Cluster[4]; //下一空簇参考值
uint08 Resv2[14];
uint08 Tail[2]; //"55 AA"
}FSInfo;
//-----------------------------------
typedef struct
{
FDI FDIes[16]; //扇区中的文件目录项数组 16
}FDIesInSEC;
//----------------------------------------
typedef struct
{
uint16 year;
uint08 month;
uint08 day;
}Date;

typedef struct
{
uint08 hour;
uint08 min;
uint08 sec;
}Time;

typedef struct  //日期与时间
{
  Date date; //日期
  Time time; //时间
}DateTime;

extern DateTime dt; //全局变量----------------------------------

typedef struct   //FAT32中对FAT表项的结构定义
{
uint08 Item[4]; //FAT32中FAT表项占用4个字节,即32位
} FAT_Item;
typedef struct         //FAT32中对FAT表扇区结构的定义
{
FAT_Item items[NITEMSINFATSEC]; //FAT扇区包含128个FAT表项
}FAT_Sec;


#define DATE_YEAR_BASE (1980)
#define TIME_HOUR_MARK  (0X001F)
#define TIME_MIN_MARK   (0X002F)
#define TIME_SEC_MARK   (0X001F)

#define DATE_YEAR_MARK  (0X007F)
#define DATE_MONTH_MARK (0X000F)
#define DATE_DAY_MARK   (0X001F)

#define TIME_HOUR_NBITS   (5)
#define TIME_MIN_NBITS    (6)
#define TIME_SEC_NBITS    (5)

#define DATE_YEAR_NBITS   (7)
#define DATE_MONTH_NBITS  (4)
#define DATE_DAY_NBITS    (5)

//FAT32初始化时初始参数装入如下结构体中
typedef struct
{
uint32 BPB_Sector_No;   //DBR(BPB)所在扇区号
uint32 Total_SizeKB;    //磁盘的总容量,单位为KB
uint32 BytesPerSector;         //每个扇区的字节数
uint32 FATsectors;      //FAT表所占扇区数
uint32 SectorsPerClust; //每簇的扇区数
uint32 FirstFATSector;         //第一个FAT表所在扇区
uint32 FirstDirSector;         //第一个目录所在扇区
uint32 FSINFO_Sec;      //FSINFO扇区所在的扇区
uint32 Free_nCluster;   //空闲簇的个数
uint32 Next_Free_Cluster; //下一空簇
}FAT32_Init_Args;

extern FAT32_Init_Args Init_Args,*pInit_Args;  //初始化参数结构体指针,用以指向某一存储设备的初始化参数集合

//FAT32中对文件信息集合的定义
typedef struct
{
sint08 File_Name[13];          //完整文件名 EX:text.txt
sint08 File_Attr;                    //文件属性
Time   File_CTime;       //文件创建时间
Date   File_CDate;       //文件创建日期
uint32 File_StartClust;  //文件开始簇
uint32 File_Size;                    //文件大小
uint32 File_CurClust;    //文件当前簇
uint32 File_CurSec;      //文件当前扇区
uint16 File_CurPos;            //文件当前扇区偏移量
uint08 File_IsEOF;       //文件是否到达结束位置
uint32 File_CurOffset;          //文件当前偏移量
uint32 FDI_Sec;          //文件目录项所在的扇区
uint08  nFDI;            //文件目录项在扇区中的索引
}FileInfo;

extern FileInfo fileinfo; //全局变量,文件信息集合


  //函数声明
//以下是对用户可用的函数的声明
uint08 FAT32_Init(void);
uint08 FAT32_Device_Init(void); //存储设备初始化,底层驱动接口
uint08 FAT32_Device_Read_Sector(uint32 addr,uint08 *buf);
uint08 To_File_Name(sint08 *name_in_fdi,sint08 *pfileName);
uint08 SFN_Match(sint08 *t,sint08*s);
uint08 FAT32_Enter_Dir(sint08 *dirpath,uint32 *pCluster,uint32 *pos); //进入目录
uint08 Analyse_FDI( FileInfo *pfi, FDI *pFDI); //目录项解析
uint32 FAT32_ReadData(FileInfo *pFI,uint32 offset,uint32 len,uint08 *app_Buffer); //数据读取
uint08 FAT32_Seek(FileInfo *pFI,uint32 offset); //文件定位
uint08 FAT32_Open_File(FileInfo *pFI,sint08 *filepath,uint32 n,uint08 is_file); //文件打开
uint08 FAT32_Close_File(FileInfo *pfi); //关闭文件,如果程序中没有打开实时文件大小更新,则文件操作完后,尤其是写入和更改操作,必须调用此函数


//----------------------------------------------------------------------------------------------
#endif




//------------------------------------------main.h---------------------------------------
#ifndef _MAIN_H_
#define _MAIN_H_
//数据类型宏定义
typedef unsigned char uint08;
typedef signed   char sint08;
typedef unsigned int  uint16;
typedef signed   int  sint16;
typedef unsigned long uint32;
//变量定义
#define BufferSize      512 //数据缓冲区,不可超过512

void Init_Timer0(void);
void delayms(uint16 u16count);
void Init_Clock(void);
void Init_watchdog(void);
void Init_Port(void);
void Init_USART0(void);
void delayms(uint16 u16count);
void USART_Send(uint08 data);
void SCI_Send_Str(uint08 *str) ;
void EEPROM_Write(uint16 add,uint08 dat);
uint08 EEPROM_Read(uint16 Add);
void SCI_Put_Inf(uint08 *inf,uint32 dat);
void SCI_Put_Num(uint32 dat);

void SPI_Init_M3(void);
void SPI_Init_M4(void);
uint08 SD_Init(void);
uint08 SD_Read_Sector(uint32 addr,uint08 *buffer);//从SD卡的指定扇区中读出512个字节,使用CMD17
uint08 SD_Read_nSector(uint32 nsec,uint32 addr,uint08 *buffer);
//uint08 SD_Write_Sector(uint32 addr,uint08 *buffer);
uint32 SD_TotalSector(void);
void SCI_Send_Str(uint08 *str);

uint08 SD_Read_Sector(uint32 addr,uint08 *buffer);         //从地址为addr的扇区中读取数据到buffer数据缓冲区中
//文件系统部分
uint08 FAT32_Device_Read_nSector(uint32 nsec,uint32 addr,uint08 *buffer); //将buffer数据缓冲区中的数据写入起始地址为addr的nsec个连续扇区中
uint32 SD_GetTotalSec(void);                                  //获取SD卡的总扇区数

#endif




//---------------------------------------------sdinit.h---------------------------------------
#ifndef _SDINIT_H_
#define _SDINIT_H_


#define TRY_TIME 200   //读TRY_TIME次,如果在TRY_TIME次中读不到回应,产生超时错误,命令写入失败

//相关宏定义
//-------------------------------------------------------------
#define SD_VER_ERR     0X00
#define SD_VER_MMC     0X01
#define SD_VER_V1      0X02
#define SD_VER_V2      0X03
#define SD_VER_V2HC    0X04


#define INIT_ERROR                  0x01 //初始化错误
#define INIT_CMD0_ERROR             0x02 //CMD0错误
#define INIT_CMD1_ERROR             0x03 //CMD1错误
#define INIT_SDV2_ACMD41_ERROR            0x04 //ACMD41错误
#define INIT_SDV1_ACMD41_ERROR            0x05 //ACMD41错误
#define READ_BLOCK_ERROR            0x08 //读块错误

#define READ_CMD18_ERROR            0x0B //在连续多块读时产生CMD18错误

#define GET_CSD_ERROR               0x0C //读CSD失败
//#define SPI_WRITE_ERROR             0x0D //在连续多块读时产生CMD18错误
//#define SPI_READ_ERROR              0x0E //读CSD失败


#endif

fat32.zip

21.05 KB, 下载次数: 104

发表于 2018-2-13 05:46 | 显示全部楼层
剕常感謝大大大分享,下來研究一下
发表于 2016-7-9 13:31 来自手机 | 显示全部楼层
6666666666
发表于 2017-12-30 10:44 | 显示全部楼层
请问楼主,声明的那些函数都是啥呀?方不方便提供一下?
发表于 2017-12-30 10:45 | 显示全部楼层
在main.h里的那些void
 楼主| 发表于 2018-2-12 12:49 | 显示全部楼层
icebee250 发表于 2017-12-30 10:44
请问楼主,声明的那些函数都是啥呀?方不方便提供一下?

下下来,都在.C文件里面
发表于 2019-12-24 10:51 | 显示全部楼层
谢谢啦!慢慢看。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 11:45 , Processed in 0.088615 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表