前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[评测]沁恒RISC-V开发板读取内存卡字库显示

[评测]沁恒RISC-V开发板读取内存卡字库显示

作者头像
单片机点灯小能手
发布2021-09-03 15:43:34
1.1K0
发布2021-09-03 15:43:34
举报
文章被收录于专栏:电子电路开发学习

再次感谢沁恒官方寄送的这块 CH32V103 开发板,集成了仿真下载器,集成了USB转串口,还兼容了Arduino接口,使用起来很方便。

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 转换表。

代码语言:javascript
复制
        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");
            }
            }
代码语言:javascript
复制

改写 cc936.c 文件里的 ff_convert 函数,改为从内存卡指定偏移位置读取对照表信息。

代码语言:javascript
复制
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编码并打印。

代码语言:javascript
复制


                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 来提高读写速度。

显示到串口助手显然不太方便,我看目录还要带个电脑?再说有电脑我还需要这样看目录?

这次我们换个地方显示。

说干就干。

从杂物堆里找来了一个12864显示屏,非常mini的那种,0.96寸,某宝上也有很多哦,10米左右即可拥有。

怎么点亮屏幕这篇文章就不说了,买屏送的资料就够亮屏了。

本次主要实现内存卡字库的读取,显示。

由于内存卡存放的文件,文件名可能是各种各样的,如果要显示任意中文字符,就需要对所有 汉字 进行取模,生成字库。

那把所有汉字打出来,再一一取模不就好了?

这里涉及2个问题:

1、汉字有上万个,常见的也有几千个,一一取模再转换未免太费神。

2、这么多汉字,如何一一对照,例如我想在这一堆字模中,快速找到“我”这个字的字模。

什么?忘记取模的时候“我”的字模排在第几位了。

对于第一个问题,感谢一些前辈大佬,已经有很多生成字库的软件了。

第二个问题,汉字编码有很多种,例如 GB2312(收录6763汉字)、GBK(收录21886汉字)、Unicode(收录6万多字)、UTF-32则收录了12万字。

已经有以上多种编码了,编码就代表着文字的排序,直接对照就好。 这里取GBK和Unicode为例。GBK中出现的第一个汉字为“丂”,编码为16进制的8140,一个汉字用2个字节表示。

如何获取汉字GBK编码?这里提供一种土办法,串口助手!

将RX和TX短接,发送设置为字符GBK编码,接收设置为16进制,发送后接收到的就是字符编码:

获取 Unicode 同理,发送切换到 Unicode 编码,接收为16进制:

取模不管对照GBK编码,还是Unicode编码,都是能实现我们这个功能的。直接选个编码开始取模:

生成字库二进制文件:

再放入内存卡即可,然后使用 FATFS 读取字库文件信息: f_open(&gd_FileFontLib16, path, FA_READ);

偏移文件读取指针: f_lseek(&gd_FileFontLib16, foffset);

读取字模信息: f_read (&gd_FileFontLib16, Buff, 32, &cout); 使用 Unicode 编码的字库时,foffset偏移就是汉字编码,使用 GBK 编码的字库时,foffset偏移按照以下计算方法:

代码语言:javascript
复制
  qh = *Str;
  ql = *(++Str);

  if(qh<0x81 || ql<0x40 || ql==0xff || qh==0xff)        // 非常用汉字
  {
    for(i=0; i<(Size<<1) ;i++)
      *Buff++ = 0x00;                                   // 填充
    return 0;
  }

  if(ql < 0x7f)
    ql-=0x40;
  else
    ql-=0x41;

  qh -= 0x81;
  foffset = ((u32)190 * qh + ql) * (Size&0x07 ? (Size>>3)+1 : Size>>3) * Size;    // 得到字库中的字节偏移量

得到字模后显示即可,就不多说了,上图:

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-08-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 电子电路开发学习 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档