串行FLASH文件系统FatFs-实际应用

news2024/11/26 18:31:47

目录

串行FLASH文件系统FatFs-实际应用

功能展示

程序代码

程序过程分析


串行FLASH文件系统FatFs-实际应用

功能展示

实验主要使用我们移植好的FatFs的FLASH文件系统实现三个功能:设备信息获取、文件定位写入功能测试和文件信息获取测试功能。

最终串口输出结果如下:

对于上一次移植的文件系统不需要做任何改动,只需要在main文件中实现功能应用即可:

程序代码

main文件内容如下:

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "ff.h"
#include "string.h"
/**
  ******************************************************************************
  *                              定义变量
  ******************************************************************************
  */
FATFS fs;                                                    /* FatFs文件系统对象 */
FIL fnew;                                                    /* 文件对象 */
FRESULT res_flash;                /* 文件操作结果 */
UINT fnum;                                  /* 文件成功读写数量 */
char fpath[100];                  /* 保存当前扫描路径 */
char readbuffer[512];             
 
/**
  ******************************************************************************
  *                                任务函数
  ******************************************************************************
  */
/* FatFs多项功能测试 */
static FRESULT miscellaneous(void)
{
  DIR dir;
  FATFS *pfs;
  DWORD fre_clust, fre_sect, tot_sect;
  
  printf("\n*************** 设备信息获取 ***************\r\n");
  /* 获取设备信息和空簇大小 */
  res_flash = f_getfree("1:", &fre_clust, &pfs);

  /* 计算得到总的扇区个数和空扇区个数 */
  tot_sect = (pfs->n_fatent - 2) * pfs->csize;
  fre_sect = fre_clust * pfs->csize;

  /* 打印信息(4096 字节/扇区) */
  printf("》设备总空间:%10lu KB。\n》可用空间:  %10lu KB。\n", tot_sect *4, fre_sect *4);
  
  printf("\n******** 文件定位和格式化写入功能测试 ********\r\n");
  res_flash = f_open(&fnew, "1:FatFs读写测试文件.txt",
                            FA_OPEN_ALWAYS|FA_WRITE|FA_READ );
    if ( res_flash == FR_OK )
    {
    /*  文件定位 */
    res_flash = f_lseek(&fnew,f_size(&fnew));
    if (res_flash == FR_OK)
    {
      /* 格式化写入,参数格式类似printf函数 */
      f_printf(&fnew,"\n在原来文件新添加一行内容\n");
      f_printf(&fnew,"》设备总空间:%10lu KB。\n》可用空间:  %10lu KB。\n", tot_sect *4, fre_sect *4);
      /*  文件定位到文件起始位置 */
      res_flash = f_lseek(&fnew,0);
      /* 读取文件所有内容到缓存区 */
      res_flash = f_read(&fnew,readbuffer,f_size(&fnew),&fnum);
      if(res_flash == FR_OK)
      {
        printf("》文件内容:\n%s\n",readbuffer);
      }
    }
    f_close(&fnew);    
    
    printf("\n********** 目录创建和重命名功能测试 **********\r\n");
    /* 尝试打开目录 */
    res_flash=f_opendir(&dir,"1:TestDir");
    if(res_flash!=FR_OK)
    {
      /* 打开目录失败,就创建目录 */
      res_flash=f_mkdir("1:TestDir");
    }
    else
    {
      /* 如果目录已经存在,关闭它 */
      res_flash=f_closedir(&dir);
      /* 删除文件 */
      f_unlink("1:TestDir/testdir.txt");
    }
    if(res_flash==FR_OK)
    {
      /* 重命名并移动文件 */
      res_flash=f_rename("1:FatFs读写测试文件.txt","1:TestDir/testdir.txt");      
    } 
    }
  else
  {
    printf("!! 打开文件失败:%d\n",res_flash);
    printf("!! 或许需要再次运行“FatFs移植与读写测试”工程\n");
  }
  return res_flash;
}

  FILINFO fno;

/**
  * 文件信息获取
  */
