本帖最后由 gtbestom 于 2021-8-10 16:59 编辑
FAT 文件配置表经过多年发展(FAT、FAT12、FAT16、FAT32、exFAT),目前已经广泛应用在 内存卡、U盘 等小容量存储介质中。
FAT~FAT16 由于最大容量限制等原因,目前已经逐步退出历史舞台,FAT32目前使用还比较广泛也是我正在使用的。
本文使用 FATFS,实现对文件目录的检索和长文件名的读取转换,最终将结果打印到串口调试助手。
先看一下U盘里都存了什么(一不小心暴露了年龄  ):
通过FATFS,我们能轻而易举的读取到这些文件的 8.3 短文件名,8字节文件名(中文则为4字),3字节后缀。
规规整整,但总觉得少了点什么,只有歌手名没有歌曲名?小写的后缀mp3变成大写的?然后~1又是什么? 想了解短文件名的可以搜一下论坛了解,这里就不重复,我们目的是长文件名,继续。
在 FATFS 配置中开启中文长文件名支持:#define _LFN_UNICODE 1,开启中文支持:#define _CODE_PAGE 936
成功了一半,由于长文件名是 Unicode 编码的,直接读出来,再通过串口打印显示,全是乱码??这里就涉及编码转换问题,串口助手一般都是显示GBK编码的字符,编码不同就导致乱码,需要用到 FATFS 里的编码转换函数进行转换,但由于 GBK 和 Unicode 转换是无规律的,只能纯粹的查表,老美给中国人设置的坑???为什么要为难中文和非英文??这个表还非常大,要占用 170K 以上的存储空间。。。
我这是一个 64K Flash 的单片机??? 
有难度才有动力啊,放入外置Flash?没有Flash,那只好放入内存卡了,刚好这些歌曲也是放在内存卡的,就建立一个目录,SYS,里面放入 Unicode 和 GBK 互转的对照表。 在内存卡插入的时候,挂载文件系统,读取 Unicode 和 GBK 转换表。- if(SD_Detect())
- {
- if(sd_status == 0)
- {
- sd_status = 1;
- printf("Insert\r\n");
- }
- }
- else
- {
- sd_status = 0;
- }
- if(sd_status == 1)
- {
- path[0] = '0';
- path[1] = ':';
- path[2] = 0;
- res_sd = f_mount(&fs,path,1);
- if(res_sd == FR_OK)
- {
- sd_status = 2;
- printf("Mount Ok\r\n");
- path[0] = '0';
- path[1] = ':';
- path[2] = '/';
- path[3] = 'S';
- path[4] = 'Y';
- path[5] = 'S';
- path[6] = '/';
- path[7] = 'U';
- path[8] = 'N';
- path[9] = 'I';
- path[10] = 'G';
- path[11] = 'B';
- path[12] = 'K';
- path[13] = '.';
- path[14] = 'B';
- path[15] = 'I';
- path[16] = 'N';
- path[17] = 0;
- res_sd = f_open(&gd_FileUNItoGBK,path,FA_READ);
- if(res_sd == FR_OK)
- printf("Load UNIGBK.BIN Ok\r\n");
- else
- printf("Load UNIGBK.BIN Fail\r\n");
- path[2] = '/';
- path[3] = 0;
- scan_files(path);
- }
- else if(res_sd == FR_NO_FILESYSTEM)
- {
- printf("No File System\r\n");
- }
- else
- {
- printf("Mount Fail\r\n");
- }
- }
改写 cc936.c 文件里的 ff_convert 函数,改为从内存卡指定偏移位置读取对照表信息。
- 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 t[2];
- WCHAR c;
- DWORD i, li, hi;
- WCHAR n;
- UINT cout;
- DWORD offset;
- if(src < 0x80) // ASCII
- return src;
- else
- {
- if(dir) // GBK TO UNICODE
- {
- offset = gd_FileUNItoGBK.fsize >> 1;
- }
- else // UNICODE TO GBK
- {
- offset = 0;
- }
- if(gd_FileUNItoGBK.fsize != 0)
- {
- hi = gd_FileUNItoGBK.fsize >> 1;
- hi = (hi >> 2) - 1;
- li = 0;
- for(n=16; n; n--)
- {
- i = li + ((hi - li) >> 1);
- f_lseek(&gd_FileUNItoGBK, (i << 2) + offset);
- f_read(&gd_FileUNItoGBK, &t, 4, &cout);
- if(src == t[0]) break;
- if(src > t[0])
- li = i;
- else
- hi = i;
- }
- c = n ? t[1] : 0;
- }
- else c = 0;
- }
- return c;
- }
核心部分基本就完成了,接下来读取长文件名,转换为GBK编码并打印。
- p = pt;
- pth = *fno.lfname ? fno.lfname : fno.fname;
- while(*pth != 0)
- {
- ct = ff_convert(*pth, 0);
- if(ct > 255)
- {
- *p = (u8)(ct>>8);
- p++;
- *p = (u8)ct;
- p++;
- }
- else
- {
- *p = (u8)ct;
- p++;
- }
- pth++;
- }
- *p = 0;
- printf("%s\r\n", pt);
这样终于能显示中文长文件名了。
由于是 SPI 接口读取内存卡,中文编码转换速度有点慢,在没有 SDIO 接口的情况下,只能调高 SPI 速率,再借助 DMA 来提高读写速度。
UNIGBK.rar
(38.46 KB, 下载次数: 27)
|