STM32F4-DISCO 学习之汉字显示

做完这个汉字显示,就觉得这真是个坑,我觉得我再也不爱汉字了.实验是成功了,只是… 有点坑哈.各种法则也比较复杂.而且,要把某些特定编码,变成GBK或者GB2312,才好搞,不然iconv效率又奇低,查表又没意思.所以,大家打开最后打包的Keil工程是,发现Main你们有乱码,就不要觉得很奇怪了.这一课最重要讲三件事:

  1. SD卡操作,复制字库到SPI Flash啊.
  2. 字库制作以及字库的查询.
  3. 融合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.

QQ截图20150921104242

原来是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函数查看内码].

屏幕特别烂,不要见怪哈.

QQ截图20150921110505

程序提供下载:

汉字显示实验

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注