static FRESULT file_check(void)
{
  
  /* 获取文件信息 */
  res_flash=f_stat("1:TestDir/testdir.txt",&fno);
  if(res_flash==FR_OK)
  {
    printf("“testdir.txt”文件信息:\n");
    printf("》文件大小: %ld(字节)\n", fno.fsize);
    printf("》时间戳: %u/%02u/%02u, %02u:%02u\n",
           (fno.fdate >> 9) + 1980, fno.fdate >> 5 & 15, fno.fdate & 31,fno.ftime >> 11, fno.ftime >> 5 & 63);
    printf("》属性: %c%c%c%c%c\n\n",
           (fno.fattrib & AM_DIR) ? 'D' : '-',      // 是一个目录
           (fno.fattrib & AM_RDO) ? 'R' : '-',      // 只读文件
           (fno.fattrib & AM_HID) ? 'H' : '-',      // 隐藏文件
           (fno.fattrib & AM_SYS) ? 'S' : '-',      // 系统文件
           (fno.fattrib & AM_ARC) ? 'A' : '-');     // 档案文件
  }
  return res_flash;
}

/**
  * @brief  scan_files 递归扫描FatFs内的文件
  * @param  path:初始扫描路径
  * @retval result:文件系统的返回值
  */
static FRESULT scan_files (char* path) 
{ 
  FRESULT res;         //部分在递归过程被修改的变量,不用全局变量    
  FILINFO fno; 
  DIR dir; 
  int i;            
  char *fn;        // 文件名    
    
#if _USE_LFN 
  /* 长文件名支持 */
  /* 简体中文需要2个字节保存一个“字”*/
  static char lfn[_MAX_LFN*2 + 1];     
  fno.lfname = lfn; 
  fno.lfsize = sizeof(lfn); 
#endif 
  //打开目录
  res = f_opendir(&dir, path); 
  if (res == FR_OK) 
    { 
    i = strlen(path); 
    for (;;) 
        { 
      //读取目录下的内容,再读会自动读下一个文件
      res = f_readdir(&dir, &fno);                                 
      //为空时表示所有项目读取完毕,跳出
      if (res != FR_OK || fno.fname[0] == 0) break;     
#if _USE_LFN 
      fn = *fno.lfname ? fno.lfname : fno.fname; 
#else 
      fn = fno.fname; 
#endif 
      //点表示当前目录,跳过            
      if (*fn == '.') continue;     
      //目录,递归读取      
      if (fno.fattrib & AM_DIR)         
            {             
        //合成完整目录名        
        sprintf(&path[i], "/%s", fn);         
        //递归遍历         
        res = scan_files(path);    
        path[i] = 0;         
        //打开失败,跳出循环        
        if (res != FR_OK) 
                    break; 
      } 
            else 
            { 
                printf("%s/%s\r\n", path, fn);                                //输出文件名    
        /* 可以在这里提取特定格式的文件路径 */        
      }//else
    } //for
  } 
  return res; 
}
/**
  * @brief  主函数
  * @param  无
  * @retval 无
  */
int main(void)
{        
    /* 初始化调试串口,一般为串口1 */
    USART_Config();    
  printf("******** 这是一个SPI FLASH 文件系统实验 *******\r\n");
  printf("\r\n 使用指南者底板时 左上角排针位置 不要将PC0盖有跳帽 防止影响PC0做SPIFLASH片选脚 \r\n");
  
    //在外部SPI Flash挂载文件系统,文件系统挂载时会对SPI设备初始化
    res_flash = f_mount(&fs,"1:",1);
  if(res_flash!=FR_OK)
  {
    printf("!!外部Flash挂载文件系统失败。(%d)\r\n",res_flash);
    printf("!!可能原因:SPI Flash初始化不成功。\r\n");
        while(1);
  }
  else
  {
    printf("》文件系统挂载成功,可以进行测试\r\n");    
  }
  
  /* FatFs多项功能测试 */
  res_flash = miscellaneous();

  
  printf("\n*************** 文件信息获取测试 **************\r\n");
  res_flash = file_check();

  
  printf("***************** 文件扫描测试 ****************\r\n");
  strcpy(fpath,"1:");
  scan_files(fpath);
  
  
    /* 不再使用文件系统,取消挂载文件系统 */
    f_mount(NULL,"1:",1);
  
  /* 操作完成,停机 */
    while(1)
    {
    }
}

