LV16-26-LCD-07-任意大小字模?

本文主要是字模缩放的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows windows11
Ubuntu Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
SecureCRT Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日
开发板 正点原子 i.MX6ULL Linux阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
STM32开发板 正点原子战舰V3(STM32F103ZET6)
点击查看本文参考资料
  • 通用
分类 网址说明
官方网站https://www.arm.com/ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档
https://www.st.com/content/st_com/zh.htmlST官方网站,在这里我们可以找到STM32的相关文档
https://www.stmcu.com.cn/意法半导体ST中文官方网站,在这里我们可以找到STM32的相关中文参考文档
http://elm-chan.org/fsw/ff/00index_e.htmlFatFs文件系统官网
教程书籍《ARM Cortex-M3权威指南》ARM公司专家Joseph Yiu(姚文祥)的力作,中文翻译是NXP的宋岩
《ARM Cortex-M0权威指南》
《ARM Cortex-M3与Cortex-M4权威指南》
开发论坛http://47.111.11.73/forum.php开源电子网,正点原子的资料下载及问题讨论论坛
https://www.firebbs.cn/forum.php国内Kinetis开发板-野火/秉火(刘火良)主持的论坛,现也做STM32和i.MX RT
https://www.amobbs.com/index.php阿莫(莫进明)主持的论坛,号称国内最早最火的电子论坛,以交流Atmel AVR系列单片机起家,现已拓展到嵌入式全平台,其STM32系列帖子有70W+。
http://download.100ask.net/index.html韦东山嵌入式资料中心,有些STM32和linux的相关资料也可以来这里找。
博客参考http://www.openedv.com/开源网-原子哥个人博客
http://blog.chinaaet.com/jihceng0622博主是原Freescale现NXP的现场应用工程师
cortex-m-resources这其实并不算是一个博客,这是ARM公司专家Joseph Yiu收集整理的所有对开发者有用的官方Cortex-M资料链接(也包含极少数外部资源链接)
  • STM32
STM32STM32 HAL库开发实战指南——基于F103系列开发板野火STM32开发教程在线文档
STM32库开发实战指南——基于野火霸道开发板野火STM32开发教程在线文档
  • SD卡
SD Association提供了SD存储卡和SDIO卡系统规范
  • 字符编码参考网站
千千秀字 (qqxiuzi.cn)字符编码及转换测试
Unicode – The World Standard for Text and EmojiUnicode官网
点击查看相关文件下载
STM32F103xx英文数据手册STM32F103xC/D/E系列的英文数据手册
STM32F103xx中文数据手册STM32F103xC/D/E系列的中文数据手册
STM32F10xxx英文参考手册(RM0008)STM32F10xxx系列的英文参考手册
STM32F10xxx中文参考手册(RM0008)STM32F10xxx系列的中文参考手册
Arm Cortex-M3 处理器技术参考手册-英文版Cortex-M3技术参考手册-英文版
STM32F10xxx Cortex-M3编程手册-英文版(PM0056)STM32F10xxx/20xxx/21xxx/L1xxxx系列Cortex-M3编程手册-英文版
SD卡相关资料——最新版本有关SD卡的一些资料可以从这里下载
SD卡相关资料——历史版本有关SD卡的一些历史版本资料可以从这里下载,比如后边看的SD卡2.0协议
SD 2.0 协议标准完整版这是一篇关于SD卡2.0协议的中文文档,还是比较有参考价值的,可以一看

我们要是想显示不同大小的字符怎么办?重新生成不同大小的字模?这当然是可以的啦,但是吧,要是需要的大小有很多怎么办,比如说我们需要16x16的,32x32的,64x64的,……,这样的话,都生成数组,然后放在文件里嘛,中文也都做成字库嘛?这样其实是可以的,只要内存足够就行,但是这样其实是很浪费资源的,它们的字模都是一样的,但是仅仅就是大小不同,我们就不能用一张表,然后对字体进行缩放,得到其他大小的字模嘛?当然可以啦,下边就来学习一下这个字模缩放的流程啦。

注意后边主要是以英文字模的缩放为例,来说明缩放的原理,我们使用的字模大小为宽16高16,对应到英文的话,就是宽8高16啦。

一、基本原理

