STM32 FATFS - 在spi的SD卡中运行fatfs

news2025/4/25 1:48:00

参考文章

STM32 + CubeMX + 硬件SPI + SD卡 + FATFS_stm32cubemx fatfs-CSDN博客

例程地址:STM32FatFS: 基于stm32的fatfs例程,配合博客文章

基于野火STM32MINI开发板

STM32配置

系统模式配置

输出串口配置

SPI配置

使用全双工模式,禁用硬件NSS引脚,8位,MSB,256分频因为在协议在 SPI 模式初始化时(如发送 CMD0、CMD8 等命令),必须使用不超过 400kHz 的时钟频率。

NSS的GPIO配置

配置FATFS

使用自定义模式,支持简体中文编码,不建议开启长文件名支持,具体原因详见上一篇。

FREERTOS配置

选用V1版本,FATFS使用了V1版本的函数,选择V2版本编译不通过,增大栈总量,因为我在默认任务中打印了很多东西,所以需要加大栈。

增大默认任务的栈

配置系统时钟,这样可以更快的执行代码,这步完全可以不做。

配置生成独立.h.c文件

代码

创建sd卡的驱动文件,我是图省事直接添加到了Core文件夹中,分别创建sd.h和sd.c

sd.h文件

#ifndef __SD_H
#define __SD_H

#include "main.h"
#include "spi.h"
#include "ff.h"

extern uint8_t SD_TYPE;

//SD卡类型
#define ERR     	0x00
#define MMC				0x01
#define V1				0x02
#define V2				0x04
#define V2HC			0x06

#define DUMMY_BYTE				 0xFF 
#define MSD_BLOCKSIZE			 512


//CMD定义
#define CMD0    0       //卡复位
#define CMD1    1
#define CMD8    8       //命令8 ,SEND_IF_COND
#define CMD9    9       //命令9 ,读CSD数据
#define CMD10   10      //命令10,读CID数据
#define CMD12   12      //命令12,停止数据传输
#define CMD16   16      //命令16,设置SectorSize 应返回0x00
#define CMD17   17      //命令17,读sector
#define CMD18   18      //命令18,读Multi sector
#define CMD23   23      //命令23,设置多sector写入前预先擦除N个block
#define CMD24   24      //命令24,写sector
#define CMD25   25      //命令25,写Multi sector
#define CMD41   41      //命令41,应返回0x00
#define CMD55   55      //命令55,应返回0x01
#define CMD58   58      //命令58,读OCR信息
#define CMD59   59      //命令59,使能/禁止CRC,应返回0x00

//数据写入回应字意义
#define MSD_DATA_OK                0x05
#define MSD_DATA_CRC_ERROR         0x0B
#define MSD_DATA_WRITE_ERROR       0x0D
#define MSD_DATA_OTHER_ERROR       0xFF
//SD卡回应标记字
#define MSD_RESPONSE_NO_ERROR      0x00
#define MSD_IN_IDLE_STATE          0x01
#define MSD_ERASE_RESET            0x02
#define MSD_ILLEGAL_COMMAND        0x04
#define MSD_COM_CRC_ERROR          0x08
#define MSD_ERASE_SEQUENCE_ERROR   0x10
#define MSD_ADDRESS_ERROR          0x20
#define MSD_PARAMETER_ERROR        0x40
#define MSD_RESPONSE_FAILURE       0xFF


enum _CD_HOLD
{
	HOLD = 0,
	RELEASE = 1,
};

typedef struct               /* Card Specific Data */
{
  uint8_t  CSDStruct;            /* CSD structure */
  uint8_t  SysSpecVersion;       /* System specification version */
  uint8_t  Reserved1;            /* Reserved */
  uint8_t  TAAC;                 /* Data read access-time 1 */
  uint8_t  NSAC;                 /* Data read access-time 2 in CLK cycles */
  uint8_t  MaxBusClkFrec;        /* Max. bus clock frequency */
  uint16_t CardComdClasses;      /* Card command classes */
  uint8_t  RdBlockLen;           /* Max. read data block length */
  uint8_t  PartBlockRead;        /* Partial blocks for read allowed */
  uint8_t  WrBlockMisalign;      /* Write block misalignment */
  uint8_t  RdBlockMisalign;      /* Read block misalignment */
  uint8_t  DSRImpl;              /* DSR implemented */
  uint8_t  Reserved2;            /* Reserved */
  uint32_t DeviceSize;           /* Device Size */
  uint8_t  MaxRdCurrentVDDMin;   /* Max. read current @ VDD min */
  uint8_t  MaxRdCurrentVDDMax;   /* Max. read current @ VDD max */
  uint8_t  MaxWrCurrentVDDMin;   /* Max. write current @ VDD min */
  uint8_t  MaxWrCurrentVDDMax;   /* Max. write current @ VDD max */
  uint8_t  DeviceSizeMul;        /* Device size multiplier */
  uint8_t  EraseGrSize;          /* Erase group size */
  uint8_t  EraseGrMul;           /* Erase group size multiplier */
  uint8_t  WrProtectGrSize;      /* Write protect group size */
  uint8_t  WrProtectGrEnable;    /* Write protect group enable */
  uint8_t  ManDeflECC;           /* Manufacturer default ECC */
  uint8_t  WrSpeedFact;          /* Write speed factor */
  uint8_t  MaxWrBlockLen;        /* Max. write data block length */
  uint8_t  WriteBlockPaPartial;  /* Partial blocks for write allowed */
  uint8_t  Reserved3;            /* Reserded */
  uint8_t  ContentProtectAppli;  /* Content protection application */
  uint8_t  FileFormatGrouop;     /* File format group */
  uint8_t  CopyFlag;             /* Copy flag (OTP) */
  uint8_t  PermWrProtect;        /* Permanent write protection */
  uint8_t  TempWrProtect;        /* Temporary write protection */
  uint8_t  FileFormat;           /* File Format */
  uint8_t  ECC;                  /* ECC code */
  uint8_t  CSD_CRC;              /* CSD CRC */
  uint8_t  Reserved4;            /* always 1*/
}
MSD_CSD;

