STM32CubeIDE 制作中英字库 显示屏显示中英文

随言:

目标制作宽度 x 高度  为16 x 16大小的中文字库 和 8 x 16大小英文字库,且在LCD显示。

第一个值小于127的解析为英文字符,否则取连续两个值解析为GB2313.

中文编码格式为GB2312,需要用2个字节表示一个汉字。

全文需要注意的是开发软件的编码格式是否是GB2312,若不是请用其他编码字库,编程思路一致。

由于大家使用的显示屏不同,但是画点的函数一定会有,如LCD_DrawPoint(uint16_t x, uint16_t y, uint32_t color)。

1. 简介(百度百科)

1.1 ASCII 编码

编程的时候使用printf打印debug信息,经常用到ASCII。这真的是再熟悉不过了。

ASCII只用到了指定的7 bit二进制数,最大到0 ~ 127。由于ASCII只能显示英文字母,

但是很多国家的文字并不是由英文字母构成的。故127后面的编码显示其他国家文字了。

1.2 GB2312

GB 2312标准共收录6763个汉字,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75%的使用频率。

对于人名、古汉语等方面出现的罕用字,GB 2312不能处理,这导致了后来GBK及GB 18030汉字字符集的出现。

在使用GB2312的程序中,通常采用EUC储存方法,以便兼容于ASCII。浏览器编码表上的“GB2312”,通常都是指“EUC-CN”表示法。每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”(也称“区字节)”,第二个字节称为“低位字节”(也称“位字节”)。“高位字节”使用了0xA1-0xF7(把01-87区的区号加上0xA0),“低位字节”使用了0xA1-0xFE(把01-94加上 0xA0)。 由于一级汉字从16区起始,汉字区的“高位字节”的范围是0xB0-0xF7,“低位字节”的范围是0xA1-0xFE,占用的码位是 72*94=6768。其中有5个空位是D7FA-D7FE。

例如“啊”字在大多数程序中,会以两个字节,0xB0(第一个字节) 0xA1(第二个字节)储存。区位码=区字节+位字节(与区位码对比:0xB0=0xA0+16,0xA1=0xA0+1)。



6763个汉字一般够我们嵌入式系统使用了, GB2312编码范围是0xA1A1 ~ 0xFEFE。

2. 制作字库

制作字库的工具很多,例如:PCtoLCD2002.exe

下图是设置生成字体数据的规则。

生成16 x 16大小的字库。

阴码:用1表示LCD该像素点点亮。

顺向:表示高字节在前。

逐行式:逐行刷写。

3. 烧写字库

生成字库保存完成后,文件尾缀是.fon文件。把尾缀改成.bin,如:GB2312.bin,文件大小是255 KB。

使用ST烧写工具,打开GB2312.bin。把GB2312.bin烧录进芯片内部flash。

我用的是F407ZG有1MB空间,烧录在最后两个分区,即起始地址是:0x80C0000

4. 检查字库数据

现在要把字库里面的数据读取出来,看看是否数据摆放位置和读取是否正确。

现在我要读取一个GB2312 '好' 字的数据出来,编码是BAC3.

国标码查询;汉字国家标准编码:GB2312、GBK、GB18030

对应的十六进制数据是:

{0x10,0x00,0x10,0xFC,0x10,0x04,0x10,0x08,0xFC,0x10,0x24,0x20,0x24,0x20,0x25,0xFE},
{0x24,0x20,0x48,0x20,0x28,0x20,0x10,0x20,0x28,0x20,0x44,0x20,0x84,0xA0,0x00,0x40},/*"好",0*/

首先知道一个16 x 16汉字占 多少个字节: size = 16 * 16 / 8 = 32byte

现在有了GB2312字符编码,用 下面公式即可定位该字符在字库位置。

Address = (((CodeH-0xA0-1)*94) +(CodeL-0xA0-1)) * size ;

测试代码:虽然拿到了字符在字库的偏移,但是不要忘了字库在flash的偏移是0x80C0000。

void GB2312_FontDataRead(uint16_t code)
{
	uint32_t i = 0;
	uint8_t CodeL = code & 0xFF;
	uint8_t CodeH = (code >> 8) & 0xFF;
	uint32_t Addr = (((CodeH - 0xA0 - 1) * 94) + (CodeL - 0xA0 - 1)) * 32;

	Addr = Addr + 0x80C0000;

	for(i = 0; i < 32; i++)
	{
		printf("0x%02X ", *(uint8_t *)(Addr + i));
	}
	printf("\r\n");
}

执行结果:

数据一致,好评。

5.显示汉字

