手把手教你实现基于RT-Thread的百度语音识别(三)

前言

本次我们还是先不实现录音功能,因为音频编解码这一块是比较有难度的,再加上RT-Thread的Audio设备也比较复杂,难以理解,我暂时没考虑好怎么讲解该部分(我自己也没理解透~),所以就放到后面再分享吧。那么这次我们就讲讲项目的第7点:

将中文字库烧写进外挂的spi flash,使用SUFD+FAL软件包读写flash,实现LCD的中文显示,用来显示语音识别结果。

原本我做项目时,是没有打算要在LCD上显示识别结果的,但是因为我使用的潘多拉开发板板载了一块1.3寸的240*240高分辨率TFTLCD显示屏,不把它用上岂不是浪费?再加上后面想到如果不用语音识别来控制外设,识别一些其他语音的时候,有个可以显示识别结果的屏幕,岂不美哉。于是乎我便开始整LCD显示了,不整不知道,一整头皮发麻,RT-Thread官方对于潘多拉开发板在驱动各方面的支持都已经做的非常好了,包括LCD驱动,但是却没有中文字库的支持,也没有相关的资料,所以我就只能自己想办法实现了。

下面我分享的内容,跟百度语音识别没有太大的关系,主要是如何在LCD上显示中文的内容,但百度语音确确实实给我在实现的路上埋下了许多坑,所以我觉得这部分还是很有必要分享的,究竟是怎么一回事,听我细细道来。

准备工作

  1. 首先你必须完成了系列文章(二)中讲解的内容;
  2. 阅读正点原子教程中SPI Flash和汉字显示两个例程,确保你清楚汉字显示原理,如何制作字库,如何将字库烧写进flash中;
  3. 准备好中文字库;

动手实践

嘻嘻~还是为了方便,字库嘛,我使用正点原子裸机例程提供的中文字库(该字库包含了12,16,24,32四种字体大小的字库),然后运行汉字显示例程的程序,该程序里包含将字库烧写进spi flash的部分,我们要的就是这个部分,其实你可以自己编程实现flash读写(so easy !),自行烧写字库,但是为了方便我就偷懒了,毕竟只要字库不损坏,咱也就烧这一次。

上面部分你可以裸机实现,无所谓,只要将字库烧进flash(我这里是W25Q128)就行了。下面重头戏来了(再次确保你已经明白了汉字显示原理),这次我们又用到了RT-Thread的两个软件包:SUFD和FAL,简单介绍一下:

SUFD(串行闪存通用驱动库)

看中文名就知道了,用来驱动spi flash的嘛。SFUD是一种开源的串行SPI Flash通用驱动库(是RT-Thread的armink大神开发的,大佬!),使用这个库,你就不必自己编写flash驱动了,基本市面上绝大多数的flash,都可以轻轻松松地给你驱起来。

FAL(Flash抽象层)

简单来说,使用该软件包,你可以方便地使用API对flash进行分区管理,读写操作等,支持自定义分区表(不得不说,强!)。

代码讲解

好了,同样,使用ENV工具把SUFD和FAL添加进工程中,开始写代码:

/*********
cn_font.c
**********/

#include <rtthread.h>
#include <rtdevice.h>	
#include <drv_qspi.h>
#include <drv_spi.h>
#include <drv_lcd.h>
#include <string.h>
#include <fal.h>
#include <cn_font.h>

/************************************************
结构体名称 : _font_info
定    义 :
    __packed typedef struct
    {
        uint8_t fontok;				//字库存在标志,0XAA,字库正常;其他,字库不存在
        uint32_t ugbkaddr; 			//unigbk的地址
        uint32_t ugbksize;			//unigbk的大小
        uint32_t f12addr;			//gbk12地址
        uint32_t gbk12size;			//gbk12的大小
        uint32_t f16addr;			//gbk16地址
        uint32_t gbk16size;			//gbk16的大小
        uint32_t f24addr;			//gbk24地址
        uint32_t gbk24size;			//gbk24的大小
        uint32_t f32addr;			//gbk32地址
        uint32_t gbk32size;			//gbk32的大小
    } _font_info;
*************************************************/
_font_info ftinfo;

/* 从字库中找出字模 */
void get_hz_mat(unsigned char *code, unsigned char *mat, uint8_t size)
{	
	ftinfo.ugbkaddr = 0x0000000+sizeof(ftinfo);
	ftinfo.ugbksize = 174344;
	ftinfo.f12addr = 0x0002A908+sizeof(ftinfo);
	ftinfo.gbk12size = 574560;
	ftinfo.f16addr = 0x000B6D68+sizeof(ftinfo);
	ftinfo.gbk16size = 766080;
	ftinfo.f24addr = 0x00171DE8+sizeof(ftinfo);
	ftinfo.gbk24size = 1723680;
	ftinfo.f32addr = 0x00316B08+sizeof(ftinfo);
	ftinfo.gbk32size = 3064320;
	
	unsigned char qh, ql;
	unsigned char i;
	unsigned long foffset;
	const struct fal_partition *partition = fal_partition_find("font");
	
	uint8_t csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size); //得到字体一个字符对应点阵集所占的字节数
	qh =*code;
	ql = *(++code);
	
	if(qh < 0x81 || ql < 0x40 || ql == 0xff || qh == 0xff) //非常用汉字
    {
        for(i = 0; i < csize; i++)*mat++ = 0x00; //填充满格
        return; //结束访问
    }
	if(ql < 0x7f)ql -= 0x40; //注意!
    else ql -= 0x41;

    qh -= 0x81;
    foffset = ((unsigned long)190 * qh + ql) * csize;	//得到字库中的字节偏移量
		
	switch(size)
    {
        case 12:
			fal_partition_read(partition, foffset + ftinfo.f12addr, mat, csize);
            break;

        case 16:
            fal_partition_read(partition, foffset + ftinfo.f16addr, mat, csize);
            break;

        case 24:
            fal_partition_read(partition, foffset + ftinfo.f24addr, mat, csize);
            break;

        case 32:
            fal_partition_read(partition, foffset + ftinfo.f32addr, mat, csize);
            break;

    }
}
	