程序过程分析

主要来看miscellaneous()、file_check()和 scan_files(fpath)三个我们自己定义的应用层函数

1、首先定义文件系统需要用到的变量

2、然后进行串口初始化和FLASH的挂载

对应串口输出

3、我们先看miscellaneous()函数:FatFs多项功能测试函数

/* FatFs多项功能测试 */
static FRESULT miscellaneous(void)
{
  DIR dir;
  FATFS *pfs;
  DWORD fre_clust, fre_sect, tot_sect;
  
  printf("\n*************** 设备信息获取 ***************\r\n");
  /* 获取设备信息和空簇大小 */
  res_flash = f_getfree("1:", &fre_clust, &pfs);

  /* 计算得到总的扇区个数和空扇区个数 */
  tot_sect = (pfs->n_fatent - 2) * pfs->csize;
  fre_sect = fre_clust * pfs->csize;

  /* 打印信息(4096 字节/扇区) */
  printf("》设备总空间:%10lu KB。\n》可用空间:  %10lu KB。\n", tot_sect *4, fre_sect *4);
  
  printf("\n******** 文件定位和格式化写入功能测试 ********\r\n");
  res_flash = f_open(&fnew, "1:FatFs读写测试文件.txt",
                            FA_OPEN_ALWAYS|FA_WRITE|FA_READ );
    if ( res_flash == FR_OK )
    {
    /*  文件定位 */
    res_flash = f_lseek(&fnew,f_size(&fnew));
    if (res_flash == FR_OK)
    {
      /* 格式化写入,参数格式类似printf函数 */
      f_printf(&fnew,"\n在原来文件新添加一行内容\n");
      f_printf(&fnew,"》设备总空间:%10lu KB。\n》可用空间:  %10lu KB。\n", tot_sect *4, fre_sect *4);
      /*  文件定位到文件起始位置 */
      res_flash = f_lseek(&fnew,0);
      /* 读取文件所有内容到缓存区 */
      res_flash = f_read(&fnew,readbuffer,f_size(&fnew),&fnum);
      if(res_flash == FR_OK)
      {
        printf("》文件内容:\n%s\n",readbuffer);
      }
    }
    f_close(&fnew);    
    
    printf("\n********** 目录创建和重命名功能测试 **********\r\n");
    /* 尝试打开目录 */
    res_flash=f_opendir(&dir,"1:TestDir");
    if(res_flash!=FR_OK)
    {
      /* 打开目录失败,就创建目录 */
      res_flash=f_mkdir("1:TestDir");
    }
    else
    {
      /* 如果目录已经存在,关闭它 */
      res_flash=f_closedir(&dir);
      /* 删除文件 */
      f_unlink("1:TestDir/testdir.txt");
    }
    if(res_flash==FR_OK)
    {
      /* 重命名并移动文件 */
      res_flash=f_rename("1:FatFs读写测试文件.txt","1:TestDir/testdir.txt");      
    } 
    }
  else
  {
    printf("!! 打开文件失败:%d\n",res_flash);
    printf("!! 或许需要再次运行“FatFs移植与读写测试”工程\n");
  }
  return res_flash;
}

(1)先看f_getfree函数,第一个参数为设备路径,第二个参数为当前空余簇的大小,第三个参数为文件系统句柄,注意为二级指针。第二、第三个参数作为输出

tot_sect = (pfs->n_fatent - 2) * pfs->csize;表示总的扇区个数=簇的个数*每个簇有几个扇区

fre_sect = fre_clust * pfs->csize;表示空余的扇区个数=空余簇的数目*每个簇的扇区个数

然后通过

