掌握SD卡协议原理,用STM32F103 完成对SD卡的数据读取(FAT文件模式)。

一、前言

SD卡的读写驱动程序是运用FATFS的基础,学了FATFS就可以在SD卡上创建文件夹及文件了。

1、SD卡

SD存储卡(Secure Digital Memory Card)是一种基于半导体快闪存储器的新一代高速存储设备。SD存储卡的技术是从MMC卡(MultiMedia Card格式上发展而来,在兼容SD存储卡基础上发展了SDIO(SD Input/ Output)卡,此兼容性包括机械,电子,电力,信号和软件,通常将SD、SDIO卡俗称SD存储卡

一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5 个部分。
存储单元是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;
电源检测单元保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;
卡及接口控制单元控制SD卡的运行状态,它包括有8个寄存器;
接口驱动器控制 SD 卡引脚的输入输出。

驱动模式
SD卡有两种驱动模式:SPI模式与SDIO模式。它们所使用的接口信号是不同的。在SPI模式下,只会用到SD卡的4根信号线,即CS、DI、SCLK与DO(分别是SD卡的片选、数据输入、时钟与数据输出)。

传输模式
SD卡共支持三种传输模式:SPI模式(独立序列输入和序列输出),1位SD模式(独立指令和数据通道,独有的传输格式),4位SD模式(使用额外的针脚以及某些重新设置的针脚。支持四位宽的并行传输)。

本篇文章主要使用SPI方式进行验证,关于SPI介绍可以参考小编之前的博客:https://blog.csdn.net/qq_54496810/article/details/121434661

2、FATFS

FATFS 是一个通用的文件系统(FAT/exFAT)模块,用于在小型嵌入式系统中实现FAT文件系统。 FatFs 组件的编写遵循ANSI C(C89),完全分离于磁盘 I/O 层,因此不依赖于硬件平台。它可以嵌入到资源有限的微控制器中,如 8051, PIC, AVR, ARM, Z80, RX等等,不需要做任何修改。

FATFS提供了以下文件访问函数:
f_open - 打开/创建文件
f_close - 关闭打开的文件
f_read - 从文件中读取数据
f_write - 将数据写入文件
f_lseek - 移动读/写指针,扩展大小
f_truncate - 截断文件大小
f_sync - 刷新缓存的数据
f_forward - 将数据转发到流
f_expand - 为文件分配连续块
f_gets - 读取字符串
f_putc - 写一个字符
f_puts - 编写字符串
f_printf - 编写格式化字符串
f_tell - 获取当前读/写指针
f_eof - 测试文件结尾
f_size - 获取尺寸
f_error - 测试错误

关于更多FATFS的详情可参考:http://elm-chan.org/fsw/ff/00index_e.html

二、工程分析

这里由于小编能力有限,故在已有的工程上进行的修改验证。
具体工程下载链接:https://pan.baidu.com/s/1L5E4BG8cqvpvtDxOdUUXoQ
提取码:e63q

1、代码分析

下载完成后,解压打开工程

1.1.SD卡写入文件名及写入内容

char SD_FileName[] = "hello.txt";
uint8_t WriteBuffer[] = "小小星亮晶晶 631907030123\n";

1.2.main函数

int main(void)
{
  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  MX_SPI1_Init();
  MX_FATFS_Init();
  MX_USART1_UART_Init();
	
	HAL_UART_Receive_IT(&huart1,&aRxBuffer1,1); 	//enable uart	

	printf(" mian \r\n");

	Get_SDCard_Capacity();	//得到使用内存并选择格式化
  while (1)
  {
		WritetoSD(WriteBuffer,sizeof(WriteBuffer));	//写入SD卡	
		
		HAL_Delay(500);
		WriteBuffer[0] = WriteBuffer[0] +10;
		WriteBuffer[1] = WriteBuffer[1] +10; 
		write_cnt ++;
		while(write_cnt > 10)
		{	
			printf(" while \r\n");
			HAL_Delay(500);
		}		
  }
}

1.3.SD初始化并得到使用内存函数

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

1.4.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");		
	}
	else
	{
		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);		 //取消挂载
	
}

2、连线

接线如下:

SD卡STM32
CSPA4
SCKPA5
MISOPA6
MISIPA7
VCC5V
GUNGUN

在这里插入图片描述
注意:

  • 确保SD格式化成FAT文件模式
    在这里插入图片描述

  • STM32的供电和SD卡模块的供电最好是5V,不然可能带不动

  • 连接使用的杜邦线尽可能的短,且连线最好紧一点,稍微的松动都可能写入失败(别问!问就是有经验)

  • 可能要等一会才初始化成功,别急(太久就算了,放弃吧!)

3、编译工程并烧录

点击编译按钮进行编译
在这里插入图片描述
编译无报错,进行.hex文件烧录
在这里插入图片描述

4、验证结果

在这里插入图片描述
可以发现串口收到数据,包括总内存、可使用内存以及已使用内存,SD卡初始化成功、挂载成功、文件写入成功等显示,若写入超过10次后,输出变为while(这是自己设置的,可以在while语句中里进行更改,没有什么影响)
在这里插入图片描述
使用读卡器打开SD卡可以看到里面生成了一个HELLO.TXT文件
在这里插入图片描述
打开该TXT文件发现出现了乱码:第一行正确,后面写入的开始出现问题
在这里插入图片描述

5、代码修改

由于写入出现错误,这里小编对main.c文件的主函数里面的while语句进行了修改

  while (1)
  {		
		WritetoSD(WriteBuffer,sizeof(WriteBuffer));		
		
		HAL_Delay(500);
		//WriteBuffer[0] = WriteBuffer[0] +10;
		//WriteBuffer[1] = WriteBuffer[1] +10;
		write_cnt ++;
		
		while(write_cnt > 10)
		{	
			printf(" while \r\n");
			HAL_Delay(500);
		}		
  }

WriteBuffer[0] = WriteBuffer[0] +10;WriteBuffer[1] = WriteBuffer[1] +10;这两句代码注释掉即可

6、正确结果

再次编译、烧录,打开写入SD卡的文件可以看到写入正确
在这里插入图片描述

三、小结

本次实验其实不难,只是容易出错,遇到了烧录写数据时由于接线不稳导致写入SD卡不成功,工程原代码有错等问题,虽然经过多次尝试才得以完成实验,但在过程中得到了宝贵的经验,可能在分析问题的方面还存在着不足之处,敬请读者指正。

四、参考链接

1.工程创建详情
2.https://blog.csdn.net/m0_58414679/article/details/122036435

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