STM32
直播中

陈伟

7年用户 1448经验值
私信 关注
[问答]

STM32-M3(野火)SD卡如何读写/移植znFAT文件访问系统

STM32-M3(野火)SD卡如何读写/移植znFAT文件访问系统

回帖(1)

李泽明

2021-10-13 14:47:48
设备: STM32F103VE-M3,板载SD卡读写模块。
源码: 野火的SD卡扇区读写驱动,振南FAT的STM32版本。
另有1G-16G SD卡若干张。
移植过程:


  • SD卡扇区读写驱动测试,SD卡单扇区读写,多扇区读写,多扇区擦除成功。
  • 以此项目为基础项目,新建ZNFAT组,添加振南项目znFAT文件夹下源文件。
  • 关联znFAT,源码如下。其中sdio_sdcard.h为野火SD卡驱动提供的读写模块。
    sdx1.h




#ifndef _SDX1_H_
#define _SDX1_H_


/*★★★★★★★★★★★★★★★★★★★★★★★★
  《振南的znFAT--嵌入式FAT32文件系统设计与实现》
         一书[上下册]已经由北航出版社正式出版发行。
         此书是振南历经3年多时间潜心编著,是现今市面上唯
         一一套讲述FAT32文件系统、SD卡等嵌入式存储技术的
         专著。书中还介绍了大量的编程技巧与振南的开发经验。
         请在各大网络销售平台搜索“znFAT”,即可购买。
         在全国各大书店也有售。
         振南的ZN-X开发板,支持51、AVR、STM32(M0/M3/M4)等
         CPU。此板可与书配套,板上各种精彩的实验实例请详见
         振南网站www.znmcu.cn
  ★★★★★★★★★★★★★★★★★★★★★★★★*/


/***************************************************************************************
★程序模块:【振南ZN-X开发板】上『SD卡1』驱动程序  〖STM32部分:STM32F103RBT6〗
★功能描述:实现了SD卡的扇区读写、多扇区读写、扇区擦除、读取总物理扇区数等功能
             此驱动可支持几乎所有的SD卡,包括MMC/SD/SDHC
★配套教程与参考资料:
   ●《振南的znFAT--嵌入式FAT32文件系统设计与实验》一书 下册 第11章《SD卡物理驱动》
         ●《振南的单片机高级外设系列视频教程》之《SD卡专辑》
****************************************************************************************/
#include "sd_type.h"


#define SD1_CS PBout(8)
#define SET_SD1_CS_PIN(val)  (SD1_CS =val)


#define TRY_TIME 10   //向SD卡写入命令之后,读取SD卡的回应次数,即读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 WRITE_CMD24_ERROR           0x06 //写块时产生CMD24错误
#define WRITE_BLOCK_ERROR           0x07 //写块错误


#define READ_BLOCK_ERROR            0x08 //读块错误


#define WRITE_CMD25_ERROR           0x09 //在连续多块写时产生CMD25错误
#define WRITE_NBLOCK_ERROR          0x0A //连续多块写失败


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

#define GET_CSD_ERROR               0x0C //读CSD失败
#define BLOCK_SIZE            512
//-------------------------------------------------------------
UINT8 SD1_Init(void); //SD卡初始化


UINT8 SD1_Write_Sector(UINT32 addr,UINT8 *buffer); //将buffer数据缓冲区中的数据写入地址为addr的扇区中
UINT8 SD1_Read_Sector(UINT32 addr,UINT8 *buffer);         //从地址为addr的扇区中读取数据到buffer数据缓冲区中
UINT8 SD1_Write_nSector(UINT32 nsec,UINT32 addr,UINT8 *buffer); //将buffer数据缓冲区中的数据写入起始地址为addr的nsec个连续扇区中
UINT8 SD1_Read_nSector(UINT32 nsec,UINT32 addr,UINT8 *buffer); //将buffer数据缓冲区中的数据写入起始地址为addr的nsec个连续扇区中
UINT8 SD1_Erase_nSector(UINT32 addr_sta,UINT32 addr_end);
UINT32 SD1_GetTotalSec(void); //获取SD卡的总扇区数