printf("》设备总空间:%10lu KB。\n》可用空间: %10lu KB。\n", tot_sect *4, fre_sect *4);

算出设备空间的信息

具体操作可查看

(2)然后进行文件定位和格式化写入功能测试

首先通过f_open打开文件,然后f_lseek定位到文件末尾,接着使用f_printf进行格式化的写入。然后使用f_lseek定位到开头,接着使用f_read进行读取文件内容。最后要使用f_close关闭文件

可参考

(3)接着进行目录创建和重命名功能测试

首先使用“res_flash=f_opendir(&dir,"1:TestDir");”打开文件夹,如果文件夹不存在,就通过“res_flash=f_mkdir("1:TestDir");”新建文件夹。如果目录已经存在,就关闭它“res_flash=f_closedir(&dir);”,然后“f_unlink("1:TestDir/testdir.txt");”删除文件

接着使用“res_flash=f_rename("1:FatFs读写测试文件.txt","1:TestDir/testdir.txt");”将“FatFs读写测试文件.txt”文件移动到“1:TestDir/”下并重命名为“testdir.txt”

4、然后看文件信息获取函数(对于时间戳,设置为当前时间可以配置RTC来实现)

  FILINFO fno;

/**
  * 

  */
static FRESULT file_check(void)
{
  
  /* 获取文件信息 */
  res_flash=f_stat("1:TestDir/testdir.txt",&fno);
  if(res_flash==FR_OK)
  {
    printf("“testdir.txt”文件信息:\n");
    printf("》文件大小: %ld(字节)\n", fno.fsize);
    printf("》时间戳: %u/%02u/%02u, %02u:%02u\n",
           (fno.fdate >> 9) + 1980, fno.fdate >> 5 & 15, fno.fdate & 31,fno.ftime >> 11, fno.ftime >> 5 & 63);
    printf("》属性: %c%c%c%c%c\n\n",
           (fno.fattrib & AM_DIR) ? 'D' : '-',      // 是一个目录
           (fno.fattrib & AM_RDO) ? 'R' : '-',      // 只读文件
           (fno.fattrib & AM_HID) ? 'H' : '-',      // 隐藏文件
           (fno.fattrib & AM_SYS) ? 'S' : '-',      // 系统文件
           (fno.fattrib & AM_ARC) ? 'A' : '-');     // 档案文件
  }
  return res_flash;
}

 

通过f_stat函数获取文件信息

可参考

5、最后看递归扫描函数scan_files

scan_files通过不断调用自身,进行文件的扫描(注意使用递归函数时,一定要把栈空间分配大一点)

首先通过“res = f_opendir(&dir, path);”打开目录

然后通过for循环不断使用“res = f_readdir(&dir, &fno); ”进行读取,如果读取到文件名为空,表示读取到目录末尾,就跳出循环;如果读取到目录,就继续调用scan_files进行递归扫描

static FRESULT scan_files (char* path) 
{ 
  FRESULT res;         //部分在递归过程被修改的变量,不用全局变量    
  FILINFO fno; 
  DIR dir; 
  int i;            
  char *fn;        // 文件名    
    
#if _USE_LFN 
  /* 长文件名支持 */
  /* 简体中文需要2个字节保存一个“字”*/
  static char lfn[_MAX_LFN*2 + 1];     
  fno.lfname = lfn; 
  fno.lfsize = sizeof(lfn); 
#endif 
  //打开目录
  res = f_opendir(&dir, path); 
  if (res == FR_OK) 
    { 
    i = strlen(path); 
    for (;;) 
        { 
      //读取目录下的内容,再读会自动读下一个文件
      res = f_readdir(&dir, &fno);                                 
      //为空时表示所有项目读取完毕,跳出
      if (res != FR_OK || fno.fname[0] == 0) break;     
#if _USE_LFN 
      fn = *fno.lfname ? fno.lfname : fno.fname; 
#else 
      fn = fno.fname; 
#endif 
      //点表示当前目录,跳过            
      if (*fn == '.') continue;     
      //目录,递归读取      
      if (fno.fattrib & AM_DIR)         
            {             
        //合成完整目录名        
        sprintf(&path[i], "/%s", fn);         
        //递归遍历         
        res = scan_files(path);    
        path[i] = 0;         
        //打开失败,跳出循环        
        if (res != FR_OK) 
                    break; 
      } 
            else 
            { 
                printf("%s/%s\r\n", path, fn);                                //输出文件名    
        /* 可以在这里提取特定格式的文件路径 */        
      }//else
    } //for
  } 
  return res; 
}