typedef struct				 /*Card Identification Data*/
{
  uint8_t  ManufacturerID;       /* ManufacturerID */
  uint16_t OEM_AppliID;          /* OEM/Application ID */
  uint32_t ProdName1;            /* Product Name part1 */
  uint8_t  ProdName2;            /* Product Name part2*/
  uint8_t  ProdRev;              /* Product Revision */
  uint32_t ProdSN;               /* Product Serial Number */
  uint8_t  Reserved1;            /* Reserved1 */
  uint16_t ManufactDate;         /* Manufacturing Date */
  uint8_t  CID_CRC;              /* CID CRC */
  uint8_t  Reserved2;            /* always 1 */
}
MSD_CID;

typedef struct
{
  MSD_CSD CSD;
  MSD_CID CID;
  uint32_t Capacity;              /* Card Capacity */
  uint32_t BlockSize;             /* Card Block Size */
  uint16_t RCA;
  uint8_t CardType;
  uint32_t SpaceTotal;            /* Total space size in file system */
  uint32_t SpaceFree;      	     /* Free space size in file system */
}
MSD_CARDINFO, *PMSD_CARDINFO;

extern MSD_CARDINFO SD0_CardInfo;


uint8_t		 	SD_init(void);
void 				SD_CS(uint8_t p);
uint32_t  	SD_GetSectorCount(void);
uint8_t 		SD_GETCID (uint8_t *cid_data);
uint8_t 		SD_GETCSD(uint8_t *csd_data);
int 				MSD0_GetCardInfo(PMSD_CARDINFO SD0_CardInfo);
uint8_t			SD_ReceiveData(uint8_t *data, uint16_t len);
uint8_t 		SD_SendBlock(uint8_t*buf,uint8_t cmd);
uint8_t 		SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt);
uint8_t 		SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt);


void SPI_setspeed(uint8_t speed);
uint8_t spi_readwrite(uint8_t Txdata);
void Get_SDCard_Capacity(void);
void WritetoSD(char filename[], BYTE write_buff[], uint8_t bufSize);



#endif

sd.c文件

#include "sd.h"
#include <stdio.h>
#include "fatfs.h"

uint8_t DFF=0xFF;
uint8_t test;
uint8_t SD_TYPE=0x00;

MSD_CARDINFO SD0_CardInfo;

//
//片选
//
void SD_CS(uint8_t p){
	if(p==0){	
		HAL_GPIO_WritePin(SD_CS_GPIO_Port,SD_CS_Pin,GPIO_PIN_SET);
		}else{
		HAL_GPIO_WritePin(SD_CS_GPIO_Port,SD_CS_Pin,GPIO_PIN_RESET);
		}
}
///
//发送命令,发完释放
//
int SD_sendcmd(uint8_t cmd,uint32_t arg,uint8_t crc){
	uint8_t r1;
  uint8_t retry;

  SD_CS(0);
	HAL_Delay(20);
  SD_CS(1);
	do{
		retry=spi_readwrite(DFF);
	}while(retry!=0xFF);

  spi_readwrite(cmd | 0x40);
  spi_readwrite(arg >> 24);
  spi_readwrite(arg >> 16);
  spi_readwrite(arg >> 8);
  spi_readwrite(arg);
  spi_readwrite(crc);
  if(cmd==CMD12)spi_readwrite(DFF);
  do
	{
		r1=spi_readwrite(0xFF);
	}while(r1&0X80);
	
	return r1;
}


//SD卡初始化