#endif




sdx1.c


#include "sdx1.h"
#include "sd_type.h"
#include "sdio_sdcard.h"


/*★★★★★★★★★★★★★★★★★★★★★★★★
  《振南的znFAT--嵌入式FAT32文件系统设计与实现》
         一书[上下册]已经由北航出版社正式出版发行。
         此书是振南历经3年多时间潜心编著,是现今市面上唯
         一一套讲述FAT32文件系统、SD卡等嵌入式存储技术的
         专著。书中还介绍了大量的编程技巧与振南的开发经验。
         请在各大网络销售平台搜索“znFAT”,即可购买。
         在全国各大书店也有售。
         振南的ZN-X开发板,支持51、AVR、STM32(M0/M3/M4)等
         CPU。此板可与书配套,板上各种精彩的实验实例请详见
         振南网站www.znmcu.cn
  ★★★★★★★★★★★★★★★★★★★★★★★★*/


/***************************************************************************************
★程序模块:【振南ZN-X开发板】上『SD卡1』驱动程序  〖STM32部分:STM32F103RBT6〗
★功能描述:实现了SD卡的扇区读写、多扇区读写、扇区擦除、读取总物理扇区数等功能
             此驱动可支持几乎所有的SD卡,包括MMC/SD/SDHC
★配套教程与参考资料:
   ●《振南的znFAT--嵌入式FAT32文件系统设计与实验》一书 下册 第11章《SD卡物理驱动》
         ●《振南的单片机高级外设系列视频教程》之《SD卡专辑》
****************************************************************************************/


//变量定义
//--------------------------------------------------------------
extern UINT8 Low_or_High1; //在IOSPI中定义


UINT8 SD1_Addr_Mode=0; //SD1的寻址方式,1为块寻址,0为字节寻址
UINT8 SD1_Ver=SD_VER_ERR; //SD卡1的版本
//---------------------------------------------------------------


#define SD1_SPI_SPEED_HIGH() Low_or_High1=0


#define SD1_SPI_SPEED_LOW()  Low_or_High1=1


#define SD1_SPI_WByte(x) SD1_IOSPI_RWByte(x)


#define SD1_SPI_RByte()  SD1_IOSPI_RWByte(0XFF)


/********************************************************************
- 功能描述:【振南ZN-X开发板】上『SD卡1』SPI接口初始化
- 参数说明:无
- 返回说明:0
- 注:SPI接口初始化后,首先工作在低速模式。SD卡在初始化的过程中要求
       SPI速度要比较低,原则上不高于400KHZ,经验值为240KHZ。如果发现
                         SD卡初始化不成功,还可继续降低SPI速度,实现速度起决于威廉希尔官方网站 与SD
                         卡品质。
********************************************************************/








/******************************************************************
- 功能描述:SD1卡初始化,针对于不同的SD1卡,如MMC、SD1或SD1HC,初始化
             方法是不同的
- 参数说明:无
- 返回说明:调用成功,返回0x00,否则返回错误码
******************************************************************/


