单片机 GUI 设计(十二)- 将 Unicode-GBK 转换表储存到文件系统
单片机 GUI 设计(十二)- 将 Unicode-GBK 转换表储存到文件系统

单片机 GUI 设计(十二)- 将 Unicode-GBK 转换表储存到文件系统

基于 GD32F303ZET6 苹果派开发板

简介

前两篇文章中,使能 FatFS 长文件名时,FatFS 自动将 Unicode-GBK 转换表储存在 Flash 中。然则这张表有点大,足足有 174,344B,再加上程序指令数据和程序中的一些常量,整个程序的大小超过了 200KB。这导致程序下载速度很慢,非常慢。为了解决这个问题,我们可以将 Unicode-GBK 转换表导出,储存在文件系统中,系统启动初始化时,将其加载到外拓的 SRAM 中。当然,因为苹果派的外拓 SRAM 只有 1MB,174KB 相对于 1MB 还是有点大的,所有读者也可以选择将 Unicode-GBK 保存到 SPI Flash 等存储介质。

Unicode-GBK 转换表简介

如果使能了 CC936,那么 Unicode-GBK 转换表将在 cc936.c 中定义,分别是 uni2oem 和 oem2uni,如下所示。其中,uni2oem 即为 Unicode 转 GBK 转换表,oem2uni 即为 GBK 转 Unicode 转换表。uni2oem 和 oem2uni 大小均为 87,172B,总共占据 174,344B。我们可以在 Windows 平台上编写一段 C 语言程序,将这两个数组输出到一个文件中。为了方便读者使用,本文提供了已经转换好的文件,文件名为 UNIGBK.BIN。UNIGBK.BIN 文件中,首先存储了 Unicode 转 GBK 转换表,然后便是 GBK 转 Unicode 转换表。两张转换表紧挨着,没有间隙。

static const WCHAR uni2oem[] = {
...
};

static const WCHAR oem2uni[] = {
...
};

移植前准备工作

为了保证源码的原汁原味,本文中并不打算直接修改源码,所以需要新建一个文件 UnicodeGbk.c 文件来负责 Unicode 和 GBK 的转换工作,并将 cc936.c 从工程中移除。在 UnicodeGbk.c 问价中,我们需要实现三个函数,一个是初始化函数,用于在系统启动一瞬间将 UNIGBK.BIN 文件加载到外拓 SRAM 中;另一个是 ff_convert,用于 Unicode 与 GBK 编码之间的转换;最后则是 ff_wtoupper,负责小写转大写的工作。

初始化函数设计

初始化函数的设计可如下所示。初始化函数中主要负责将 UNIGBK.BIN 文件加载到外拓 SRAM 中,因为加载 UNIGBK.BIN 文件时,也会涉及到 Unicode 和 GBK 之间的转换,所以 UNIGBK.BIN 文件的文件名及其后缀一定要是全英文的,不能有中文,否则会读取失败。

static unsigned int s_iUnicodeTableSize = 0;
static unsigned char* s_pUnicodeGbkTable = NULL;
static WCHAR* s_pUnicodeToGbk = NULL;
static WCHAR* s_pGbkToUnicode = NULL;

void InitUnicodeGbk(void)
{
  //获取 Unicode GBK 转换表大小
  s_iUnicodeTableSize = ForceGetFileSize(UNICODE_GBK_TABLE_DIR);

  //读取整个 Unicode GBK 转换表到外拓 SRAM
  s_pUnicodeGbkTable = MallocAndCopy(UNICODE_GBK_TABLE_DIR);

  //获取 Unicode 转 GBK 首地址
  s_pUnicodeToGbk = (WCHAR*)s_pUnicodeGbkTable;

  //获取 GBK 转 Unicode 首地址
  s_pGbkToUnicode = (WCHAR*)(s_pUnicodeGbkTable + (s_iUnicodeTableSize / 2));
}

转换函数设计

ff_convert 函数的设计可参考 FatFS 源码,即 cc936.c 文件,具体如下所示。因为初始化函数中已经将 Unicode 转 GBK 和 GBK 转 Unicode 两个转换表的首地址保存了下来,所以只需要对源码做出很小的修改,在此更多的时将代码格式转换了过来,用以保持我们自己的风格。

WCHAR ff_convert(WCHAR chr, UINT dir)
{
  const WCHAR *p;
  WCHAR c;
  int i, n, li, hi;

  // ASCII 码,直接返回
  if (chr < 0x80) 
  {
    c = chr;
  } 
  
  // Unicode 和 GBK 转换
  else 
  {
    //GBK 转 Unicode
    if (dir) 
    {
      p = s_pGbkToUnicode;
      hi = (s_iUnicodeTableSize / 2) / 4 - 1;
    } 
    
    //Unicode 转 GBK
    else 
    {
      p = s_pUnicodeToGbk;
      hi = (s_iUnicodeTableSize / 2) / 4 - 1;
    }

    //转换
    li = 0;
    for (n = 16; n; n--) 
    {
      i = li + (hi - li) / 2;
      if (chr == p[i * 2]) 
      {
        break;
      }
      if (chr > p[i * 2])
      {
        li = i;
      }
      else
      {
        hi = i;
      }
    }
    c = n ? p[i * 2 + 1] : 0;
  }

  return c;
}

转大写函数设计

ff_wtoupper 函数的设计可以原封不动的照搬 FatFS 源码,如下所示。虽然这个函数里也有两张转换表,但是这两张转换表太小了,不需要提取到文件系统中。

WCHAR ff_wtoupper(WCHAR chr)
{
  static const WCHAR tbl_lower[] = { ... };
  static const WCHAR tbl_upper[] = { ... };
  int i;
  for (i = 0; tbl_lower[i] && chr != tbl_lower[i]; i++) ;
  return tbl_lower[i] ? tbl_upper[i] : chr;
}

最后下载验证即可。

总结

至于为什么不在 ff_convert 函数里直接读取文件系统中的转换数据,主要是因为每转换一个编码,就要读取 UNIGBK.BIN 文件文件一次,在 FatFS 中,打开文件等操作效率很低,如果是实时从文件系统中获取转换数据,会明显影响到读取速度。

注意:如果你所有的文件名都是英文的,没有中文路径,那么 Unicode GBK 转换表也不是必须的。从 ff_convert 函数可以看出,英文字符的 ASCII 编码和 Unicode 编码一致,如果系统中的资源真的很有限,那么也可以考虑不要 Unicode GBK 转换表,或者是不用长文件名。

实验结果

实验结果如下所示。

源码

本章节中的源码请参考《单片机 GUI 设计(零)- 大纲

一条评论

评论已关闭。