其中path字符串数组容量尽量设置够大。

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

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

相关文章

4.带你入门matlab排序最值标准差标差(matlab程序)

%% 学习目标:排序,最值,标准差,方差 代码及结果 %% 排序 clear all; X[1 3 4;8 3 5;2 7 4] y1sort(X) %按列由小到大排序 y2sort(X,2) %按行由小到大排序 y3sort(X,1,descend) %按列由大…

自定义程序包不存在的解决方法

方案一&#xff1a; 在pom文件中加入以下代码 <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.4.2</version><configuration><skipTests>true</sk…

数据驱动的决策优化,半导体CMS系统在企业管理中的应用

在当今信息时代&#xff0c;数据的重要性在企业管理中愈发凸显。半导体制造业作为一个高度自动化和数据密集的行业&#xff0c;利用数据进行决策优化对于企业的成功至关重要。在这方面&#xff0c;PreMaint CMS系统扮演着关键的角色&#xff0c;为企业管理提供了数据驱动的决策…

Git Commit的规范及高级使用方法

git commit是日常工作中使用率极高的一个命令&#xff0c;但是根据我从业5年的经验来看&#xff0c;大多数人在用git commit命令时都很粗糙&#xff0c;比如git commit -m 后跟的message是五花八门&#xff0c;有用中文的&#xff0c;有用英文的&#xff0c;甚至还有直接跟111的…

小程序搭建教程

小程序搭建流程 随着移动互联网的发展&#xff0c;小程序成为了营销和服务的重要渠道之一。在本文中&#xff0c;我们将介绍如何搭建一个小程序的详细流程&#xff0c;包括公众号申请、支付微信认证费、复用资质申请小程序、开通微信支付、绑定第三方开发平台、添加商品服务内…

基于SpringBoot的企业人力资源管理系统设计与实现

1.引言 随着经济改革开放的发展&#xff0c;企业之间的竞争也越来越激烈&#xff0c;在这样的大环境下&#xff0c;企业要想立于不败之地&#xff0c;不仅要有好的商品&#xff0c;更要有一支强有力的团队&#xff0c;其本质就是人才。企业对人才的需求不断的增加&#xff0c;…

视频剪辑素材网站,免费可商用,赶紧马住~

推荐几个高质量视频素材网站&#xff0c;免费可商用&#xff0c;建议收藏&#xff01; 菜鸟图库 https://www.sucai999.com/video.html?vNTYxMjky 菜鸟图库虽然是一个设计网站&#xff0c;但它还有非常丰富的视频和音频素材&#xff0c;视频素材全部都是高清无水印&#xff…

485远传水表抄表系统

485远传水表抄表系统是一种基于RS-485通信协议的远程抄表系统&#xff0c;它利用现代通信技术&#xff0c;实现了对水表数据的远程采集、传输和处理&#xff0c;是智慧城市建设的重要组成部分。本文将从以下几个方面对485远传水表抄表系统进行全面介绍&#xff1a; 一、系统组成…

【Spring Boot】Spring Boot的系统配置 — 系统配置文件

系统配置文件 Spring Boot的系统配置文件&#xff0c;包括application.properties和application.yml配置文件的使用以及YML和Properties配置文件有什么区别&#xff0c;最后介绍如何更改Spring Boot的启动图案。 1.application.properties Spring Boot支持两种不同格式的配置…

pyqt结合深度学习框架做系统