其实对于字模缩放来说,基本原理很简单,我们以之前从linux内核源码中复制出来的8x16的字模 “A”为例。

1. 原字模数据

1.1 字模数组

它对应的字模数组为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
uint8_t fontdata_8x16[] = {
/* 65 0x41 'A' */
0x00, /* 0000 0000 */
0x00, /* 0000 0000 */
0x10, /* 0001 0000 */
0x38, /* 0011 1000 */
0x6c, /* 0110 1100 */
0xc6, /* 1100 0110 */
0xc6, /* 1100 0110 */
0xfe, /* 1111 1110 */
0xc6, /* 1100 0110 */
0xc6, /* 1100 0110 */
0xc6, /* 1100 0110 */
0xc6, /* 1100 0110 */
0x00, /* 0000 0000 */
0x00, /* 0000 0000 */
0x00, /* 0000 0000 */
0x00, /* 0000 0000 */
};

1.2 打印函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void printf_Charater_8x16(void)
{
int i, j;
uint8_t ch;

for ( i=0; i<16; i++) /* i 用作行计数,一共16行 */
{
for (j=0; j<8; j++) /*j 用作一字节内数据的移位计数, 一行像素一个字节 */
{
ch = fontdata_8x16[i];/* 一个数据位一个数据位地处理,先处理高位 */
if(ch & (0x80 >> j)) // 0x80 = 1000 0000
{
printf("*"); // 如果最高位为 1,输出 * 号,表示笔迹
}
else
{
printf(" "); // 如果最高位为 0,输出空格,表示空白
}

}
printf("\r\n"); // 输出完一行像素,换行
}
printf("\r\n\r\n"); // 一个字符输出完毕
}

1.3 打印效果

image-20230528165704437

2. 横向放大2倍

2.1 基本原理

我们想要横向放大两倍,那么我们可以采用这样的方式,我们将每一列,都复制一列,然后填充到这一列的后一列:

image-20230528174511513

2.2 字模数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
uint8_t fontdata_16x16[] = {
0x00, 0x00, /* 0000 0000 0000 0000 */
0x00, 0x00, /* 0000 0000 0000 0000 */
0x03, 0x00, /* 0000 0011 0000 0000 */
0x0F, 0xc0, /* 0000 1111 1100 0000 */
0x3c, 0xf0, /* 0011 1100 1111 0000 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xff, 0xfc, /* 1111 1111 1111 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0x00, 0x00, /* 0000 0000 0000 0000 */
0x00, 0x00, /* 0000 0000 0000 0000 */
0x00, 0x00, /* 0000 0000 0000 0000 */
0x00, 0x00, /* 0000 0000 0000 0000 */
};

2.3 打印函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void printf_Charater_16x16(void)
{
int i, j;
uint16_t ch;

for ( i=0; i<16; i++) /* i 用作行计数,一共16行 */
{
for (j=0; j<16; j++) /*j 用作一字节内数据的移位计数, 一行像素一个字节 */
{
ch = fontdata_16x16[2*i];
ch = ch << 8;
ch |= fontdata_16x16[2*i+1];
if(ch & (0x8000 >> j)) // 0x8000 = 1000 0000 0000 0000
{
printf("*"); // 如果最高位为 1,输出 * 号,表示笔迹
}
else
{
printf(" "); // 如果最高位为 0,输出空格,表示空白
}

}
printf("\r\n"); // 输出完一行像素,换行
}
printf("\r\n\r\n"); // 一个字符输出完毕
}

2.4 打印效果

image-20230528174615296

3. 纵向放大2倍

3.1 基本原理

与横向放大类似,只是每行往下一行复制一行,行数加一倍:

image-20230528174738588

