28.基于SD卡的FatFs文件系统—[野火]i.MXRT库开发实战指南——基于i.MXRT1052文档

[野火]i.MXRT库开发实战指南——基于i.MXRT1052

贡献与投稿

常见问题

·CPU:MIMXRT1052CVL5B·主频:600M

上一章我们已经全面介绍了SD卡的识别和简单的数据读写,也进行了简单的读写测试,不过像这样直接操作SD卡存储单元,在实际应用中是不现实的。SD卡一般用来存放文件,所以都需要加载文件系统到里面。

即使读者可能不了解文件系统,读者也一定对“文件”这个概念十分熟悉。数据在PC上是以文件的形式储存在磁盘中的,这些数据的形式一般为ASCII码或二进制形式。在上一章我们已经写好了SD卡的驱动函数,我们可以非常方便的在SD上读写数据。如需要一串字符写入SD卡,调用SD_WriteBlocks函数即可。

但是,这样直接存储数据会带来极大的不便,如难以记录有效数据的位置,难以确定存储介质的剩余空间,以及应以何种格式来解读数据。就如同一个巨大的图书馆无人管理,杂乱无章地存放着各种书籍,难以查找所需的文档。想象一下图书馆的采购人员购书后,把书籍往馆内一扔,拍拍屁股走人,当有人来借阅某本书的时候,就不得不一本本地查找。这样直接存储数据的方式对于小容量的存储介质如EEPROM还可以接受,但对于SPIFlash芯片或者SD卡之类的大容量设备,我们需要一种高效的方式来管理它的存储内容。

这些管理方式即为文件系统,它是为了存储和管理数据,而在存储介质建立的一种组织结构,这些结构包括操作系统引导区、目录和文件。常见的windows下的文件系统格式包括FAT32、NTFS、exFAT。在使用文件系统前,要先对存储介质进行格式化。格式化先擦除原来内容,在存储介质上新建一个文件分配表和目录。这样,文件系统就可以记录数据存放的物理地址,剩余空间。

使用文件系统时,数据都以文件的形式存储。写入新文件时,先在目录中创建一个文件索引,它指示了文件存放的物理地址,再把数据存储到该地址中。当需要读取数据时,可以从目录中找到该文件的索引,进而在相应的地址中读取出数据。具体还涉及到逻辑地址、簇大小、不连续存储等一系列辅助结构或处理过程。

文件系统的存在使我们在存取数据时,不再是简单地向某物理地址直接读写,而是要遵循它的读写格式。如经过逻辑转换,一个完整的文件可能被分开成多段存储到不连续的物理地址,使用目录或链表的方式来获知下一段的位置。

上一章的SD卡驱动只完成了向物理地址存储块中写入数据的工作,而根据文件系统格式的逻辑转换部分则需要额外的代码来完成。实质上,这个逻辑转换部分可以理解为当我们需要写入一段数据时,由它来求解向什么物理地址写入数据、以什么格式写入及写入一些原始数据以外的信息(如目录)。这个逻辑转换部分代码我们也习惯称之为文件系统。

上面提到的逻辑转换部分代码(文件系统)即为本章的要点,文件系统庞大而复杂,它需要根据应用的文件系统格式而编写,而且一般与驱动层分离开来,很方便移植,所以工程应用中一般是移植现成的文件系统源码。

FatFs是面向小型嵌入式系统的一种通用的FAT文件系统。它完全是由AISIC语言编写并且完全独立于底层的I/O介质。因此它可以很容易地不加修改地移植到其他的处理器当中,如8051、PIC、AVR、SH、Z80、H8、ARM等。FatFs支持FAT12、FAT16、FAT32等格式,所以我们利用前面写好的SD卡驱动,把FatFs文件系统代码移植到工程之中,就可以利用文件系统的各种函数,对SD卡以“文件”格式进行读写操作了。

FatFs文件系统的源码可以从fatfs官网下载:

第一步:打开链接如图281所示。

图281FatFs源码获取(1)

第二步:选择下载的版本如图282所示。在版本列表中选择需要的版本即可,本章以使用R0.13b。

图282FatFs源码获取(2)

在移植FatFs文件系统到开发板之前,我们先要到FatFs的官网获取源码,最新版本为R0.13c,官网有对FatFs做详细的介绍,有兴趣可以了解。解压之后可看到里面有doc和src这两个文件夹,见图283。doc文件夹里面是一些使用帮助文档;src才是FatFs文件系统的源码。

