做完这个汉字显示,就觉得这真是个坑,我觉得我再也不爱汉字了.实验是成功了,只是... 有点坑哈.各种法则也比较复杂.而且,要把某些特定编码,变成GBK或者GB2312,才好搞,不然iconv效率又奇低,查表又没意思.所以,大家打开最后打包的Keil工程是,发现Main你们有乱码,就不要觉得很奇怪了.这一课最重要讲三件事:
- SD卡操作,复制字库到SPI Flash啊.
- 字库制作以及字库的查询.
- 融合TFT操作.
关于SD卡操作呢,上次只是涉及一些皮毛.所以我觉得我再也不爱汉字了,不过倒是有一个很好的东西哦,就是说把大的数组存在SPI Flash里面,在速度允许情况下,这种查表方法还是很值得使用的.特别是,这一次,我们也将cc936里面的大数组转移过去,就是把数组的元素抽取,然后顺序排列,挤到Flash你们,省了宝贵的片内Flash空间[虽然片内那么那么的大...],但是,生命在于折腾.
如果从片内Flash查找内容,首先要做的肯定就是查表啊,表,就是有个索引,这个索引我们在做这个内容的时候,就应该做出来,然而,我们又知道GB2312的算法,所以,知道表头,就可以知道字的点阵,在整个数据块的哪里,自然就可以找到相应的内容了.我们生成多种字体,其中有16*16点阵.字号9的,也有20*20点阵,字号11的,等等.然后cc936转移过去,也就还有一个UGBK,然后还有一个时候存在字库的标签啊... 结果这个结构体应该是这样的:
__packed typedef struct
{
uint8_t FontSign; /* OK = 0xAA*/
uint32_t UGBK_A;
uint32_t UGBK_L;
uint32_t YaHei1609_A;
uint32_t YaHei1609_L;
uint32_t YaHei2011_A;
uint32_t YaHei2011_L;
uint32_t YaHei2413_A;
uint32_t YaHei2413_L;
uint32_t YaHei3218_A;
uint32_t YaHei3218_L;
} _Font_Flash;
extern _Font_Flash _FtInfo;
我们已经预先的,把所有的内容的索引,做出来,虽然最终我不一定用到这么多的字库.但是大家可以直接生成的哦.另外因为这个索引,可以固化到MCU里面,也可以上电是否从SPI Flash读取出来,供我们使用.我们选择了后面那一种,因为这样我换了Flash,只要知道基地址,就不至于不能用了.同时,反正要检查标志,所以,这样看起来更合理一些.
uint8_t Font_Init(void)
{
uint8_t t = 0;
W25QXX_Init();
while(t < 10)
{
t++;
W25QXX_Read((uint8_t *)&_FtInfo, FONTINFOADDR, sizeof(_FtInfo));
if(_FtInfo.FontSign == 0XAA)break;
}
if(_FtInfo.FontSign != 0XAA)return 1;
return 0;
}
由于拷贝整个字库,是个体力活而已,所以,抽取片段,分析.第一步,是挂在因磁盘[TF卡],然后申请内存.
f_mount(&sdfs, "0:", 1);
pname = malloc(100);
buf = malloc(4096);
fftemp = (FIL *)malloc(sizeof(FIL));
if(buf == NULL || pname == NULL || fftemp == NULL)
{
free(fftemp);
free(pname);
free(buf);
return 1;
}
因为这里申请也就几KB的内存,并不需要外部SRAM,也不要以为这东西有多大哈,就4KB而已,编译是否可能不通过,最终原因是Heap的大小不足啊.只好打开startup文件[别看到汇编就怕怕的],修改Heap Size.
原来是0x200,太小了,改到0x1000就可以分配4K的单次内存,改64K就分配更大,但也更浪费[据说],当然,我做实验的,也不管的那么多.然后尝试打开文件,打开失败还记得释放内存,不然就是浪费,每个文件都试着打开一下,保证字体都在.
res = f_open(fftemp, (const TCHAR *)pname, FA_READ );
if(res)
{
free(pname);
free(buf);
return 2;
}
其中pname就是文件名,fftemp就是指针.返回FR_OK,也就是0,就是成功.
然后就开始清空需要写的扇区,扇区总数的计算方法就是,字库大小除以4096字节.比如我占用673扇区,其实是因为我字体大小是673*4096,当然别忘了还有UGBK哦.
for(i = 0; i < FONTSECSIZE; i++)
{
FUPD_Progress(x + 20 * size / 2, y, size, FONTSECSIZE, i);
W25QXX_Read((uint8_t *)buf, ((FONTINFOADDR / 4096) + i) * 4096, 4096);
for(j = 0; j < 1024; j++)
{
if(buf[j] != 0XFFFFFFFF)break;
}
if(j != 1024)W25QXX_Erase_Sector((FONTINFOADDR / 4096) + i);
}
清空后,就可以开始倒字体,我们执行了一个独立的函数Updata_FontX,然后每个地址独立计算方法:
_FtInfo.YaHei1609_A = _FtInfo.UGBK_A + _FtInfo.UGBK_L;
_FtInfo.YaHei1609_L = fftemp->fsize;
flashaddr = _FtInfo.YaHei1609_A;
然后就可以开始取码,首先取码有两种,一种是GBK - GB2312,一种是GB2312直接取,当然还有UTF-8的,折腾了一下,发现效率太低,还是妥协于GB2312直接来吧.算法也就简单了,因为GB2312是从A1A1开始的,也就是一个中文空格.然后一个点阵的大小,所需多少个16进制,用csize表示,结果就是:
csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size); //得到字体一个字符对应点阵集所占的字节数
qh = *code;
ql = *(++code);
ql -= 0xa1;
qh -= 0xa1;
foffset = ((unsigned long)94 * qh + ql) * (size * 2);
然后就可以解决了,知道offset,加上基地址,就可以得到文字,如例:
W25QXX_Read(mat, foffset + _FtInfo.YaHei1609_A, csize);
的好了,本文不长,不过要折腾也是够长的,记得要输入中文的地方,编码是GB2312哦,不然可不显示哦[调试是否可以到Get_HzMat函数查看内码].
屏幕特别烂,不要见怪哈.
程序提供下载:

