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

目录

串行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字符串数组容量尽量设置够大。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

竹烟淮雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值