STM32CubeMX学习--SD_FATFS

编译环境

STM32CubeMX………: 6.2.1
MDK……………………: 5.27.1.0
Firmware Package……: V1.9.0
硬件 ……………………: 安富莱V7开发板

STM32CubeMX配置

时钟根据需要设置即可,除SD分频系数,不影响其他功能,另外开启串口1外设。
STM32CubeMX_SD配置
注意分频系数,与使用SD卡速度有关。
STM32CubeMX_FATFS配置
在Platform Setting下设置SD卡插入检测引脚。

代码修改

在fatfs.c下新增加以下代码

#include "string.h"
#include "stdio.h"
DIR DirInf;
FILINFO FileInf;

static void DispMenu(void);
static void ViewRootDir(void);
static void CreateNewFile(void);
static void ReadFileData(void);
static void CreateDir(void);
static void DeleteDirFile(void);
static void WriteFileTest(void);
/* FatFs API的返回值 */
static const char * FR_Table[]= 
{
	"FR_OK:成功",				                             /* (0) Succeeded */
	"FR_DISK_ERR:底层硬件错误",			                 /* (1) A hard error occurred in the low level disk I/O layer */
	"FR_INT_ERR:断言失败",				                     /* (2) Assertion failed */
	"FR_NOT_READY:物理驱动没有工作",			             /* (3) The physical drive cannot work */
	"FR_NO_FILE:文件不存在",				                 /* (4) Could not find the file */
	"FR_NO_PATH:路径不存在",				                 /* (5) Could not find the path */
	"FR_INVALID_NAME:无效文件名",		                     /* (6) The path name format is invalid */
	"FR_DENIED:由于禁止访问或者目录已满访问被拒绝",         /* (7) Access denied due to prohibited access or directory full */
	"FR_EXIST:文件已经存在",			                     /* (8) Access denied due to prohibited access */
	"FR_INVALID_OBJECT:文件或者目录对象无效",		         /* (9) The file/directory object is invalid */
	"FR_WRITE_PROTECTED:物理驱动被写保护",		             /* (10) The physical drive is write protected */
	"FR_INVALID_DRIVE:逻辑驱动号无效",		                 /* (11) The logical drive number is invalid */
	"FR_NOT_ENABLED:卷中无工作区",			                 /* (12) The volume has no work area */
	"FR_NO_FILESYSTEM:没有有效的FAT卷",		             /* (13) There is no valid FAT volume */
	"FR_MKFS_ABORTED:由于参数错误f_mkfs()被终止",	         /* (14) The f_mkfs() aborted due to any parameter error */
	"FR_TIMEOUT:在规定的时间内无法获得访问卷的许可",		 /* (15) Could not get a grant to access the volume within defined period */
	"FR_LOCKED:由于文件共享策略操作被拒绝",				 /* (16) The operation is rejected according to the file sharing policy */
	"FR_NOT_ENOUGH_CORE:无法分配长文件名工作区",		     /* (17) LFN working buffer could not be allocated */
	"FR_TOO_MANY_OPEN_FILES:当前打开的文件数大于_FS_SHARE", /* (18) Number of open files > _FS_SHARE */
	"FR_INVALID_PARAMETER:参数无效"	                     /* (19) Given parameter is invalid */
};
void fatfs_test(uint8_t cmd)
{
			printf("\r\n");
			switch (cmd)
			{
				case '1':
					printf("【1 - ViewRootDir】\r\n");
					ViewRootDir();		/* 显示SD卡根目录下的文件名 */
					break;

				case '2':
					printf("【2 - CreateNewFile】\r\n");
					CreateNewFile();	/* 创建一个新文件,写入一个字符串 */
					break;

				case '3':
					printf("【3 - ReadFileData】\r\n");
					ReadFileData();		/* 读取根目录下armfly.txt的内容 */
					break;

				case '4':
					printf("【4 - CreateDir】\r\n");
					CreateDir();		/* 创建目录 */
					break;

				case '5':
					printf("【5 - DeleteDirFile】\r\n");
					DeleteDirFile();	/* 删除目录和文件 */
					break;

				case '6':
					printf("【6 - TestSpeed】\r\n");
					WriteFileTest();	/* 速度测试 */
					break;
				
				default:
					DispMenu();
					break;
			}
	
}
/*
*********************************************************************************************************
*	函 数 名: DispMenu
*	功能说明: 显示操作提示菜单
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
static void DispMenu(void)
{
	printf("\r\n------------------------------------------------\r\n");
	printf("请选择操作命令,打开SD卡模拟U盘操作期间不支持再调用命令1-6:\r\n");
	printf("1 - 显示根目录下的文件列表\r\n");
	printf("2 - 创建一个新文件armfly.txt\r\n");
	printf("3 - 读armfly.txt文件的内容\r\n");
	printf("4 - 创建目录\r\n");
	printf("5 - 删除文件和目录\r\n");
	printf("6 - 读写文件速度测试\r\n");
	printf("a - 打开SD卡模拟U盘\r\n");
	printf("b - 关闭SD卡模拟U盘\r\n");
}

/*
*********************************************************************************************************
*	函 数 名: ViewRootDir
*	功能说明: 显示SD卡根目录下的文件名
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
extern SD_HandleTypeDef hsd1;
static void ViewRootDir(void)
{
	FRESULT result;
	uint32_t cnt = 0;
	FILINFO fno;
	

	
 	/* 挂载文件系统 */
	result = f_mount(&SDFatFS, SDPath, 0);	/* Mount a logical drive */
	if (result != FR_OK)
	{
		printf("挂载文件系统失败 (%s)\r\n", FR_Table[result]);
	}

	/* 打开根文件夹 */
	result = f_opendir(&DirInf, SDPath); /* 如果不带参数,则从当前目录开始 */
	if (result != FR_OK)
	{
		printf("打开根目录失败  (%s)\r\n", FR_Table[result]);
		return;
	}

	printf("属性        |  文件大小 | 短文件名 | 长文件名\r\n");
	for (cnt = 0; ;cnt++)
	{
		result = f_readdir(&DirInf, &FileInf); 		/* 读取目录项,索引会自动下移 */
		if (result != FR_OK || FileInf.fname[0] == 0)
		{
			break;
		}

		if (FileInf.fname[0] == '.')
		{
			continue;
		}

		/* 判断是文件还是子目录 */
		if (FileInf.fattrib & AM_DIR)
		{
			printf("(0x%02d)目录  ", FileInf.fattrib);
		}
		else
		{
			printf("(0x%02d)文件  ", FileInf.fattrib);
		}

		f_stat(FileInf.fname, &fno);
		
		/* 打印文件大小, 最大4G */
		printf(" %10d", (int)fno.fsize);


		printf("  %s\r\n", (char *)FileInf.fname);	/* 长文件名 */
	}
 
    /* 打印卡速度信息 */
    if(hsd1.SdCard.CardSpeed == CARD_NORMAL_SPEED)
    {
        printf("Normal Speed Card <12.5MB/S, MAX Clock < 25MHz, Spec Version 1.01\r\n");           
    }
    else if (hsd1.SdCard.CardSpeed == CARD_HIGH_SPEED)
    {
        printf("High Speed Card <25MB/s, MAX Clock < 50MHz, Spec Version 2.00\r\n");            
    }
    else if (hsd1.SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED)
    {
        printf("UHS-I SD Card <50MB/S for SDR50, DDR50 Cards, MAX Clock < 50MHz OR 100MHz\r\n");
        printf("UHS-I SD Card <104MB/S for SDR104, MAX Clock < 108MHz, Spec version 3.01\r\n");   
    }    

    
	/* 卸载文件系统 */
	 f_mount(NULL, SDPath, 0);
}
/*
*********************************************************************************************************
*	函 数 名: CreateNewFile
*	功能说明: 在SD卡创建一个新文件,文件内容填写“www.armfly.com”
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
char FsWriteBuf[1024] = {"FatFS Write Demo \r\n www.armfly.com \r\n"};
static void CreateNewFile(void)
{
	FRESULT result;
	uint32_t bw;
	char path[32];


 	/* 挂载文件系统 */
	result = f_mount(&SDFatFS, SDPath, 0);			/* Mount a logical drive */
	if (result != FR_OK)
	{
		printf("挂载文件系统失败 (%s)\r\n", FR_Table[result]);
	}

	/* 打开文件 */
	sprintf(path, "%sarmfly.txt", SDPath);
	result = f_open(&SDFile, path, FA_CREATE_ALWAYS | FA_WRITE);
	if (result == FR_OK)
	{
		printf("armfly.txt 文件打开成功\r\n");
	}
	else
	{
		printf("armfly.txt 文件打开失败  (%s)\r\n", FR_Table[result]);
	}

	/* 写一串数据 */
	result = f_write(&SDFile, FsWriteBuf, strlen(FsWriteBuf), &bw);
	if (result == FR_OK)
	{
		printf("armfly.txt 文件写入成功\r\n");
	}
	else
	{
		printf("armfly.txt 文件写入失败  (%s)\r\n", FR_Table[result]);
	}

	/* 关闭文件*/
	f_close(&SDFile);

	/* 卸载文件系统 */
	f_mount(NULL, SDPath, 0);
}
/*
*********************************************************************************************************
*	函 数 名: ReadFileData
*	功能说明: 读取文件armfly.txt前128个字符,并打印到串口
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
char FsReadBuf[1024];
static void ReadFileData(void)
{
	FRESULT result;
	uint32_t bw;
	char path[64];

	
 	/* 挂载文件系统 */
	result = f_mount(&SDFatFS, SDPath, 0);			/* Mount a logical drive */
	if (result != FR_OK)
	{
		printf("挂载文件系统失败 (%s)\r\n", FR_Table[result]);
	}

	/* 打开文件 */
	sprintf(path, "%sarmfly.txt", SDPath);
	result = f_open(&SDFile, path, FA_OPEN_EXISTING | FA_READ);
	if (result !=  FR_OK)
	{
		printf("Don't Find File : armfly.txt\r\n");
		return;
	}

	/* 读取文件 */
	result = f_read(&SDFile, FsReadBuf, sizeof(FsReadBuf), &bw);
	if (bw > 0)
	{
		FsReadBuf[bw] = 0;
		printf("\r\narmfly.txt 文件内容 : \r\n%s\r\n", FsReadBuf);
	}
	else
	{
		printf("\r\narmfly.txt 文件内容 : \r\n");
	}

	/* 关闭文件*/
	f_close(&SDFile);

	/* 卸载文件系统 */
	f_mount(NULL, SDPath, 0);
}

