之前在百度语音系列文章中,我们使用RT-Thread的SUFD和fal软件包读取存储在外部flash的字库,实现了将语音识别结果显示到LCD上。在这过程中我们知道,百度语音识别的结果是utf-8的编码,而LCD显示需要的是gbk编码,需要经过utf-8 -> unicode -> gbk得到gbk编码,所以我们又实现了两个函数:
/* utf-8转unicode */
utf8_to_unicode()
/* unicode转gbk */
unicode_to_gbk()
有了这两个函数,我们就可以将语音识别结果(utf-8)转成LCD显示所需要的gbk编码。
这里简单说明一下:utf-8和unicode之间有直接的关系,只需根据它们之间的关系,编写转换函数即可(即utf8_to_unicode( )),但unicode和gbk两者却没有直接的关系了,想要实现它们的转换,就必须进行查表,unicode和gbk各自对应一个数组,这两个数组组成一个对应表,前面文章实现时在头文件中包含了这个表,unicode_to_gbk( )的功能也就是查表。
但是因为这个表对单片机来说太大了,直接写在程序里太占用flash,所以我们可以把它和字库一样放进外部flash里,其实我们之前用的正点原子的字库它就包含了这个表,那么。。。别问我那时候为什么不直接用外部flash的表,因为我那时候尝试了很久都没有成功。。。但是经过我后面的不断尝试,终于搞定了,下面就分享一下:如何使用RT-Thread的fal软件包,读取外部flash的转换表,实现unicode转gbk。
学习过FATFS文件系统和汉字显示的同学应该对cc936.c这个文件很熟悉,cc936.c是文件系统里用来支持长文件名的,该文件里包含了两个数组 oem2uni 和 uni2oem ,其实存放的就是unicode和gbk的互相转换对照表,同时cc936.c还提供了ff_convert函数实现unicode和gbk的互换。
那我们要做的就是修改ff_convert函数,实现从外部flash读取转换表,因为RT-Thread的文件系统也有ff_convert函数,所以我将这里的改为myff_convert:
unsigned short myff_convert ( /* Converted code, 0 means conversion error */
unsigned short src, /* Character code to be converted */
unsigned int dir /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */
)
{
ftinfo.ugbkaddr = 0x0000000+sizeof(ftinfo); //fal分区中转换表的地址
ftinfo.ugbksize = 174344; //转换表的大小
const struct fal_partition *partition = fal_partition_find("font"); //转换表所在分区为font
unsigned short t[2];
unsigned short c;
uint32_t i, li, hi;
uint16_t n;
uint32_t gbk2uni_offset=0;
if (src < 0x80)c = src;//ASCII,直接不用转换.
else
{
if(dir) //GBK 2 UNICODE
{
gbk2uni_offset=ftinfo.ugbksize/2;
}else //UNICODE 2 GBK
{
gbk2uni_offset=0;
}
/* Unicode to OEMCP */
hi=ftinfo.ugbksize/2;//对半开.
hi =hi / 4 - 1;
li = 0;
for (n = 16; n; n--)
{
i = li + (hi - li) / 2;
fal_partition_read(partition, ftinfo.ugbkaddr+i*4+gbk2uni_offset, (uint8_t*)&t, 4);
//W25QXX_Read((u8*)&t,ftinfo.ugbkaddr+i*4+gbk2uni_offset,4);//读出4个字节
if (src == t[0]) break;
if (src > t[0])li = i;
else hi = i;
}
c = n ? t[1] : 0;
}
return c;
}
myff_convert实现了单个字符的转换,紧接着:
void unicode2gbk(uint8_t *src,uint8_t *dst)
{
uint16_t temp;
uint8_t buf[2];
while(*src)
{
buf[0]=*src++;
buf[1]=*src++;
temp=(uint16_t)myff_convert((unsigned short)*(uint16_t*)buf,0);
if(temp<0X80)
{
*dst=temp;
dst++;
}
else
{
*(uint16_t*)dst=swap16(temp);
dst+=2;
}
}
*dst=0;//添加结束符
}
unicode2gbk便是最后供我们使用的转换函数了。
这次我用一个天气预报程序来简单验证,同样是解析json数据后:
/* 调用utf8_to_unicode将解析的天气数据(utf-8)转成unicode */
utf8_to_unicode(hh->valuestring, (char*)buf);
/* 使用上面实现的unicode2gbk将unicode转成gbk */
unicode2gbk((uint8_t*)buf,(uint8_t*)buffer);
/* 显示 */
show_str(100, 80, 200, 32, (uint8_t*)buffer, 32);
图中的"广州"和"多云"便是解析转码后的网络数据,测试无误。