uint8_t SD_init(void)
{
	uint8_t r1;	
	uint8_t buff[6] = {0};
	uint16_t retry; 
	uint8_t i;
	
//	MX_SPI3_Init();
	SPI_setspeed(SPI_BAUDRATEPRESCALER_256);
	SD_CS(0);
	for(retry=0;retry<10;retry++){
			spi_readwrite(0xFF);
	}
	
	//SD卡进入IDLE状态
	do{
		r1 = SD_sendcmd(CMD0 ,0, 0x95);	
	}while(r1!=0x01);
	
	//查看SD卡的类型
	SD_TYPE=0;
	r1 = SD_sendcmd(CMD8, 0x1AA, 0x87);
	if(r1==0x01){
		for(i=0;i<4;i++)buff[i]=spi_readwrite(0xFF);	//Get trailing return value of R7 resp
		if(buff[2]==0X01&&buff[3]==0XAA)//卡是否支持2.7~3.6V
		{
			retry=0XFFFE;
			do
			{
				SD_sendcmd(CMD55,0,0X01);	//发送CMD55
				r1=SD_sendcmd(CMD41,0x40000000,0X01);//发送CMD41
			}while(r1&&retry--);
			if(retry&&SD_sendcmd(CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
			{
				for(i=0;i<4;i++)buff[i]=spi_readwrite(0XFF);//得到OCR值
				if(buff[0]&0x40){
					SD_TYPE=V2HC;
				}else {
					SD_TYPE=V2;
				}						
			}
		}else{
			SD_sendcmd(CMD55,0,0X01);			//发送CMD55
			r1=SD_sendcmd(CMD41,0,0X01);	//发送CMD41
			if(r1<=1)
			{		
				SD_TYPE=V1;
				retry=0XFFFE;
				do //等待退出IDLE模式
				{
					SD_sendcmd(CMD55,0,0X01);	//发送CMD55
					r1=SD_sendcmd(CMD41,0,0X01);//发送CMD41
				}while(r1&&retry--);
			}else//MMC卡不支持CMD55+CMD41识别
			{
				SD_TYPE=MMC;//MMC V3
				retry=0XFFFE;
				do //等待退出IDLE模式
				{											    
					r1=SD_sendcmd(CMD1,0,0X01);//发送CMD1
				}while(r1&&retry--);  
			}
			if(retry==0||SD_sendcmd(CMD16,512,0X01)!=0)SD_TYPE=ERR;//错误的卡
		}
	}
	SD_CS(0);
	SPI_setspeed(SPI_BAUDRATEPRESCALER_2);
	if(SD_TYPE)return 0;
	else return 1;
}


//读取指定长度数据
uint8_t SD_ReceiveData(uint8_t *data, uint16_t len)
{

   uint8_t r1;
   SD_CS(1);									   
   do
   { 
      r1 = spi_readwrite(0xFF);	
      HAL_Delay(100);
		}while(r1 != 0xFE);	
  while(len--)
  {
   *data = spi_readwrite(0xFF);
   data++;
  }
  spi_readwrite(0xFF);
  spi_readwrite(0xFF); 										  		
  return 0;
}
//向sd卡写入一个数据包的内容 512字节
uint8_t SD_SendBlock(uint8_t*buf,uint8_t cmd)
{	
	uint16_t t;	
uint8_t r1;	
	do{
		r1=spi_readwrite(0xFF);
	}while(r1!=0xFF);
	
	spi_readwrite(cmd);
	if(cmd!=0XFD)//不是结束指令
	{
		for(t=0;t<512;t++)spi_readwrite(buf[t]);//提高速度,减少函数传参时间
	    spi_readwrite(0xFF);//忽略crc
	    spi_readwrite(0xFF);
		t=spi_readwrite(0xFF);//接收响应
		if((t&0x1F)!=0x05)return 2;//响应错误									  					    
	}						 									  					    
    return 0;//写入成功
}

//获取CID信息
uint8_t SD_GETCID (uint8_t *cid_data)
{
		uint8_t r1;
	  r1=SD_sendcmd(CMD10,0,0x01); //读取CID寄存器
		if(r1==0x00){
			r1=SD_ReceiveData(cid_data,16);
		}
		SD_CS(0);
		if(r1)return 1;
		else return 0;
}
//获取CSD信息
uint8_t SD_GETCSD(uint8_t *csd_data){
		uint8_t r1;	 
    r1=SD_sendcmd(CMD9,0,0x01);//发CMD9命令,读CSD寄存器
    if(r1==0)
	{
    	r1=SD_ReceiveData(csd_data, 16);//接收16个字节的数据 
    }
	SD_CS(0);//取消片选
	if(r1)return 1;
	else return 0;
}
//获取SD卡的总扇区数
uint32_t SD_GetSectorCount(void)
{
    uint8_t csd[16];
    uint32_t Capacity;  
    uint8_t n;
		uint16_t csize;  					    
	//取CSD信息,如果期间出错,返回0
    if(SD_GETCSD(csd)!=0) return 0;	    
    //如果为SDHC卡,按照下面方式计算
    if((csd[0]&0xC0)==0x40)	 //V2.00的卡
    {	
		csize = csd[9] + ((uint16_t)csd[8] << 8) + 1;
		Capacity = (uint32_t)csize << 10;//得到扇区数	 		   
    }else//V1.XX的卡
    {	
		n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
		csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
		Capacity= (uint32_t)csize << (n - 9);//得到扇区数   
    }
    return Capacity;
}
int MSD0_GetCardInfo(PMSD_CARDINFO SD0_CardInfo)
{
  uint8_t r1;
  uint8_t CSD_Tab[16];
  uint8_t CID_Tab[16];

  /* Send CMD9, Read CSD */
  r1 = SD_sendcmd(CMD9, 0, 0xFF);
  if(r1 != 0x00)
  {
    return r1;
  }

  if(SD_ReceiveData(CSD_Tab, 16))
  {
	return 1;
  }

  /* Send CMD10, Read CID */
  r1 = SD_sendcmd(CMD10, 0, 0xFF);
  if(r1 != 0x00)
  {
    return r1;
  }

  if(SD_ReceiveData(CID_Tab, 16))
  {
	return 2;
  }  

  /* Byte 0 */
  SD0_CardInfo->CSD.CSDStruct = (CSD_Tab[0] & 0xC0) >> 6;
  SD0_CardInfo->CSD.SysSpecVersion = (CSD_Tab[0] & 0x3C) >> 2;
  SD0_CardInfo->CSD.Reserved1 = CSD_Tab[0] & 0x03;
  /* Byte 1 */
  SD0_CardInfo->CSD.TAAC = CSD_Tab[1] ;
  /* Byte 2 */
  SD0_CardInfo->CSD.NSAC = CSD_Tab[2];
  /* Byte 3 */
  SD0_CardInfo->CSD.MaxBusClkFrec = CSD_Tab[3];
  /* Byte 4 */
  SD0_CardInfo->CSD.CardComdClasses = CSD_Tab[4] << 4;
  /* Byte 5 */
  SD0_CardInfo->CSD.CardComdClasses |= (CSD_Tab[5] & 0xF0) >> 4;
  SD0_CardInfo->CSD.RdBlockLen = CSD_Tab[5] & 0x0F;
  /* Byte 6 */
  SD0_CardInfo->CSD.PartBlockRead = (CSD_Tab[6] & 0x80) >> 7;
  SD0_CardInfo->CSD.WrBlockMisalign = (CSD_Tab[6] & 0x40) >> 6;
  SD0_CardInfo->CSD.RdBlockMisalign = (CSD_Tab[6] & 0x20) >> 5;
  SD0_CardInfo->CSD.DSRImpl = (CSD_Tab[6] & 0x10) >> 4;
  SD0_CardInfo->CSD.Reserved2 = 0; /* Reserved */
  SD0_CardInfo->CSD.DeviceSize = (CSD_Tab[6] & 0x03) << 10;
  /* Byte 7 */
  SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[7]) << 2;
  /* Byte 8 */
  SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[8] & 0xC0) >> 6;
  SD0_CardInfo->CSD.MaxRdCurrentVDDMin = (CSD_Tab[8] & 0x38) >> 3;
  SD0_CardInfo->CSD.MaxRdCurrentVDDMax = (CSD_Tab[8] & 0x07);
  /* Byte 9 */
  SD0_CardInfo->CSD.MaxWrCurrentVDDMin = (CSD_Tab[9] & 0xE0) >> 5;
  SD0_CardInfo->CSD.MaxWrCurrentVDDMax = (CSD_Tab[9] & 0x1C) >> 2;
  SD0_CardInfo->CSD.DeviceSizeMul = (CSD_Tab[9] & 0x03) << 1;
  /* Byte 10 */
  SD0_CardInfo->CSD.DeviceSizeMul |= (CSD_Tab[10] & 0x80) >> 7;
  SD0_CardInfo->CSD.EraseGrSize = (CSD_Tab[10] & 0x7C) >> 2;
  SD0_CardInfo->CSD.EraseGrMul = (CSD_Tab[10] & 0x03) << 3;
  /* Byte 11 */
  SD0_CardInfo->CSD.EraseGrMul |= (CSD_Tab[11] & 0xE0) >> 5;
  SD0_CardInfo->CSD.WrProtectGrSize = (CSD_Tab[11] & 0x1F);
  /* Byte 12 */
  SD0_CardInfo->CSD.WrProtectGrEnable = (CSD_Tab[12] & 0x80) >> 7;
  SD0_CardInfo->CSD.ManDeflECC = (CSD_Tab[12] & 0x60) >> 5;
  SD0_CardInfo->CSD.WrSpeedFact = (CSD_Tab[12] & 0x1C) >> 2;
  SD0_CardInfo->CSD.MaxWrBlockLen = (CSD_Tab[12] & 0x03) << 2;
  /* Byte 13 */
  SD0_CardInfo->CSD.MaxWrBlockLen |= (CSD_Tab[13] & 0xc0) >> 6;
  SD0_CardInfo->CSD.WriteBlockPaPartial = (CSD_Tab[13] & 0x20) >> 5;
  SD0_CardInfo->CSD.Reserved3 = 0;
  SD0_CardInfo->CSD.ContentProtectAppli = (CSD_Tab[13] & 0x01);
  /* Byte 14 */
  SD0_CardInfo->CSD.FileFormatGrouop = (CSD_Tab[14] & 0x80) >> 7;
  SD0_CardInfo->CSD.CopyFlag = (CSD_Tab[14] & 0x40) >> 6;
  SD0_CardInfo->CSD.PermWrProtect = (CSD_Tab[14] & 0x20) >> 5;
  SD0_CardInfo->CSD.TempWrProtect = (CSD_Tab[14] & 0x10) >> 4;
  SD0_CardInfo->CSD.FileFormat = (CSD_Tab[14] & 0x0C) >> 2;
  SD0_CardInfo->CSD.ECC = (CSD_Tab[14] & 0x03);
  /* Byte 15 */
  SD0_CardInfo->CSD.CSD_CRC = (CSD_Tab[15] & 0xFE) >> 1;
  SD0_CardInfo->CSD.Reserved4 = 1;

  if(SD0_CardInfo->CardType == V2HC)
  {
	 /* Byte 7 */
	 SD0_CardInfo->CSD.DeviceSize = (uint16_t)(CSD_Tab[8]) *256;
	 /* Byte 8 */
	 SD0_CardInfo->CSD.DeviceSize += CSD_Tab[9] ;
  }

  SD0_CardInfo->Capacity = SD0_CardInfo->CSD.DeviceSize * MSD_BLOCKSIZE * 1024;
  SD0_CardInfo->BlockSize = MSD_BLOCKSIZE;

  /* Byte 0 */
  SD0_CardInfo->CID.ManufacturerID = CID_Tab[0];
  /* Byte 1 */
  SD0_CardInfo->CID.OEM_AppliID = CID_Tab[1] << 8;
  /* Byte 2 */
  SD0_CardInfo->CID.OEM_AppliID |= CID_Tab[2];
  /* Byte 3 */
  SD0_CardInfo->CID.ProdName1 = CID_Tab[3] << 24;
  /* Byte 4 */
  SD0_CardInfo->CID.ProdName1 |= CID_Tab[4] << 16;
  /* Byte 5 */
  SD0_CardInfo->CID.ProdName1 |= CID_Tab[5] << 8;
  /* Byte 6 */
  SD0_CardInfo->CID.ProdName1 |= CID_Tab[6];
  /* Byte 7 */
  SD0_CardInfo->CID.ProdName2 = CID_Tab[7];
  /* Byte 8 */
  SD0_CardInfo->CID.ProdRev = CID_Tab[8];
  /* Byte 9 */
  SD0_CardInfo->CID.ProdSN = CID_Tab[9] << 24;
  /* Byte 10 */
  SD0_CardInfo->CID.ProdSN |= CID_Tab[10] << 16;
  /* Byte 11 */
  SD0_CardInfo->CID.ProdSN |= CID_Tab[11] << 8;
  /* Byte 12 */
  SD0_CardInfo->CID.ProdSN |= CID_Tab[12];
  /* Byte 13 */
  SD0_CardInfo->CID.Reserved1 |= (CID_Tab[13] & 0xF0) >> 4;
  /* Byte 14 */
  SD0_CardInfo->CID.ManufactDate = (CID_Tab[13] & 0x0F) << 8;
  /* Byte 15 */
  SD0_CardInfo->CID.ManufactDate |= CID_Tab[14];
  /* Byte 16 */
  SD0_CardInfo->CID.CID_CRC = (CID_Tab[15] & 0xFE) >> 1;
  SD0_CardInfo->CID.Reserved2 = 1;

  return 0;  
}


