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

希岩 发表于 2016-6-30 20:02

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

    参考为删减的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;
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_OFFPORTB|=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);
         
if(pcmd==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={0};      
uint08 pCMD0 ={0x40,0x00,0x00,0x00,0x00,0x95}; //CMD0,将SD卡从默认上电后的SD模式切换到SPI模式,使SD卡进入IDLE状态
uint08 pCMD1 ={0x41,0x00,0x00,0x00,0x00,0x01}; //CMD1,MMC卡使用CMD1命令进行初始化
uint08 pCMD8 ={0x48,0x00,0x00,0x01,0xAA,0x87}; //CMD8,用于鉴别SD卡的版本,并可从应答得知SD卡的工作电压
uint08 pCMD16={0x50,0x00,0x00,0x02,0x00,0x01}; //CMD16,设置扇区大小为512字节,此命令用于在初始化完成之后进行试探性的操作,
                                                          //如果操作成功,说明初始化确实成功
uint08 pCMD55={0x77,0x00,0x00,0x00,0x00,0x01}; //CMD55,用于告知SD卡后面是ACMD,即应用层命令 CMD55+ACMD41配合使用
                                                          //MMC卡使用CMD1来进行初始化,而SD卡则使用CMD55+ACMD41来进行初始化
uint08 pACMD41H={0x69,0x40,0x00,0x00,0x00,0x01}; //ACMD41,此命令用于检测SD卡是否初始化完成,MMC卡,不适用此命令,针对2.0的SD卡
uint08 pACMD41S={0x69,0x00,0x00,0x00,0x00,0x01}; //ACMD41,此命令用于检测SD卡是否初始化完成,MMC卡,不适用此命令,针对1.0的SD卡

uint08 pCMD58={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=SPI_Rece(); rbuf=SPI_Rece(); //读取4个字节的R7回应,通过它可知此SD卡是否支持2.7~3.6V的工作电压
      rbuf=SPI_Rece(); rbuf=SPI_Rece();         
      if(rbuf==0X01 && rbuf==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=SPI_Rece(); rbuf=SPI_Rece(); //读取4个字节的OCR,其中CCS指明了是SDHC还是普通的SD
          rbuf=SPI_Rece(); rbuf=SPI_Rece();      
    if(rbuf&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=(uint08)((addr&0xFF000000)>>24);                //将字节地址写入到CMD17字节序列中
pCMD16=(uint08)((addr&0x00ff0000)>>16);
pCMD16=(uint08)((addr&0x0000ff00)>>8);
//pCMD17=(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={0x52,0x00,0x00,0x00,0x00,0x01}; //CMD18的字节序列
uint08 pCMD12={0x1C,0x00,0x00,0x00,0x00,0x01}; //CMD12,强制停止命令

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

pCMD18=(uint08)(addr>>24); //将字节地址写入到CMD17字节序列中
pCMD18=(uint08)(addr>>16);
pCMD18=(uint08)(addr>>8);
pCMD18=(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;
uint32 Capacity;
uint08 n,i;
uint16 csize;

uint08 pCMD9={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&0xC0)==0x40)          //SD2.0的卡
{      
      csize=pCSD+(((uint16)(pCSD))<<8)+1;
    Capacity=((uint32)csize)<<10;//得到扇区数                           
}
else //SD1.0的卡
{      
      n=(pCSD&0x0F)+((pCSD&0x80)>>7)+((pCSD&0x03)<<1)+2;
      csize=(pCSD>>6)+((uint16)pCSD<<2)+((uint16)(pCSD&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 //数据重定向函数名定义


/*========================文件函数关系=====================*/
//打开文件所用函数
#ifdefFAT32_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
//读数据
#ifdefFAT32_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)) && ('A'==(FST)) && ('T'==(FST)) && ('3'==(FST)) && ('2'==(FST))) //检验文件系统是否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;    //分区的第一个扇区
uint08 Size;      //分区的大小,总扇区数
}DPT_Item;
//MBR:分区扇区(绝对0扇区)定义如下
typedef struct
{
uint08   PartCode; //MBR的引导程序
DPT_Item Part;       //4个分区记录
uint08   BootSectSig0;//55
uint08   BootSectSig1;//AA
}MBR;
//FAT32中对BPB的定义如下一共占用90个字节
typedef struct
{uint08 BS_jmpBoot;   //跳转指令            offset:0
uint08 BS_OEMName;   //OEM名称             offset:3
uint08 BPB_BytesPerSec;//每扇区字节数      offset:11
uint08 BPB_SecPerClus;    //每簇扇区数          offset:13
uint08 BPB_RsvdSecCnt; //保留扇区数目      offset:14
uint08 BPB_NumFATs;       //此卷中FAT表数       offset:16
uint08 BPB_RootEntCnt; //FAT32为0            offset:17
uint08 BPB_TotSec16;   //FAT32为0            offset:19
uint08 BPB_Media;         //存储介质            offset:21
uint08 BPB_FATSz16;    //FAT32为0            offset:22
uint08 BPB_SecPerTrk;//磁道扇区数          offset:24
uint08 BPB_NumHeads;   //磁头数            offset:26
uint08 BPB_HiddSec;    //FAT区前隐扇区数   offset:28
uint08 BPB_TotSec32;   //该卷总扇区数      offset:32
uint08 BPB_FATSz32;    //一个FAT表扇区数   offset:36
uint08 BPB_ExtFlags;   //FAT32特有         offset:40
uint08 BPB_FSVer;      //FAT32特有         offset:42
uint08 BPB_RootClus;   //根目录簇号          offset:44
uint08 FiSInfo;      //保留扇区FSINFO扇区数offset:48
uint08 BPB_BkBootSec;//通常为6             offset:50
uint08 BPB_Reserved;//扩展用            offset:52
uint08 BS_DrvNum;         //                  offset:64
uint08 BS_Reserved1;      //                  offset:65
uint08 BS_BootSig;      //                  offset:66
uint08 BS_VolID;       //                  offset:67
uint08 BS_FilSysType; //                           offset:71
uint08 BS_FilSysType1; //"FAT32"             offset:82
} DBR;

//FAT32中对文件目录项的定义如下一共占用32个字节
typedef struct
{
uint08 Name;      // 文件名,不足部分以空格补充
uint08 Extension;         // 扩展名,不足部分以空格补充
uint08 Attributes;         // 文件属性
uint08 LowerCase;            // 0
uint08 CTime10ms;             // 创建时间的10毫秒位
uint08 CTime;             // 创建时间
uint08 CDate;             // 创建日期
uint08 ADate;             // 访问日期
uint08 HighClust;   // 开始簇的高字
uint08 MTime;             // 最近的修改时间
uint08 MDate;             // 最近的修改日期
uint08 LowClust;         // 开始簇的低字
uint08 FileSize;      // 文件大小
} FDI;
//FAT32中对文件系统信息结构的定义
typedefstruct
{
uint08 Head; //"RRaA"
uint08 Resv1;
uint08 Sign; //"rrAa"
uint08 Free_Cluster; //剩余空簇数
uint08 Next_Free_Cluster; //下一空簇参考值
uint08 Resv2;
uint08 Tail; //"55 AA"
}FSInfo;
//-----------------------------------
typedef struct
{
FDI FDIes; //扇区中的文件目录项数组 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; //FAT32中FAT表项占用4个字节,即32位
} FAT_Item;
typedef struct         //FAT32中对FAT表扇区结构的定义
{
FAT_Item items; //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;          //完整文件名 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;          //文件目录项所在的扇区
uint08nFDI;            //文件目录项在扇区中的索引
}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 intuint16;
typedef signed   intsint16;
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

sp055368 发表于 2018-2-13 05:46

剕常感謝大大大分享,下來研究一下

starrain 发表于 2016-7-9 13:31

6666666666

icebee250 发表于 2017-12-30 10:44

请问楼主,声明的那些函数都是啥呀?方不方便提供一下?

icebee250 发表于 2017-12-30 10:45

在main.h里的那些void

希岩 发表于 2018-2-12 12:49

icebee250 发表于 2017-12-30 10:44
请问楼主,声明的那些函数都是啥呀?方不方便提供一下?

下下来,都在.C文件里面

suzhou_chen 发表于 2019-12-24 10:51

谢谢啦!慢慢看。
页: [1]
查看完整版本: 使用AVR单片机读取SD卡文件(FAT32文件系统)