3.2 字模数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
uint8_t fontdata_8x32[] = {
/* 65 0x41 'A' */
0x00, /* 0000 0000 */
0x00, /* 0000 0000 */
0x00, /* 0000 0000 */
0x00, /* 0000 0000 */
0x10, /* 0001 0000 */
0x10, /* 0001 0000 */
0x38, /* 0011 1000 */
0x38, /* 0011 1000 */
0x6c, /* 0110 1100 */
0x6c, /* 0110 1100 */
0xc6, /* 1100 0110 */
0xc6, /* 1100 0110 */
0xc6, /* 1100 0110 */
0xc6, /* 1100 0110 */
0xfe, /* 1111 1110 */
0xfe, /* 1111 1110 */
0xc6, /* 1100 0110 */
0xc6, /* 1100 0110 */
0xc6, /* 1100 0110 */
0xc6, /* 1100 0110 */
0xc6, /* 1100 0110 */
0xc6, /* 1100 0110 */
0xc6, /* 1100 0110 */
0xc6, /* 1100 0110 */
0x00, /* 0000 0000 */
0x00, /* 0000 0000 */
0x00, /* 0000 0000 */
0x00, /* 0000 0000 */
0x00, /* 0000 0000 */
0x00, /* 0000 0000 */
0x00, /* 0000 0000 */
0x00, /* 0000 0000 */
};

3.3 打印函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void printf_Charater_8x32(void)
{
int i, j;
uint8_t ch;

for ( i=0; i<32; i++) /* i 用作行计数,一共32行 */
{
for (j=0; j<8; j++) /*j 用作一字节内数据的移位计数, 一行像素一个字节 */
{
ch = fontdata_8x32[i];/* 一个数据位一个数据位地处理,先处理高位 */
if(ch & (0x80 >> j)) // 0x80 = 1000 0000
{
printf("*"); // 如果最高位为 1,输出 * 号,表示笔迹
}
else
{
printf(" "); // 如果最高位为 0,输出空格,表示空白
}

}
printf("\r\n"); // 输出完一行像素,换行
}
printf("\r\n\r\n"); // 一个字符输出完毕
}

3.4 打印效果

image-20230528175113851

4. 整体放大2倍

4.1 基本原理

整体放大的原理与上边类似,只是综合了横向放大和纵向放大:

image-20230528175250945

4.2 字模数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
uint8_t fontdata_16x32[] = {
0x00, 0x00, /* 0000 0000 0000 0000 */
0x00, 0x00, /* 0000 0000 0000 0000 */
0x00, 0x00, /* 0000 0000 0000 0000 */
0x00, 0x00, /* 0000 0000 0000 0000 */
0x03, 0x00, /* 0000 0011 0000 0000 */
0x03, 0x00, /* 0000 0011 0000 0000 */
0x0f, 0xc0, /* 0000 1111 1100 0000 */
0x0f, 0xc0, /* 0000 1111 1100 0000 */
0x3c, 0xf0, /* 0011 1100 1111 0000 */
0x3c, 0xf0, /* 0011 1100 1111 0000 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xff, 0xfc, /* 1111 1111 1111 1100 */
0xff, 0xfc, /* 1111 1111 1111 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0xf0, 0x3c, /* 1111 0000 0011 1100 */
0x00, 0x00, /* 0000 0000 0000 0000 */
0x00, 0x00, /* 0000 0000 0000 0000 */
0x00, 0x00, /* 0000 0000 0000 0000 */
0x00, 0x00, /* 0000 0000 0000 0000 */
0x00, 0x00, /* 0000 0000 0000 0000 */
0x00, 0x00, /* 0000 0000 0000 0000 */
0x00, 0x00, /* 0000 0000 0000 0000 */
0x00, 0x00, /* 0000 0000 0000 0000 */
};

4.3 打印函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void printf_Charater_16x32(void)
{
int i, j;
uint16_t ch;

for ( i=0; i<32; i++) /* i 用作行计数,一共32行 */
{
for (j=0; j<16; j++) /*j 用作一字节内数据的移位计数, 一行像素一个字节 */
{
ch = fontdata_16x32[2*i];
ch = ch << 8;
ch |= fontdata_16x32[2*i+1];
if(ch & (0x8000 >> j)) // 0x8000 = 1000 0000 0000 0000
{
printf("*"); // 如果最高位为 1,输出 * 号,表示笔迹
}
else
{
printf(" "); // 如果最高位为 0,输出空格,表示空白
}

}
printf("\r\n"); // 输出完一行像素,换行
}
printf("\r\n\r\n"); // 一个字符输出完毕
}

4.4 打印效果

image-20230528180517272

5. 循环与宽高

从上边我们可以得到如下结论

