[RISC-V MCU 创新应用比赛] CH32V103 FATFS实现中文长文件名读取

[复制链接]
6675|4
 楼主| gtbestom 发表于 2021-8-10 10:21 | 显示全部楼层 |阅读模式
本帖最后由 gtbestom 于 2021-8-10 16:59 编辑

  FAT 文件配置表经过多年发展(FAT、FAT12、FAT16、FAT32、exFAT),目前已经广泛应用在 内存卡、U盘 等小容量存储介质中。
  FAT~FAT16 由于最大容量限制等原因,目前已经逐步退出历史舞台,FAT32目前使用还比较广泛也是我正在使用的。
  本文使用 FATFS,实现对文件目录的检索和长文件名的读取转换,最终将结果打印到串口调试助手。
  

  先看一下U盘里都存了什么(一不小心暴露了年龄):


1.png

  通过FATFS,我们能轻而易举的读取到这些文件的 8.3 短文件名,8字节文件名(中文则为4字),3字节后缀。

   789876111eb38371ab.png

  规规整整,但总觉得少了点什么,只有歌手名没有歌曲名?小写的后缀mp3变成大写的?然后~1又是什么?
  想了解短文件名的可以搜一下论坛了解,这里就不重复,我们目的是长文件名,继续。

  在 FATFS 配置中开启中文长文件名支持:#define _LFN_UNICODE    1,开启中文支持:#define _CODE_PAGE  936
