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

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

  1. SD卡扇区读写驱动测试,SD卡单扇区读写,多扇区读写,多扇区擦除成功。
  2. 以此项目为基础项目,新建ZNFAT组,添加振南项目znFAT文件夹下源文件。
  3. 关联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;
}


  1. 写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("串口设置完毕\r\n");
	
	znFAT_Device_Init(); //存储设备初始化
    printf("SD卡初始化完毕\r\n");
	
	printf( " \r\n CardType is :%d ", SDCardInfo.CardType );
	printf( " \r\n CardCapacity is :%d ", SDCardInfo.CardCapacity );
	printf( " \r\n CardBlockSize is :%d ", SDCardInfo.CardBlockSize );
	printf( " \r\n RCA is :%d ", SDCardInfo.RCA);
	printf( " \r\n ManufacturerID is :%d \r\n", SDCardInfo.SD_cid.ManufacturerID );

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

	 if(res){
printf("格式化失败 , Err Code:%d\n",res);
 }else{
printf("格式化成功 , Err Code:%d\n",res);
 }*/
 
	res=znFAT_Init(); //文件系统初始化	 
	
 if(!res) //文件系统初始化成功
 {
  printf("Suc. to init FS\r\n");
	 
  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: %d\n",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:%d\n",res);
 }else{
printf("Suc to delete file , Err Code:%d\n",res);
 }	
 */
 res=znFAT_Create_File(&fileinfo,"/ZNMCU.TXT",&dt); //创建文件
 if(!res) //如果创建文件成功
 {
  printf("Suc. to creat file.\r\n");
  printf("================================\n");
  printf("File_Name(Short 8.3):%s\n",fileinfo.File_Name);
  printf("File_Size:%d\n",fileinfo.File_Size);
	 
  printf("File_CDate:%d-%d-%d %d:%d:%d\n",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:%d\n",fileinfo.File_StartClust);
  printf("File_CurClust:%d\n",fileinfo.File_CurClust);
  printf("File_CurSec:%d\n",fileinfo.File_CurSec);
  printf("File_CurPos:%d\n",fileinfo.File_CurPos);
  printf("File_CurOffset:%d\n",fileinfo.File_CurOffset);
  printf("================================\n");
	 
  for(i=0;i<20;i++) buf[i]="akkjdjnknksjk897w9887309e09e"[i];

    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 %d\n:",len);
      }
	  else
	  {
	    printf("Write Data,times:%d\n",i);
	    printf("Current file offset is %d\n",fileinfo.File_CurOffset);
	  }
	}

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

 znFAT_Flush_FS(); //刷新文件系统
 
 res = znFAT_Open_File(&fileinfo,"/ZNMCU.TXT",0,1);
 if(res){
		printf("Fail to open file2 , Err Code:%d\n",res);
 }else{
		printf("Suc to open file2 , Err Code:%d\n",res);
 }
 znFAT_ReadData(&fileinfo,5,12,buf2);
 i=0;
 printf("读出数据1:");
 while(buf2[i]!=0){
	 
	 printf("%c",buf2[i]);
	 i++;
 }	
 znFAT_Close_File(&fileinfo); 
 res = znFAT_Open_File(&fileinfo,"/ABCDE.TXT",0,1);
 if(res){
printf("Fail to open file1 , Err Code:%d\n",res);
 }else{
printf("Suc to open file1 , Err Code:%d\n",res);
 }
 
 
 znFAT_ReadData(&fileinfo,5,12,buf2);
 i=0;
 printf("读出数据2:");
 while(buf2[i]!=0){
	 
	 printf("%c",buf2[i]);
	 i++;
 }
 znFAT_Close_File(&fileinfo); 
 while(1);				    
}   



  1. 编译,修改错误。原因是sys.c/sys.h中void NVIC_Configuration(void)函数与原项目重名,去掉sys中那个。另外,把FWlib需要的外设驱动添加进来,并包括相应头文件,没用到的可以去掉。
  2. 编译通过,烧写到开发板。插上4G(或以上)SD卡,串口连接到电脑,复位。从串口输出的数据看出,写入的文件可以成功读出。但SD卡插到电脑却看不到文件!
  3. 用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;
	}
  1. 此时,已经可以向4G及以上的SDHC卡中正确读写文件,并与windows文件系统一致。
  2. 但是2G及以下的SD标准2.0卡仍然无法写入文件(驱动正常),卡在文件初始化这一步。通过查看znFAT_Init()函数,发现中止在znFAT_Device_Read_Sector((pInit_Args->BPB_Sector_No),znFAT_Buffer); 这一行。进一步输出中间值,并查看相应扇区内容,发现读取的扇区地址无效,SD卡任何位置都查找不到FAT32文件系统的特征字符串。这说明标准卡与大容量卡的文件系统有所差异。
  3. 稍后将考察文件系统,尝试进一步解决问题。

GAME PAUSE…

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值