图283FatFs文件目录

打开doc文件夹,可看到如图284的文件目录:

图284doc文件夹的文件目录

doc文件夹下各个文件简要介绍如下:

打开source文件夹,可看到如图285所示的文件目录:

图285source文件夹的文件目录

option文件夹下是一些可选的外部c文件,包含了多语言支持需要用到的文件和转换函数。

00history.txt介绍了FatFs的版本更新情况。

00readme.txt说明了当前目录下diskio.c、diskio.h、ff.c、ff.h、integer.h的功能。

src文件夹下的源码文件功能简介如下:

建议阅读这些源码的顺序为:integer.h–>diskio.c–>ff.c。

阅读文件系统源码ff.c文件需要一定的功底,建议读者先阅读FAT32的文件格式,再去分析ff.c文件。若仅为使用文件系统,则只需要理解integer.h及diskio.c文件并会调用ff.c文件中的函数就可以了。本章主要讲解如何把FATFS文件系统移植到开发板上,并编写一个简单读写操作范例。

移植FatFs之前我们先通过FatFs的程序结构图了解FatFs在程序中的关系网络,见图286。

图286FatFs程序结构图

用户应用程序需要由用户编写,想实现什么功能就编写什么的程序,一般我们只用到f_mount()、f_open()、f_write()、f_read()就可以实现文件的读写操作。

FatFs组件是FatFs的主体,文件都在源码src文件夹中,其中ff.c、ff.h、integer.h以及diskio.h四个文件我们不需要改动,只需要修改ffconf.h和diskio.c两个文件。

底层设备输入输出要求实现存储设备的读写操作函数、存储设备信息获取函数等等。我们使用SD卡作为物理设备,在上一章节已经编写好了SD卡的驱动程序,这里我们就直接使用。

FatFs属于软件组件,不需要附带其他硬件电路。我们使用SD卡作为物理存储设备,其硬件电路在上一章已经做了分析,这里就直接使用。

上一章我们已经实现了SD卡芯片驱动程序,并实现了读写测试,为移植FatFs方便,我们直接拷贝一份工程,我们在工程基础上添加FatFs组件,并修改main函数的用户程序即可。

NXP官方已经将R0.13b版本的FatFs文件系统移植到了RT1052,保存在“SDK_2.5.0_MIMXRT1052xxxxBmiddlewarefatfs”目录下。本小节讲解如何将NXP官方移植的FatFs文件系统添加到我们的工程中。

图287添加fatfs文件夹

图288复制NXP官方文件

图289复制FatFs到工程

至此,我们需要的文件已经全部添加到了fatfs文件夹下。下一步就是将这些文件添加到工程。

图2810添加FatFS文件到工程

图2811添加FATFS路径到工程选项

图2812首次编译错误输出

打开“SD卡—FatFs文件系统读写SD卡—FatFs文件系统librariesfatfs”在fatfs文件夹内并没有ffconf.h文件,但是有一个ffconf_template.h文件。从文件名不难得出这是FatFs配置文件样板,本实验不用这个配置文件,我们直接复制NXP官方配置好文件。在“SDK_2.5.0_MIMXRT1052xxxxBboardsevkbimxrt1050fatfs_examplesfatfs_sdcard”目录下是基于NXP官方评估板的例程,由于我们开发板SD卡接口不同,我们不使用NXP官方评估板例程,但是FatFs文件系统配置文件ffconf.h文件是通用的,我们直接复制ffconf.h文件到我们的工程即可,最终结果如图2813所示。

图2813添加ffconf.h文件

图2814第二次编译错误提示

错误的原因是在main.c文件和fsl_sd_disk.c文件定义了同名变量g_sd。我们修改任意一个文件中的变量名或者在在其中一个文件变量定义改为extern引用。

至此,再次编译正常情况下没有错误。下一步需要为FatFs文件系统添加底层驱动函数以及修改ffconfig.h配置文件。

FatFs文件系统与底层介质的驱动分离开来,对底层介质的操作都要交给用户去实现,它仅仅是提供了一个函数接口而已。表281为FatFs移植时用户必须支持的函数。通过表281我们可以清晰知道很多函数是在一定条件下才需要添加的,只有前三个函数是必须添加的。我们完全可以根据实际需求选择实现用到的函数。

