“长文件名”“短文件名”的详细概念请自行去百度谷歌搜狗搜索。
我们现在只需要知道一个文件名称长了,就是长文件名,例如sdjflasdjfas.txt;一个文件的名称短了,就是短文件名,例如a.txt。
有人会问,文件名干嘛要分长短?这个问题属于计算机历史问题了。想深究,去百度吧。
咱们先看看用“不支持长文件名”的文件系统和“支持长文件名”的文件系统分别读一个比较长的文件名显示的效果吧。
下面是“不支持长文件名”的文件系统读出来的:
下面是“支持长文件名”的文件系统读出来的:
很显然,文件系统如果不支持长文件名,岂能用哉?
以FatFs为例,刚刚移植好的文件系统,默认是不支持长文件名的,要想支持长文件名,需要打开ffconf.h文件进行配置,找到_USE_LFN,把值从0改到1。如下图所示:
改成1以后,任务还没有完成。为了能够支持中文,还需要把_CODE_PAGE的值改为936,如下图所示:
这时候,你编译一下,系统会有如下错误产生:
.\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.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。