//写SD卡
//buf:数据缓存区
//sector:起始扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
uint8_t SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
	uint8_t r1;
	if(SD_TYPE!=V2HC)sector *= 512;//转换为字节地址
	if(cnt==1)
	{
		r1=SD_sendcmd(CMD24,sector,0X01);//读命令
		if(r1==0)//指令发送成功
		{
			r1=SD_SendBlock(buf,0xFE);//写512个字节	   
		}
	}else
	{
		if(SD_TYPE!=MMC)
		{
			SD_sendcmd(CMD55,0,0X01);	
			SD_sendcmd(CMD23,cnt,0X01);//发送指令	
		}
 		r1=SD_sendcmd(CMD25,sector,0X01);//连续读命令
		if(r1==0)
		{
			do
			{
				r1=SD_SendBlock(buf,0xFC);//接收512个字节	 
				buf+=512;  
			}while(--cnt && r1==0);
			r1=SD_SendBlock(0,0xFD);//接收512个字节 
		}
	}   
	SD_CS(0);//取消片选
	return r1;//
}	
//读SD卡
//buf:数据缓存区
//sector:扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
	uint8_t r1;
	if(SD_TYPE!=V2HC)sector <<= 9;//转换为字节地址
	if(cnt==1)
	{
		r1=SD_sendcmd(CMD17,sector,0X01);//读命令
		if(r1==0)//指令发送成功
		{
			r1=SD_ReceiveData(buf,512);//接收512个字节	   
		}
	}else
	{
		r1=SD_sendcmd(CMD18,sector,0X01);//连续读命令
		do
		{
			r1=SD_ReceiveData(buf,512);//接收512个字节	 
			buf+=512;  
		}while(--cnt && r1==0); 	
		SD_sendcmd(CMD12,0,0X01);	//发送停止命令
	}   
	SD_CS(0);//取消片选
	return r1;//
}