/*
*********************************************************************************************************
*	函 数 名: CreateDir
*	功能说明: 在SD卡根目录创建Dir1和Dir2目录,在Dir1目录下创建子目录Dir1_1
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
static void CreateDir(void)
{
	FRESULT result;
	char path[64]; 

	
 	/* 挂载文件系统 */
	result = f_mount(&SDFatFS, SDPath, 0);			/* Mount a logical drive */
	if (result != FR_OK)
	{
		printf("挂载文件系统失败 (%s)\r\n", FR_Table[result]);
	}

	/* 创建目录/Dir1 */
	sprintf(path, "%sDir1", SDPath);
	result = f_mkdir(path);
	if (result == FR_OK)
	{
		printf("f_mkdir Dir1 Ok\r\n");
	}
	else if (result == FR_EXIST)
	{
		printf("Dir1 目录已经存在(%d)\r\n", result);
	}
	else
	{
		printf("f_mkdir Dir1 失败 (%s)\r\n", FR_Table[result]);
		return;
		
	}

	/* 创建目录/Dir2 */
	sprintf(path, "%sDir2", SDPath);
	result = f_mkdir(path);
	if (result == FR_OK)
	{
		printf("f_mkdir Dir2 Ok\r\n");
	}
	else if (result == FR_EXIST)
	{
		printf("Dir2 目录已经存在(%d)\r\n", result);
	}
	else
	{
		printf("f_mkdir Dir2 失败 (%s)\r\n", FR_Table[result]);
		return;
	}

	/* 创建子目录 /Dir1/Dir1_1	   注意:创建子目录Dir1_1时,必须先创建好Dir1 */
	sprintf(path, "%sDir1/Dir1_1", SDPath);
	result = f_mkdir(path); /* */
	if (result == FR_OK)
	{
		printf("f_mkdir Dir1_1 成功\r\n");
	}
	else if (result == FR_EXIST)
	{
		printf("Dir1_1 目录已经存在 (%d)\r\n", result);
	}
	else
	{
		printf("f_mkdir Dir1_1 失败 (%s)\r\n", FR_Table[result]);
		return;
	}

	/* 卸载文件系统 */
	f_mount(NULL, SDPath, 0);
}

