基于 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 转换表,或者是不用长文件名。
实验结果
实验结果如下所示。
-将-Unicode-GBK-转换表储存到文件系统中-实验结果-20230304-1024x768.jpg)
源码
本章节中的源码请参考《单片机 GUI 设计(零)- 大纲》
持续关注~~