基于 GD32F303ZET6 苹果派开发板
简介
在本章中将介绍如何显示透明字体,即实现 “text” 或 “label” 的功能。
LCDShowChar 函数
LCD 驱动中提供了绘制字符函数,用于显示一个 ASCII 码,LCDShowChar 函数的形参如下所示。
void LCDShowChar(
u16 x, //字符横坐标
u16 y, //字符纵坐标
u8 code, //字符编码,即 ASCII 码
u8 size, //字体大小
u8 mode) //显示模式
x 和 y 即为字符的坐标原点,要注意,屏幕的坐标原点在左上角,为统一管理,字符的原点我们也定义在字符的左上角。
code 即为字符编码。LCD 驱动中存储了 6×12、8×16 和 12×24 ASCII 码点阵数据,位于 “LCD/Font.h”,用于显示英文字符。如果用户想要显示中文,那么就需要将 code 形参拓展到 16 位甚至是 32 位,并提供中文字符的点阵数据。使用 LCDShowChar 函数显示字符时,code 参数直接填入字符对应的 ASCII 码即可。
size 为字体大小,可输入 12、16 或 24,分别对应 6×12、8×16 和 12×24 字体。
mode 为显示模式,输入 0 可显示带背景颜色的字符。LCDShowChar 显示的字符颜色由 g_iLCDPointColor 控制,背景颜色由 g_iLCDBackColor 控制。输入 1 将不绘制背景色,可显示透明效果,为本章中讨论的重点。
带透明效果和不带透明效果显示的字符比较
要想显示带背景的字符,可参考如下所示代码。
//带背景的字符
g_iLCDPointColor = BLACK;
g_iLCDBackColor = WHITE;
LCDShowChar(400, 240, 'A', 24, 0);
显示效果如下所示。

可以看到字符 ‘A’ 显示在白色方框中。可我们在 PC 端或移动端做界面设计时,很少遇到带有背景色的文字。如果我们想让字体完美的融入背景中,怎么办?可以将 LCDShowChar 函数的 mode 形参设为 1,即可显示透明效果,如下所示。

字符重叠
前边的实验中可以看到,只要将 LCDShowChar 函数最后一个参数,即 mode 参数设为 1,即可将字符融入背景中,但这又会引入新的问题,如下所示。假设字符 ‘A’ 显示后,由于某种原因,需要刷新此区域,改为显示 ‘B’ 字符,我们也同样调用 LCDShowChar 函数显示。
//字符重叠
g_iLCDPointColor = BLACK;
g_iLCDBackColor = WHITE;
LCDShowChar(400, 240, 'A', 24, 1);
LCDShowChar(400, 240, 'B', 24, 1);
正常来说应该显示的是字符 ‘B’,可是实际显示的图案如下所示。仔细看的话可以发现,这时字符 ‘A’ 和 字符 ‘B’ 重叠后呈现的效果。显示字符 ‘B’ 时,字符 ‘A’ 被当成了背景,又因为字符 ‘A’ 与字符 ‘B’ 颜色相同,就会造成字符重叠的现象。如果是纯色区域,为避免字符重叠,字符 ‘B’ 可以用带背景的方式显示,但如果背景是图片,这就比较麻烦了。

为了正常显示后续的字体,需要在单片机里设置一个背景缓冲区,绘制第一个字符之前首先将背景图案保存到缓冲区中,每次显示字体之前,首先还原背景图案,再绘制字符,这样就可以避免字符重叠。
static u16 s_arrBackground[12 * 24];
u16 x, y, i;
//保存背景
i = 0;
for(x = 0; x < 12; x++)
{
for(y = 0; y < 24; y++)
{
s_arrBackground[i++] = LCDReadPoint(x + 400, y + 240);
}
}
//绘制字符 'A'
g_iLCDPointColor = BLACK;
LCDShowChar(400, 240, 'A', 24, 1);
//显示字符 'B' 之前先还原背景
i = 0;
for(x = 0; x < 12; x++)
{
for(y = 0; y < 24; y++)
{
LCDFastDrawPoint(x + 400, y + 240, s_arrBackground[i++]);
}
}
//显示字符 'B'
LCDShowChar(400, 240, 'B', 24, 1);
封装代码
将上述代码封装,即可实现一个简单的 “text” 或 “label” 控件。头文件如下所示
//Text 控件
typedef struct
{
u16 x, y, width, height; //原点、宽度、高度
u8 size; //字体大小,可以是 12、16、24
u16* background; //控件背景,由动态内存自动创建
}StructText;
void TextInit(StructText* widget); //初始化 Text 控件
void TextShow(StructText* widget, char* text); //更新 Text 显示
源文件如下所示。源文件中使用到了 malloc 函数申请动态内存,背景缓冲区一般比较大,所以要将启动文件中的堆区设置的大一些,具体为 “ARM/System/startup_gd32f30x_hd.s” 文件,第 27 行的 “Heap_Size” 即为堆区大小。
//包含头文件
#include "Text.h"
#include "stdio.h"
#include "LCD.h"
//初始化 Text 控件
void TextInit(StructText* widget)
{
u16 x, y, i;
//为背景图片申请动态内存
widget->background = (u16*)malloc(widget->width * widget->height * sizeof(u16));
//保存背景
i = 0;
for(x = widget->x; x < widget->x + widget->width; x++)
{
for(y = widget->y; y < widget->y + widget->height; y++)
{
widget->background[i++] = LCDReadPoint(x, y);
}
}
}
//更新 Text 显示
void TextShow(StructText* widget, char* text)
{
u16 x, y, i;
//重绘背景
i = 0;
for(x = widget->x; x < widget->x + widget->width; x++)
{
for(y = widget->y; y < widget->y + widget->height; y++)
{
LCDFastDrawPoint(x, y, widget->background[i++]);
}
}
//绘制字符
x = widget->x;
y = widget->y;
while(0 != *text)
{
LCDShowChar(x, y, *text, widget->size, 1);
text++;
x = x + widget->size / 2;
}
}
测试代码如下。
void main(void)
{
static StructText s_structTextWidget;
//初始化 Text 控件
s_structTextWidget.x = 400;
s_structTextWidget.y = 240;
s_structTextWidget.width = 12 * 10;
s_structTextWidget.size = 24;
s_structTextWidget.height = s_structTextWidget.size;
TextInit(&s_structTextWidget);
while(1)
{
//显示字符串 "ABCD"
TextShow(&s_structTextWidget, "ABCD");
LEDFlicker(0);
DelayNms(500);
//显示字符串 "EFGH"
TextShow(&s_structTextWidget, "EFGH");
LEDFlicker(0);
DelayNms(500);
}
}
将代码烧入苹果派开发板后,就可以看到字符串 “ABCD” 和 “EFGH” 交替显示,没有重叠现象。
上述代码只实现了最基础的 Text 控件功能,还有好多地方需要完善,例如支持多行显示、支持左对齐、右对齐和显示等等。
实验结果
实验结果如下所示。
-显示透明字体-实验结果-20230304-1024x768.jpg)
源码
本章节中的源码请参考《单片机 GUI 设计(零)- 大纲》