/*
*********************************************************************************************************
*	函 数 名: DeleteDirFile
*	功能说明: 删除SD卡根目录下的 armfly.txt 文件和 Dir1,Dir2 目录
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
static void DeleteDirFile(void)
{
	FRESULT result;
	uint8_t i;
	char path[64]; 
	
 	/* 挂载文件系统 */
	result = f_mount(&SDFatFS, SDPath, 0);			/* Mount a logical drive */
	if (result != FR_OK)
	{
		printf("挂载文件系统失败 (%s)\r\n", FR_Table[result]);
	}
	
	/* 删除目录/Dir1 【因为还存在目录非空(存在子目录),所以这次删除会失败】*/
	sprintf(path, "%sDir1", SDPath);
	result = f_unlink(path);
	if (result == FR_OK)
	{
		printf("删除目录Dir1成功\r\n");
	}
	else if (result == FR_NO_FILE)
	{
		printf("没有发现文件或目录 :%s\r\n", "/Dir1");
	}
	else
	{
		printf("删除Dir1失败(错误代码 = %d) 文件只读或目录非空\r\n", result);
	}

	/* 先删除目录/Dir1/Dir1_1 */
	sprintf(path, "%sDir1/Dir1_1", SDPath);
	result = f_unlink(path);
	if (result == FR_OK)
	{
		printf("删除子目录/Dir1/Dir1_1成功\r\n");
	}
	else if ((result == FR_NO_FILE) || (result == FR_NO_PATH))
	{
		printf("没有发现文件或目录 :%s\r\n", "/Dir1/Dir1_1");
	}
	else
	{
		printf("删除子目录/Dir1/Dir1_1失败(错误代码 = %d) 文件只读或目录非空\r\n", result);
	}

	/* 先删除目录/Dir1 */
	sprintf(path, "%sDir1", SDPath);
	result = f_unlink(path);
	if (result == FR_OK)
	{
		printf("删除目录Dir1成功\r\n");
	}
	else if (result == FR_NO_FILE)
	{
		printf("没有发现文件或目录 :%s\r\n", "/Dir1");
	}
	else
	{
		printf("删除Dir1失败(错误代码 = %d) 文件只读或目录非空\r\n", result);
	}

	/* 删除目录/Dir2 */
	sprintf(path, "%sDir2", SDPath);
	result = f_unlink(path);
	if (result == FR_OK)
	{
		printf("删除目录 Dir2 成功\r\n");
	}
	else if (result == FR_NO_FILE)
	{
		printf("没有发现文件或目录 :%s\r\n", "/Dir2");
	}
	else
	{
		printf("删除Dir2 失败(错误代码 = %d) 文件只读或目录非空\r\n", result);
	}

	/* 删除文件 armfly.txt */
	sprintf(path, "%sarmfly.txt", SDPath);
	result = f_unlink(path);
	if (result == FR_OK)
	{
		printf("删除文件 armfly.txt 成功\r\n");
	}
	else if (result == FR_NO_FILE)
	{
		printf("没有发现文件或目录 :%s\r\n", "armfly.txt");
	}
	else
	{
		printf("删除armfly.txt失败(错误代码 = %d) 文件只读或目录非空\r\n", result);
	}

	/* 删除文件 speed1.txt */
	for (i = 0; i < 20; i++)
	{
		sprintf(path, "%sSpeed%02d.txt", SDPath, i);/* 每写1次,序号递增 */	
		result = f_unlink(path);
		if (result == FR_OK)
		{
			printf("删除文件%s成功\r\n", path);
		}
		else if (result == FR_NO_FILE)
		{
			printf("没有发现文件:%s\r\n", path);
		}
		else
		{
			printf("删除%s文件失败(错误代码 = %d) 文件只读或目录非空\r\n", path, result);
		}
	}

	/* 卸载文件系统 */
	f_mount(NULL, SDPath, 0);
}