135086111e3cef2799.png


  成功了一半,由于长文件名是 Unicode 编码的,直接读出来,再通过串口打印显示,全是乱码??这里就涉及编码转换问题,串口助手一般都是显示GBK编码的字符,编码不同就导致乱码,需要用到 FATFS 里的编码转换函数进行转换,但由于 GBK 和 Unicode 转换是无规律的,只能纯粹的查表,老美给中国人设置的坑???为什么要为难中文和非英文??这个表还非常大,要占用 170K 以上的存储空间。。。

  我这是一个 64K Flash 的单片机???


  有难度才有动力啊,放入外置Flash?没有Flash,那只好放入内存卡了,刚好这些歌曲也是放在内存卡的,就建立一个目录,SYS,里面放入 Unicode 和 GBK 互转的对照表。
  在内存卡插入的时候,挂载文件系统,读取 Unicode 和 GBK 转换表。
  1.         if(SD_Detect())
  2.             {
  3.                 if(sd_status == 0)
  4.                 {
  5.                     sd_status = 1;
  6.                     printf("Insert\r\n");
  7.                 }
  8.             }
  9.             else
  10.             {
  11.                 sd_status = 0;
  12.         }

  13.             if(sd_status == 1)
  14.             {
  15.                 path[0] = '0';
  16.             path[1] = ':';
  17.             path[2] = 0;
  18.                 res_sd = f_mount(&fs,path,1);
  19.                 if(res_sd == FR_OK)
  20.                 {
  21.                     sd_status = 2;
  22.                     printf("Mount Ok\r\n");

  23.                 path[0] = '0';
  24.                 path[1] = ':';
  25.                 path[2]  = '/';
  26.                 path[3]  = 'S';
  27.                 path[4]  = 'Y';
  28.                 path[5]  = 'S';
  29.                 path[6]  = '/';
  30.                 path[7]  = 'U';
  31.                 path[8]  = 'N';
  32.                 path[9]  = 'I';
  33.                 path[10] = 'G';
  34.                 path[11] = 'B';
  35.                 path[12] = 'K';
  36.                 path[13] = '.';
  37.                 path[14] = 'B';
  38.                 path[15] = 'I';
  39.                 path[16] = 'N';
  40.                 path[17] = 0;

  41.                 res_sd = f_open(&gd_FileUNItoGBK,path,FA_READ);
  42.                 if(res_sd == FR_OK)
  43.                     printf("Load UNIGBK.BIN Ok\r\n");
  44.                 else
  45.                     printf("Load UNIGBK.BIN Fail\r\n");

  46.                     path[2] = '/';
  47.                     path[3] = 0;
  48.                     scan_files(path);
  49.                 }
  50.                 else if(res_sd == FR_NO_FILESYSTEM)
  51.                 {
  52.                 printf("No File System\r\n");
  53.                 }
  54.                 else
  55.                 {
  56.                 printf("Mount Fail\r\n");
  57.             }
  58.             }

  改写 cc936.c 文件里的 ff_convert 函数,改为从内存卡指定偏移位置读取对照表信息。
  1. WCHAR ff_convert (  /* Converted code, 0 means conversion error */
  2.     WCHAR   src,    /* Character code to be converted */
  3.     UINT    dir     /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */
  4. )
  5. {
  6.     WCHAR t[2];
  7.     WCHAR c;
  8.     DWORD i, li, hi;
  9.     WCHAR n;
  10.     UINT cout;
  11.     DWORD offset;

  12.     if(src < 0x80)  // ASCII
  13.         return src;
  14.     else
  15.     {
  16.         if(dir)     // GBK TO UNICODE
  17.         {
  18.             offset = gd_FileUNItoGBK.fsize >> 1;
  19.         }
  20.         else        // UNICODE TO GBK
  21.         {
  22.             offset = 0;
  23.         }
  24.         if(gd_FileUNItoGBK.fsize != 0)
  25.         {
  26.             hi = gd_FileUNItoGBK.fsize >> 1;
  27.             hi = (hi >> 2) - 1;
  28.             li = 0;
  29.             for(n=16; n; n--)
  30.             {
  31.                 i = li + ((hi - li) >> 1);
  32.                 f_lseek(&gd_FileUNItoGBK, (i << 2) + offset);
  33.                 f_read(&gd_FileUNItoGBK, &t, 4, &cout);
  34.                 if(src == t[0]) break;
  35.                 if(src > t[0])
  36.                     li = i;
  37.                 else
  38.                     hi = i;
  39.             }
  40.             c = n ? t[1] : 0;
  41.         }
  42.         else c = 0;
  43.     }
  44.     return c;
  45. }

  核心部分基本就完成了,接下来读取长文件名,转换为GBK编码并打印。

  1.                 p = pt;
  2.                 pth = *fno.lfname ? fno.lfname : fno.fname;
  3.                 while(*pth != 0)
  4.                 {
  5.                     ct = ff_convert(*pth, 0);

  6.                     if(ct > 255)
  7.                     {
  8.                         *p = (u8)(ct>>8);
  9.                         p++;
  10.                         *p = (u8)ct;
  11.                         p++;
  12.                     }
  13.                     else
  14.                     {
  15.                         *p = (u8)ct;
  16.                         p++;
  17.                     }
  18.                     pth++;
  19.                 }
  20.                 *p = 0;

  21.                 printf("%s\r\n", pt);

  这样终于能显示中文长文件名了。

400376111eb5a536b9.png

  由于是 SPI 接口读取内存卡,中文编码转换速度有点慢,在没有 SDIO 接口的情况下,只能调高 SPI 速率,再借助 DMA 来提高读写速度。

UNIGBK.rar (38.46 KB, 下载次数: 27)

asmine 发表于 2021-8-10 15:21 | 显示全部楼层
你这个,技术含量很高啊,
要慢慢消化
qiangtech 发表于 2021-9-15 11:18 | 显示全部楼层
翻看了楼主的连贴,写得很好下了功夫。
kkzz 发表于 2022-11-2 18:56 | 显示全部楼层
fatfs的文件系统读写的时候速度太慢了。
maudlu 发表于 2022-11-2 19:11 | 显示全部楼层
这个中文字符是如何确定的?              
您需要登录后才可以回帖 登录 | 注册

本版积分规则

19

主题

114

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部