时隔将近两个月&#xff0c;我胡汉三又回来啦&#xff01;&#xff01;&#xff01;将近两个月玩了一个月&#xff0c;半个月面试&#xff0c;半个月吃土沉淀&#xff08;有收获&#xff09;也投出去一篇论文&#xff08;外审中&#xff0c;关于深度学习神经网络改进的一篇病害…

如何将代理IP集成到自动化测试框架中?

自动化测试框架是现代软件工程中不可或缺的一部分。它们提供了许多好处&#xff0c;包括&#xff1a;更快的测试速度&#xff0c;更高的可靠性和更广泛的测试覆盖范围。然而&#xff0c;在测试过程中&#xff0c;有些网站可能需要使用代理IP&#xff0c;这是因为一些网站会限制…

踩坑记录:xorm的sql()函数后无法使用FindAndCount()

一、起因 懒省事想用 sql 写个有连表操作的分页查询语句&#xff0c;看到 xorm 中 sql 要和代码紧密纠缠在一起就不爽&#xff0c;所有就想用 xorm 中的 session.SQL(sql).Limit(size, offset).FindAndCount() 方法解决。不曾想。。。 sql: expected 15 destination argument…

[计算机入门] 磁盘、磁盘分区、盘符、卷标

2.5 磁盘、磁盘分区、盘符、卷标 磁盘&#xff1a;指计算机中存储数据和文件的物理设备&#xff0c;也称为硬盘。 磁盘分区&#xff1a;指将一个磁盘分成多个逻辑部分&#xff0c;每个部分可以独立地进行管理和操作&#xff0c;例如格式化、安装操作系统等。 如上图&#xf…

AWS 中文入门开发教学 46- S3 - AWS的存储核心, Simple Storage Service

知识点 S3的基础知识官网 https://aws.amazon.com/cn/s3 基础介绍 Amazon Simple Storage Service (Amazon S3) 是一种对象存储服务,(其实就是文件存储) 提供行业领先的可扩展性、数据可用性、安全性和性能。这意味着各种规模和行业的客户都可以使用 S3 来存储并保护各种…

归并排序—C语言实现

前言 &#x1f970;在学数据结构的第一节课就知道了数据结构课程是要管理并且学会操作数据&#xff0c;当然操作数据首先想到的就是数据的排序&#xff0c;排过顺序的数据的使用价值才够大。前面我们学习了顺序表也学习了链表等等&#xff0c;这些就是储存数据的方法&#xff0…

2021机器学习阶段性复盘

文章目录 特征选择一、GBDT和Xgboost简介二、 GBDTLR协同过滤&#xff0c;SVD的劣势&#xff1a;极大化似然估计Wide&DeepxDeepFM朴素贝叶斯SVM转为拉格朗日函数转为对偶问题 决策树分类交叉熵损失函数数据预处理连续型特征为什么取对数 深度学习激活函数的作用Softmax与Si…

剑指offer64.求1+2+...+n

看到题脑子里就有了想法&#xff0c;这还不简单&#xff0c;直接用递归啊&#xff0c;return一个nsumNums(n-1)写完之后发现到了0你得终止但是不能用if等语句你没办法终止&#xff0c;想了大概十分钟放弃了&#xff0c;这没办法终止啊&#xff0c;然后看了题解。 class Soluti…

QCFS-related work

一、代码 def replace_maxpool2d_by_avgpool2d(model): # 将模型中的所有MaxPool2d层替换为AvgPool2d层。for name, module in model._modules.items(): # 函数使用递归方式遍历模型的所有模块&#xff0c;通过model._modules.items()获取模型的子模块以及它们对应的名称。if …

Java电子招投标采购系统源码-适合于招标代理、政府采购、企业采购

功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查看所…

【Elasticsearch】文档操作

目录 3.文档操作 3.1.新增文档 3.2.查询文档 3.3.删除文档 3.4.修改文档 3.4.1.全量修改 3.4.2.增量修改 3.5.总结 3.文档操作 3.1.新增文档 语法&#xff1a; POST /索引库名/_doc/文档id {"字段1": "值1","字段2": "值2"…