如何设置FatFs文件系统支持长文件名

  • 学习17583次

“长文件名”“短文件名”的详细概念请自行去百度谷歌搜狗搜索。

我们现在只需要知道一个文件名称长了,就是长文件名,例如sdjflasdjfas.txt;一个文件的名称短了,就是短文件名,例如a.txt。

有人会问,文件名干嘛要分长短?这个问题属于计算机历史问题了。想深究,去百度吧。

咱们先看看用“不支持长文件名”的文件系统和“支持长文件名”的文件系统分别读一个比较长的文件名显示的效果吧。

 

下面是“不支持长文件名”的文件系统读出来的:

长文件名

下面是“支持长文件名”的文件系统读出来的:

长文件名

很显然,文件系统如果不支持长文件名,岂能用哉?

以FatFs为例,刚刚移植好的文件系统,默认是不支持长文件名的,要想支持长文件名,需要打开ffconf.h文件进行配置,找到_USE_LFN,把值从0改到1。如下图所示:

FatFs_LFN

改成1以后,任务还没有完成。为了能够支持中文,还需要把_CODE_PAGE的值改为936,如下图所示:

936中文GBK

这时候,你编译一下,系统会有如下错误产生:

.\RationEB_Proj.axf: Error: L6218E: Undefined symbol ff_convert (referred from ff.o).
.\RationEB_Proj.axf: Error: L6218E: Undefined symbol ff_wtoupper (referred from ff.o).
Not enough information to list image symbols.
Finished: 1 information, 0 warning and 2 error messages.
".\RationEB_Proj.axf" - 2 Error(s), 0 Warning(s).

提示,找不到ff_convert()和ff_wtoupper()这两个函数。

 

ff_convert()函数用来把Unicode和GBK之间进行转换。因为文件系统的文件名默认存储方式为Unicode编码,而我们编译器甚至是电脑,用的中文码为GBK。

比如说,现在我要把一个文件名读出来显示到TFT上,当我们读完文件名以后,文件名实际上是由Unicode编码的,这时候,我们就需要找到这些Unicode码对应的GBK码,因为我们的字库是按照GBK编码的,所以需要用到ff_convert()函数里面的Unicode转GBK转换表来转换。由于中文有2万多个汉字,这张转换表实在是太大了,编译不通过,因为单片机容量太小了。所以我们把这张表格放到外部的FLASH吧。

再比如说,我们要在SD卡上新建文件,我们给它的名字是由GBK编码的,而文件名存储,必须是Unicode才行,这时候就需要用到GBK转Unicode的表格。这张表同样很大,所以我们把这张表也放到外部的FLASH里面。

 

ff_wtoupper()函数是用来英文大小写转换的,比如说,我们把文件名写为ABC.TXT,我们读abc.txt同样会读到这个文件。就是这个文件起的作用。

 

这两个函数,位于cc936.c文件中,所以我们要把这个文件添加到工程中,cc936.c文件位了FatFs源码的option文件夹当中。

添加好后的工程如下所示:

添加cc936

打开cc936.c文件,ff_wtoupper()文件不用修改。把ff_convert()函数里面的U2G和G2U两张表格数据删除,把函数修改为读取外部的FLASH,来进行U2G和G2U的转换。修改后的函数为:

WCHAR ff_convert ( /* Converted code, 0 means conversion error */
WCHAR src, /* Character code to be converted */
UINT dir /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */
)
{
  WCHAR c;
  uint32_t offset; // W25X16地址便宜
  uint8_t GBKH,GBKL; // GBK码高位与低位
  uint8_t unigbk[2]; // 
  uint8_t gbkuni[2]; // 

  if (src < 0x80) /* ASCII */
  { 
    c = src;
  }
  else
  {
    if(dir == 0) /* Unicode to GBK */
    {
      if( (src > 0x4DFF) && (src < 0x9FA6) )
      {
        offset = ((((uint32_t)src - 0x4E00) * 2) + 0x0C0000); 
        W25Q16_Read(unigbk,offset,2); 
        c = (((uint16_t)unigbk[0])<<8)+(uint16_t)unigbk[1]; 
      }
      else c = 0xA1A1; //如果是其它符号,都用NULL代替
    }
    else if(dir == 1) /* GBK to Unicode */
    {
      GBKH=(uint8_t)(src>>8); 
      GBKL=(uint8_t)(src); 
      GBKH-=0x81;
      GBKL-=0x40;
      offset=((uint32_t)192*GBKH+GBKL)*2;
      W25Q16_Read(gbkuni,offset+0x0D0000,2); 
      c = (((uint16_t)gbkuni[1])<<8)+(uint16_t)gbkuni[0];
    }
  }
  return c;
}

