[RISC-V MCU 创新应用比赛]

CH32V103 FATFS实现中文长文件名读取

[复制链接]
5463|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 转换表。
        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);

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

400376111eb5a536b9.png

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

UNIGBK.rar (38.46 KB)

使用特权

评论回复

相关帖子

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 | 显示全部楼层
这个中文字符是如何确定的?              

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

17

主题

98

帖子

0

粉丝