SD卡挂载FatFs文件系统

news2024/11/15 9:50:16

一、简介

实验目的:SD卡挂载FATFS文件系统,并生成.txt文件

MCU:ST32F103ZET6

SD卡:16G;SPI读写模式;

引脚定义:VCC:5V
                  GND:GND
                  MISO:PA6
                  MOSI:PA7
                  SCK:PA5
                  CS:PA4

实物图:

 二、工程源码

前面的文章有SD卡和SPI的源码,所以就展示需要改动的diskio.c和ffconf.h文件以及main.c文件:

diskio.c

/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs     (C)ChaN, 2019        */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be        */
/* attached to the FatFs via a glue function rather than modifying it.   */
/* This is an example of glue functions to attach various exsisting      */
/* storage control modules to the FatFs module with a defined API.       */
/*-----------------------------------------------------------------------*/
 
#include "ff.h"			/* Obtains integer types */
#include "diskio.h"		/* Declarations of disk functions */
#include "SD.h"	
#include "usart.h"	
#include "SPI.h"	
#include "delay.h"	
 

#define SD_CARD      0 							//SD卡设备,0就是SD卡的设备编号

 
/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/
 
 
//查询当前驱动器状态
DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
		return RES_OK;

}
 
 
 
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/
//磁盘初始化
DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber to identify the drive */
)
{
		uint8_t res=0;
	
		res=SD_Init();																			//SD卡初始化
//		if(res)																						//STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句,可能导致SPI读写异常(当然,如果你的SPI和SD卡配置没有问题,这个可有可无)
//		{
//				SPI1_SetSpeed(SPI_BaudRatePrescaler_256);			
//				SPI1_ReadWriteByte(0xff);											//提供额外的8个时钟
//				SPI1_SetSpeed(SPI_BaudRatePrescaler_2);
//		}
		
		if(res)return  STA_NOINIT;
		else return RES_OK; 																//初始化成功
		
}
 
 
 
/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/
//从存储设备的扇区读取设备
DRESULT disk_read (
	BYTE pdrv,		/* Physical drive nmuber to identify the drive */
	BYTE *buff,		/* Data buffer to store read data */
	LBA_t sector,	/* Start sector in LBA */
	UINT count		/* Number of sectors to read */
)
{
	uint8_t res;
//	int result;
	if (!count)
	{
		return RES_PARERR;																//count不能等于0,否则返回参数错误
	}
	switch (pdrv) {
		
	case SD_CARD :
			 res=SD_ReadDisk(buff,sector,count);	 					//读SD卡
			 if(res == 0)
			 {
					return RES_OK;
			 }else{
					return RES_ERROR;
			 }
	default:
					return RES_ERROR;
	}
	 
}
 
 
 
/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/
//向存储设备的扇区写入数据
#if FF_FS_READONLY == 0
 
DRESULT disk_write (
	BYTE pdrv,			/* Physical drive nmuber to identify the drive */
	const BYTE *buff,	/* Data to be written */
	LBA_t sector,		/* Start sector in LBA */
	UINT count			/* Number of sectors to write */
)
{
	uint8_t res;

	if (!count)
	{
		return RES_PARERR;																			//count不能等于0,否则返回参数错误	
	}
	switch (pdrv) {
		
	case SD_CARD :
			 res=SD_WriteDisk((uint8_t*)buff,sector,count);				//写SD卡
			 if(res == 0)
			 {
					return RES_OK;
		   }else{
					return RES_ERROR;
			 } 
	}	
	if(res == 0x00)return RES_OK;	 
  else return RES_ERROR;	
}
 