前三个函数是实现读文件最基本需求。接下来三个函数是实现创建文件、修改文件需要的。为实现格式化功能,需要在disk_ioctl添加两个获取物理设备信息选项。我们一般只有实现前面六个函数就可以了,已经足够满足大部分功能。

为支持简体中文长文件名称需要添加ff_convert和ff_wtoupper函数,实际这两个已经在cc936.c文件中实现了,我们只要直接把cc936.c文件添加到工程中就可以了。

后面六个函数一般都不用。如真有需要可以参考syscall.c文件(srcoption文件夹内)。

表281FatFs移植需要用户支持函数

函数

条件(ffconf.h)

备注

disk_statusdisk_initializedisk_read

总是需要

底层设备驱动函数

disk_writeget_fattimedisk_ioctl(CTRL_SYNC)

FF_FS_READONLY==0

disk_ioctl(GET_SECTOR_COUNT)disk_ioctl(GET_BLOCK_SIZE)

FF_USE_MKFS==1

disk_ioctl(GET_SECTOR_SIZE)

FF_MAX_SS!=FF_MIN_SS

disk_ioctl(CTRL_TRIM)

FF_USE_TRIM==1

ff_convertff_wtoupper

FF_USE_LFN!=0

Unicode支持,为支持简体中文,添加cc936.c到工程即可

ff_cre_syncobjff_del_syncobjff_req_grantff_rel_grant

FF_FS_REENTRANT==1

FatFs可重入配置,需要多任务系统支持(一般不需要)

ff_mem_allocff_mem_free

FF_USE_LFN==3

长文件名支持,缓冲区设置在堆空间(一般设置_USE_LFN=2)

底层设备驱动函数是存放在diskio.c文件,我们的目的就是把diskio.c中的函数接口与SD卡驱动连接起来。总共有五个函数,分别为设备状态获取(disk_status)、设备初始化(disk_initialize)、扇区读取(disk_read)、扇区写入(disk_write)、其他控制(disk_ioctl)。

接下来,我们对每个函数结合SD卡驱动做详细讲解。

宏定义

1234567/*为每个设备定义一个物理编号*/#defineRAMDISK0/*预留给外扩RAM使用*/#defineUSBDISK1/*预留给USB使用*/#defineSDDISK2/*SD卡设备物理编号*/#defineMMCDISK3/*预留给MMC卡使用mmc*/#defineSDSPIDISK4/*预留给SPI接口SD卡使用*/#defineNANDDISK5/*预留给nandflash使用*/这两个宏定义在FatFs中非常重要,FatFs是支持多物理设备的,必须为每个物理设备定义一个不同的编号。

本实验只用到了SD卡物理接口编号SDDISK,其他设备编号可以屏蔽掉,使用时再加上。

设备状态获取

123456789DSTATUSsd_disk_status(uint8_tphysicalDrive){if(physicalDrive!=SDDISK){returnSTA_NOINIT;}return0;}很明显,该函数是一个空函数。进入函数后使用if语句判断操作的是否为SD卡,如果不是,返回错误标志,如果是,返回0。我们暂时用不到这个函数,所以不必修改。

设备初始化

