28. LCD

您所在的位置:网站首页 数据显示的英文 28. LCD

28. LCD

2024-07-04 15:16| 来源: 网络整理| 查看: 265

28.3.2.2. 代码分析¶

ASCII字模数据

要显示字符首先要有字库数据, 在工程的“fonts.c”文件中我们定义了一系列大小为24x32、16x24、8x16的ASCII码表的字模数据, 其形式见 代码清单:显示中英文-3 。

代码清单:显示中英文-3 部分英文字库16x24大小(fonts.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19/* * 常用ASCII表,偏移量32,大小:24(高度)* 16 (宽度) */ //@conslons字体,阴码点阵格式,逐行顺向取摸 const uint8_t ASCII16x24_Table [ ] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x03,0x80,0x01,0x80,0x01,0x80, 0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80, 0x01,0x80,0x01,0x80,0x01,0x80,0x00,0x00, 0x00,0x00,0x03,0xc0,0x03,0xc0,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*以下部分省略.....,包含从 空格 至 波浪号 的ASCII码图形字模数据*/

由于ASCII中的字符并不多,所以本工程中直接以C语言数组的方式存储这些字模数据,C语言的const数组是作为常量直接存储到STM32芯片的内部FLASH中的, 所以如果您不需要显示中文,可以不用外部的SPI-FLASH芯片,可省去烧录字库的麻烦。以上代码定义的ASCII16x24_Table数组是16x24大小的ASCII字库。

管理英文字模的结构体

为了方便使用各种不同的字体,工程中定义了一个“sFont”结构体类型,并利用它定义存储了不同字体信息的结构体变量, 见 代码清单:显示中英文-4 。

代码清单:显示中英文-4 管理英文字模的结构体(fonts.c文件)¶ 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/*字体格式*/ typedef struct _tFont { const uint8_t *table; /*指向字模数据的指针*/ uint16_t Width; /*字模的像素宽度*/ uint16_t Height; /*字模的像素高度*/ } sFONT; sFONT Font8x16 = { ASCII8x16_Table, 8, /* 字模宽 */ 16, /* 字模高 */ }; sFONT Font16x24 = { ASCII16x24_Table, 16, /* 字模宽 */ 24, /* 字模高 */ }; sFONT Font24x32 = { ASCII24x32_Table, 24, /* 字模宽 */ 32, /* 字模高 */ };

这个结构体类型定义了三个变量,第一个是指向字模数据的指针,即前面提到的C语言数组,每二、三个变量存储了该字模单个字符的像素宽度和高度。 利用这个类型定义了Font8x16、Font16x24之类的变量,方便显示时寻址。

切换字体

在程序中若要方便切换字体,还需要定义一个存储了当前选择字体的变量LCD_Currentfonts,代码清单:显示中英文-5 。

代码清单:显示中英文-5 切换字体(bsp_lcd.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11/*用于存储当前选择的字体格式的全局变量*/ static sFONT *LCD_Currentfonts; /** * @brief 设置字体格式(英文) * @param fonts: 选择要设置的字体格式 * @retval None */ void LCD_SetFont(sFONT *fonts) { LCD_Currentfonts = fonts; }

使用LCD_SetFont可以切换LCD_Currentfonts指向的字体类型,函数的可输入参数即前面的Font8x16、Font16x24之类的变量。

ASCII字符显示函数

利用字模数据以及上述结构体变量,我们可以编写一个能显示各种字体的通用函数,见 代码清单:显示中英文-6

代码清单:显示中英文-6 ASCII字符显示函数¶ 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 36 37 38 39 40 41/** * @brief 在 ILI9341 显示器上显示一个英文字符 * @param usX :在特定扫描方向下字符的起始X坐标 * @param usY :在特定扫描方向下该点的起始Y坐标 * @param cChar :要显示的英文字符 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色 * @retval 无 */ void ILI9341_DispChar_EN ( uint16_t usX, uint16_t usY, const char cChar ) { uint8_t byteCount, bitCount,fontLength; uint16_t ucRelativePositon; uint8_t *Pfont; //对ascii码表偏移(字模表不包含ASCII表的前32个非图形符号) ucRelativePositon = cChar - ' '; //每个字模的字节数 fontLength = (LCD_Currentfonts->Width*LCD_Currentfonts->Height)/8; //字模首地址 /*ascii码表偏移值乘以每个字模的字节数,求出字模的偏移位置*/ Pfont = (uint8_t *)&LCD_Currentfonts->table[ucRelativePositon * fontLength]; //设置显示窗口 ILI9341_OpenWindow ( usX, usY, LCD_Currentfonts->Width, LCD_Currentfonts->Height); ILI9341_Write_Cmd ( CMD_SetPixel ); //按字节读取字模数据 //由于前面直接设置了显示窗口,显示数据会自动换行 for ( byteCount = 0; byteCount bitCount) ) ILI9341_Write_Data ( CurrentTextColor ); else ILI9341_Write_Data ( CurrentBackColor ); } } }

这个函数与前文中的串口打印字模到上位机的那个函数原理是一样的,只是这个函数要使用液晶显示, 且字模数据并不是一个个独立的数组,而是所有字符的字模都放到同一个数组里,所以显示时,要根据字符编码计算字模数据的偏移, 并把串口打印的处理改成像素点显示。该函数的说明如下:

(1) 输入参数

这个字符显示函数有usX、usY及cChar参数。其中usX和usY分别表示字符显示位置的(X,Y)坐标; 而输入参数cChar是要显示的英文字符,如字符’A’,字符’空格’等。

(2) 根据字符计算字模的数组偏移

在显示前,首先要提取出字符相应的字模数据。由于ASCII码中的编码0~31(NULL~US符号)是不存在图形表示的,为节省空间, 字模表中只包含图形符号相关的数据,例如,对于Font8x16的字模表,每个字符的字模数据长度=8*16/8=16字节,那么0~15字节表示的是’空格’字模, 16~31字节表 示的是’!’号字模,32~47字节表示的是’ “ ’号字模。

因此,显示函数中通过cChar获知要显示的字符后,使用ucRelativePositon变量存储cChar减去字符’空格’的ASCII码值, 即求出图形ASCII码的偏移值;然后使用fontLength变量存储根据当前选择的字体宽度与高度计算出的单个字模数据长度; 最后根据它在字模数组表中的偏移值(ucRelativePositon与fontLength的乘积)求出该字模的地址指针, 存储在指针变量Pfont中,后面就可以直接使用该指针获取字模数据了,从Pfont至(Pfont+fontLength-1)范围内的都是输入字符cChar的字模数据。

(3) 设置显示窗口并发送显示命令

计算出字模数据的指针后,可以准备开始显示,函数中使用ILI9341_OpenWindow函数根据输入的显示坐标及字模的宽高设置一个字符的显示窗口, 并使用ILI9341_Write_Cmd函数发送设置像素点的命令(CMD_SetPixel),有了这两个操作后, 下面使用的ILI9341_Write_Data函数发送的像素点数据将会一行一行地显示到窗口中(沿X方向,到达X结尾后沿Y方向显示下一行), 见图 设置显示窗口后的像素数据扫描过程 。

(4) 行循环与列循环

由于根据字模大小设置了显示窗口,使用ILI9341_Write_Data函数发送像素数据时到达单行的结尾它会自动换行,所以在发送数据时不需要再考虑换行。 代码中直接使用两层循环处理字模数据,其中外层for循环用于遍历字模的字节数据,一个字节一个字节地读取,而字节数据的处理则交给内层for循环, 当外层for循环遍历完fontLength个字节表示处理完一个字符的字模,即显示完一个字符;内层for循环用于遍历字模单个字节数据的每个数据位, 数据位为1时就发送一个点的字体颜色(CurrentTextColor),数据位为0时就发送背景颜色(CurrentBackColor)。

经过ILI9341_DispChar_EN函数的处理,可显示一个英文字符,要显示字符串时,重复调用本函数即可。

显示字符串

对ILI9341_DispChar_EN函数进行封装,我们可以得到ASCII字符的字符串显示函数,见 代码清单:显示中英文-7 。

代码清单:显示中英文-7字符串显示函数¶ 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/** * @brief 在 ILI9341 显示器上显示英文字符串 * @param usX :在特定扫描方向下字符的起始X坐标 * @param usY :在特定扫描方向下字符的起始Y坐标 * @param pStr :要显示的英文字符串的首地址 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色 * @retval 无 */ void ILI9341_DispString_EN ( uint16_t usX ,uint16_t usY, char * pStr ) { while ( * pStr != '\0' ) { if ( ( usX - ILI9341_DispWindow_X_Star + LCD_Currentfonts->Width )> LCD_X_LENGTH) { usX = ILI9341_DispWindow_X_Star; usY += LCD_Currentfonts->Height; } if ( ( usY -ILI9341_DispWindow_Y_Star + LCD_Currentfonts->Height )>LCD_Y_LENGTH ) { usX = ILI9341_DispWindow_X_Star; usY = ILI9341_DispWindow_Y_Star; } ILI9341_DispChar_EN ( usX, usY, * pStr); pStr ++; usX += LCD_Currentfonts->Width; } }

本函数中的输入参数pStr为指向要显示的字符串的指针,在函数的内部利用while循环把字符串中的字符一个个地利用ILI9341_DispChar_EN函数显示到液晶屏上, 当遇到字符串结束符’0’时完成显示,退出while循环,结束函数。在while循环的开头,有两个if判断操作, 它们分别用于判断显示字符时的X及Y坐标是否超出屏幕的边沿,若超出了则换到下一行,使用这个函数, 可以很方便地利用“ILI9341_DispString_EN (10,20,”test”)”这样的格式在液晶屏上直接显示一串字符。

使用宏计算Y坐标

使用ILI9341_DispString_EN函数显示时,需要注意字Y方向字符覆盖的问题,例如在(10,20)坐标处显示了一行字体高度为16像素的字符串, 如果再显示另一串字符时指定的坐标为(10,25),那么由于高度预留不足,会出现字符覆盖的现象,因此调用时需要小心计算Y方向的坐标。 为了简化操作,代码中定义了一个宏LINE及函数ILI9341_DispStringLine_EN来显示字符串, 见 代码清单:显示中英文-8 。

代码清单:显示中英文-8 使用宏计算Y坐标¶ 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#define LINE(x) ((x) * (((sFONT *)LCD_GetFont())->Height)) /** * @brief 在 ILI9341 显示器上显示英文字符串 * @param line :在特定扫描方向下字符串的起始Y坐标 * 本参数可使用宏LINE(0)、LINE(1)等方式指定文字坐标, * 宏LINE(x)会根据当前选择的字体来计算Y坐标值。 * 显示中文且使用LINE宏时,需要把英文字体设置成Font8x16 * @param pStr :要显示的英文字符串的首地址 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色 * @retval 无 */ void ILI9341_DispStringLine_EN ( uint16_t line, char * pStr ) { uint16_t usX = 0; while ( * pStr != '\0' ) { if(( usX - ILI9341_DispWindow_X_Star + LCD_Currentfonts->Width ) > LCD_X_LENGTH ) { usX = ILI9341_DispWindow_X_Star; line += LCD_Currentfonts->Height; } if((line - ILI9341_DispWindow_Y_Star + LCD_Currentfonts->Height )> LCD_Y_LENGTH ) { usX = ILI9341_DispWindow_X_Star; line = ILI9341_DispWindow_Y_Star; } ILI9341_DispChar_EN ( usX, line, * pStr); pStr ++; usX += LCD_Currentfonts->Width; } }

本函数主体与ILI9341_DispString_EN差异不大,主要是删减了X方向的坐标,另外使用输入参数line来指定Y方向的坐标,调用本函数时, 一般配合上面的LINE宏来使用,该宏会根据当前选择的字体高度来计算字符Y方向的间隔,如当前字体为8x16时,字体高度为16像素, 所以LINE(1)会返回数值16,LINE(3)会返回数值48。 利用它配合本函数可以使用“ ILI9341_DispStringLine_EN (LINE(1),”test”);” 的形式来使字符串显示在第1行,即Y方向为16像素处, 当然,使用前面的函数“ ILI9341_DispString_EN(10,LINE(1),”test”); ”同样也是可以的,并且可指定X坐标。

清除屏幕字符

在实际应用中,还经常需要把当前屏幕显示的内容清除掉,这可以使用 代码清单:显示中英文-9 中的函数。

代码清单:显示中英文-9 清除屏幕字符¶ 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/** * @brief 对ILI9341显示器的某一窗口以某种颜色进行清屏 * @param usX :在特定扫描方向下窗口的起点X坐标 * @param usY :在特定扫描方向下窗口的起点Y坐标 * @param usWidth :窗口的宽度 * @param usHeight :窗口的高度 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色 * @retval 无 */ void ILI9341_Clear ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight ) { ILI9341_OpenWindow ( usX, usY, usWidth, usHeight ); ILI9341_FillColor ( usWidth * usHeight, CurrentBackColor ); } /** * @brief 清除某行文字 * @param Line: 指定要删除的行 * 本参数可使用宏LINE(0)、LINE(1)等方式指定要删除的行, * 宏LINE(x)会根据当前选择的字体来计算Y坐标值,并删除当前字体高度的第x行。 * @retval None */ void LCD_ClearLine(uint16_t Line) {/* 清屏,显示背景颜色 */ ILI9341_Clear(0,Line,LCD_X_LENGTH,((sFONT *)LCD_GetFont())->Height); }

代码中的ILI9341_Clear函数可以直接清除一个指定的矩形,它会把该矩形显示成当前设置的背景颜色CurrentBackColor,实现清除图像的效果, 而LCD_ClearLine函数对它进行了封装,使用LINE宏配合,可以比较方便地清除单行字符串,如调用LCD_ClearLine(LINE(1))可以清除第1行的字符串。

显示ASCII码示例

下面我们再来看main文件是如何利用这些函数显示ASCII码字符的, 见 代码清单:显示中英文-10 。

代码清单:显示中英文-10 显示ASCII码的main函数¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22/** * @brief 主函数 * @param 无 * @retval 无 */ int main ( void ) { ILI9341_Init (); //LCD 初始化 USART_Config(); printf("\r\n ********** 液晶屏英文显示程序*********** \r\n"); printf("\r\n 本程序不支持中文,显示中文的程序请学习下一章 \r\n"); //其中0、3、5、6 模式适合从左至右显示文字, //不推荐使用其它模式显示文字 其它模式显示文字会有镜像效果 //其中 6 模式为大部分液晶例程的默认显示方向 ILI9341_GramScan ( 6 ); while ( 1 ) { LCD_Test(); } }

main函数中主要是对液晶屏初始化,初始化完成后就能够显示ASCII码字符了,无需利用SPI-FLASH及SD卡。 在while循环中调用的LCD_Test函数包含了显示字符的函数调用示例,见 代码清单:显示中英文-11 。

代码清单:显示中英文-11 LCD_Test函数中的ASCII码显示示例¶ 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/*用于测试各种液晶的函数*/ void LCD_Test(void) { /*演示显示变量*/ static uint8_t testCNT = 0; char dispBuff[100]; testCNT++; LCD_SetFont(&Font8x16); LCD_SetColors(RED,BLACK); ILI9341_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH); /* 清屏,显示全黑 */ /********显示字符串示例*******/ ILI9341_DispStringLine_EN(LINE(0),"BH 3.2 inch LCD para:"); ILI9341_DispStringLine_EN(LINE(1),"Image resolution:240x320 px"); ILI9341_DispStringLine_EN(LINE(2),"ILI9341 LCD driver"); ILI9341_DispStringLine_EN(LINE(3),"XPT2046 Touch Pad driver"); /********显示变量示例*******/ LCD_SetFont(&Font16x24); LCD_SetTextColor(GREEN); /*使用c标准库把变量转化成字符串*/ sprintf(dispBuff,"Count : %d ",testCNT); LCD_ClearLine(LINE(4)); /* 清除单行文字 */ /*然后显示该字符串即可,其它变量也是这样处理*/ ILI9341_DispStringLine_EN(LINE(4),dispBuff); /*以下省略其它液晶演示示例*/ }

这段代码包含了使用字符串显示函数显示常量字符和变量的示例。显示常量字符串时,直接使用双引号括起要显示的字符串即可, 根据C语言的语法,这些字符串会被转化成常量数组,数组内存储对应字符的ASCII码,然后存储到STM32的FLASH空间, 函数调用时通过指针来找到对应的ASCII码,液晶显示函数使用前面分析过的流程,转换成液晶显示输出。

在很多场合下,我们可能需要使用液晶屏显示代码中变量的内容,这时很多用户就不知道该如何解决了,上面的LCD_Test函数结尾处演示了如何处理。 它主要是使用一个C语言标准库里的函数sprintf,把变量转化成ASCII码字符串,转化后的字符串存储到一个数组中, 然后我们再利用液晶显示字符串的函数显示该数组的内容即可。spritnf函数的用法与printf函数类似,使用它时需要包含头文件string.h。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3