#endif
 
 
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/
//控制设备特定功能和通用的读/写以外的其他功能
DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
	DRESULT res;

	if(pdrv==SD_CARD)																												//SD卡
	{
			switch (cmd) 
			{
				case SD_CARD :
						SD_CS(1);																										 // 选择 SD 卡		
						do{
							delay_ms(20);
						}while(SPI1_ReadWriteByte(0xFF)!=0xFF);											//等待SD卡准备好
						res=RES_OK;
						SD_CS(0);																										//取消选择
		        break;	 
		    case GET_SECTOR_SIZE:
						*(DWORD*)buff = 512; 																				//设置扇区大小512
		        res = RES_OK;
		        break;	 
		    case GET_BLOCK_SIZE:
						*(WORD*)buff = 8;																						//设置扇区大小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 defined function to give a current time to fatfs module      */
//31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */                                                                                                                                                                                                                                          
//15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */                                                                                                                                                                                                                                                
DWORD get_fattime (void)
{				 
	return 0;
}	
//这个函数是用来获取时间的,没有实现其功能,但是如果没有的话,就会报get_fattime函数缺失的错误,需要的话可以实现其功能。



ffconf.h

/*---------------------------------------------------------------------------/
/  Configurations of FatFs Module
/---------------------------------------------------------------------------*/

#define FFCONF_DEF	80286	/* Revision ID */									//版本号

/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/

#define FF_FS_READONLY	0	
/* 
这行代码定义了一个宏FF_FS_READONLY,其值为0,用于配置FatFs文件系统模块的读写模式。具体解释如下:

FF_FS_READONLY:
值为0:启用读写模式。系统将支持读取和写入操作。
值为1:启用只读模式。在只读模式下,系统将禁用写入相关的API函数,如f_write()、f_sync()、f_unlink()、f_mkdir()、f_chmod()、f_rename()、f_truncate()和f_getfree()。这意味着用户只能进行读取操作,无法修改或删除文件和目录。. */



#define FF_FS_MINIMIZE	0
/*
这行代码定义了一个宏FF_FS_MINIMIZE,其值为0,用于设置FatFs文件系统模块的功能最小化级别。具体解释如下:

FF_FS_MINIMIZE:
值为0:启用所有基本功能。所有API函数均可用。
值为1: 移除一些函数,如f_stat()、f_getfree()、f_unlink()、f_mkdir()、f_truncate()和f_rename()。
值为2: 在级别1的基础上,进一步移除f_opendir()、f_readdir()和f_closedir()函数。
值为3: 在级别2的基础上,移除f_lseek()函数。
选择不同的最小化级别可以减少代码体积或节省资源,具体取决于应用的需求*/




#define FF_USE_FIND		0
/* 
这段代码定义了一个宏 FF_USE_FIND,用于控制 FatFs 文件系统模块中目录读取函数的启用情况。具体来说,宏 FF_USE_FIND 的作用如下:

FF_USE_FIND:
值为 0:禁用过滤目录读取函数 f_findfirst() 和 f_findnext()。这意味着不会启用这些函数,目录读取将不支持基于过滤条件的查找。
值为 1:启用 f_findfirst() 和 f_findnext() 函数,允许使用这些函数进行目录中的文件查找。这提供了基本的文件筛选功能,可以根据文件名模式进行查找。
值为 2:在启用基本过滤功能的基础上,还支持 altname[] 的匹配。即,f_findfirst() 和 f_findnext() 函数不仅可以根据文件名模式进行查找,还可以根据备用名称进行匹配,提供更灵活的文件查找选项。
根据需求设置 FF_USE_FIND 的值,可以调整 FatFs 的文件查找功能的复杂程度。 */



#define FF_USE_MKFS		1	
/* 
FF_USE_MKFS 控制是否启用 f_mkfs() 函数:

0 表示禁用 f_mkfs(),即不允许格式化文件系统。
1 表示启用 f_mkfs(),允许通过该函数创建和格式化文件系统。 */



#define FF_USE_FASTSEEK	1
/* 
FF_USE_FASTSEEK 选项控制 FatFs 库中的快速寻址功能是否启用。具体来说:

#define FF_USE_FASTSEEK 0:禁用快速寻址功能。在这种设置下,文件的寻址(跳转到文件的特定位置)将不会使用额外的数据结构来加速。这可能会导致文件内位置跳转的速度较慢,但能节省内存。

#define FF_USE_FASTSEEK 1:启用快速寻址功能。开启后,FatFs 会使用额外的数据结构来加速文件的寻址操作,从而提高性能,但需要额外的内存开销。

选择启用或禁用此功能取决于你对性能的需求以及系统的内存限制。 */



#define FF_USE_EXPAND	0
/* 
这段代码定义了 FF_USE_EXPAND,用于控制是否启用 f_expand 函数:

FF_USE_EXPAND = 0:禁用 f_expand 函数。
FF_USE_EXPAND = 1:启用 f_expand 函数。 */



#define FF_USE_CHMOD	
/* 
这段代码定义了 FF_USE_CHMOD,用于控制是否启用文件属性操作函数:

FF_USE_CHMOD = 0:禁用 f_chmod() 和 f_utime() 函数,这些函数用于修改文件属性和修改时间。
FF_USE_CHMOD = 1:启用 f_chmod() 和 f_utime() 函数。
启用 FF_USE_CHMOD 时,FF_FS_READONLY 也需要设置为 0,即文件系统必须不是只读的。*/



#define FF_USE_LABEL	0
/* 
这段代码定义了 FF_USE_LABEL,用于控制是否启用卷标操作函数:

FF_USE_LABEL = 0:禁用卷标函数 f_getlabel() 和 f_setlabel()。
FF_USE_LABEL = 1:启用卷标函数 f_getlabel() 和 f_setlabel()。
这些函数用于获取和设置卷标。 */



#define FF_USE_FORWARD	0
/* 
这段代码定义了 FF_USE_FORWARD,用于控制是否启用 f_forward() 函数:

FF_USE_FORWARD = 0:禁用 f_forward() 函数。
FF_USE_FORWARD = 1:启用 f_forward() 函数。
f_forward() 是用于在文件中定位的功能,通常用于在文件中前进到指定位置。 */



#define FF_USE_STRFUNC	0
#define FF_PRINT_LLI		1
#define FF_PRINT_FLOAT	1
#define FF_STRF_ENCODE	3
/* 
这段代码配置了字符串函数的选项:

FF_USE_STRFUNC:控制是否启用字符串函数(如 f_gets(), f_putc(), f_puts(), f_printf()):
0:禁用所有字符串函数。
1:启用字符串函数,不进行 LF-CRLF 转换。
2:启用字符串函数,并进行 LF-CRLF 转换。
FF_PRINT_LLI:是否支持 long long 类型作为 f_printf() 的参数。

1:启用对 long long 的支持。
FF_PRINT_FLOAT:是否支持浮点数作为 f_printf() 的参数。

1:支持浮点数。
2:支持更精确的浮点数。
FF_STRF_ENCODE:指定文件中字符编码的假设。

0:ANSI/OEM 编码。
1:UTF-16LE 编码。
2:UTF-16BE 编码。
3:UTF-8 编码。
*/



/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/

#define FF_CODE_PAGE	936									//如果需要支持简体中文,需要把ffconf.h中的_CODE_PAGE的宏改成936
/* This option specifies the OEM code page to be used on the target system.
/  Incorrect code page setting can cause a file open failure.
/
/   437 - U.S.
/   720 - Arabic
/   737 - Greek
/   771 - KBL
/   775 - Baltic
/   850 - Latin 1
/   852 - Latin 2
/   855 - Cyrillic
/   857 - Turkish
/   860 - Portuguese
/   861 - Icelandic
/   862 - Hebrew
/   863 - Canadian French
/   864 - Arabic
/   865 - Nordic
/   866 - Russian
/   869 - Greek 2
/   932 - Japanese (DBCS)
/   936 - Simplified Chinese (DBCS)
/   949 - Korean (DBCS)
/   950 - Traditional Chinese (DBCS)
/     0 - Include all code pages above and configured by f_setcp()
*/


#define FF_USE_LFN		0
#define FF_MAX_LFN		255
/*
这段代码配置了长文件名(LFN,Long File Name)的支持选项:

FF_USE_LFN:控制是否启用长文件名(LFN)支持。

0:禁用长文件名(LFN)。此时,FF_MAX_LFN 没有作用。
1:启用长文件名(LFN),使用静态工作缓冲区(存储在 BSS 区域)。此配置下的 LFNs 不支持多线程。
2:启用长文件名(LFN),使用动态工作缓冲区(存储在栈上)。请注意栈溢出问题。
3:启用长文件名(LFN),使用动态工作缓冲区(存储在堆上)。需要添加内存管理函数(ff_memalloc() 和 ff_memfree())到项目中。
FF_MAX_LFN:定义了工作缓冲区的大小,单位为 UTF-16 代码单元。范围从 12 到 255,推荐设置为 255 以完全支持长文件名规范。这个值决定了用于存储长文件名的缓冲区大小。

注意:
启用长文件名支持需要将 ffunicode.c 添加到项目中。
启用 LFN 时,工作缓冲区的大小为 (FF_MAX_LFN + 1) * 2 字节,并且当启用 exFAT 时,还需要额外的 (FF_MAX_LFN + 44) / 15 * 32 字节。
使用栈或堆作为工作缓冲区时,需要考虑相关的资源管理和内存分配策略。 */



#define FF_LFN_UNICODE	2
/* 
FF_LFN_UNICODE 配置了在启用长文件名(LFN)时的字符编码方式:

0:使用 ANSI/OEM 编码,当前代码页(TCHAR 为 char)。
1:使用 UTF-16 编码(TCHAR 为 WCHAR)。
2:使用 UTF-8 编码(TCHAR 为 char)。
3:使用 UTF-32 编码(TCHAR 为 DWORD)。
这会影响 API 和字符串 I/O 函数的行为,具体取决于所选择的编码方式*/



#define FF_LFN_BUF		255
#define FF_SFN_BUF		12
/* 
这些宏定义了 FILINFO 结构中用于存储文件名的缓冲区大小。这些设置在读取目录项时用来存储文件名:

FF_LFN_BUF:定义了用于存储长文件名(LFN)的缓冲区大小。这个大小需要足够大,以确保可以读取和存储文件系统中可能出现的最长文件名。在启用长文件名(LFN)时,设置为 255 表示能够支持较长的文件名。
FF_SFN_BUF:定义了用于存储短文件名(SFN)的缓冲区大小。短文件名通常遵循 8.3 格式,即最大长度为 8 个字符的文件名加上一个 3 字符的扩展名。设置为 12 提供了足够的空间来存储这些短文件名。
注意事项:

当长文件名(LFN)未启用时,这些选项将不会生效。
文件名的最大长度取决于字符编码方式,具体取决于 FF_LFN_UNICODE 的设置。
根据 FF_LFN_UNICODE 的设置,文件名的实际长度可能会有所不同。例如,如果使用 UTF-8 编码,可能需要处理变长字符。. */



#define FF_FS_RPATH		0
/* 
FF_FS_RPATH 配置了对相对路径的支持:

0:禁用相对路径,并移除相关函数。
1:启用相对路径,提供 f_chdir() 和 f_chdrive() 函数。
2:在 1 的基础上,还提供 f_getcwd() 函数,用于获取当前工作目录。
选择合适的设置可以根据需求决定是否需要对路径进行相对处理。*/



/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/

#define FF_VOLUMES		1							
/* 
FF_VOLUMES 用于指定系统支持的逻辑驱动器数量:

当前设置为 1,意味着系统只支持 1 个逻辑驱动器。
可以将其设置为 1 到 10 之间的值,以支持更多的逻辑驱动器。
这影响文件系统如何挂载和管理驱动器。增加此值可以允许更多的驱动器在系统中使用。 */



#define FF_STR_VOLUME_ID	0
#define FF_VOLUME_STRS		"RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
/* 
在你的代码中,_STR_VOLUME_ID 选项用于定义如何表示卷标(volume ID)。

_STR_VOLUME_ID = 0: 使用数字 ID(例如,"0"、"1"、"2")来标识卷。
_STR_VOLUME_ID = 1: 使用字符串 ID,字符串由 _VOLUME_STRS 定义。例如,你可以用 "RAM"、"NAND"、"CF" 等作为卷标。
确保 _VOLUME_STRS 中的字符串数量与系统中逻辑卷的数量一致。*/



#define FF_MULTI_PARTITION	0
/* 
FF_MULTI_PARTITION 配置了对物理驱动器上多个分区的支持:

0:禁用对多个分区的支持。在这种情况下,每个逻辑驱动器号绑定到相同的物理驱动器号,并且仅在该物理驱动器上找到的 FAT 卷会被挂载。
1:启用对多个分区的支持。这样,每个逻辑驱动器号可以绑定到任意物理驱动器和分区,这些驱动器和分区在 VolToPart[] 中列出。此外,还将提供 f_fdisk() 函数,用于分区管理。
启用多分区支持可以让文件系统管理更多的分区和驱动器,提高灵活性,但也增加了配置和管理的复杂性。 */



#define FF_MIN_SS		512
#define FF_MAX_SS		4096								
/*
FF_MIN_SS 和 FF_MAX_SS 的定义如下:

FF_MIN_SS: 设定最小扇区大小。这个值通常设置为 512 字节,以支持大多数系统、通用的存储卡和硬盘。

FF_MAX_SS: 设定最大扇区大小。此值可以设置为 512、1024、2048 或 4096 字节。你当前将其设置为 4096 字节,适用于需要较大扇区的设备,如某些板载闪存或光盘。

说明
当 FF_MAX_SS 大于 FF_MIN_SS 时,FatFs 文件系统被配置为支持可变扇区大小模式。在这种情况下,disk_ioctl() 函数需要实现 GET_SECTOR_SIZE 命令,以允许系统处理不同的扇区大小。

设置适当的扇区大小可以优化文件系统的性能和兼容性,尤其是在处理不同类型的存储介质时。 */



#define FF_LBA64		0
/* 
FF_LBA64 用于控制是否支持 64 位逻辑块地址(LBA):

0 表示禁用 64 位 LBA。
1 表示启用 64 位 LBA,但需要同时启用 exFAT 文件系统(FF_FS_EXFAT == 1)。
启用 64 位 LBA 允许支持更大的存储容量,但必须确保文件系统也支持该功能。 */



#define FF_MIN_GPT		0x10000000
/* 
FF_MIN_GPT 定义了在 f_mkfs 和 f_fdisk 函数中切换到 GPT 分区格式的最小扇区数:

设置为 0x10000000(即 268,435,456 扇区),表示当分区大小达到此值时,系统可以切换到 GPT(GUID 分区表)格式。
如果 FF_LBA64 为 0,则此选项无效,因为 GPT 需要 64 位 LBA 支持才能工作。 */



#define FF_USE_TRIM		0
/*
FF_USE_TRIM 控制是否支持 ATA-TRIM:

0 表示禁用 ATA-TRIM 功能。
1 表示启用 ATA-TRIM 功能,但需要在 disk_ioctl() 函数中实现 CTRL_TRIM 命令。
启用 ATA-TRIM 可以帮助提高固态硬盘的性能和寿命。*/



/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/

#define FF_FS_TINY		0
/* 
这段代码定义了 FF_FS_TINY,用于选择缓冲区配置:

FF_FS_TINY = 0:使用正常缓冲区配置。
FF_FS_TINY = 1:使用紧凑缓冲区配置,文件对象(FIL)的大小会减少 FF_MAX_SS 字节,文件数据传输使用文件系统对象(FATFS)中的公共扇区缓冲区。 */



#define FF_FS_EXFAT		0
/* 
这段代码定义了 FF_FS_EXFAT,用于控制是否支持exFAT文件系统:

FF_FS_EXFAT = 0:禁用exFAT支持。
FF_FS_EXFAT = 1:启用exFAT支持。
要启用exFAT,必须同时启用长文件名(LFN),即 FF_USE_LFN 必须设置为 1 或更高。同时,启用exFAT会导致不兼容ANSI C(C89) */



#define FF_FS_NORTC		0
#define FF_NORTC_MON	1
#define FF_NORTC_MDAY	1
#define FF_NORTC_YEAR	2022
/* 
这段代码解释了 FF_FS_NORTC 宏选项在 FatFs 文件系统模块中的作用,主要涉及时间戳功能的启用与禁用:
FF_FS_NORTC:

值为 1:禁用时间戳功能。如果系统没有实时时钟(RTC)或不需要有效的时间戳,可以将此选项设置为 1。在这种情况下,所有由 FatFs 修改的对象将具有固定的时间戳,该时间戳由 FF_NORTC_MON、FF_NORTC_MDAY 和 FF_NORTC_YEAR 定义,均为本地时间。
值为 0:启用时间戳功能。在这种模式下,需要实现 get_fattime() 函数来从实时时钟读取当前时间。FF_NORTC_MON、FF_NORTC_MDAY 和 FF_NORTC_YEAR 将不再起作用,因为实际时间戳将从 RTC 中读取。
注意:

这些选项对只读配置 (FF_FS_READONLY = 1) 没有影响。在只读模式下,文件系统不会进行任何修改操作,因此不需要时间戳功能。
总结来说,FF_FS_NORTC 控制 FatFs 是否使用实时时钟来为文件系统对象生成时间戳。如果系统没有 RTC,或者时间戳不重要,可以通过将 FF_FS_NORTC 设置为 1 来简化配置。如果需要动态时间戳功能,则需要设置为 0 并实现时间读取功能。 */



#define FF_FS_NOFSINFO	0
/*
这段代码定义了 FF_FS_NOFSINFO,控制FAT32文件系统如何处理FSINFO中的信息。bit0 和 bit1 是设置选项:

bit0=0:使用FSINFO中的自由簇计数。
bit0=1:不信任FSINFO中的自由簇计数,强制进行完整FAT扫描。
bit1=0:使用FSINFO中的最后分配簇编号。
bit1=1:不信任FSINFO中的最后分配簇编号。*/


#define FF_FS_LOCK		0
/* 
这段代码定义了一个宏 FF_FS_LOCK,用于配置 FatFs 文件系统模块中的文件锁定功能。具体功能如下:

FF_FS_LOCK:
值为 0:禁用文件锁定功能。在这种模式下,FatFs 不会管理文件的锁定状态,因此应用程序必须自行避免非法的文件打开、删除或重命名操作,以防止数据损坏或其他问题。适用于只读模式(即 FF_FS_READONLY 为 1)的情况下。
值大于 0:启用文件锁定功能。此值定义了在文件锁定控制下可以同时打开的文件/子目录的最大数量。启用文件锁定可以防止重复打开文件或进行非法操作(如同时打开同一文件的多个实例),从而避免潜在的冲突和数据损坏。
启用文件锁定功能时,FF_FS_LOCK 的值表示系统允许的最大并发文件操作数,而文件锁定功能与线程安全性(FF_FS_REENTRANT)是独立配置的。这意味着即使在启用线程安全功能时,文件锁定仍然可以提供额外的保护。 */



#define FF_FS_REENTRANT	0
#define FF_FS_TIMEOUT	1000
/* 
这段代码定义了两个宏,用于配置 FatFs 文件系统模块的线程安全性和超时时间:

FF_FS_REENTRANT:

值为 0:禁用线程安全功能。这意味着 FatFs 模块在多线程环境下可能会出现竞争条件,文件访问不受线程安全控制。
值为 1:启用线程安全功能。在这种模式下,用户必须提供同步处理程序(如 ff_mutex_create()、ff_mutex_delete()、ff_mutex_take() 和 ff_mutex_give())。这些处理程序用于在多线程环境下管理对 FatFs 的访问,防止数据竞争。
FF_FS_TIMEOUT:

定义了超时时间,单位为操作系统的时间刻度(tick)。当启用线程安全功能时,FatFs 可能会等待某些同步操作完成,FF_FS_TIMEOUT 指定了等待的最长时间。
启用线程安全时,需实现适当的同步处理程序,以确保 FatFs 在多线程环境中的可靠性。*/



/*--- End of configuration options ---*/

main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"	
#include "ff.h"	
#include "diskio.h"
#include "SD.h"
#include "SPI.h"

FATFS fs;


void WritetoSD(BYTE write_buff[],uint8_t bufSize);
char SD_FileName[] = "屌爆了.txt";
uint8_t WriteBuffer[] = "01 write buff to sd \r\n";


uint8_t write_cnt =0;	//写SD卡次数

BYTE work[FF_MAX_SS];

///

/
		


	int main(void)
	{	
		

		
	
		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
		uart_init(115200);
		delay_init();	    	
		
//
		WritetoSD(WriteBuffer,sizeof(WriteBuffer));		

			
			
			delay_ms(500);
			WriteBuffer[0] = WriteBuffer[0] +10;
			WriteBuffer[1] = WriteBuffer[1] +10;
			write_cnt ++;
			
			while(write_cnt > 10)
			{	
				printf(" while \r\n");
				delay_ms(500);
			}				

}







void WritetoSD(BYTE write_buff[],uint8_t bufSize)
{
	
	FIL file;
	uint8_t res=0;
	UINT Bw;	
	
	res = SD_Init();		//SD卡初始化
	printf("res = %d\r\n",res);
	
	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, work, sizeof(work));		//格式化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);
	}		
} 




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

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