原始宽高 目标宽高 缩放说明 字节数
8x16 8x16 不缩放 16行数据,每行8位(一个字节)
8x16 16x16 横向放大2倍 16行数据,每行16位(两个字节)
8x16 8x32 纵向放大2倍 32行数据,每行8位(一个字节)
8x16 16x32 整体放大2倍 32行数据,每行16位(两个字节)

我们要是按行处理的话:

1
2
3
4
需要处理的行数 = 高
每一行需要处理的像素点数 = 宽
每一行需要处理的字节数 = 宽 / 8
单个字模所占的字节数 = 宽 x 高 / 8

二、缩放实现

我这里只是实现了英文字符的缩放实现,主要是熟悉一下原理。

1. 位到字节的映射

首先,我们为了方便处理,将字模的每一个像素点都映射成一个字节,就是原本一个像素点是一个位表示的,现在扩展到1个字节,扩展后的这个字节依然表示这个像素点点亮或者不点亮,取值只有1和0两种,仅仅是从一个位变成了一个字节而已。

1
2
3
4
5
6
7
8
9
10
/**
* @brief 字模缩放前将位转化为字节
* @note
* @param in_width 原始的字模的宽
* @param in_height 原始的字模的高
* @param in_data 原始的字模的起始地址
* @param bitToByteBuff 每一位转化为字节后的暂存地址
* @retval
*/
int8_t wordModule_BitToByte(uint16_t in_width, uint16_t in_height, uint8_t *in_data, uint8_t *bitToByteBuff);

函数实现可以查看Hardware/lcd.c · sumumm/STM32F103-Prj - 码云 - 开源中国 (gitee.com)

2. 字模缩放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @brief 字模缩放
* @note 一般都输出宽高都是输入宽高的1倍,2倍,3倍等
* @param in_width 原始的字模的宽
* @param in_height 原始的字模的高
* @param out_width 缩放后的字模的宽
* @param out_height 缩放后的字模的高
* @param in_data 原始的字模的起始地址
* @param out_data 缩放后的字模的起始地址
* @param bitToByteBuff 将像素位转化为字节表示的缓冲区
* @retval
*/
int8_t wordModule_Zoom(uint16_t in_width, uint16_t in_height,
uint16_t out_width, uint16_t out_height,
uint8_t *in_data, uint8_t *out_data, uint8_t *bitToByteBuff);

函数实现可以查看Hardware/lcd.c · sumumm/STM32F103-Prj - 码云 - 开源中国 (gitee.com)

3. 显示缩放后的字符

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @brief 利用缩放后的字模显示字符
* @note
* @param Xpos 字符显示位置x
* @param Ypos 字符显示位置y
* @param Font_width 字符宽度
* @param Font_Height 字符高度
* @param cChar 要显示的字符
* @param DrawModel 是否反色显示
* @retval
*/
void LCD_ShowChar_Ex(uint16_t usX, uint16_t usY, uint16_t Font_width, uint16_t Font_Height, uint8_t cChar, uint16_t DrawModel);

函数实现可以查看Hardware/lcd.c · sumumm/STM32F103-Prj - 码云 - 开源中国 (gitee.com)

4. 测试函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void LCD_Test(void)
{

// 读取id
ILI9341_ID_Read();

ILI9341_Config();
lcd_param.LCD_SCAN_MODE = XY_NORMAL;
ILI9341_GramScan(lcd_param.LCD_SCAN_MODE);

LCD_SetTextColor(RED);
LCD_ShowString(0, 0, "--->");
LCD_ShowString(0, lcd_param.LCD_Currentfonts->Height, "|");
LCD_ShowString(0, 2*lcd_param.LCD_Currentfonts->Height, "|");
LCD_ShowString(0, 3*lcd_param.LCD_Currentfonts->Height, "v");

// zoomChar(8, 16, 32, 64, fontdata_8x16, NULL,0);
// LCD_ShowChar_Ex(100,100, 32, 64, zoomTempBuff, 0);
// printf_Charater_16x32();
// uint8_t temp[8*16] = {0};
// bitToByte(8, 16, fontdata_8x16, temp);

LCD_ShowChar_Ex(100, 100, 24, 32, 'A', 0);
LCD_SetFontSize(&font_24x32);
LCD_ShowChar(100, 200,'A');
}

最后缩放确实是实现了,但是以我所使用的字体,放大后锯齿特别严重,这个吧,可能还需要算法优化,以后再说吧。