uint8_t spi_readwrite(uint8_t Txdata){
	uint8_t Rxdata;	
	HAL_SPI_TransmitReceive(&hspi1,&Txdata,&Rxdata,1,100);
	return Rxdata;
}
//SPI1波特率设置
void SPI_setspeed(uint8_t speed){
	hspi1.Init.BaudRatePrescaler = speed;
}


void Get_SDCard_Capacity(void)
{
	FRESULT result;
	FATFS *fs;
	DWORD fre_clust,AvailableSize,UsedSize;  
	uint16_t TotalSpace;
	uint8_t res;
	printf("SD卡初始化!\r\n");
	res = SD_init();		//SD卡初始化
	if(res == 1)
	{
		printf("SD卡初始化失败! \r\n");		
	}
	else
	{
		printf("SD卡初始化成功! \r\n");		
	}
	
	/* 挂载 */
	result=f_mount(&USERFatFS,USERPath,1);		//挂载
	if(result == FR_NO_FILESYSTEM)		//没有文件系统,格式化
	{
//		test_sd =1;				//用于测试格式化
		printf("没有文件系统! \r\n");		
		result = f_mkfs(USERPath, 0, 0);		//格式化sd卡
		if(result == FR_OK)
		{
			printf("格式化成功! \r\n");		
			result = f_mount(NULL,USERPath,1); 		//格式化后先取消挂载
			result = f_mount(&USERFatFS,USERPath,1);			//重新挂载	
			if(result == FR_OK)
			{
				printf("SD卡已经成功挂载,可以进进行文件写入测试!\r\n");
			}	
		}
		else
		{
			printf("格式化失败! \r\n");		
		}
	}
	else if(res == FR_OK)
	{
		printf("挂载成功! \r\n");		
	}
	else
	{
		printf("挂载失败! \r\n");
	}

	result = f_getfree(USERPath, &fre_clust, &fs);  /* 根目录 */
	if ( result == FR_OK ) 
	{
		TotalSpace=(uint16_t)(((fs->n_fatent - 2) * fs->csize ) / 2 /1024);
		AvailableSize=(uint16_t)((fre_clust * fs->csize) / 2 /1024);
		UsedSize=TotalSpace-AvailableSize;              
		/* Print free space in unit of MB (assuming 512 bytes/sector) */
		printf("\r\n%d MB total drive space.\r\n""%ld MB available.\r\n""%ld MB  used.\r\n",TotalSpace, AvailableSize,UsedSize);
	}
	else 
	{
		printf("Get SDCard Capacity Failed (%d)\r\n", result);
	}		
}