相关文章

常用环境部署(十八)——CentOS7搭建DNS服务器

一、安装Bind服务器软件并启动 1、安装Bind服务 yum -y install bind bind* 2、 启动服务 systemctl start named 3、开机自启动 systemctl enable named 二、查看named进程是否正常启动 1、检查进程 ps -eaf|grep named 2、检查监听端口 ss -nult|grep :53 三、关闭…

EmguCV学习笔记 C# 11.6 图像分割

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

宠物毛发对人体有什么危害?宠物空气净化器小米、希喂、352对比实测

作为一个呼吸科医生,我自己也养猫。软软糯糯的小猫咪谁不爱啊,在养猫的过程中除了欢乐外,也面临着一系列的麻烦,比如要忍耐猫猫拉粑粑臭、掉毛、容易带来细菌等等的问题。然而我发现,现在许多年轻人光顾着养猫快乐了&a…

Vue生命周期钩子在UniApp中的应用

1、Vue 3 生命周期钩子介绍 onMounted()//注册一个回调函数,在组件挂载完成后执行。onUpdated()//注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。onUnmounted()//注册一个回调函数,在组件实例被卸载之后调用。onBefor…

西部数据发布的一款西数硬盘检测修复工具-支持WD-L/WD-ROYL板,能进行硬盘软复位,可识别硬盘查看或清除-供大家学习参考