/*
*********************************************************************************************************
*	函 数 名: WriteFileTest
*	功能说明: 测试文件读写速度
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
#define TEST_FILE_LEN			(2*1024*1024)	/* 用于测试的文件长度 */
#define BUF_SIZE				(4*1024)		/* 每次读写SD卡的最大数据长度 */
uint8_t g_TestBuf[BUF_SIZE];
static void WriteFileTest(void)
{
	FRESULT result;
	char path[64]; 
	uint32_t bw;
	uint32_t i,k;
	uint32_t runtime1,runtime2,timelen;
	uint8_t err = 0;
	static uint8_t s_ucTestSn = 0;

	
	for (i = 0; i < sizeof(g_TestBuf); i++)
	{
		g_TestBuf[i] = (i / 512) + '0';
	}

  	/* 挂载文件系统 */
	result = f_mount(&SDFatFS, SDPath, 0);			/* Mount a logical drive */
	if (result != FR_OK)
	{
		printf("挂载文件系统失败 (%s)\r\n", FR_Table[result]);
	}

	/* 打开文件 */
	sprintf(path, "%sSpeed%02d.txt", SDPath, s_ucTestSn++); /* 每写1次,序号递增 */	
	result = f_open(&SDFile, path, FA_CREATE_ALWAYS | FA_WRITE);

	/* 写一串数据 */
	printf("开始写文件%s %dKB ...\r\n", path, TEST_FILE_LEN / 1024);
	
	runtime1 = HAL_GetTick();	/* 读取系统运行时间 */
	for (i = 0; i < TEST_FILE_LEN / BUF_SIZE; i++)
	{
		result = f_write(&SDFile, g_TestBuf, sizeof(g_TestBuf), &bw);
		if (result == FR_OK)
		{
			if (((i + 1) % 8) == 0)
			{
				printf(".");
			}
		}
		else
		{
			err = 1;
			printf("%s文件写失败\r\n", path);
			break;
		}
	}
	runtime2 = HAL_GetTick();	/* 读取系统运行时间 */
	
	if (err == 0)
	{
		timelen = (runtime2 - runtime1);
		printf("\r\n  写耗时 : %dms   平均写速度 : %dB/S (%dKB/S)\r\n",
			timelen,
			(TEST_FILE_LEN * 1000) / timelen,
			((TEST_FILE_LEN / 1024) * 1000) / timelen);
	}

	f_close(&SDFile);		/* 关闭文件*/


	/* 开始读文件测试 */
	result = f_open(&SDFile, path, FA_OPEN_EXISTING | FA_READ);
	if (result !=  FR_OK)
	{
		printf("没有找到文件: %s\r\n", path);
		return;
	}

	printf("开始读文件 %dKB ...\r\n", TEST_FILE_LEN / 1024);
	
	runtime1 = HAL_GetTick();	/* 读取系统运行时间 */
	for (i = 0; i < TEST_FILE_LEN / BUF_SIZE; i++)
	{
		result = f_read(&SDFile, g_TestBuf, sizeof(g_TestBuf), &bw);
		if (result == FR_OK)
		{
			if (((i + 1) % 8) == 0)
			{
				printf(".");
			}

			/* 比较写入的数据是否正确,此语句会导致读卡速度结果降低到 3.5MBytes/S */
			for (k = 0; k < sizeof(g_TestBuf); k++)
			{
				if (g_TestBuf[k] != (k / 512) + '0')
				{
				  	err = 1;
					printf("Speed1.txt 文件读成功,但是数据出错\r\n");
					break;
				}
			}
			if (err == 1)
			{
				break;
			}
		}
		else
		{
			err = 1;
			printf("Speed1.txt 文件读失败\r\n");
			break;
		}
	}

	runtime2 = HAL_GetTick();	/* 读取系统运行时间 */
	
	if (err == 0)
	{
		timelen = (runtime2 - runtime1);
		printf("\r\n  读耗时 : %dms   平均读速度 : %dB/S (%dKB/S)\r\n", timelen,
			(TEST_FILE_LEN * 1000) / timelen, ((TEST_FILE_LEN / 1024) * 1000) / timelen);
	}

	/* 关闭文件*/
	f_close(&SDFile);

	/* 卸载文件系统 */
	f_mount(NULL, SDPath, 0);
}