void WritetoSD(char filename[], BYTE write_buff[], uint8_t bufSize)
{
    FIL file;
    uint8_t res = 0;
    UINT Bw;

    res = f_open(&file, filename, FA_OPEN_ALWAYS | FA_WRITE);
    if ((res & FR_DENIED) == FR_DENIED) {
        printf("卡存储已满,写入失败!\r\n");
    }

    f_lseek(&file, f_size(&file));  // 确保写词写入不会覆盖之前的数据
    if (res == FR_OK) {
        printf("打开成功/创建文件成功! \r\n");
        res = f_write(&file, write_buff, bufSize, &Bw);  // 写数据到SD卡
        if (res == FR_OK) {
            printf("文件写入成功! \r\n");
        } else {
            printf("文件写入失败! \r\n");
        }
    } else {
        printf("打开文件失败!\r\n");
    }

    f_close(&file);              // 关闭文件
}

void UnmountSD(void)
{
	f_mount(NULL, USERPath, 1);  // 取消挂载
}
///END//

修改FATFS\Target\user_diskio.c文件
添加头文件

//添加头文件
#include "diskio.h"		/* Declarations of disk functions */
#include "sd.h"
#include "cmsis_os.h"

修改USER_initialize函数

DSTATUS USER_initialize (
	BYTE pdrv           /* Physical drive nmuber to identify the drive */
)
{
  /* USER CODE BEGIN INIT */
    uint8_t res;
    res = SD_init();  // SD_Initialize()
    if (res)  // STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句,可能导致SPI读写异常
    {
        SPI_setspeed(SPI_BAUDRATEPRESCALER_256);
        spi_readwrite(0xff);  // 提供额外的8个时钟
        SPI_setspeed(SPI_BAUDRATEPRESCALER_2);
    }
    if (res)
        return STA_NOINIT;
    else
        return RES_OK;  // 初始化成功
  /* USER CODE END INIT */
}

修改USER_status函数

DSTATUS USER_status (
	BYTE pdrv       /* Physical drive number to identify the drive */
)
{
  /* USER CODE BEGIN STATUS */
    switch (pdrv) {
    case 0:
        return RES_OK;
    case 1:
        return RES_OK;
    case 2:
        return RES_OK;
    default:
        return STA_NOINIT;
    }
  /* USER CODE END STATUS */
}

修改USER_read函数

DRESULT USER_read (
	BYTE pdrv,      /* Physical drive nmuber to identify the drive */
	BYTE *buff,     /* Data buffer to store read data */
	DWORD sector,   /* Sector address in LBA */
	UINT count      /* Number of sectors to read */
)
{
  /* USER CODE BEGIN READ */
    uint8_t res;
    if (!count) {
        return RES_PARERR; /* count不能等于0,否则返回参数错误 */
    }
    switch (pdrv) {
    case 0:
        res = SD_ReadDisk(buff, sector, count);
        if (res == 0) {
            return RES_OK;
        } else {
            return RES_ERROR;
        }
    default:
        return RES_ERROR;
    }
  /* USER CODE END READ */
}

修改USER_write函数