使用方法: 1、运行WDR5.3正式版.exe 2、导入WDR5.3.key 3、PORTTALK.SYS放入系统目录 第一步:注册完打开软件 第二步:设置维修盘端口:点击设置--》端口--》会出现主要端口 ,次要端口 ,定制端口 USB。一般如果不是USB移动硬盘都选择“定制端口”

WebGL入门(019):WebGLSync 简介、使用方法、示例代码

还是大剑师兰特:曾是美国某知名大学计算机专业研究生,现为航空航海领域高级前端工程师;CSDN知名博主,GIS领域优质创作者,深耕openlayers、leaflet、mapbox、cesium,canvas,webgl,ech…

轨迹预测(2)CVPR-24:基于社交互动角度表示的行人轨迹预测(代码已开源)

一.写在前面 今天要分享的是CVPR 2024上的一篇精彩的行人轨迹预测论文。这篇文章的灵感来源于海洋动物,它们通过回声定位来感知水下同伴的位置。研究者们构建了一种名为 SocialCircle 的全新基于角度的社交互动表示模型,用于动态反映行人相对…

MATLAB | R2024b更新了哪些好玩的东西?

Hey, 又到了一年两度的MATLAB更新时刻,MATLAB R2024b正式版发布啦!,直接来看看有哪些我认为比较有意思的更新吧! 1 小提琴图 天塌了,我这两天才写了个半小提琴图咋画,MATLAB 官方就出了小提琴图绘制方法。 小提琴图…