UINT8 SD1_Init(void)
{
UINT8 Status;
Status=SD_Init(); //SD卡的初始化函数



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


/******************************************************************
- 功能描述:对SD1卡若干个扇区进行擦除,擦除后扇区中的数据大部分情况
             下为全0(有些卡擦除后为全0XFF,如要使用此函数,请确认)
- 参数说明:addr_sta:开始扇区地址   addr_end:结束扇区地址
- 返回说明:调用成功,返回0x00,否则返回错误码
******************************************************************/


UINT8 SD1_Erase_nSector(UINT32 addr_sta,UINT32 addr_end)
{
UINT8 Status;
Status = SD_Erase(addr_sta,addr_end);


return Status;


}


/****************************************************************************
- 功能描述:将buffer指向的512个字节的数据写入到SD1卡的addr扇区中
- 参数说明:addr:扇区地址
             buffer:指向数据缓冲区的指针
- 返回说明:调用成功,返回0x00,否则返回错误码
- 注:SD1卡初始化成功后,读写扇区时,尽量将SPI速度提上来,提高效率
****************************************************************************/


UINT8 SD1_Write_Sector(UINT32 addr,UINT8 *buffer)        //向SD1卡中的指定地址的扇区写入512个字节,使用CMD24(24号命令)
{  
UINT8 Status;

Status = SD_WriteBlock(buffer, addr, BLOCK_SIZE);
Status = SD_WaitWriteOperation();           //等待dma传输结束
while(SD_GetStatus() != SD_TRANSFER_OK); //等待sdio到sd卡传输结束

return(Status);                 //返回0,说明写扇区操作成功
}


/****************************************************************************
- 功能描述:读取addr扇区的512个字节到buffer指向的数据缓冲区
- 参数说明:addr:扇区地址
             buffer:指向数据缓冲区的指针
- 返回说明:调用成功,返回0x00,否则返回错误码
- 注:SD1卡初始化成功后,读写扇区时,尽量将SPI速度提上来,提高效率
****************************************************************************/


UINT8 SD1_Read_Sector(UINT32 addr,UINT8 *buffer)//从SD1卡的指定扇区中读出512个字节,使用CMD17(17号命令)
{
UINT8 Status;
Status = SD_ReadBlock(buffer, addr, BLOCK_SIZE);//读取数据
Status = SD_WaitReadOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);


return Status;
}


/****************************************************************************
- 功能描述:向addr扇区开始的nsec个扇区写入数据(★硬件多扇区写入)
- 参数说明:nsec:扇区数
             addr:开始扇区地址
             buffer:指向数据缓冲区的指针
- 返回说明:调用成功,返回0x00,否则返回错误码
- 注:SD1卡初始化成功后,读写扇区时,尽量将SPI速度提上来,提高效率
****************************************************************************/


UINT8 SD1_Write_nSector(UINT32 nsec,UINT32 addr,UINT8 *buffer)       
{  
UINT8 Status;
Status = SD_WriteMultiBlocks(buffer, addr, BLOCK_SIZE, nsec);
Status = SD_WaitWriteOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
return(Status);                 //返回0,说明写扇区操作成功
}


/****************************************************************************
- 功能描述:读取addr扇区开始的nsec个扇区的数据(★硬件多扇区读取)
- 参数说明:nsec:扇区数
             addr:开始扇区地址
             buffer:指向数据缓冲区的指针
- 返回说明:调用成功,返回0x00,否则返回错误码
- 注:SD1卡初始化成功后,读写扇区时,尽量将SPI速度提上来,提高效率
****************************************************************************/


UINT8 SD1_Read_nSector(UINT32 nsec,UINT32 addr,UINT8 *buffer)
{
UINT8 Status;
Status = SD_ReadMultiBlocks(buffer, addr, BLOCK_SIZE, nsec);
Status = SD_WaitReadOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);


return Status;
}


/****************************************************************************
- 功能描述:获取SD1卡的总扇区数(通过读取SD1卡的CSD1寄器组计算得到总扇区数)
- 参数说明:无
- 返回说明:返回SD1卡的总扇区数
- 注:无
****************************************************************************/


UINT32 SD1_GetTotalSec(void)
{
       
// SD_CardInfo SDCardInfo;
SD_GetCardInfo(&SDCardInfo);


return SDCardInfo.CardCapacity;
}






写main方法测试基本功能:文件创建,追加,打开,读取,关闭。注意相应的功能需在config.h中打开相应宏。
#include "delay.h"
#include "sys.h"
#include "usart1.h"         
#include "znfat.h"
#include "sdx1.h"
#include "sdio_sdcard.h"