工程链接

https://download.csdn.net/download/qq992035949/19212701

### 回答1: 这个错误指出VT系统没有选择一张有效的网络适配器。VT系统需要网络适配器来连接互联网或者局域网。如果没有正确选择网络适配器,VT系统将无法连接到网络,导致无法运行一些应用程序。 要解决这个问题,需要在VT系统设置中选择一张有效的网络适配器。首先需要确保计算机已经正确安装了网络适配器驱动程序。然后打开VT系统,进入“设置”菜单,在“网络”选项中选择一张有效的网络适配器并保存更改。如果还不能解决问题,需要检查计算机的网络连接,并确保网络适配器的设置正确。 总之,要解决这个错误,需要在VT系统中正确选择一张有效的网络适配器,并且确保计算机与网络连接正常。这样才能够保证VT系统正常工作并连接到互联网或局域网。 ### 回答2: 当您在VirtualBox中尝试使用Canoe模拟器时,可能会遇到错误消息“there is no valid network adapter selected for vt system”。这个错误通常意味着您没有正确设置网络适配器。 首先,确保您已经为虚拟机正确设置了网络适配器。您可以在虚拟机设置中找到网络选项,并启用网络适配器。您可以选择使用不同种类的网络适配器,例如虚拟桥接、NAT或Host-only模式。确保您选择的网络适配器与Canoe模拟器兼容。 其次,您需要确保在虚拟机设置中启用了Intel VT-x虚拟化选项。Canoe模拟器需要Intel VT-x虚拟化技术来运行。您可以在计算机的BIOS中启用Intel VT-x虚拟化选项。 最后,如果您仍然遇到这个问题,尝试更新VirtualBox到最新版本,以确保它与Canoe模拟器兼容。 总的来说,确保正确设置网络适配器,并启用Intel VT-x虚拟化选项,将有助于解决Canoe模拟器报错的问题。 ### 回答3: 这个错误提示意味着在使用Canoe时,VT系统没有选择有效的网络适配器。VT系统是一个虚拟化工具,它允许用户在同一台计算机上运行多个操作系统。Canoe是一种基于VT系统的仿真软件,用于开发和测试控制系统。 这个错误通常是由于计算机上没有可用的网络适配器或者选择的网络适配器与VT系统不兼容所导致的。要解决这个问题,首先需要确定计算机上是否有可用的网络适配器,如果没有,需要安装或启用一个。然后需要确保选择的网络适配器与VT系统兼容,可以参考VT系统的文档或者厂商的支持论坛来获取更多信息。 另外,还有一些可能引起此错误的其他因素,如网络设置不正确或系统配置错误等。可以尝试重新配置网络设置或检查系统配置,以便解决这个问题。 总之,当出现此错误提示时,需要先检查计算机上网络适配器的可用性和兼容性,并根据情况进行相应的修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值