DRESULT USER_write (
	BYTE pdrv,          /* Physical drive nmuber to identify the drive */
	const BYTE *buff,   /* Data to be written */
	DWORD sector,       /* Sector address in LBA */
	UINT count          /* Number of sectors to write */
)
{
  /* USER CODE BEGIN WRITE */
  /* USER CODE HERE */
    uint8_t res;
    if (!count) {
        return RES_PARERR; /* count不能等于0,否则返回参数错误 */
    }
    switch (pdrv) {
    case 0:
        res = SD_WriteDisk((uint8_t *)buff, sector, count);
        if (res == 0) {
            return RES_OK;
        } else {
            return RES_ERROR;
        }
    default:
        return RES_ERROR;
    }
  /* USER CODE END WRITE */
}

修改USER_ioctl函数

DRESULT USER_ioctl (
	BYTE pdrv,      /* Physical drive nmuber (0..) */
	BYTE cmd,       /* Control code */
	void *buff      /* Buffer to send/receive control data */
)
{
  /* USER CODE BEGIN IOCTL */
    DRESULT res;
    switch (cmd) {
    case CTRL_SYNC:
        SD_CS(1);
        do {
            osDelay(20);
        } while (spi_readwrite(0xFF) != 0xFF);
        res = RES_OK;
        SD_CS(0);
        break;
    case GET_SECTOR_SIZE:
        *(WORD *)buff = 512;
        res = RES_OK;
        break;
    case GET_BLOCK_SIZE:
        *(WORD *)buff = 8;
        res = RES_OK;
        break;
    case GET_SECTOR_COUNT:
        *(DWORD *)buff = SD_GetSectorCount();
        res = RES_OK;
        break;
    default:
        res = RES_PARERR;
        break;
    }
    return res;
  /* USER CODE END IOCTL */
}

在mian.c中添加支持printf的函数

/* USER CODE BEGIN 0 */

int __io_putchar(int ch)
{
    /* Implementation of __io_putchar */
	/* e.g. write a character to the UART1 and Loop until the end of transmission */
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFFFFFF);

    return ch;
}
int __io_getchar(void)
{
  /* Implementation of __io_getchar */
    char rxChar;

    // This loops in case of HAL timeout, but if an ok or error occurs, we continue
    while (HAL_UART_Receive(&huart1, (uint8_t *)&rxChar, 1, 0xFFFFFFFF) == HAL_TIMEOUT);

    return rxChar;
}
/* USER CODE END 0 */

下面编写freertos中的实现功能
修改freertos.c文件
添加头文件

/* USER CODE BEGIN Includes */
/* 添加头文件 */
#include <stdio.h>
#include "sd.h"

/* USER CODE END Includes */

修改默认任务

void StartDefaultTask(void const * argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
  char SD_FileName[] = "hello.txt";
  uint8_t WriteBuffer[] = "01 write buff to sd \r\n";
  uint8_t write_cnt =0;	//写SD卡次数
  Get_SDCard_Capacity();	//得到使用内存并选择格式化
  osDelay(1000);
  WritetoSD(SD_FileName, WriteBuffer,sizeof(WriteBuffer));
  osDelay(1000);
  WritetoSD(SD_FileName, WriteBuffer,sizeof(WriteBuffer));
  UnmountSD();
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END StartDefaultTask */
}

运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2326607.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FreeCAD傻瓜教程-装配体Assembly的详细使用过程

源起&#xff1a; 看了官方的教程说明&#xff0c;感觉太过简单&#xff0c;好多细节没有体现&#xff0c;且该部分的翻译还没有。这里是做个记录&#xff0c;对使用过程中的细节进行图文说明&#xff0c;以方便真正的新手能够快速应用&#xff0c;制作出自己的零件&#xff0c…

数字电子技术基础(三十七)——利用Multisim软件实现16线-4线编码器和4线-16线译码器

1 利用Multisim软件来实现16线-4线编码器 在之前的博客中完成了利用Multisim软件实现8线-3线优先编码器&#xff0c;现在使用Multisim软件来实现16线-4线编码器&#xff0c;其原理图如下所示&#xff1a; 使用字发生器来实现16线-4线编码器&#xff0c;器件选择&#xff1a; …

02_MySQL安装及配置

文章目录 一、下载二、安装及配置2.1、选择安装类型2.2、检查需要的依赖2.3、安装2.4、配置2.4.1、配置类型和网络2.4.2、配置账户和角色2.4.3、配置Windows服务2.4.4、让配置生效 2.5、验证是否安装成功 三、卸载3.1、运行MySQL安装工具3.2、卸载及清理3.3、卸载之后的检查工作…

Windows11,微软软件(VSCODE/EDG)错误登录,0x80190001错误

修改网络设置 运行以下命令&#xff0c;打开网络共享中心 Start-Process "control.exe" -ArgumentList "/name Microsoft.NetworkAndSharingCenter" 点击左下角的 选项 TLS 1.1 1.2 1.3 这三个选项 1.0 不建议启用&#xff0c;不安全 1.1 可以不用启用…

力扣刷题-热题100题-第29题(c++、python)

19. 删除链表的倒数第 N 个结点 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/remove-nth-node-from-end-of-list/description/?envTypestudy-plan-v2&envIdtop-100-liked 计算链表长度 对于链表&#xff0c;难的就是不知道有多少元素&#xff…

阻止上传可执行程序