/* 显示一个指定大小的汉字 */
void show_font(uint16_t x, uint16_t y, uint8_t *font, uint8_t size)
{
	uint16_t colortemp;
    uint8_t sta;
    uint8_t temp, t, t1;
    uint8_t dzk[128];
    uint8_t csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size);			//得到字体一个字符对应点阵集所占的字节数

    if(size != 12 && size != 16 && size != 24 && size != 32)return;	//不支持的size

    get_hz_mat(font, dzk, size);							//得到相应大小的点阵数据

    if((size == 16) || (size == 24) || (size == 32))	//16、24、32号字体
    {
        sta = 8;

        lcd_address_set(x, y, x + size - 1, y + size - 1);

        for(t = 0; t < csize; t++)
        {
            temp = dzk[t];			//得到点阵数据

            for(t1 = 0; t1 < sta; t1++)
            {
                if(temp & 0x80) colortemp = 0x0000;

                else colortemp = 0xFFFF;

                lcd_write_half_word(colortemp);
                temp <<= 1;
            }
        }
    }
    else if(size == 12)	//12号字体
    {
        lcd_address_set(x, y, x + size - 1, y + size - 1);

        for(t = 0; t < csize; t++)
        {
            temp = dzk[t];			//得到点阵数据

            if(t % 2 == 0)sta = 8;

            else sta = 4;

            for(t1 = 0; t1 < sta; t1++)
            {
                if(temp & 0x80) colortemp = 0x0000;

                else colortemp = 0xFFFF;

                lcd_write_half_word(colortemp);
                temp <<= 1;
            }
        }
    }
}
	
/* 在指定位置开始显示一个字符串 */
void show_str(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t *str, uint8_t size)
{
	uint16_t x0 = x;
    uint16_t y0 = y;
    uint8_t bHz = 0;   //字符或者中文

    while(*str != 0) //数据未结束
    {
        if(!bHz)
        {
            if(*str > 0x80)bHz = 1; //中文

            else              //字符
            {
                if(x > (x0 + width - size / 2)) //换行
                {
                    y += size;
                    x = x0;
                }

                if(y > (y0 + height - size))break; //越界返回

                if(*str == 13) //换行符号
                {
                    y += size;
                    x = x0;
                    str++;
                }

                else lcd_show_char(x, y, *str, size); //有效部分写入

                str++;
                x += size / 2; //字符,为全字的一半
            }
        }
        else //中文
        {
            bHz = 0; //有汉字库

            if(x > (x0 + width - size)) //换行
            {
                y += size;
                x = x0;
            }

            if(y > (y0 + height - size))break; //越界返回

            show_font(x, y, str, size); //显示这个汉字,空心显示
            str += 2;
            x += size; //下一个汉字偏移
        }
    }
}

上面的代码中实现了三个函数:

get_hz_mat()
show_font()
show_str()

这三个函数都是移植正点原子的,后两个基本没有变化,你只需注意get_hz_mat(),因为它涉及到了flash的读写,而我们这里不再是裸机的读写方式,而是使用FAL软件包提供的API,尤其注意读写的位置,FAL软件包读的是分区相对位置!

有了上面这三个函数,初始化LCD完成后,你就可以使用show_str( )显示中文啦,例:

show_str(120, 220, 200, 16, (rt_uint8_t *)"霹雳大乌龙", 16);

需要注意的是,你的源文件编码不能使用utf-8,否则将会显示错误的编码,这里提供一种解决方案:
使用notepad++,按如下设置:
在这里插入图片描述

好了,现在LCD已经可以显示中文了,但是你以为事情到这就结束了吗,no!!!一个致命的问题来了,这也是我前面说的百度语音埋下的坑。前面我提到,源文件不能使用utf-8编码,是针对源文件中的中文来说的,中文编码若是utf-8,那么你的LCD显示将会是错误的编码,用我上面提供的解决方案可以避免这个问题,但是!相信有人已经猜到了,我们要显示语音识别结果,它可不是直接写在源文件里的,而是我们接收百度语音返回的数据后,解析出来的,而它恰恰就是utf-8编码,你说悲不悲剧,这时上面说的解决方案可不管用了,那么怎么解决?我们下回分解。(不是卖关子,而是写在一起篇幅就太长了)

本次就分享到这吧~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值