12345678910111213141516171819202122232425262728293031DSTATUSdisk_initialize(BYTEpdrv/*物理设备编号*/){/**********************第一部分*************************/DSTATUSstat;switch(pdrv){#ifdefRAM_DISK_ENABLEcaseRAMDISK:stat=ram_disk_initialize(pdrv);returnstat;#endif#ifdefUSB_DISK_ENABLEcaseUSBDISK:stat=USB_HostMsdInitializeDisk(pdrv);returnstat;#endif/***********************第二部分*************************/#ifdefSD_DISK_ENABLEcaseSDDISK:stat=sd_disk_initialize(pdrv);returnstat;#endif/******************此处省略其他条件编译语句********************/default:break;}returnSTA_NOINIT;}disk_initialize函数也是有一个参数pdrv,用来指定设备物理编号。对于SD卡我们调用sd_disk_initialize函数实现对SD卡使用的GPIO初始化、USDHC初始化、SD卡初始化。sd_disk_initialize函数如代码清单285所示。

读取扇区

12345678910111213141516171819202122232425DRESULTdisk_read(BYTEpdrv,/*设备物理编号*/BYTE*buff,/*数据缓存区*/DWORDsector,/*起始数据块编号*/UINTcount/*读取数据块数量*/){DRESULTres;switch(pdrv){/******************此处省略其他条件编译语句********************/#ifdefSD_DISK_ENABLEcaseSDDISK:res=sd_disk_read(pdrv,buff,sector,count);returnres;#endif/******************此处省略其他条件编译语句********************/default:break;}returnRES_PARERR;}disk_read函数有四个形参。pdrv为设备物理编号。buff是一个BYTE类型指针变量,buff指向用来存放读取到数据的存储区首地址。sector是一个DWORD类型变量,指定要读取数据的起始数据块编号。count是一个UINT类型变量,指定读取数据块的数量。

sd_disk_read函数原型如代码清单287所示。

123456789101112131415161718DRESULTsd_disk_read(uint8_tphysicalDrive,uint8_t*buffer,uint32_tsector,uint8_tcount){/**************************第一部分**************************/if(physicalDrive!=SDDISK){returnRES_PARERR;}/***************************第二部分*************************/if(kStatus_Success!=SD_ReadBlocks(&g_sd,buffer,sector,count)){returnRES_ERROR;}returnRES_OK;}同其它sd_disk_XXX函数类似,首先判断操作的是SD卡。第二部分,直接调用fsl_sd.c文件中的SD_ReadBlocks函数,该函数执行最终的SD卡读操作。

扇区写入

12345678910111213141516171819202122DRESULTdisk_write(BYTEpdrv,/*设备物理编号*/constBYTE*buff,/*数据缓存区*/DWORDsector,/*起始数据块编号*/UINTcount/*读取数据块数量*/){DRESULTres;switch(pdrv){/******************此处省略其他条件编译语句********************/#ifdefSD_DISK_ENABLEcaseSDDISK:res=sd_disk_write(pdrv,buff,sector,count);returnres;#endif/******************此处省略其他条件编译语句********************/default:break;}returnRES_PARERR;}disk_write函数有四个形参。pdrv为设备物理编号。buff是一个BYTE类型指针变量,buff指向用来存放读要写入数据的首地址。sector是一个DWORD类型变量,指定要写入数据的起始块编号。count是一个UINT类型变量,指定写入数据块的数量。如果操作的是SD卡,调用sd_disk_write函数完成SD卡写入操作,函数原型如代码清单289所示。

123456789101112131415161718DRESULTsd_disk_write(uint8_tphysicalDrive,constuint8_t*buffer,uint32_tsector,uint8_tcount){/**************************第一部分**************************/if(physicalDrive!=SDDISK){returnRES_PARERR;}/***************************第二部分*************************/if(kStatus_Success!=SD_WriteBlocks(&g_sd,buffer,sector,count)){returnRES_ERROR;}returnRES_OK;}同其它sd_disk_XXX函数类似,首先判断操作的是SD卡。第二部分,直接调用fsl_sd.c文件中的SD_WriteBlocks函数,该函数执行最终的SD卡写操作。

其他控制

12345678910111213141516171819202122DRESULTdisk_ioctl(BYTEpdrv,/*物理编号*/BYTEcmd,/*控制指令*/void*buff/*写入或者读取数据地址指针*/){DRESULTres;switch(pdrv){/******************此处省略其他条件编译语句********************/#ifdefSD_DISK_ENABLEcaseSDDISK:res=sd_disk_ioctl(pdrv,cmd,buff);returnres;#endif/******************此处省略其他条件编译语句********************/default:break;}returnRES_PARERR;}disk_ioctl函数有三个形参,pdrv为设备物理编号,cmd为控制指令,包括获取SD卡块数量命令(GET_SECTOR_COUNT)、获取SD卡块大小命令(GET_SECTOR_SIZE)、获取可擦除扇区块大小命(GET_BLOCK_SIZE)以及结束写入命令(CTRL_SYNC)。buff为指令对应的数据指针。

如果设置的是SD卡,则会调用sd_disk_ioctl函数,函数原型如代码清单2811所示。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657/****************************第一部分*************************/sd_card_tg_sd;DRESULTsd_disk_ioctl(uint8_tphysicalDrive,uint8_tcommand,void*buffer){DRESULTresult=RES_OK;if(physicalDrive!=SDDISK){returnRES_PARERR;}/****************************第二部分***********************/switch(command){caseGET_SECTOR_COUNT:if(buffer){*(uint32_t*)buffer=g_sd.blockCount;}else{result=RES_PARERR;}break;/***********************第三部分************************/caseGET_SECTOR_SIZE:if(buffer){*(uint32_t*)buffer=g_sd.blockSize;}else{result=RES_PARERR;}break;/***********************第四部分************************/caseGET_BLOCK_SIZE:if(buffer){*(uint32_t*)buffer=g_sd.csd.eraseSectorSize;}else{result=RES_PARERR;}break;/***********************第五部分************************/caseCTRL_SYNC:result=RES_OK;break;default:result=RES_PARERR;break;}returnresult;}下面简要讲解各部分代码。

ffconf.h文件是FatFs功能配置文件,我们可以对文件内容进行修改,使得FatFs更符合我们的要求。ffconf.h对每个配置选项都做了详细的使用情况说明。下面只列出相对于源码修改的配置,其他配置采用默认即可。

本实验主要使用FatFs软件功能,不需要其他硬件模块,使用与SD卡读写测试实验相同硬件配置即可。

在28.3FatFs文件系统移植章节我们介绍了FatFs文件系统移植步骤,完成了diskio.c文件内SD卡底层驱动程序的编写以及ffconf.h文件的修改。本小节将会调用FatFs文件系统提供的API操作SD卡。

挂载FATFS文件系统测试

使用文件系统之前,要挂载文件系统。在挂载过程中会调用diskio.c文件中的底层驱动文件完成SD卡的初始化等操作,挂载成功之后就可以调用FatFs文件系统中的API函数了。挂载测试函数如代码清单2813所示。

12345FRESULTf_mount(FATFS*fs,/*指向文件系统对象结构体指针(NULL:unmount)*/constTCHAR*path,/*要挂载/卸载的逻辑驱动器号*/BYTEopt/*选择操作0:取消挂载(delayedmount),1:立即挂载*/)函数参数介绍如下:

如果挂载成功,通过串口输出“挂载文件系统失败”提示信息,然后执行到第六部分代码。

为设备创建文件系统会格式化物理设备,所以创建之前要保证确认SD卡可以被格式化。程序在while(true)循环中使用GETCHAR函数获取串口上位机输入的选择,如果选择确定格式化则执行第五部分代码。

123456789FRESULTf_mkfs(constTCHAR*path,/*逻辑驱动编号*/BYTEopt,/*格式化选项*/DWORDau,/*分配单元的带下,单位(字节)*//*指向用于格式化进程的工作缓冲区的指针。当给定一个空指针时,函数为工作缓冲区分配一块堆空间,此时设置len没有作用*/void*work,UINTlen/*工作缓冲区的大小(以字节为单位)*/)函数各个参数介绍如下:

f_mkfs函数执行完成之后根据返回值判断文件系统创建是否成功,如果创建成功则再次调用f_mount函数挂载文件系统。否则输出错误提示,进入死循环程序。

创建和打开文件测试

文件的创建和打开使用相同的函数,只是函数配置参数不同。我们以文件创建测试为例讲解,文件打开测试函数类似。

123456789101112131415161718192021222324252627282930FRESULTf_touch_test(char*dir){/*********************第一部分***************************/FRESULTerror=FR_OK;FILg_fileObject;/*Fileobject*/PRINTF("\r\n创建“%s”文件......\r\n",dir);/*********************第二部分**************************/error=f_open(&g_fileObject,_T(dir),FA_CREATE_NEW);/**********************第三部分**************************/if(error){if(error==FR_EXIST){PRINTF("文件已经存在.\r\n");}else{PRINTF("创建文件失败\r\n");returnerror;}}else{PRINTF("创建文件成功\r\n");}returnerror;}代码主要内容是调用f_open函数创建文件。函数执行结束后需要根据返回值判断文件创建结果。下面简要讲解个各部分代码。

12345FRESULTf_open(FIL*fp,/*[OUT]指向文件描述结构体*/constTCHAR*path,/*[IN]文件名(路径)*/BYTEmode/*[IN]打开模式*/);函数各个参数讲解如下:

表格281文件打开方式

模式

含义

FA_READ

以只读方式打开。数据可以从文件中读取。

FA_WRITE

指定对对象的写访问。数据可以写入文件。

FA_OPEN_EXISTING

打开该文件。如果文件不存在,该函数将失败。(默认)

FA_CREATE_NEW

创建一个新文件。如果文件存在,则该函数将返回FR_EXIST,表示文件已存在

FA_CREATE_ALWAYS

创建一个新文件。如果文件存在,它将被截断并覆盖。

FA_OPEN_ALWAYS

打开文件,如果文件存在,则打开该文件。如果没有,将创建一个新文件。

FA_OPEN_APPEND

与FA_OPEN_ALWAYS相同,文件打开后只是读/写指针默认在文件的末尾

不同打开模式可以通过“或”的方式组合,常用的几种组合如表格282所示

表格282文件打开方式常用组合

以只读方式打开

以读写方式打开

新建文件并以只写方式打开,如果文件存在则将原文件截断并覆盖

FA_CREATE_ALWAYSFA_READ

新建文件并以读、写方式打开,如果文件存在则将原文件截断并覆盖

FA_WRITE末尾添加内容。

以只写方式打开文件,如果文件不存在,创建文件。如果文件存在,在文件

FA_OPEN_APPENDFA_READ

FA_WRITE件末尾添加内容。

以读、写方式打开文件,如果文件不存在,创建文件。如果文件存在,在文

FA_WRITET,表示文件已存在

以只读方式创建一个文件,如果文件存在,则该函数将返回FR_EXIS

FA_CREATE_NEWFA_READ

FA_WRITEST,表示文件已存在

以读、写方式创建一个文件,如果文件存在,则该函数将返回FR_EXI

一般情况下,我们选择以上组合方式即可满足对文件的读写需求。

创建目录测试

12345678910111213141516171819202122232425262728FRESULTf_mkdir_test(char*dir){/***********************第一部分****************/FRESULTerror;PRINTF("\r\n创建目录“%s”......\r\n",dir);/**********************第二部分*****************/error=f_mkdir(_T(dir));/**********************第三部分******************/if(error){if(error==FR_EXIST){PRINTF("目录已经存在\r\n");}else{PRINTF("创建目录失败.\r\n");returnerror;}}else{PRINTF("创建目录成功\r\n");}returnerror;}与创建文件类似。首先调用FatFs提供的函数f_mkdir创建目录,然后根据返回值判断创建结果。这部分代码比较简单,这里不再赘述。

读取目录测试

获取一个文件夹下的文件和目录需要两步。第一,使用f_opendir函数打开路径。第二步,调用f_readdir函数读取目录下的文件或文件夹。如代码清单2820所示。

1234FRESULTf_opendir(DIR*dp,/*指向要创建的目录对象的指针*/constTCHAR*path/*指向目录路径的指针*/)f_opendir函数共有两个参数,dp用于保存所创建目录对象的信息,打开成功后就可以使用该指针作为参数读取目录内容了。path,指定要打开的目录。

1234FRESULTf_readdir(DIR*dp,/*指向打开的路径*/FILINFO*fno/*指向保存文件信息的结构体指针*/)第二部分代码我们使用f_opendir函数打开了一个路径,f_readdir函数用于依次读取路径里的内容,每次读取都会将读到的信息保存一个FILINFO类型的文件信息结构体中,FILINFO结构体如代码清单2823所示。

12345#defineAM_RDO0x01/*只读*/#defineAM_HID0x02/*隐藏*/#defineAM_SYS0x04/*系统文件*/#defineAM_DIR0x10/*目录*/#defineAM_ARC0x20/*文件*/一个文件或目录的属性由这些选项组合得到。

调用f_readdir函数读取之后,我们可以根据返回值得到读取是否成功。根据传回的文件信息结构体指针FILINFO得到文件属性信息。我们在程序中判断读到的是文件还是文件夹,然后分别通过串口输出。

文件读写测试

123456FRESULTf_write(FIL*fp,/*指向文件描述符*/constvoid*buff,/*指向要发送的数据*/UINTbtw,/*发送数据量*/UINT*bw/*实际发送数据量*/)结合本程序,各个参数的作用简单讲解如下:

写入结束判断函数返回值类型,如果发送错误或者预期写入数据量与实际写入数据量不一致则输出错误提示。

1234FRESULTf_lseek(FIL*fp,/*文件描述符*/FSIZE_tofs/*相对于文件开始处的偏移*/)f_lseek函数共有两个参数,一个用于指定文件描述符,另外指定文件读写指针相对于文件起始位置的偏移,本次要从文件开始读取,所以偏移设为0即可。

同其他函数,根据函数的返回值判断函数执行是否成功,并输出提示信息。

123456FRESULTf_read(FIL*fp,/*指向文件描述符*/void*buff,/*接收数据缓冲区*/UINTbtr,/*接收数据量*/UINT*br/*实际接收数据量*/)f_read函数与f_write函数的参数非常类似,只是将读改为写,详细请参考f_write函数或者官方f_read函数说明文档。

THE END
1.软件测试类资料整理测试计划书,测试报告,测试方案,性能测试测试一下测试一下 2 引言 2.1 编写目的 2.3 测试人员 2.4 项目背景 2.5 测试目标 2.6 简写和缩略词 2.7 参考资料 2.8 测试提交文档 2.9 测试进度 3 测试环境 3.1 软硬件环境 4 测试工具 5 测试策略 5.1 测试阶段划分及内容 5.1.1 集成测试 5.1.2 系统测试 https://www.bilibili.com/opus/1012102948759535617
2.图书馆管理系统软件测试20231123.doc文档简介测试分析报告1引言1.1编写目的本测试报告为图书出租管理系统的测试报告,目的在于总结测试阶段的测试以及分析测试结果,描述网站是否到达用户注册登录与图书出租功能目标。预期参考人员包括范逸雪,高郗聪。1.2背景说明:被测试软件系统的名称:图书出租管理系统该软件的任务提出者:张银柯开发者:冉亚瑞,唐川裕用户:河南https://www.renrendoc.com/paper/298954810.html
3.图书管理系统测试报告书软件测试.docx要测试人员苗玉丹参与测试人员付正弟、梅飞、杨梦迪、袁静3.1.2测试时间列出测试的跨度和工作量,最好种以上不同的用例设计方法。2.2测试环境与配置简要介绍测试环境及其配置。提示:清单如下,如果系统/项对测试结论部分感兴趣,开发人员希望从缺陷结果以及分析得到产品开发质量的信息,项目管理者对测试执行中成 要测试人https://max.book118.com/html/2024/0128/5200241014011101.shtm
4.[图书馆管理系统]软件测试说明书本文档将详细说明本系统采用采用软件工程的方法进行软件测试。尽量发现本系统BUG并即时改正,以避免在运行过程中造成损失。 1.1 1。1。1登录板块 用户登录测试 用户登录的JS页面用白盒法测试。 管理员登录测试 用户登录的JS页面用白盒法测试。 用户注销测试 用户登录的JS页面用白盒法测试. 管理员注销测试 用户登录https://wenku.baidu.com/view/0d4f415bbb4ae45c3b3567ec102de2bd9605dedc.html
5.图书馆软件测试文档实例,图书馆管理系统测试用例系统能按条件检索出图书信息显示在窗口 中 To Do 选中需修改的图书,按F2键系统显示该图书详细编目信息,可进行修 改保存或删除To Do 借书证管理TC-zj-001系统“主菜单”下选择“流通”,点击“借书证 管理”下借书证办理 系统弹出”借书证办理“窗口 https://blog.csdn.net/weixin_42300418/article/details/119020734
6.图书管理系统测试报告1软件测试文档大小: 101.82K 文档页数: 11页 顶/踩数: 0/0 收藏人数: 0 评论次数: 0 文档热度: 文档分类: IT计算机--软件测试 系统标签: 测试图书测试报告管理系统软件黑盒 图书管理系统 测试报告 第五组 2014年6月2日 目录 1.引言 1.1编写目的 1.2项目背景https://www.docin.com/p-4554526387.html
7.图书馆管理系统测试用例表.pdf该【图书馆管理系统测试用例表 】是由【hh思密达】上传分享,文档一共【16】页,该文档可以免费在线阅读,需要了解更多关于【图书馆管理系统测试用例表 】的内容,可以使用淘豆网的站内搜索功能,选择自己适合的文档,以下文字是截取该文章内的部分文字,如需要获得完整电子版,请下载此文档到您的设备,方便您编辑和打印。:https://www.taodocs.com/p-883074790.html
8.学校图书馆管理信息系统开发的可行性研究报告(通用10篇)该次系统开发的对象是银杏酒店管理学院,开发该系统的目的主要是对有关读者,书籍,借书,还书,存书等方面的信息进行统一管理,本系统结构分为读者信息管理模块、书籍信息管理模块、借阅信息管理模块和管理者信息管理模块。图书馆信息化是对信息管理系统化、科学化要求甚高的单位。通过该系统图书馆在很多个方面的效率都得到https://www.360wenmi.com/f/fileiv3v59e5.html
9.电子图书管理系统,电子图书阅览室,图书管理系统哪个好,电子阅览室数字图书馆软件(电子阅览室软件)是一款强大的数字资源信息管理系统,具有先进的和人性化的设计理念。具备一整套完善、高效的资源管理、分类、搜索、阅读等核心技术和机制。独创的“双登录模式”更是极大的方便了管理员的管理和终端用户的使用,真正体现了管理平台的人性化、灵活化和通用化。 https://www.tongru.net/dztsglxt.html
10.关于软件项目工作计划(精选5篇)测试服务器:2台 办公电脑:12台 网络设备、打印机等辅助设备 3. 软件配置: 操作系统、数据库、开发工具等必要软件 版本控制、项目管理等软件工具 四、项目风险管理 1. 技术风险:采用成熟的技术方案和开源框架,减少技术风险。 2. 人员风险:合理安排人员,避免人员流失对项目造成影响。 https://www.ruiwen.com/word/guanyuruanjianxiangmugongzuojihua.html
11.Java课程设计学籍信息管理系统「建议收藏」学生的学籍信息是记录学生的重要信息档案,如何以电子文档形式记录下学生的学籍信息是每个学校必须做的事情,该学生学籍信息管理系统就是为了方便学校记录下每一个学生的基本信息,生成电子数据库,并且能够做到查询、更改、删除、浏览等功能操作,让学籍信息的管理更加方便快捷。 二、设计方案 1、系统框架 学籍信息管理系统https://cloud.tencent.com/developer/article/2054519
12.图书管理系统的设计与实现:从数据库到用户界面图书管理系统是一种用于图书馆或书店管理图书信息的软件应用,它通常包括图书入库、借阅、归还、查询等功能。 系统需求分析 在设计图书管理系统之前,需要明确系统的基本需求,包括用户角色、功能模块、数据存储等。 数据库设计 数据库是图书管理系统的核心,用于存储图书信息、用户信息、借阅记录等数据。 https://www.ctyun.cn/zhishi/p-423659
13.世界500强所属国企事业单位高新企业的面谈机会别错过!5. 熟悉医疗器械产品法规或质量管理标准(如:GMP、FDA、MDR法规等)。 影像软件算法岗(北京 朝阳) 1.参与骨科机器人相关图像处理算法的设计、开发、优化和测试; 2.参与骨科放射影像后处理算法的设计和实现; 3.参与相关设计、开发、测试、验证等文档的编写; https://www.thepaper.cn/newsDetail_forward_17587878
14.软件实训心得体会(通用15篇)我深刻的认识到要成为一名优秀的软件开发人员不是一件容易的事情,不仅要有足够的干劲和热情,还要有扎实的编写代码基础,必须要有事先对文档进行可靠性报告,功能说明书,详细设计说明书等的编写和一些风险评估的编写的能力。除了图书馆,最能让我感觉到身在大学的就是实训机房,在匆匆过去的两个月内,我往返于实训机房https://www.fwsir.com/xinde/html/xinde_20221230125541_2171950.html
15.计算机一级《WPS》考试题库及答案202419.图书馆管理系统中实体图书和实体借阅人之间的联系是()。 AN:1 BM:N C1:1 D1:N 【参考答案】 1.B 2.C 3.D 4.A 5.D 计算机一级《WPS》考试题库及答案 3 1. 下列叙述中正确的是 A .线性表是线性结构 B .栈与队列是非线性结构 C .线性链表是非线性结构 D .二叉树是线性结构 2. 数据的存https://www.oh100.com/kaoshi/ncre1/tiku/284396.html
16.软件实习报告15篇经过为期4个月的专业实习,令我更深一步的了解和学习了软件开发的一般过程,不再是以前那样,都不明白软件开发是什么东西。对于一个应用系统他们为什么要那么多人来做,而这么多人一齐做,代码又是如何进行管理的。对于每一个应用系统,企业到底用到哪些技术,他们为什么要选取这些技术,我们开发人员的主要任务是什么等等,这https://www.gdyjs.com/shiyongwen/shixibaogao/356810.html
17.软件开发工作总结15篇软件功能模块的设计工作,在张工的指导、讲解下初次尝试完成了管理软件的维护子系统的功能模块设计;在九月中旬跟随公司施工人员在 一号矿进行业务调研,从而在我们自己的管理系统中,取其长、补其短,也借此机会了解实际的煤矿生产情况,加深对煤矿产业业务流程的理解;在九月底便根据张工的指导开始工程技术文档子系统的功能https://www.cnfla.com/gongzuozongjie/3046214.html