既然拿到的数据也是正确的,那么就在拿到数据判读哪位bit位1,即画点。

由于大家使用的显示屏不同,但是画点的函数一定会有,如LCD_DrawPoint(uint16_t x, uint16_t y, uint32_t color)。

void LCD_ShowGB2312_16X16(uint16_t x, uint16_t y, uint16_t color, uint16_t code)
{
	uint8_t CodeL = code & 0xFF;
	uint8_t CodeH = (code >> 8) & 0xFF;
	uint8_t i = 0, j = 0, z = 0, temp = 0;
	uint32_t Addr = 0;

	if(code < 0xA1A1 || code > 0xFEFE) return;

	Addr = (((CodeH - 0xA0 - 1) * 94) + (CodeL - 0xA0 - 1)) * 16 * 16 / 8;
	Addr += 0x80C0000;

	for(i = 0; i < 16; i++)
	{
		for(j = 0; j < 2; j++)
		{
			temp = *(uint8_t*)(Addr + (i*2)+j);
			for(z = 0; z < 8; z++)
			{
				if(temp & (0x80 >> z))
					LCD_Fast_DrawPoint(x + (j * 8) + z, y, color);
			}
		}
		y++;
	}
}

现象:

6. 制作英文字库

注意:由于英文在16x16矩阵中实际是只用了8 x 16,故计算字符偏移: size = 8 * 16 / 8 = 16byte

后面步骤一样,改名bin,烧录到flash地址0x80A0000,然后读取数据对比。

显示代码:

void LCD_ShowASCII_8x16(uint16_t x, uint16_t y, uint16_t color, uint8_t ch)
{
	uint32_t Addr = 0x80A0000;
	uint8_t i = 0, j = 0, temp = 0;

	if(ch > 127) return;

	Addr += ch * 16;
	printf("ch = %X, a = %lX\r\n", ch, Addr);

	for(i = 0; i < 16; i++)
	{
		temp = *(uint8_t*)(Addr + i);
		for(j = 0; j < 8; j++)
		{
			if(temp & (0x80 >> j))
				LCD_Fast_DrawPoint(x + j, y, color);
		}
		y++;
	}
}

显示英文字符' S ',效果:

               

7. 补充代码

显示中英混合字符串函数:

ASCII码是用1个字节表示,而GB2312是用2个字节表示。

但是ASCII最大是127,故凡是大于127的编码都按照GB2312解析,取两个字节。

void LCD_ShowString(uint16_t x, uint16_t y, uint16_t color, uint8_t* pData)
{
	uint16_t temp = 0;
	uint16_t len = strlen((char*)pData);

	while(len > 0)
	{
		temp = *pData;
		if(temp <= 127)		// ASCII
		{
			LCD_ShowASCII_8x16(x, y, color, temp);
			x += 8;
			len -= 1;
			pData += 1;
		}
		else				// GB2312
		{
			if(len < 2) return;
			temp = ((*pData) << 8) + (*(pData + 1));
			LCD_ShowGB2312_16X16(x, y, color, temp);
			x += 16;
			pData += 2;
			len -= 2;
		}
	}
}

支持类库函数printf打印输出在屏幕上

void LCD_Printf(uint16_t x, uint16_t y, uint16_t color, const char *format, ...)
{
	uint16_t length = 0;
    va_list args;
    uint8_t buf[100] = {0};

    va_start(args, format);
    length = vsnprintf((char *)buf, sizeof(buf), (char *)format, args);
    va_end(args);
    if(length == 0) return;
    LCD_ShowString(x, y, color, buf);
}

8. 最后测试

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_FSMC_Init();
  MX_USART1_UART_Init();

  /* USER CODE BEGIN 2 */
  printf("Sudaroot\r\n");
  LCD_Init();
  GB2312_FontDataRead(0xBAC3);
  LCD_ShowGB2312_16X16(100, 100, RED, 0xBAC3);
  LCD_ShowASCII_8x16(100, 200, BLUE, 'S');
  LCD_ShowString(100, 200, RED, (uint8_t*)"你好吗?Sudaroot");
  LCD_Printf(100, 300, RED, "%s = %d分", "Sudaroot", 59);
  /* USER CODE END 2 */

  while (1)
  {

  }
}

效果:

  全篇完。

本人是一个嵌入式未入门小白,博客仅仅代表我个人主观见解,记录成长笔记。
笔记是以最简单的方式,只展示最核心的原理。
若有与 大神大大 见解有歧义,我绝对坚信 大神大大 见解是对的,我的是错的。
若无积分等无法下载源码,可加入QQ群657407920下载交流经验。感谢~!

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值