/*★★★★★★★★★★★★★★★★★★★★★★★★
  《振南的znFAT--嵌入式FAT32文件系统设计与实现》
         一书[上下册]已经由北航出版社正式出版发行。
         此书是振南历经3年多时间潜心编著,是现今市面上唯
         一一套讲述FAT32文件系统、SD卡等嵌入式存储技术的
         专著。书中还介绍了大量的编程技巧与振南的开发经验。
         请在各大网络销售平台搜索“znFAT”,即可购买。
         在全国各大书店也有售。
         振南的ZN-X开发板,支持51、AVR、STM32(M0/M3/M4)等
         CPU。此板可与书配套,板上各种精彩的实验实例请详见
         振南网站www.znmcu.cn
  ★★★★★★★★★★★★★★★★★★★★★★★★*/


/********************************************************************************
  注:此例程用于演示振南znFAT的功能,硬件CPU为STM32F103RBT6(其它CPU平台请变通)
*********************************************************************************/


struct znFAT_Init_Args Init_Args; //初始化参数集合
struct FileInfo fileinfo; //文件信息集合
struct DateTime dt; //日期与时间
unsigned char buf[20];
unsigned char buf2[20];
int main(void)
{         
        unsigned int res=0,i=0;
    unsigned long len=0;
       
        delay_init();
        NVIC_Configuration();


          /* USART1 config */
        USART1_Config();
        printf("串口设置完毕rn");
       
        znFAT_Device_Init(); //存储设备初始化
    printf("SD卡初始化完毕rn");
       
        printf( " rn CardType is :%d ", SDCardInfo.CardType );
        printf( " rn CardCapacity is :%d ", SDCardInfo.CardCapacity );
        printf( " rn CardBlockSize is :%d ", SDCardInfo.CardBlockSize );
        printf( " rn RCA is :%d ", SDCardInfo.RCA);
        printf( " rn ManufacturerID is :%d rn", SDCardInfo.SD_cid.ManufacturerID );


        znFAT_Select_Device(0,&Init_Args); //选择设备
        /*
        res = znFAT_Make_FS(SD1_GetTotalSec(),0);


         if(res){
printf("格式化失败 , Err Code:%dn",res);
}else{
printf("格式化成功 , Err Code:%dn",res);
}*/

        res=znFAT_Init(); //文件系统初始化         
       
if(!res) //文件系统初始化成功
{
  printf("Suc. to init FSrn");
         
  printf("BPB_Sector_No: %d",Init_Args.BPB_Sector_No);   
  printf("Total_SizeKB: %d",Init_Args.Total_SizeKB);
  printf("BytesPerSector: %d",Init_Args.BytesPerSector);
  printf("FATsectors: %d",Init_Args.FATsectors);  
  printf("SectorsPerClust: %d",Init_Args.SectorsPerClust);
  printf("FirstFATSector: %d",Init_Args.FirstFATSector);
  printf("FirstDirSector: %d",Init_Args.FirstDirSector);
  printf("FSsec: %d",Init_Args.FSINFO_Sec);
  printf("Next_Free_Cluster: %d",Init_Args.Next_Free_Cluster);
  printf("FreenCluster: %dn",Init_Args.Free_nCluster);
}
else //文件系统初始化失败
{
        printf("Fail to init FS , Err Code: %d",res);
}

  //==================================================================
  dt.date.year=2012; dt.date.month=12; dt.date.day=21;
  dt.time.hour=15;   dt.time.min=14;   dt.time.sec=35;
/*
res = znFAT_Delete_File("/ZNMCU.TXT");
if(res){
printf("Fail to delete file , Err Code:%dn",res);
}else{
printf("Suc to delete file , Err Code:%dn",res);
}       
*/
res=znFAT_Create_File(&fileinfo,"/ZNMCU.TXT",&dt); //创建文件
if(!res) //如果创建文件成功
{
  printf("Suc. to creat file.rn");
  printf("================================n");
  printf("File_Name(Short 8.3):%sn",fileinfo.File_Name);
  printf("File_Size:%dn",fileinfo.File_Size);
         
  printf("File_CDate:%d-%d-%d %d:%d:%dn",fileinfo.File_CDate.year,fileinfo.File_CDate.month,fileinfo.File_CDate.day,fileinfo.File_CTime.hour,fileinfo.File_CTime.min,fileinfo.File_CTime.sec);
         
  printf("File_StartClust:%dn",fileinfo.File_StartClust);
  printf("File_CurClust:%dn",fileinfo.File_CurClust);
  printf("File_CurSec:%dn",fileinfo.File_CurSec);
  printf("File_CurPos:%dn",fileinfo.File_CurPos);
  printf("File_CurOffset:%dn",fileinfo.File_CurOffset);
  printf("================================n");
         
  for(i=0;i<20;i++) buf="akkjdjnknksjk897w9887309e09e";


    for(i=0;i<10;i++)
        {
      len=znFAT_WriteData(&fileinfo,20,buf); //向文件写入数据
          if(len==ERR_OVER_FILE_MAX_SIZE || len==ERR_OVER_DISK_SPACE)
          {
                printf("Have some Err %dn:",len);
      }
          else
          {
            printf("Write Data,times:%dn",i);
            printf("Current file offset is %dn",fileinfo.File_CurOffset);
          }
        }


    znFAT_Close_File(&fileinfo); //关闭文件
}
else //如果创建文件不成功
{
  printf("Fail to creat file , Err Code:%dn",res);
}


znFAT_Flush_FS(); //刷新文件系统

res = znFAT_Open_File(&fileinfo,"/ZNMCU.TXT",0,1);
if(res){
                printf("Fail to open file2 , Err Code:%dn",res);
}else{
                printf("Suc to open file2 , Err Code:%dn",res);
}
znFAT_ReadData(&fileinfo,5,12,buf2);
i=0;
printf("读出数据1:");
while(buf2!=0){
         
         printf("%c",buf2);
         i++;
}       
znFAT_Close_File(&fileinfo);
res = znFAT_Open_File(&fileinfo,"/ABCDE.TXT",0,1);
if(res){
printf("Fail to open file1 , Err Code:%dn",res);
}else{
printf("Suc to open file1 , Err Code:%dn",res);
}


znFAT_ReadData(&fileinfo,5,12,buf2);
i=0;
printf("读出数据2:");
while(buf2!=0){
         
         printf("%c",buf2);
         i++;
}
znFAT_Close_File(&fileinfo);
while(1);                                    
}   