点击工具中的文件服务器资源管理器 、然后点击文件屏蔽管理中的文件屏蔽&#xff0c;然后导入目标文件选择要限制的属性即可

DirectX修复工具免费版下载安装教程(附安装包)

文章目录 前言一、DirectX修复工具免费版介绍二、DirectX修复工具免费版安装教程1. 下载安装包2. 解压文件3. 以管理员身份运行4. 开始检测与修复5. 查看修复详情 前言 本教程主要介绍的是DirectX修复工具免费版下载安装教程&#xff0c;帮您轻松解决 DirectX 相关问题。 一、…

UE5学习笔记 FPS游戏制作33 游戏保存

文章目录 核心思想创建数据对象创建UIUI参数和方法打开UI存档文件的位置可以保存的数据类型 核心思想 UE自己有保存游戏的功能&#xff0c;核心节点&#xff0c;类似于json操作&#xff0c;需要一个数据类的对象来进行保存和读取 创建存档 加载存档 保存存档 创建数据对象…

TypeScript vs. JavaScript:技术对比与核心差异解析

引言 在 Web 前端开发领域&#xff0c;JavaScript&#xff08;JS&#xff09;长期占据主导地位&#xff0c;但随着项目复杂度的提升&#xff0c;开发者逐渐面临维护性差、协作困难等问题。TypeScript&#xff08;TS&#xff09;作为 JavaScript 的超集&#xff0c;通过静态类型…

《C奥林匹斯宝典:基础篇 - 重载函数》

一、重载函数 &#xff08;一&#xff09;函数模板重载 详细解析&#xff1a;函数模板提供了一种通用的函数定义方式&#xff0c;可针对不同类型进行实例化。当存在函数模板与普通函数、其他函数模板同名时&#xff0c;就构成了函数模板重载。编译器在编译阶段&#xff0c;依…

【408--考研复习笔记】计算机网络----知识点速览

目录 一、计算机网络体系结构 1.计算机网络的定义与功能&#xff1a; 2.网络体系结构相关概念&#xff1a; 3.OSI 七层模型与 TCP/IP 模型&#xff1a; 4.通信方式与交换技术&#xff1a; 电路交换 报文交换 分组交换 5.端到端通信和点到点通信&#xff1a; 6.计算机…

TiDB 可观测性解读(二)丨算子执行信息性能诊断案例分享

导读 可观测性已经成为分布式系统成功运行的关键组成部分。如何借助多样、全面的数据&#xff0c;让架构师更简单、高效地定位问题、分析问题、解决问题&#xff0c;已经成为业内的一个技术焦点。本系列文章将深入解读 TiDB 的关键参数&#xff0c;帮助大家更好地观测系统的状…

15:00开始面试,15:08就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

蓝桥杯准备(前缀和差分)

import java.util.Scanner; public class qianzhuihe {public static void main(String[] args) {int N,M;Scanner scnew Scanner(System.in);Nsc.nextInt();Msc.nextInt();int []treesnew int[N1];//设为N1的意义&#xff0c;防止越界int []prefixSumnew int[N1];for(int i1;i…

Minimind 训练一个自己专属语言模型

发现了一个宝藏项目&#xff0c; 宣传是完全从0开始&#xff0c;仅用3块钱成本 2小时&#xff01;即可训练出仅为25.8M的超小语言模型MiniMind&#xff0c;最小版本体积是 GPT-3 的 17000&#xff0c;做到最普通的个人GPU也可快速训练 https://github.com/jingyaogong/minimi…

STM32八股【5】----- TIM定时器

1. TIM定时器分类 STM32 的定时器主要分为以下几类&#xff1a; 高级定时器&#xff08;Advanced TIM&#xff0c;TIM1/TIM8&#xff09; 具备 PWM 生成、死区控制、互补输出等高级功能&#xff0c;适用于电机控制和功率转换应用。通用定时器&#xff08;General-purpose TIM…

厘米级定位赋能智造升级:品铂科技UWB技术驱动工厂全流程自动化与效能跃升”

在智能制造中的核心价值体现在‌高精度定位、流程优化、安全管理‌等多个维度&#xff0c;具体应用如下&#xff1a; 一、‌核心技术与定位能力‌ ‌厘米级高精度定位‌ UWB技术通过‌纳秒级窄脉冲信号‌&#xff08;带宽超500MHz&#xff09;实现高时间分辨率&#xff0c;结合…

C++刷题(四):vector

&#x1f4dd;前言说明&#xff1a; 本专栏主要记录本人的基础算法学习以及刷题记录&#xff0c;使用语言为C。 每道题我会给出LeetCode上的题号&#xff08;如果有题号&#xff09;&#xff0c;题目&#xff0c;以及最后通过的代码。没有题号的题目大多来自牛客网。对于题目的…

【虚拟仪器技术】Labview虚拟仪器技术应用教程习题参考答案[13页]

目录 第1章 第2章 第3章 第4章 第&#xff15;章 第&#xff16;章 第&#xff17;章 第8章 第1章 1. 简述虚拟仪器概念。 参考答案&#xff1a;虚拟仪器是借助于强大的计算机软件和硬件环境的支持&#xff0c;建立虚拟的测试仪器面板&#xff0c;完成仪器的控制、数…