【LCD】液晶显示中英文实验(4)
本文介绍了在嵌入式系统中显示中英文字符的两种实现方法:外部Flash存储和SD卡存储。硬件设计包括获取字模数据、编写液晶显示函数和测试程序。代码分析部分详细说明了显示32×32点阵汉字的函数实现,通过GetGBKCode从外部存储器读取字模数据并逐位绘制。同时提供了显示混合字符串的函数,支持自动换行处理。最后介绍了从SPI-Flash获取GB2312/GBK编码字模数据的方法,包括地址计算和初始化
文章目录
【LCD】液晶显示中英文实验系列文章
【LCD】液晶显示中英文实验(1)
【LCD】液晶显示中英文实验(2)
【LCD】液晶显示中英文实验(3)
【LCD】液晶显示中英文实验(4)
【LCD】液晶显示中英文实验(5)
前言
显示 ASCII 编码比较简单,由于字库文件小,甚至都不需要使用外部的存储器,而显示汉字时,由于我们的字库是存储到外部存储器上的,这涉及到额外的获取字模数据的操作。同时,本文我们将使用两种方式进行存储字模数据显示中英文,第一种是存储在外部Flash,第二种是存储在SD卡里面,接下来我们就来做一下这两个实验显示GB2312编码格式的字符。这两个工程使用的字
库文件内容相同,只是字库存储的位置不一样,工程中我们把获取字库数据相关的函数代码写在“fonts.c”及“fonts.h”文件中,字符显示的函数仍存储在 LCD 驱动文件“bsp_ili9806g_lcd.c”及“bsp_ili9806g_lcd.h”中。
一、硬件设计
- 获取字模数据
- 根据字模格式,编写液晶显示函数
- 编写测试程序,控制液晶显示汉字
二、代码分析
显示汉字字符:
该函数接收中文字符显示的起始坐标 (usX, usY) 和字符的国标码 usChar,先划定显示窗口,再通过 GetGBKCode 读取该字符的 32×32 点阵字模数据,最后逐行解析字模数据(每行 4 字节 = 32 位),逐位绘制前景色 / 背景色,最终在屏幕上显示出 32×32 尺寸的中文字符。
/*******************中文********** 在显示屏上显示的字符大小 ***************************/
#define WIDTH_CH_CHAR 32 //中文字符宽度
#define HEIGHT_CH_CHAR 32 //中文字符高度
//缓存读取回来的字模数据
static uint8_t ucBuffer [ WIDTH_CH_CHAR*HEIGHT_CH_CHAR/8 ];
/**
* @brief 在 ILI9806G 显示器上显示一个中文字符
* @param usX :在特定扫描方向下字符的起始X坐标
* @param usY :在特定扫描方向下字符的起始Y坐标
* @param usChar :要显示的中文字符(国标码)
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9806G_DispChar_CH ( uint16_t usX, uint16_t usY, uint16_t usChar )
{
uint8_t rowCount, bitCount;
uint32_t usTemp;
// 占用空间太大,改成全局变量
// uint8_t ucBuffer [ WIDTH_CH_CHAR*HEIGHT_CH_CHAR/8 ];
//设置显示窗口
ILI9806G_OpenWindow ( usX, usY, WIDTH_CH_CHAR, HEIGHT_CH_CHAR );
ILI9806G_Write_Cmd ( CMD_SetPixel );
//取字模数据
GetGBKCode ( ucBuffer, usChar );
for ( rowCount = 0; rowCount < HEIGHT_CH_CHAR; rowCount++ )
{
/* 取出四个字节的数据,在lcd上即是一个汉字的一行 */
usTemp = ucBuffer [ rowCount * 4 ];
usTemp = ( usTemp << 8 );
usTemp |= ucBuffer [ rowCount * 4 + 1 ];
usTemp = ( usTemp << 8 );
usTemp |= ucBuffer [ rowCount * 4 + 2 ];
usTemp = ( usTemp << 8 );
usTemp |= ucBuffer [ rowCount * 4 + 3 ];
for ( bitCount = 0; bitCount < WIDTH_CH_CHAR; bitCount ++ )
{
if ( usTemp & ( 0x80000000 >> bitCount ) ) //高位在前
ILI9806G_Write_Data ( CurrentTextColor );
else
ILI9806G_Write_Data ( CurrentBackColor );
}
}
}
执行示例:
显示中英文字符串:
该函数接收字符串起始坐标 (usX, usY) 和字符串首地址 pStr,循环遍历字符串:
- 若为英文字符(ASCII ≤ 126):调用 ILI9806G_DispChar_EN 显示,按当前英文字体尺寸偏移坐标;
- 若为中文字符(ASCII > 126):解析双字节 GBK 编码,调用 ILI9806G_DispChar_CH 显示,按 32×32 尺寸偏移坐标;
- 全程检测屏幕边界,超出宽度则换行,超出高度则回到屏幕顶部,保证字符串不越界。
/**
* @brief 在 ILI9806G 显示器上显示中英文字符串
* @param usX :在特定扫描方向下字符的起始X坐标
* @param usY :在特定扫描方向下字符的起始Y坐标
* @param pStr :要显示的字符串的首地址
* @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
* @retval 无
*/
void ILI9806G_DispString_EN_CH ( uint16_t usX , uint16_t usY, char * pStr )
{
uint16_t usCh;
while( * pStr != '\0' )
{
if ( * pStr <= 126 ) //英文字符
{
if ( ( usX - ILI9806G_DispWindow_X_Star + LCD_Currentfonts->Width ) > LCD_X_LENGTH )
{
usX = ILI9806G_DispWindow_X_Star;
usY += LCD_Currentfonts->Height;
}
if ( ( usY - ILI9806G_DispWindow_Y_Star + LCD_Currentfonts->Height ) > LCD_Y_LENGTH )
{
usX = ILI9806G_DispWindow_X_Star;
usY = ILI9806G_DispWindow_Y_Star;
}
ILI9806G_DispChar_EN ( usX, usY, * pStr );
usX += LCD_Currentfonts->Width;
pStr ++;
}
else //汉字字符
{
if ( ( usX - ILI9806G_DispWindow_X_Star + WIDTH_CH_CHAR ) > LCD_X_LENGTH )
{
usX = ILI9806G_DispWindow_X_Star;
usY += HEIGHT_CH_CHAR;
}
if ( ( usY - ILI9806G_DispWindow_Y_Star + HEIGHT_CH_CHAR ) > LCD_Y_LENGTH )
{
usX = ILI9806G_DispWindow_X_Star;
usY = ILI9806G_DispWindow_Y_Star;
}
usCh = * ( uint16_t * ) pStr;
usCh = ( usCh << 8 ) + ( usCh >> 8 );
ILI9806G_DispChar_CH ( usX, usY, usCh );
usX += WIDTH_CH_CHAR;
pStr += 2; //一个汉字两个字节
}
}
}
获取 SPI-FLASH 中的字模数据:
这段代码是用于从外部 FLASH 存储器中读取 GB2312/GBK 编码中文字符对应的点阵字模数据的核心函数,主要功能是根据中文字符的国标码计算其在 FLASH 中的存储位置,并读取对应的字模数据到指定缓冲区。
/*使用FLASH字模*/
/*中文字库存储在FLASH的起始地址*/
/*FLASH*/
#define GBKCODE_START_ADDRESS 1254*4096
/*获取字库的函数*/
//定义获取中文字符字模数组的函数名,ucBuffer为存放字模数组名,usChar为中文字符(国标码)
#define GetGBKCode( ucBuffer, usChar ) GetGBKCode_from_EXFlash( ucBuffer, usChar )
int GetGBKCode_from_EXFlash( uint8_t * pBuffer, uint16_t c);
/*使用FLASH字模*/
//字模GB2312_H3232配套的函数
//中文字库存储在FLASH的起始地址 :
//GBKCODE_START_ADDRESS 在fonts.h文件定义
/**
* @brief 获取FLASH中文显示字库数据
* @param pBuffer:存储字库矩阵的缓冲区
* @param c : 要获取的文字
* @retval None.
*/
int GetGBKCode_from_EXFlash( uint8_t * pBuffer, uint16_t c)
{
unsigned char High8bit,Low8bit;
unsigned int pos;
static uint8_t everRead=0;
/*第一次使用,初始化FLASH*/
if(everRead == 0)
{
SPI_FLASH_Init();
everRead = 1;
}
High8bit= c >> 8; /* 取高8位数据 */
Low8bit= c & 0x00FF; /* 取低8位数据 */
/*GB2312 公式*/
pos = ((High8bit-0xa1)*94+Low8bit-0xa1)*WIDTH_CH_CHAR*HEIGHT_CH_CHAR/8;
SPI_FLASH_BufferRead(pBuffer,GBKCODE_START_ADDRESS+pos,WIDTH_CH_CHAR*HEIGHT_CH_CHAR/8); //读取字库数据
// printf ( "%02x %02x %02x %02x\n", pBuffer[0],pBuffer[1],pBuffer[2],pBuffer[3]);
return 0;
}
获取 SD 卡中的字模数据:
这段代码是从 SD 卡中读取 GB2312 编码的 32x32 点阵中文字模数据的核心实现,和之前的 FLASH 版本逻辑一致,但存储介质换成了 SD 卡,使用 FatFs 文件系统进行操作。
/*使用SD字模*/
/*SD卡字模路径*/
#define GBKCODE_FILE_NAME "0:/srcdata/GB2312_H3232.FON"
/*获取字库的函数*/
//定义获取中文字符字模数组的函数名,ucBuffer为存放字模数组名,usChar为中文字符(国标码)
#define GetGBKCode( ucBuffer, usChar ) GetGBKCode_from_sd( ucBuffer, usChar )
int GetGBKCode_from_sd ( uint8_t * pBuffer, uint16_t c);
/*使用SD字模*/
static FIL fnew; /* file objects */
static FATFS fs; /* Work area (file system object) for logical drives */
static FRESULT res_sd;
static UINT br; /* File R/W count */
//字库文件存储位置,fonts.h中的宏:
//#define GBKCODE_FILE_NAME "0:/Font/GB2312_H3232.FON"
/**
* @brief 获取SD卡中文显示字库数据
* @param pBuffer:存储字库矩阵的缓冲区
* @param c : 要获取的文字
* @retval None.
*/
int GetGBKCode_from_sd ( uint8_t * pBuffer, uint16_t c)
{
unsigned char High8bit,Low8bit;
unsigned int pos;
static uint8_t everRead = 0;
High8bit= c >> 8; /* 取高8位数据 */
Low8bit= c & 0x00FF; /* 取低8位数据 */
pos = ((High8bit-0xa1)*94+Low8bit-0xa1)*WIDTH_CH_CHAR*HEIGHT_CH_CHAR/8;
/*第一次使用,挂载文件系统,初始化sd*/
if(everRead == 0)
{
res_sd = f_mount(&fs,"0:",1);
everRead = 1;
}
res_sd = f_open(&fnew , GBKCODE_FILE_NAME, FA_OPEN_EXISTING | FA_READ);
if ( res_sd == FR_OK )
{
f_lseek (&fnew, pos); //指针偏移
//32*32大小的汉字 其字模 占用32*32/8个字节
res_sd = f_read( &fnew, pBuffer, WIDTH_CH_CHAR*HEIGHT_CH_CHAR/8, &br );
f_close(&fnew);
return 0;
}
else
return -1;
}
显示 GB2312 字符示例:
这段代码是整个液晶屏中文显示程序的主函数入口,核心功能是完成硬件初始化、打印提示信息、设置液晶显示方向,并进入循环执行 LCD 测试逻辑。
int main(void)
{
//LCD 初始化
ILI9806G_Init ();
/* USART config */
Debug_USART_Config();
printf("\r\n ********** 液晶屏中文显示程序(字模文件在SD卡)*********** \r\n");
printf("\r\n 实验前请阅读工程中的readme.txt文件说明,存储字模数据到SD卡\r\n");
//其中0、3、5、6 模式适合从左至右显示文字,
//不推荐使用其它模式显示文字 其它模式显示文字会有镜像效果
//其中 6 模式为大部分液晶例程的默认显示方向
ILI9806G_GramScan ( 6 );
Printf_Charater();
while ( 1 )
{
LCD_Test();
}
}
测试函数:
这是一个完整的液晶屏功能测试函数,核心作用是循环演示 ILI9806G 液晶屏的各类显示能力 —— 包括中英文文字显示、变量动态显示,以及直线、矩形、圆形等基本图形绘制,每个演示环节都有清晰的视觉区分和延时效果
/*用于测试各种液晶的函数*/
void LCD_Test(void)
{
/*演示显示变量*/
static uint8_t testCNT = 0;
char dispBuff[100];
testCNT++;
LCD_SetFont(&Font16x32);
LCD_SetColors(RED,BLACK);
ILI9806G_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH); /* 清屏,显示全黑 */
/********显示字符串示例*******/
ILI9806G_DispStringLine_EN_CH(LINE(0),"野火4.3寸LCD参数:");
ILI9806G_DispStringLine_EN_CH(LINE(2),"分辨率:480x800 px");
ILI9806G_DispStringLine_EN_CH(LINE(3),"ILI9806G液晶驱动");
ILI9806G_DispStringLine_EN_CH(LINE(4),"GT911触摸屏驱动");
/********显示变量示例*******/
LCD_SetTextColor(GREEN);
/*使用c标准库把变量转化成字符串*/
sprintf(dispBuff,"显示变量计数 : %d ",testCNT);
ILI9806G_ClearLine(LINE(7)); /* 清除单行文字 */
/*然后显示该字符串即可,其它变量也是这样处理*/
ILI9806G_DispStringLine_EN(LINE(7),dispBuff);
/*******显示图形示例******/
/* 画直线 */
ILI9806G_ClearLine(LINE(7));/* 清除单行文字 */
LCD_SetTextColor(BLUE);
ILI9806G_DispStringLine_EN_CH(LINE(7),"画直线:");
LCD_SetTextColor(RED);
ILI9806G_DrawLine(50,270,420,275);
ILI9806G_DrawLine(50,300,420,375);
LCD_SetTextColor(GREEN);
ILI9806G_DrawLine(50,370,420,475);
ILI9806G_DrawLine(50,400,420,475);
LCD_SetTextColor(BLUE);
ILI9806G_DrawLine(50,420,420,325);
ILI9806G_DrawLine(50,450,420,395);
Delay(0x2FFFFFF);
ILI9806G_Clear(0,32*7,LCD_X_LENGTH,LCD_Y_LENGTH-32*7); /* 清屏,显示全黑 */
/*画矩形*/
ILI9806G_ClearLine(LINE(7)); /* 清除单行文字 */
LCD_SetTextColor(BLUE);
ILI9806G_DispStringLine_EN_CH(LINE(7),"画矩形:");
LCD_SetTextColor(RED);
ILI9806G_DrawRectangle(50,300,200,100,1);
LCD_SetTextColor(GREEN);
ILI9806G_DrawRectangle(100,300,200,120,0);
LCD_SetTextColor(BLUE);
ILI9806G_DrawRectangle(250,300,200,150,1);
Delay(0x2FFFFFF);
ILI9806G_Clear(0,32*7,LCD_X_LENGTH,LCD_Y_LENGTH-32*7); /* 清屏,显示全黑 */
/* 画圆 */
ILI9806G_ClearLine(LINE(7)); /* 清除单行文字 */
LCD_SetTextColor(BLUE);
ILI9806G_DispStringLine_EN_CH(LINE(7),"画圆:");
LCD_SetTextColor(RED);
ILI9806G_DrawCircle(150,400,60,1);
LCD_SetTextColor(GREEN);
ILI9806G_DrawCircle(250,400,60,0);
LCD_SetTextColor(BLUE);
ILI9806G_DrawCircle(350,400,60,1);
Delay(0x2FFFFFF);
ILI9806G_Clear(0,32*7,LCD_X_LENGTH,LCD_Y_LENGTH-32*7); /* 清屏,显示全黑 */
}
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐

所有评论(0)