利用SPI协议读写SD卡

利用SPI协议模拟SDIO读写SD卡


一、HAL库配置

配置USART1

利用SPI协议读写SD卡
配置SPI1

利用SPI协议读写SD卡
配置FATFS

利用SPI协议读写SD卡
GPIO配置

利用SPI协议读写SD卡
时钟配置

利用SPI协议读写SD卡

Serial Wire配置

利用SPI协议读写SD卡
生成工程

利用SPI协议读写SD卡

二、移植并添加工程

(一)移植驱动文件

利用SPI协议读写SD卡
SDdriver.cSDdriver.h文件添加到工程中
利用SPI协议读写SD卡
复制到与工程同一路径后后,在工程里面添加SDdriver.c文件,并添加包含该文件的路径。

(二)修改user_diskio.c文件

①添加包含的头文件

#include "diskio.h"		/* Declarations of disk functions */
#include "SDdriver.h"

利用SPI协议读写SD卡
接下来分别对USER_initialize、USER_status、USER_read、USE_WRITE、USE_IOCTL函数进行修改,将其内容替换为下面对应函数相关内容

②修改USER_initialize函数

	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; //初始化成功

利用SPI协议读写SD卡
③修改USER_status函数

 switch (pdrv)
	{
		case 0 :
			return RES_OK;
		case 1 :
			return RES_OK;
		case 2 :
			return RES_OK;
		default:
			return STA_NOINIT;
	}

④修改USER_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;
	}

⑤修改USE_WRITE函数

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;
	}

⑥修改USE_IOCTL函数

  DRESULT res;
	 switch(cmd)
	    {
		    case CTRL_SYNC:
						SD_CS(1);
						do{
							HAL_Delay(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;

(三)main文件配置

包含头文件

#include "SDdriver.h"

在main函数前添加变量与函数定义

uint16_t uart_value[3];
uint8_t aRxBuffer1;	//uart rx buff 

void WritetoSD(BYTE write_buff[],uint8_t bufSize);
char SD_FileName[] = "test.txt";
uint8_t WriteBuffer[] = "山有木兮\r\n";

//uint8_t test_sd =0;	//用于测试格式化
uint8_t write_cnt =0;	//写SD卡次数

void WritetoSD(BYTE write_buff[],uint8_t bufSize)
{
	FATFS fs;
	FIL file;
	uint8_t res=0;
	UINT Bw;	
	
	res = SD_init();		//SD卡初始化
	
	if(res == 1)
	{
		printf("SD卡初始化失败! \r\n");		
	}
	
	res=f_mount(&fs,"0:",1);		//挂载
	
//	if(test_sd == 0)		//用于测试格式化
	if(res == FR_NO_FILESYSTEM)		//没有文件系统,格式化
	{
//		test_sd =1;				//用于测试格式化
		printf("没有文件系统! \r\n");		
		res = f_mkfs("", 0, 0);		//格式化sd卡
		if(res == FR_OK)
		{
			//printf("格式化成功! \r\n");		
			res = f_mount(NULL,"0:",1); 		//格式化后先取消挂载
			res = f_mount(&fs,"0:",1);			//重新挂载	
			if(res == FR_OK)
			{
				//printf("SD卡已经成功挂载,可以进进行文件写入测试!\r\n");
			}	
		}
		else
		{
			printf("格式化失败! \r\n");		
		}
	}
	else if(res == FR_OK)
	{
		//printf("挂载成功! \r\n");		
	}
	else
	{
		printf("挂载失败! \r\n");
	}	
	
	res = f_open(&file,SD_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);						//关闭文件		
	f_mount(NULL,"0:",1);		 //取消挂载
	
}


void Get_SDCard_Capacity(void)
{
	FRESULT result;
	FATFS FS;
	FATFS *fs;
	DWORD fre_clust,AvailableSize,UsedSize;  
	uint16_t TotalSpace;
	uint8_t res;
	
	res = SD_init();		//SD卡初始化
	if(res == 1)
	{
		printf("SD卡初始化失败! \r\n");		
	}
	else
	{
		printf("SD卡初始化成功! \r\n");		
	}
	
	/* 挂载 */
	res=f_mount(&FS,"0:",1);		//挂载
	if (res != FR_OK)
	{
		printf("FileSystem Mounted Failed (%d)\r\n", result);
	}

	res = f_getfree("0:", &fre_clust, &fs);  /* 根目录 */
	if ( res == 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""%d MB available.\r\n""%d MB  used.\r\n",TotalSpace, AvailableSize,UsedSize);
	}
	else 
	{
		printf("Get SDCard Capacity Failed (%d)\r\n", result);
	}		
} 

将main函数替代为以下内容

int main(void)
{
  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */
  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI1_Init();
  MX_FATFS_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	HAL_UART_Receive_IT(&huart1,&aRxBuffer1,1); 	//enable uart	

	printf(" mian \r\n");
	Get_SDCard_Capacity();	//得到使用内存并选择格式化
  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		WritetoSD(WriteBuffer,sizeof(WriteBuffer));		

		printf("writing\r\n");
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

(四)其他配置及接线

在usart.c中重定向printf()函数

int fputc(int ch, FILE *f)    
{
    HAL_UART_Transmit(&huart1, (unsigned char *)&ch, 1, 0xFFFF);    //修改【4】,串口重定向
    return ch;
}

在usart.h声明并包含头文件stdio.h
打开Micro LIB
编译完成即可
本实验是基于源码自己重新新建了一个工程,如果怕麻烦,可直接下载源码

源码: 提取码27r2

三、实例演示

接线方式

SD卡 stm32系统板
CS PA4
SCK PA5
MISO PA6
MOSI PA7
VCC 5V
GND GND

上位机显示

初始化成功,正在写入数据

利用SPI协议读写SD卡

写数据成功,查看已写好的数据

利用SPI协议读写SD卡


总结

驱动SD卡的时候,最好将供电电压提高到5V,提高更强的驱动能力。我之前默认使用3.3v由于驱动能力不够,所以一直初始化失败,卡顿了好久。所以基于硬件,稍微有一点有错就会导致整体失败,要特别注意细节。

STM32用cube配置FATFS模式下SPI读写SD卡

上一篇:Javaweb-在idea中配置Tomcat


下一篇:SCRUM的局限性