编译,修改错误。原因是sys.c/sys.h中void NVIC_Configuration(void)函数与原项目重名,去掉sys中那个。另外,把FWlib需要的外设驱动添加进来,并包括相应头文件,没用到的可以去掉。
编译通过,烧写到开发板。插上4G(或以上)SD卡,串口连接到电脑,复位。从串口输出的数据看出,写入的文件可以成功读出。但SD卡插到电脑却看不到文件!
用winhex查看SD卡扇区内容,发现znFAT调用底层驱动的入参是以扇区为地址,而野火提供的驱动却按照字节处理。所以要修改驱动源码。将sdio_sdcard.c中的
if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    BlockSize = 512;
    ReadAddr /= 512;
  }


改为


if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    BlockSize = 512;
  //  ReadAddr /= 512;
  }else{
                ReadAddr *= 512;
        }




  • 此时,已经可以向4G及以上的SDHC卡中正确读写文件,并与windows文件系统一致。
  • 但是2G及以下的SD标准2.0卡仍然无法写入文件(驱动正常),卡在文件初始化这一步。通过查看znFAT_Init()函数,发现中止在znFAT_Device_Read_Sector((pInit_Args->BPB_Sector_No),znFAT_Buffer); 这一行。进一步输出中间值,并查看相应扇区内容,发现读取的扇区地址无效,SD卡任何位置都查找不到FAT32文件系统的特征字符串。这说明标准卡与大容量卡的文件系统有所差异。
  • 稍后将考察文件系统,尝试进一步解决问题。

GAME PAUSE…
举报

更多回帖

发帖
×
20
完善资料,
赚取积分