一、项目要求
在 eMMC驱动移植实验的基础上,加上 FatFs 文件系统,实现 eMMC 卡中文件的读写及其它操作
目的:
(1)了解 FatFs 文件系统的原理
(2)掌握 FatFs 文件系统的移植方法
(3)实现 eMMC卡中文件的读写
(4)测试 eMMC读写速度并分析优化
二、FATFS原理
1、底层接口,包括存储媒介读/写接口(disk I/O)和供给文件创建修改时间的实时时钟,需要我们根据平台和存储介质编写移植代码。
2、中间层FATFS模块,实现了FAT 文件读/写协议。FATFS模块提供的是ff.c和ff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去即可。
3、最顶层是应用层,使用者无需理会FATFS的内部结构和复杂的FAT 协议,只需要调用FATFS模块提供给用户的一系列应用接口函数,如f_open,f_read,f_write 和f_close等,就可以像在PC 上读/写文件那样简单。
三、FATFS移植
1、版本 FatFs - FAT file system module R0.11 ©ChaN, 2015
2、下载地址 http://elm-chan.org/fsw/ff/00index_e.html
3、将下载文件解压到工程目录
4、在mdk工程界面中新建FatFS目录,添加如下文件:
5、魔术棒中设置头文件路径
操作到这里,工程文件结构就算完整了,接下来就是修改文件代码。这里有两个文件需要修改,分别为 user_diskio.c 文件和 ffconf.h 文件。
6、读写函数框架构建
user_diskio.c文件是用户需要完成的diskio驱动程序框架,包括初始化驱动器、获取驱动器状态、读写驱动器等接口函数,需要用户根据使用的芯片和底层函数接口自行配置。
(1)从emmc卡中读取数据函数:
/**
* @brief Reads Sector(s)
* @param pdrv: Physical drive number (0..)
* @param *buff: Data buffer to store read data
* @param sector: Sector address (LBA)
* @param count: Number of sectors to read (1..128)
* @retval DRESULT: Operation result
*/
DRESULT USER_read (
BYTE pdrv, /* 物理扇区,多个设备时用到(0...) */
BYTE *buff, /* 数据缓存区 */
DWORD sector, /*扇区首地址in LBA */
UINT count /* 扇区个数(1..128) */
)
{
DRESULT res = RES_ERROR;
uint32_t timeout;
uint32_t alignedAddr;
alignedAddr = (uint32_t)buff & ~0x1F;
//更新相应的DCache
SCB_CleanDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
if(HAL_MMC_ReadBlocks_DMA(&hmmc1, (uint8_t*)buff,
(uint32_t) (sector),
count) == HAL_OK)
{
/* Wait that the reading process is completed or a timeout occurs */
timeout = HAL_GetTick();
while((RX_Flag == 0) && ((HAL_GetTick() - timeout) < MMC_TIMEOUT))
{
}
/* incase of a timeout return error */
if (RX_Flag == 0)
{
res = RES_ERROR;
}
else
{
RX_Flag = 0;
timeout = HAL_GetTick();
while((HAL_GetTick() - timeout) < MMC_TIMEOUT)
{
if (HAL_MMC_GetCardState(&hmmc1) == HAL_MMC_CARD_TRANSFER)
{
res = RES_OK;
//使相应的DCache无效
SCB_InvalidateDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
break;
}
}
}
}
return res;
}
(2)向emmc卡中写入数据函数:
/**
* @brief Writes Sector(s)
* @param pdrv: Physical drive number (0..)
* @param *buff: Data to be written
* @param sector: Sector address (LBA)
* @param count: Number of sectors to write (1..128)
* @retval DRESULT: Operation result
*/
#if _USE_WRITE == 1
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 */
)
{
DRESULT res = RES_ERROR;
uint32_t alignedAddr;
uint32_t timeout;
alignedAddr = (uint32_t)buff & ~0x1F;
//更新相应的DCache
SCB_CleanDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
if(HAL_MMC_WriteBlocks_DMA(&hmmc1, (uint8_t*)buff,
(uint32_t) (sector),
count) == HAL_OK)
{
/* Wait that the reading process is completed or a timeout occurs */
timeout = HAL_GetTick();
while((TX_Flag == 0) && ((HAL_GetTick() - timeout) < MMC_TIMEOUT))
{
}
/* incase of a timeout return error */
if (TX_Flag == 0)
{
res = RES_ERROR;
}
else
{
TX_Flag = 0;
timeout = HAL_GetTick();
while((HAL_GetTick() - timeout) < MMC_TIMEOUT)
{
if (HAL_MMC_GetCardState(&hmmc1) == HAL_MMC_CARD_TRANSFER)
{
res = RES_OK;
//使相应的DCache无效
SCB_InvalidateDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
break;
}
}
}
}
return res;
}
7、在ffconf.h 文件中 进行如下修改:
#define _USE_MKFS 1 // 0–>1
#define _CODE_PAGE 936 //932–>936 日文到中文
#define _USE_LFN 1 // 0->1 支持长文件名
#define _VOLUMES 1 //根据你使用的盘符数 自行更改
#define _MAX_SS 4096 // 512–>4096 SPI 扇区大小为4096
#define _FS_LOCK 3 // 0–>3 支持同时打开文件数 3个
四、FATFS函数接口
FatFs 模块为应用程序提供了下列函数:
1、f_mount
在 FatFs 模块上注册 / 注销一个工作区 ( 文件系统对象 )
f_mount ( BYTE Drive, /* 逻辑驱动器号 */
FATFS FileSystemObject); / 工作区指针 */
参数 1:Drive
注册 / 注销工作区的逻辑驱动器号 (0-9) 。
参数 2:FileSystemObject
工作区 ( 文件系统对象 ) 指针。
返回值
FR_OK (0) 函数成功。
FR_INVALID_DRIVE 驱动器号无效
f_mount 函数在 FatFs 模块上注册 / 注销一个工作区。 在使用任何其他文件函数之前,必须使用该函数为每个卷注册一个工作区。要注销一个工作区,只要指定 FileSystemObject 为 NULL 即可,然后该工作区可以被丢弃。
该函数只初始化给定的工作区,以及将该工作区的地址注册到内部表中,不访问磁盘 I/O 层。卷装入过程是在f_mount 函数后或存储介质改变后的第一次文件访问时完成的。
2、f_open
创建 / 打开一个用于访问文件的文件对象
f_open ( FIL FileObject, / 空白文件对象结构指针 */
const XCHAR FileName, / 文件名指针 /
BYTE ModeFlags ); / 模式标志 */
参数1:FileObject 将被创建的文件对象结构的指针。
参数2:FileName NULL 结尾的字符串指针,该字符串指定了将被创建或打开的文件名。
参数3:ModeFlags 指定文件的访问类型和打开方法。它是由下列标志的一个组合指定的。
注意:当 _FS_READONLY == 1 时,模式标志
FA_WRITE, FA_CREATE_ALWAYS, FA_CREATE_NEW, FA_OPEN_ALWAYS是无效的。
返回值:
FR_OK (0) 函数成功,该文件对象有效。
FR_NO_FILE 找不到该文件。
FR_NO_PATH 找不到该路径。
FR_INVALID_NAME 文件名无效。
FR_INVALID_DRIVE 驱动器号无效。
FR_EXIST 该文件已存在。
FR_DENIED 由于下列原因,所需的访问被拒绝:
(1)以写模式打开一个只读文件。
(2)由于存在一个同名的只读文件或目录,而导致文件无法被创建。
(3)由于目录表或磁盘已满,而导致文件无法被创建。
FR_NOT_READY 由于驱动器中没有存储介质或任何其他原因,而导致磁盘驱动器无法工作。
FR_WRITE_PROTECTED 在存储介质被写保护的情况下,以写模式打开或创建文件对象。
FR_DISK_ERR 由于底层磁盘 I/O 接口函数中的一个错误,而导致该函数失败。
FR_INT_ERR 由于一个错误的 FAT 结构或一个内部错误,而导致该函数失败。
FR_NOT_ENABLED 逻辑驱动器没有工作区。
FR_NO_FILESYSTEM 磁盘上没有有效地 FAT 卷。
如果函数成功,则创建一个文件对象。该文件对象被后续的读 / 写函数用来访问文件。如果想要关闭一个打开的文件对象,则使用 f_close 函数。如果不关闭修改后的文件,那么文件可能会崩溃。在使用任何文件函数之前,必须使用 f_mount 函数为驱动器注册一个工作区。只有这样,其他文件函数才能正常工作。
3、f_close
关闭一个打开的文件
f_close ( FIL FileObject ); / 文件对象结构的指针 */
参数1:FileObject 指向将被关闭的已打开的文件对象结构的指针。
返回值
FR_OK (0) 文件对象已被成功关闭。
FR_DISK_ERR 由于底层磁盘 I/O 函数中的错误,而导致该函数失败。
FR_INT_ERR 由于一个错误的 FAT 结构或一个内部错误,而导致该函数失败。
FR_NOT_READY 由于驱动器中没有存储介质或任何其他原因,而导致磁盘驱动器无法工作。
FR_INVALID_OBJECT 文件对象无效。
f_close 函数关闭一个打开的文件对象。无论向文件写入任何数据,文件的缓存信息都将被写回到磁盘。该函数成功后,文件对象不再有效,并且可以被丢弃。如果文件对象是在只读模式下打开的,不需要使用该函数,也能被丢弃。
4、f_read
从一个文件读取数据
f_read ( FIL FileObject, / 文件对象结构的指针 */
Void Buffer, / 存储读取数据的缓冲区的指针 /
UINT ByteToRead, / 要读取的字节数 */
UINT ByteRead ); / 返回已读取字节数变量的指针 */
参数 1:FileObject 指向将被读取的已打开的文件对象结构的指针。
参数 2:Buffer 指向存储读取数据的缓冲区的指针。
参数 3:ByteToRead 要读取的字节数, UINT 范围内。
参数 4:ByteRead 指向返回已读取字节数的 UINT 变量的指针。在调用该函数后,无论结果如何,数值都是有效的。
返回值
FR_OK (0) 函数成功。
FR_DENIED 由于文件是以非读模式打开的,而导致该函数被拒绝。
FR_DISK_ERR 由于底层磁盘 I/O 函数中的错误,而导致该函数失败。
FR_INT_ERR 由于一个错误的 FAT 结构或一个内部错误,而导致该函数失败。
FR_NOT_READY 由于驱动器中没有存储介质或任何其他原因,而导致磁盘驱动器无法工作。
FR_INVALID_OBJECT 文件对象无效。
文件对象中的读 / 写指针以已读取字节数增加。该函数成功后,应该检查 *ByteRead 来检测文件是否结束。在读操作过程中,一旦 *ByteRead < ByteToRead ,则读 / 写指针到达了文件结束位置。
5、f_write
写入数据到一个文件
FRESULT f_write ( FIL FileObject, / 文件对象结构的指针 */
const void Buffer, / 存储写入数据的缓冲区的指针 /
UINT ByteToWrite, / 要写入的字节数 */
UINT ByteWritten ); / 返回已写入字节数变量的指针 */
参数1:FileObject 指向将被写入的已打开的文件对象结构的指针。
参数2:Buffer 指向存储写入数据的缓冲区的指针。
参数3:ByteToRead 要写入的字节数, UINT 范围内。
参数4:ByteRead指向返回已写入字节数的 UINT 变量的指针。在调用该函数后,无论结果如何,数值都是有效的。
返回值
FR_OK (0) 函数成功。
FR_DENIED 由于文件是以非写模式打开的,而导致该函数被拒绝。
FR_DISK_ERR 由于底层磁盘 I/O 函数中的错误,而导致该函数失败。
FR_INT_ERR 由于一个错误的 FAT 结构或一个内部错误,而导致该函数失败。
FR_NOT_READY 由于驱动器中没有存储介质或任何其他原因,而导致磁盘驱动器无法工作。
FR_INVALID_OBJECT 文件对象无效。
文件对象中的读 / 写指针以已写入字节数增加。该函数成功后,应该检查 *ByteWritten 来检测磁盘是否已满。在写操作过程中,一旦 *ByteWritten < *ByteToWritten ,则意味着该卷已满。
五、FATFS读写测试
1、eMMC卡写入速度测试
uint32_t bytes_write; /* File write counts */
#define DATA_SIZE 102400
#define DATA_CYCLE 2048
uint8_t write[DATA_SIZE]
/*获取系统时间*/
time1 = HAL_GetTick();
for(i=0; i
f_write(&fil, write, DATA_SIZE, bytes_write);
/*获取系统时间*/
time2 = HAL_GetTick();
time = time1-time2;
write_spead = (DATA_SIZE*DATA_CYCLE/(1024*1024))/(time/1000) ;
(1)单总线模式下:
文件大小200M
写入耗时215.098秒
平均写入速度0.93M/S
(2)8位总线模式下:
文件大小200M
写入耗时33.755秒
平均写入速度5.93M/S
2、测试方法
由于单片机内存有限,所以写入测试采用小文件循环写入的方式。
1、在1bit总线传输模式下,设置数据总大小为定值,改变数据块大小,共三组实验。
2、在8bit总线传输模式下,设置数据总大小为定值,改变数据块大小,共三组实验。
结论
根据表1、表2中测试数据可以看出,当数据总大小一定时,数据块越大写入速度越快;而当数据块大小一定时,数据总大小改变对写入速度影响不大。由此可以得出结论,影响数据写入速度的主要原因是数据块的大小。
对比表1和表2测试数据可以看出,,同样大小的文件,使用8bit总线传输模式的传输速度相较于1bit总线传输速度快的多,所以传输总线宽度决定了传输速度的上限。
一、项目要求
在 eMMC驱动移植实验的基础上,加上 FatFs 文件系统,实现 eMMC 卡中文件的读写及其它操作
目的:
(1)了解 FatFs 文件系统的原理
(2)掌握 FatFs 文件系统的移植方法
(3)实现 eMMC卡中文件的读写
(4)测试 eMMC读写速度并分析优化
二、FATFS原理
1、底层接口,包括存储媒介读/写接口(disk I/O)和供给文件创建修改时间的实时时钟,需要我们根据平台和存储介质编写移植代码。
2、中间层FATFS模块,实现了FAT 文件读/写协议。FATFS模块提供的是ff.c和ff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去即可。
3、最顶层是应用层,使用者无需理会FATFS的内部结构和复杂的FAT 协议,只需要调用FATFS模块提供给用户的一系列应用接口函数,如f_open,f_read,f_write 和f_close等,就可以像在PC 上读/写文件那样简单。
三、FATFS移植
1、版本 FatFs - FAT file system module R0.11 ©ChaN, 2015
2、下载地址 http://elm-chan.org/fsw/ff/00index_e.html
3、将下载文件解压到工程目录
4、在mdk工程界面中新建FatFS目录,添加如下文件:
5、魔术棒中设置头文件路径
操作到这里,工程文件结构就算完整了,接下来就是修改文件代码。这里有两个文件需要修改,分别为 user_diskio.c 文件和 ffconf.h 文件。
6、读写函数框架构建
user_diskio.c文件是用户需要完成的diskio驱动程序框架,包括初始化驱动器、获取驱动器状态、读写驱动器等接口函数,需要用户根据使用的芯片和底层函数接口自行配置。
(1)从emmc卡中读取数据函数:
/**
* @brief Reads Sector(s)
* @param pdrv: Physical drive number (0..)
* @param *buff: Data buffer to store read data
* @param sector: Sector address (LBA)
* @param count: Number of sectors to read (1..128)
* @retval DRESULT: Operation result
*/
DRESULT USER_read (
BYTE pdrv, /* 物理扇区,多个设备时用到(0...) */
BYTE *buff, /* 数据缓存区 */
DWORD sector, /*扇区首地址in LBA */
UINT count /* 扇区个数(1..128) */
)
{
DRESULT res = RES_ERROR;
uint32_t timeout;
uint32_t alignedAddr;
alignedAddr = (uint32_t)buff & ~0x1F;
//更新相应的DCache
SCB_CleanDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
if(HAL_MMC_ReadBlocks_DMA(&hmmc1, (uint8_t*)buff,
(uint32_t) (sector),
count) == HAL_OK)
{
/* Wait that the reading process is completed or a timeout occurs */
timeout = HAL_GetTick();
while((RX_Flag == 0) && ((HAL_GetTick() - timeout) < MMC_TIMEOUT))
{
}
/* incase of a timeout return error */
if (RX_Flag == 0)
{
res = RES_ERROR;
}
else
{
RX_Flag = 0;
timeout = HAL_GetTick();
while((HAL_GetTick() - timeout) < MMC_TIMEOUT)
{
if (HAL_MMC_GetCardState(&hmmc1) == HAL_MMC_CARD_TRANSFER)
{
res = RES_OK;
//使相应的DCache无效
SCB_InvalidateDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
break;
}
}
}
}
return res;
}
(2)向emmc卡中写入数据函数:
/**
* @brief Writes Sector(s)
* @param pdrv: Physical drive number (0..)
* @param *buff: Data to be written
* @param sector: Sector address (LBA)
* @param count: Number of sectors to write (1..128)
* @retval DRESULT: Operation result
*/
#if _USE_WRITE == 1
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 */
)
{
DRESULT res = RES_ERROR;
uint32_t alignedAddr;
uint32_t timeout;
alignedAddr = (uint32_t)buff & ~0x1F;
//更新相应的DCache
SCB_CleanDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
if(HAL_MMC_WriteBlocks_DMA(&hmmc1, (uint8_t*)buff,
(uint32_t) (sector),
count) == HAL_OK)
{
/* Wait that the reading process is completed or a timeout occurs */
timeout = HAL_GetTick();
while((TX_Flag == 0) && ((HAL_GetTick() - timeout) < MMC_TIMEOUT))
{
}
/* incase of a timeout return error */
if (TX_Flag == 0)
{
res = RES_ERROR;
}
else
{
TX_Flag = 0;
timeout = HAL_GetTick();
while((HAL_GetTick() - timeout) < MMC_TIMEOUT)
{
if (HAL_MMC_GetCardState(&hmmc1) == HAL_MMC_CARD_TRANSFER)
{
res = RES_OK;
//使相应的DCache无效
SCB_InvalidateDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
break;
}
}
}
}
return res;
}
7、在ffconf.h 文件中 进行如下修改:
#define _USE_MKFS 1 // 0–>1
#define _CODE_PAGE 936 //932–>936 日文到中文
#define _USE_LFN 1 // 0->1 支持长文件名
#define _VOLUMES 1 //根据你使用的盘符数 自行更改
#define _MAX_SS 4096 // 512–>4096 SPI 扇区大小为4096
#define _FS_LOCK 3 // 0–>3 支持同时打开文件数 3个
四、FATFS函数接口
FatFs 模块为应用程序提供了下列函数:
1、f_mount
在 FatFs 模块上注册 / 注销一个工作区 ( 文件系统对象 )
f_mount ( BYTE Drive, /* 逻辑驱动器号 */
FATFS FileSystemObject); / 工作区指针 */
参数 1:Drive
注册 / 注销工作区的逻辑驱动器号 (0-9) 。
参数 2:FileSystemObject
工作区 ( 文件系统对象 ) 指针。
返回值
FR_OK (0) 函数成功。
FR_INVALID_DRIVE 驱动器号无效
f_mount 函数在 FatFs 模块上注册 / 注销一个工作区。 在使用任何其他文件函数之前,必须使用该函数为每个卷注册一个工作区。要注销一个工作区,只要指定 FileSystemObject 为 NULL 即可,然后该工作区可以被丢弃。
该函数只初始化给定的工作区,以及将该工作区的地址注册到内部表中,不访问磁盘 I/O 层。卷装入过程是在f_mount 函数后或存储介质改变后的第一次文件访问时完成的。
2、f_open
创建 / 打开一个用于访问文件的文件对象
f_open ( FIL FileObject, / 空白文件对象结构指针 */
const XCHAR FileName, / 文件名指针 /
BYTE ModeFlags ); / 模式标志 */
参数1:FileObject 将被创建的文件对象结构的指针。
参数2:FileName NULL 结尾的字符串指针,该字符串指定了将被创建或打开的文件名。
参数3:ModeFlags 指定文件的访问类型和打开方法。它是由下列标志的一个组合指定的。
注意:当 _FS_READONLY == 1 时,模式标志
FA_WRITE, FA_CREATE_ALWAYS, FA_CREATE_NEW, FA_OPEN_ALWAYS是无效的。
返回值:
FR_OK (0) 函数成功,该文件对象有效。
FR_NO_FILE 找不到该文件。
FR_NO_PATH 找不到该路径。
FR_INVALID_NAME 文件名无效。
FR_INVALID_DRIVE 驱动器号无效。
FR_EXIST 该文件已存在。
FR_DENIED 由于下列原因,所需的访问被拒绝:
(1)以写模式打开一个只读文件。
(2)由于存在一个同名的只读文件或目录,而导致文件无法被创建。
(3)由于目录表或磁盘已满,而导致文件无法被创建。
FR_NOT_READY 由于驱动器中没有存储介质或任何其他原因,而导致磁盘驱动器无法工作。
FR_WRITE_PROTECTED 在存储介质被写保护的情况下,以写模式打开或创建文件对象。
FR_DISK_ERR 由于底层磁盘 I/O 接口函数中的一个错误,而导致该函数失败。
FR_INT_ERR 由于一个错误的 FAT 结构或一个内部错误,而导致该函数失败。
FR_NOT_ENABLED 逻辑驱动器没有工作区。
FR_NO_FILESYSTEM 磁盘上没有有效地 FAT 卷。
如果函数成功,则创建一个文件对象。该文件对象被后续的读 / 写函数用来访问文件。如果想要关闭一个打开的文件对象,则使用 f_close 函数。如果不关闭修改后的文件,那么文件可能会崩溃。在使用任何文件函数之前,必须使用 f_mount 函数为驱动器注册一个工作区。只有这样,其他文件函数才能正常工作。
3、f_close
关闭一个打开的文件
f_close ( FIL FileObject ); / 文件对象结构的指针 */
参数1:FileObject 指向将被关闭的已打开的文件对象结构的指针。
返回值
FR_OK (0) 文件对象已被成功关闭。
FR_DISK_ERR 由于底层磁盘 I/O 函数中的错误,而导致该函数失败。
FR_INT_ERR 由于一个错误的 FAT 结构或一个内部错误,而导致该函数失败。
FR_NOT_READY 由于驱动器中没有存储介质或任何其他原因,而导致磁盘驱动器无法工作。
FR_INVALID_OBJECT 文件对象无效。
f_close 函数关闭一个打开的文件对象。无论向文件写入任何数据,文件的缓存信息都将被写回到磁盘。该函数成功后,文件对象不再有效,并且可以被丢弃。如果文件对象是在只读模式下打开的,不需要使用该函数,也能被丢弃。
4、f_read
从一个文件读取数据
f_read ( FIL FileObject, / 文件对象结构的指针 */
Void Buffer, / 存储读取数据的缓冲区的指针 /
UINT ByteToRead, / 要读取的字节数 */
UINT ByteRead ); / 返回已读取字节数变量的指针 */
参数 1:FileObject 指向将被读取的已打开的文件对象结构的指针。
参数 2:Buffer 指向存储读取数据的缓冲区的指针。
参数 3:ByteToRead 要读取的字节数, UINT 范围内。
参数 4:ByteRead 指向返回已读取字节数的 UINT 变量的指针。在调用该函数后,无论结果如何,数值都是有效的。
返回值
FR_OK (0) 函数成功。
FR_DENIED 由于文件是以非读模式打开的,而导致该函数被拒绝。
FR_DISK_ERR 由于底层磁盘 I/O 函数中的错误,而导致该函数失败。
FR_INT_ERR 由于一个错误的 FAT 结构或一个内部错误,而导致该函数失败。
FR_NOT_READY 由于驱动器中没有存储介质或任何其他原因,而导致磁盘驱动器无法工作。
FR_INVALID_OBJECT 文件对象无效。
文件对象中的读 / 写指针以已读取字节数增加。该函数成功后,应该检查 *ByteRead 来检测文件是否结束。在读操作过程中,一旦 *ByteRead < ByteToRead ,则读 / 写指针到达了文件结束位置。
5、f_write
写入数据到一个文件
FRESULT f_write ( FIL FileObject, / 文件对象结构的指针 */
const void Buffer, / 存储写入数据的缓冲区的指针 /
UINT ByteToWrite, / 要写入的字节数 */
UINT ByteWritten ); / 返回已写入字节数变量的指针 */
参数1:FileObject 指向将被写入的已打开的文件对象结构的指针。
参数2:Buffer 指向存储写入数据的缓冲区的指针。
参数3:ByteToRead 要写入的字节数, UINT 范围内。
参数4:ByteRead指向返回已写入字节数的 UINT 变量的指针。在调用该函数后,无论结果如何,数值都是有效的。
返回值
FR_OK (0) 函数成功。
FR_DENIED 由于文件是以非写模式打开的,而导致该函数被拒绝。
FR_DISK_ERR 由于底层磁盘 I/O 函数中的错误,而导致该函数失败。
FR_INT_ERR 由于一个错误的 FAT 结构或一个内部错误,而导致该函数失败。
FR_NOT_READY 由于驱动器中没有存储介质或任何其他原因,而导致磁盘驱动器无法工作。
FR_INVALID_OBJECT 文件对象无效。
文件对象中的读 / 写指针以已写入字节数增加。该函数成功后,应该检查 *ByteWritten 来检测磁盘是否已满。在写操作过程中,一旦 *ByteWritten < *ByteToWritten ,则意味着该卷已满。
五、FATFS读写测试
1、eMMC卡写入速度测试
uint32_t bytes_write; /* File write counts */
#define DATA_SIZE 102400
#define DATA_CYCLE 2048
uint8_t write[DATA_SIZE]
/*获取系统时间*/
time1 = HAL_GetTick();
for(i=0; i
f_write(&fil, write, DATA_SIZE, bytes_write);
/*获取系统时间*/
time2 = HAL_GetTick();
time = time1-time2;
write_spead = (DATA_SIZE*DATA_CYCLE/(1024*1024))/(time/1000) ;
(1)单总线模式下:
文件大小200M
写入耗时215.098秒
平均写入速度0.93M/S
(2)8位总线模式下:
文件大小200M
写入耗时33.755秒
平均写入速度5.93M/S
2、测试方法
由于单片机内存有限,所以写入测试采用小文件循环写入的方式。
1、在1bit总线传输模式下,设置数据总大小为定值,改变数据块大小,共三组实验。
2、在8bit总线传输模式下,设置数据总大小为定值,改变数据块大小,共三组实验。
结论
根据表1、表2中测试数据可以看出,当数据总大小一定时,数据块越大写入速度越快;而当数据块大小一定时,数据总大小改变对写入速度影响不大。由此可以得出结论,影响数据写入速度的主要原因是数据块的大小。
对比表1和表2测试数据可以看出,,同样大小的文件,使用8bit总线传输模式的传输速度相较于1bit总线传输速度快的多,所以传输总线宽度决定了传输速度的上限。
举报