两张大表放到外部flash,这里就精简很多了吧。

不过,这时候,还是有一些中文符号无法显示,例如常用的书名号《》,中文顿号、,中文双引号 单引号,中文括号【】等都不能显示。这时候,我们可以手动添加进去让其显示,如下:

WCHAR ff_convert ( /* Converted code, 0 means conversion error */
WCHAR src, /* Character code to be converted */
UINT dir /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */
)
{
  WCHAR c;
  uint32_t offset; // W25X16地址便宜
  uint8_t GBKH,GBKL; // GBK码高位与低位
  uint8_t unigbk[2]; // 
  uint8_t gbkuni[2]; // 

  if (src < 0x80) /* ASCII */
  { 
    c = src;
  }
  else
  {
    if(dir == 0) /* Unicode to GBK */
    {
      switch(src)
      {
      case 0x3001: c = 0xA1A2;break;	   // 支持符号: 、 中文顿号
      case 0x300A: c = 0xA1B6;break;     // 支持符号:《
      case 0x300B: c = 0xA1B7;break; 	   //  支持符号:》
      case 0x201C: c = 0xA1B0;break;	   // 支持符号: “	中文左双引号
      case 0x201D: c = 0xA1B1;break;     // 支持符号:”   中文右双引号
      case 0x2606: c = 0xA1EE;break; 	   //  支持符号:☆
      case 0x2605: c = 0xA1EF;break;	   // 支持符号: ★
      case 0x2018: c = 0xA1AE;break;     // 支持符号:‘ 中文左单引号
      case 0x2019: c = 0xA1AF;break; 	   //  支持符号:’中文右单引号
      case 0x3010: c = 0xA1BE;break;	   // 支持符号: 【
      case 0x3011: c = 0xA1BF;break;     // 支持符号: 】
      case 0x3016: c = 0xA1BC;break; 	   //  支持符号:〖
      case 0x3017: c = 0xA1BD;break;	   // 支持符号: 〗
      case 0x2299: c = 0xA1D1;break;     // 支持符号:⊙
      case 0x2116: c = 0xA1ED;break; 	   //  支持符号:№
      case 0x2236: c = 0xA1C3;break;	   // 支持符号: ∶
      case 0x203B: c = 0xA1F9;break;     // 支持符号:※
      case 0x221E: c = 0xA1DE;break; 	   //  支持符号:∞
      default:
      if( (src > 0x4DFF) && (src < 0x9FA6) )
      {
        offset = ((((uint32_t)src - 0x4E00) * 2) + 0x0C0000); 
        W25Q16_Read(unigbk,offset,2); 
        c = (((uint16_t)unigbk[0])<<8)+(uint16_t)unigbk[1]; 
      }
      else c = 0xA1A1; //如果是其它符号,都用NULL代替
      break;
    }
    else if(dir == 1) /* GBK to Unicode */
    {
      GBKH=(uint8_t)(src>>8); 
      GBKL=(uint8_t)(src); 
      GBKH-=0x81;
      GBKL-=0x40;
      offset=((uint32_t)192*GBKH+GBKL)*2;
      W25Q16_Read(gbkuni,offset+0x0D0000,2); 
      c = (((uint16_t)gbkuni[1])<<8)+(uint16_t)gbkuni[0];
    }
  }
  return c;
}

如果你还想支持其他一下特殊符号,可以利用Unicode码和GBK码转换软件自行添加。

想看该例程源文件。可以下载瑞生LPC1114 V3.0开发板资料,位于应用篇例程6。

发表评论

关闭菜单