从边缘到云端,合宙DTURTU打造无缝物联网解决方案

随着物联网(IoT)技术的飞速发展,万物互联的时代已经到来, 如何高效、稳定地连接边缘设备与云端平台,实现数据的实时采集、传输与处理,成为了推动物联网应用落地的关键。 DTU(数据传输单元&…

Python面试宝典第48题:找丑数

题目 我们把只包含质因子2、3和5的数称作丑数(Ugly Number)。比如:6、8都是丑数,但14不是,因为它包含质因子7。习惯上,我们把1当做是第一个丑数。求按从小到大的顺序的第n个丑数。 示例 1: 输入…

单链表的查找与长度计算

注&#xff1a;本文只探讨"带头结点"的情况(查找思路类似循环找到第i-1 个结点的代码) 一.按位查找&#xff1a; 1.代码演示&#xff1a; 版本一&#xff1a; #include<stdio.h> #include<stdlib.h> ​ ​ //定义单链表结点类型 typedef struct LNo…

C语言 | Leetcode C语言题解之第401题二进制手表

题目&#xff1a; 题解&#xff1a; char** readBinaryWatch(int turnedOn, int* returnSize) {char** ans malloc(sizeof(char*) * 12 * 60);*returnSize 0;for (int i 0; i < 1024; i) {int h i >> 6, m i & 63; // 用位运算取出高 4 位和低 6 位if (h &…

针对网上nbcio-boot代码审计的actuator方法的未授权访问漏洞和ScriptEngine的注入漏洞的补救

针对网上下面文章的漏洞补救 奇安信攻防社区-代码审计之nbcio-boot从信息泄露到Getshell 一、未授权分析 在ShiroConfig中放开了actuator方法的未授权访问 org/jeecg/config/shiro/ShiroConfig.java:156 上面问题,先注释掉上面的未授权访问,实际就是代码注释掉 二、、RCE…

秒懂Linux之管道通信

目录 前言 进程间通信目的 管道通信 原理 匿名管道 测试样例 情况与特点 模拟进程池 命名管道 全部代码 前言 两个进程之间可以进行数据的直接传递吗&#xff1f;——不可以&#xff0c;进程必须得具备独立性。 进程间通信目的 数据传输&#xff1a;一个进程需要将…

odoo14 | 报错:Database backup error: Access Denied

这两天抽空想为自己快速做一个简单的管理系统&#xff0c;来信息化管理一下自己家里的一些菜谱、电视剧下载清单等事情&#xff0c;我又不想大动干戈的用Java写管理系统&#xff0c;我就想用已经手生了两年半的odoo快速搭一个系统用用得了&#xff0c;结果还遇上了这么个事 根…

Java设计模式—面向对象设计原则(四) ----->接口隔离原则ISP (完整详解,附有代码+案例)

文章目录 3.4 接口隔离原则(ISP)3.4.1 概述3.4.2 案列 3.4 接口隔离原则(ISP) Interface Segregation Principle&#xff0c;简称ISP 3.4.1 概述 客户端测试类不应该被迫依赖于它不使用的方法&#xff1b;一个类对另一个类的依赖应该建立在最小的接口上。 3.4.2 案列 面看…

PMP--一模--解题--21-30

文章目录 9.资源管理21、 [单选] 项目经理发现一个不可预料的高影响风险已经成为项目的一个因素&#xff0c;团队成员之间的自身利益导致问题得不到解决&#xff0c;项目经理必须快速行动&#xff0c;让团队重新集中精力&#xff0c;以便项目恢复进度&#xff0c;项目经理应该使…

通信工程学习:什么是LCAS链路容量调整机制

LCAS&#xff1a;链路容量调整机制 LCAS&#xff08;Link Capacity Adjustment Scheme&#xff09;链路容量调整机制是一种在ITU-T G.7042中定义的技术&#xff0c;旨在解决传统SDH&#xff08;同步数字体系&#xff09;网络在传输数据业务时带宽分配不灵活的问题。以下是LCAS链…

【 C++ 】C/C++内存管理

前言&#xff1a; &#x1f618;我的主页&#xff1a;OMGmyhair-CSDN博客 目录 一、C/C内存分布 二、C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free malloc&#xff1a; calloc&#xff1a; realloc&#xff1a; free&#xff1a; 三、C内存管理方式…

用Mapmost聚类图分析世界

聚类地图是一种数据可视化工具&#xff0c;能够帮助用户在地图上直观地显示大量地理数据点。当数据点过多时&#xff0c;单独显示每个点会使地图变得混乱&#xff0c;而聚类地图通过将相近的数据点聚集在一起&#xff0c;减少了视觉复杂性&#xff0c;便于分析和理解。聚类地图…