| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 嵌入式 -> 每节课都是一个项目 手把手用STM32打造联网气象站-9-用LCD显示中文英文和图片 -> 正文阅读 |
|
[嵌入式]每节课都是一个项目 手把手用STM32打造联网气象站-9-用LCD显示中文英文和图片 |
目录 目录 ??2.2.4创建CurrentChfonts指针,指向这个外部变量 ??2.2.5ILI9341_DispGivenChar_CH显示字符 前面介绍了LCD的初始化过程,如何通过FSMC接口模拟8080接口,如何像ILI9341写入不同数据,如何完成ILI9341初始化,如何向LCD写入不同形状的图形。 接下来,我们需要进一步了解,如何像LCD写入中英文文字和图像。毕竟大部分应用场景中,写入文字和图像才是最有必要的。比如我们需要在屏幕上显示温湿度数据,或者我们需要显示番茄时钟的倒计时,或者说我们需要在屏幕上面显示到底实现了多少个番茄,都是需要采用屏幕进行中文显示,英文显示,还有图像显示的。 1. 显示英文为了能够显示英文,我们至少需要准备好下面三件事情: 1. 创建一个表,根据表中的序号,从表格中显示英文26个小写字母和26个大写字母; 2. 把每个字母按照类似画点的方法,用屏幕上的小点来显示出来,这些小点构成一个数组; 3. 当需要显示字母或者字符串的时候,按图索骥,从表里找到对应的字母,然后按照数组方式,写到屏幕上即可。 思路有了,就可以接着动手干了。 1.1 从ASCII码表说起计算机只能识别 0 和 1,文字也只能以 0 和 1 的形式在计算机里存储,但是人类识别的语言为中文或者英文,所以我们需 要对文字进行编码才能让计算机处理,编码的过程就是规定特定的 01 数字串来表示特定的 文字,最简单的字符编码例子是 ASCII 码。 ASCII表中,最简单的方式,会用65表示A,97表示a,48表示0,32表示空格,也就是' '。而ASCII表的一个规律是:空格之前的ASCII字码是不能显示的。 0~31的字符为非显示字符,屏幕不能显示。因此,我们只需要把ASCII字符减去空格对应的编码,就可以确定这个字符在ASCII显示表中的偏移量。 例如:我们需要显示字母A, A对应的编码为65, 空格符号对应的编码为32, 我们只需要计算A-' '=33,就可以得到A在子码表里面的偏移量就是33,我们找到第33行数组,把它写到屏幕里面就可以了。 1.2 创建单个字模找到偏移量之后,我们需要进一步研究一下,如何把这个字母来恰当的显示出来,这个时候,就需要用到字模了。 例如,我们需要显示A这个字符,相当于在屏幕上,在特定点进行填充,最后形成A这个字符。我们用字模工具,来创建这个字符对应的编码。 这个是我们常用的字模软件,主要设置如下: 1.选择字体为Consolas字体; 2. 字宽16 字高 16; 3.选项中,选择逐行扫描; 最后选择生成字模,下面创建的数组,就是A这个字对应的字模,对应数组为: {0x00,0x00,0x00,0x00,0x18,0x1C,0x34,0x24,0x26,0x62,0x7E,0x43,0xC1,0x00,0x00,0x00},/*"A",0*/ 1.3 显示单个字符?前面已经用字模生成了A这个字符对应数组,接下来我们用一个函数把这个字符在LCD上面显示出来。 一个字符为8行16列,其中每一行为一个byte,总共16个byte构成了16列数据。 每一行中8个bit,当这个bit为1时,显示文本颜色,当这个bit为0时,显示背景颜色; 例如第五行中 这一行对应的数据为0x18,对应的二进制位0001 1000,因此这一行中,中间两个bit为1,写字体颜色,其他bit为0,写背景颜色。 这里fontLength为16,表示每个字模对应16个字节。byteCount为字节序号,也就是这个字符的行数。 里面的for循环完成一行内容的显示,外面 的for循环完成16行显示,最终完成字符A在LCD上面的输出。 1.4 从ASCII表中输出前面我们把A这个字符对应的数组在屏幕上显示的过程讲解了一下。在实际工程中,我们预先把ASCII表对应的字模都创建好,在需要实际输出的时候,根据字母在ASCII表中的编号,找到对应字模,完成显示。 1.4.1定义一个结构体类型用来设置字体库一种字体库必须包括:字符宽度,字符高度,以及对应ASCII表的地址信息。我们把这些信息放在一起,构建一个结构体。 备注:上面的typedef struct _tFont{} sFont 这种写法,可以用_tFont font1这种方式初始化,相当于定义了两种结构体类型,第一种是_tFont,第二种是sFONT,两种可以互换,_tFont就是sFONT,sFONT就是_tFont; 如果把_tFont去掉,写成typedef struct {}sFONT, 语法上面也是没有任何问题的。 1.4.2 用结构体类型,定义字体库结构体变量? ?Font8x16这个结构体变量中,第一个成员变量为字体库数组的地址,第二个成员变量为每个字符的宽度,第三个成员变量为每个字符的高度。 1.4.3 创建字体库数组ASCII8X16_Table对应的就是字体库数组。 字体库数组从空格开始,按照顺序依次排列。 其中字符表中最后一个字符是~ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x71,0x4b,0x06,0x00,0x00,0x00,0x00,0x00,/*"~",0*/ 当我们需要显示一个字符时,找到字符在字体库中的位置,在LCD上进行显示即可。 1.4.4 字体变量初始化前面定义了Font8x16这个结构体变量,这个变量是一个全局变量。我们再定义一个静态结构体变量指针,LCD_Currentfonts,用这个指针指向这个全局变量。 这里我们复习一下结构体指针的知识点: 1. 当结构体作为参数传递时,常常使用结构体指针; 2. Currentfonts->Weight和Currentfonts->Height这种方式可以得到结构体指针指向的结构体的成员变量; 另外需要注意的时:CurrentTextColor和CurrentBackColor这两个变量都是静态全局变量。我们在代码中使用静态全局变量的好处是: 1.避免暴露变量:静态全局变量仅仅在文件内部可见,文件外部不可见。设置静态全局变量的值往往需要通过set函数来设置,查询变量往往通过get函数来设置。通过隐藏变量,降低代码文件之间的耦合性。 ?例如CurrentTextColor这个静态全局变量,只能通过SetTextColor函数进行设置,而不能直接在其他文件中,修改数值。这个变量在其他文件中是不可见的。 2. 上电完成初始化:静态全局变量放在静态数据区,所以代码开始自动完成初始化,而且初始值为0。 1.4.5调用方式这样我们在main函数中,即可愉快的调用字符输出函数,实现单个字符输出。其中第一个参数和第二个参数为显示字符的x和y的位置。 字符输出的结果如上图所示,可以看到,每个字符都有8行16列的点阵组合而成。 1.5 英文字符串输出单个字符输出在实际使用过程中还不太方便,这时就需要采用字符串输出了。把字符输出组合为字符串输出,需要以下几个步骤: 1.通过while(*pStr!='\0')这个循环,判断是否达到了字符串结尾; 2. 需要计算字符输出的位置:如果x方向大于屏幕宽度,y方向需要增加一个字符的距离,实现换行,如果y方向大于屏幕高度,则x y全部置零,从头开始写入; 3. 位置确定好之后,直接调用DispChar_en,完成输出。 在读代码前,我们需要自己先思考一下,如何实现这个功能,然后再和教程中的代码进行比较,这样可以提示用代码解决问题的能力。 ?上面代码中,首先通过while循环,检查*pStr!='\0',判断是否到了字符串结尾。然后检查x和y数值,判断是否需要换行或者翻页。最后在目标位置,显示字符串信息。 执行结果如图所示,可以看到,已经能够准确的实现按照字符,进行换行。 纸上得来终觉浅,绝知此事要躬行! 马上下载测试代码,看看效果。 1.内容概要:演示讲解如何用字模软件,创建字模,如何创建font.c文件,如何显示单个字符,如何显示字符串-Linux文档类资源-CSDN下载 ?2. 显示中文英文字母简单,只有26个英文字母,构建的ASCII表也非常简单。因此,显示英文的方法一般都是把全部ASCII用字模创建出来,放到fonts.c中。 然而中文的显示就会复杂很多。中文的字符数量非常多,如果都放到fonts.c文件,会把MCU内存吃尽。因此,我们必须有一些新的计数,来支持中文的显示。 2.1 GBK编码和GB2312编码显示英文的时候,我们采用ASCII编码方式,将每个字符减去空格' ',得到的就是这个字符在ASCII表格里面的偏移量。通过偏移量,从fonts.c中,将文件输出出来。 针对中文,我们一般采用GBK编码,GBK编码把ASCII和中文GB2321合并起来了。对于一个字符,我们首先判断其数值是否大于126,如果不大于126,为英文,采用英文显示。否则为中文,采用中文显示即可。 GB2312中,采用了类似于ASCII的方式,对中文进行了编码。 2.1.1 分区表示 2.1.2 双字节编码 GB2312编码范围:A1A1-FEFE,其中汉字的编码范围为B0A1-F7FE,第一字节0xB0-0xF7(对应区号:16-87),第二个字节0xA1-0xFE(对应位号:01-94)。 2.1.3 解码过程 GB2312采用分区方式,对汉字进行编码,例如:16分区如上图所示,每一个区有94个字符。 ?我们现在收到一个编码B0FC,我们如何将它破解为对应汉字呢? 解码过程如下: 0xB0FC对应的高位为0XB0,我们将其减去0XA0,得到了16,说明这个字符在16区; 0XFC-0XA0=92 说明这个字符为16区92字符,对应的字符为“包”。 在STM32中文字库中,是从0开始计数的,也就是说16区前面有15个区,这15个区中有15X94=1401个字符,然后再加上91,也就是偏移91格,就可以得到对应的字符:“包” 2.2 单个字符显示我们采用字模,创建一些字符,实现单个字符显示功能,下面是详细步骤 2.2.1创建16X16的字模数组我们先创建这个字模数组,这个数组中只有两个字,每个字都包含了32个byte。 因为这里每2个byte也就是16个bit,构成了一行。 2.2.2创建ChFont16x16结构体变量?2.2.3外部声明ChFont16x16结构体变量? 这样声明之后,就可以在其他地方使用这个变量了。 ??2.2.4创建CurrentChfonts指针,指向这个外部变量?前面吧这个变量声明成为extern,后面才能在9341.c中调用此文件,否则无法调用文件。 ??2.2.5ILI9341_DispGivenChar_CH显示字符?上面函数中,没有直接给出字符,而是给出了字符编号pos。因为我们自己创建的字符库里面,只有2个字符,所以只需要给出编号即可。如果是完整的gb2312的库,由于库尺寸太大,我们需要把字符放到spi-flash中。 因为我们只简单创建了2个字符,所以我们在参数中,直接指定字符串的序号即可。参数pos就是字符在字体库里面的序号信息。 由于每个字模包含了32个字节,所以fontLength=32; 采用OpenWindow完成开创后,程序按照顺序,依次把32个字节中的每个bit写入屏幕中,构成了这个中文字符。如果这个bit为1,就写入文本颜色,如果这个bit为0,就写入背景颜色。 创建好函数之后,不要忘记在头文件中进行声明。声明之后,main.c才可以调用。 ??2.2.6 main.c中调用函数验证功能main.c中,调用这个函数。函数第一个参数为x坐标,第二个参数为y坐标,第三个参数为在字库中的顺序。 2.2.7 代码执行结果代码执行结果如上图所示,我们可以正确显示处中文字符。 纸上得来终觉浅,绝知此事要躬行! 马上下载测试代码,看看效果。 https://download.csdn.net/download/book_drabit/86166922 ?3. 创建简单中文字库,并混合显示中英文前面我们学习了如何创建英文,如何创建中文,如何创建英文字库,如何创建简单的中文字库。 但是这里有个问题,当我们需要显示复杂一些的中文时,我们不可能到字库中,按照顺序一个一个把它找出来,比如:我们不可能去找出来“可”在字库中排第10位,“志”在字库中排20位,而应该是:我们直接在调用参数这里设置好中文,然后就可以直接显示出来。 更进一步,我们需要实现中文和英文的混合显示,这样在后面的工程代码中才能够良好使用它。 3.1 显示中文字符串在这部分,我们修改字库定义,使得我们可以通过查找的方法,从字库中找到对应的字符。 3.1.1重新定义字库结构体?我们前面的字库结构体类型是按照上面定义的,其中Table放对应字库的地址,这里需要修改。因为这里没有地方放置中文编码,我们只能按照顺序来定位中文,不能按照字符来查找。 ?中文字库结构体类型修改为上面定义的形式; 两者的区别是: 第一种定义中,Table为一个数组,这个数组中,包含了全部英文字体数组,但是不具备中文编码定义 第二种定义中,ChMini这个结构体类型,仅仅包含了一个汉字的数组;但是它还包含了两个Byte,用来存放对应中文编码。名字中增加了一个mini,表示它仅仅是我们自己定义的一个mini字库,而不是按照GKB定义的全体字库。 3.1.2 在fonts.c中,定义字库变量前面定义结构体类型的目的,是为了定义字库变量; 上面定义的ChMini16x16[]这个数组变量中,每一行均为一个汉字对应的数组,例如:第一行前面32byte为“可”对应的编码,最后2个byte为“可”对应的编码。注意:fonts.c这里需要采用gb2312编码方式,这样才能够查找识别。 最后一行为自行定义的一个X,字符在这个数组中查找不到后,显示为X。 同样,不要忘记在fonts.h中,对变量进行声明。 ?3.1.3 增加字库长度宏定义为了更加方便的了解字库中数字的数量,我们增加了宏定义CH_MINI_SIZE。 由于一个字库占据了33个byte,再加上结尾处有一个X作为替换字符,所以整个字库的尺寸为: 34*(4+1) ??所以我们字库变量更新为上面的定义。 ?对应的变量声明也做了更新。 在C中,我们尽量避免直接采用数字,而是尽量采用宏定义的方式。直接采用数字不方便后面进行修改,采用宏定义的方式,后面修改起来非常方便。 ?3.1.4 在字库中,查找对应的字符当我们采用GKB全字库时,可以根据编码顺序进行查找。例如“包”对应的编码是B0FC,我们把前后两个字节都减去A0,得到1692,我们到字库中,把16区第92个编码取出来,显示在屏幕上面即可。 但是对于mini字库,这种方式就不再有效,因为很多字在mini字库中是没有的。所以我们只能用查找法,一个一个查找,找到对应的数字。 ?上面用到的while+break方法,也是C中常用的编程技巧,我们可以把它叫做“折断法”。也就是当找到了目标数据后,我们通过break当前的循环,来获得目标数据的位置。 上面代码中第二个需要注意的点是:用两个byte来表示中文。对于每个中文,都可以分为byte[0]和byte[1];通过比对这个byte[0]和byte[1],可以判断字符是否能够对应; 第三个需要注意的点是:指针数组转换。DispMiniChar_CH这个函数的形参是char *str,但是我们实际使用时,是用str[0]和str[1]来获取对应参数。用数组来获取指针指向的数值,这也是非常常用的编程方法。 ?3.1.5 把两个8bit组合成为一个16bit数?ChMini16x16[index].F16x16 [ rowCount * 2 ]这个代码有些难以理解,我们需要将它分解一下,就可以一目了然了: ChMini16x16[index]:是字库中第n个字符,比如:“可”对应了ChMini16x16[0], “志”对应了ChMini16x16[1]; ?ChMini16x16[index].F16x16是获取这个字符对应的结构体,这个字符结构体包含了两个成员变量,.?F16x16为字库信息;.Byte[0]和.Byte[1]为对应的汉字编码; [ rowCount * 2 ]为F16x16字库信息中编码顺序,用来在屏幕上面显示一行; 上面的复杂代码的解析方法,叫做“代码分解法”,是我们面临复杂代码时,最佳方法。
usTemp从?ChMini16x16[index].F16x16 [ rowCount * 2 ]中获取第一个byte;
然后把这个byte左移;
然后再去除第二个byte,通过相或,得到最后的2个byte组成的数据。 这样的操作叫做“位操作组合法”,通过位操作组合,我们可以把多个byte组成int,或者把一个int中的多个byte单独分离出来。这些都是我们编程过程中,需要熟练掌握的技巧。 ?3.1.5 调用代码完成显示最后调用代码,完成功能的显示。 显示结果如下: 上面图中信这个字有点丑,主要原因时采用了微软雅黑字体的原因。 ? 在微软雅黑字体中这个信就是长成这个样子的。 ?3.2 混合显示中英文为了混合显示中英文,我们需要对字符编码进行判断。当字符编码小于127时,用英文显示,否则用中文显示。 3.2.1显示英文部分当*pStr<=126时,采用英文显示。注意这里参数为*pStr,因此传递的是数值,而不是地址。 完成显示后pStr++,来指向后面一个字符。 备注:++i和i++的小区别,你还记得吗?事实上,i++和++i两个语句执行完毕后,i的数值都会加一。两者唯一的区别是: (++i)这个表达式的值为i+1; (i++)这个表达式的值为i; ?3.2.2显示中文部分如果字符数值大于126则需要调用中文显示。 注意,中文显示函数传递参数为pStr也就是指针,而不是数值,因为中文字符没有排序,因此用指针传递数值更加方便。 另外需要注意的是: pStr+=2,因为每个中文字符带有两个byte。 ?下载代码,查看一下执行结果吧: https://download.csdn.net/download/book_drabit/86212085 执行结果如下图: ? 4. 显示图片接下来详细讲解制作图片和显示图片的过程。对应的工程源码也将在下面提供。 4.1显示单色图片单色图片显示不仅仅可以用于LCD,也可以用于单色OLED,墨水屏等单色显示的设备中。 4.1.1制作单色图片上面这个emoji图片。接下来我们把它做成单色显示的文件。 我们接着用工具把图片二值化,也就是将其转为黑白颜色图片,这样方便LCD进行显示。这里需要用到PhotoShop这个工具进行处理 选择 图像-> 模式->灰度 扔掉颜色信息,这时颜色变成黑白色 再选择 图像-> 调整 ->阈值 ?调整一个合适的阈值。? 在 图像 -> 图像大小 这里,重新设置图像大小。 把图像大小调整为232X232,这样正好是8的倍数。因为我们在写屏幕的时候,是按照8bit分步写入的。 ? 再用字模软件打开,其他参数不变,把每行点阵修改为29,因为232/8=29,这样正好一行byte对应一行图像。 选择 生成字模 ,再把生成的字模赋值到fonts.c中。 生成的字模文件中,带有很多大括号(232X2=464个),可以在vscode中,选择->列选择模式, 这样可以很快地删除这些大括号。 图片字模创建出来的数组如上,上面仅仅显示了部分内容,详细见代码中的数组。 ?4.1.2显示单色图片?? ?1. 入参为图片的起始点坐标和结束点坐标,用这个坐标进行开窗。入参还包括*buf这个指针,用来指示图片对应参数; 2. p=*buf++;这里需要理解i++和++i的区别,p=*buf++这种方式,p从*buf开始,因为buf++作为表达式,其数值为buf而不是buf+1; 3. 通过起始坐标和结束坐标,设置LCD绘图区域范围,也就是OpenWindow开创; 4. 看到3个for循环不要懵,用分解法一步一步搞清楚: 4.1 最里面for循环,用来显示一个byte也就是8bit数据,这个和中英文字符显示是一模一样的。每次显示一个字节后,指向下一个字节; 4.2 第二个for循环,用来完成整个图片中,一行的显示。因为里面已经显示了8bit了,所以外面这个for循环,循环数量为usWidth/8; 4.3 第三个for循环,用来完成整个图片的显示,每次rowCount加1之后,开始新的一行的绘制。 ?4.1.3 调用并查看结果在main.c中调用,并查看运行结果。 希望这张笑脸给辛苦学编程的你带来好心情。 ??4.2显示彩色图片黑白图片显示的计数,可以广泛应用在OLED,墨水屏的显示中。但是我们这里用的是TFT真彩屏,所以我们需要进一步掌握彩色图片显示过程。 4.2.1制作彩色图片?我们找到上面一张图片,分辨率为200X200。 用Image2Lcd打开,输出灰度选择16位真彩色,然后点击保存;得到下面的数组。数组长度为80000,相当于 200 X 200 X2,因为每一个像素点为16bit也就是2byte。 注意:需要勾选Big Endian标签,在4.2.3中将详细解释字节序; 4.2.2 输出图片当我们绘制实心长方形时,采用方法是: 1.OpenWindow,创建绘图区域; 2. FillColor,将文字颜色填入绘图区域,其中中间步骤又包括: ?2.1 设置SetPixel命令,准备开始写入数据 2.2 用for循环写入数据 我们采用同样方法,只是把FillColor中的TextColor更换为图片中的颜色即可。 上面代码中,采用同样步骤,完成图片的写入: 1. OpenWindow设定写入区域; 2. SetPixel命令准备写入; 3. WriteData不停写入,在写入数据时,将2个字节的拼接成为一个16bit数据,写入屏幕。? 4.2.3 关于字节序在图片转换文字的时候,提到了字节序。上面红框部分,是两种字节序,其中第二个是STM32对应的Big Edian,也就是大字节序。 大小字节序又叫做大小端,是用来区分数值在内存中摆放的顺序。例如,有一个4byte的变量
上面这个数中,高位为0x12, 低位为0x78。这个数在内存中有两种分步方式: 第一种方式,低位数据0x78在地址低位,这个叫做小端,特点是:低位+低位; 第二种方式,低位数据0x78在地址高位,这样叫做大端,特点是:低位+高位,因此为大端; 我们常用的STM32为大端,也就是低位数据在高位地址。 对于一个芯片,如果我们不知道它是大端还是小端,如何通过代码,判断它是大端还是小端呢?按照上面的原理,我们可以从0x12345678中取出低位地址中存放的数据,如果这个数据是低位数据0x78,则说明是:低位地址+低位数据,则为小端字节序;如果得到的数据为0x12,说明低位地址存放高位数据,则为大端字节序。
?对应代码如上,其中:
这样代码把a的地址强制转换为(uint8_t *)这样8bit数据对应的地址,用这种方法,从32bit数据中,取出低地址对应的8bit数据。这个操作也正是C的灵活之处。 4.2.4 调用函数,输出图片调用函数,我们即可输出对应图片。? ? 通过这种方式,我们就可以顺利输出透红透红的番茄了。 (40条消息) STM32创建黑白图片和彩色图片,在TFTLCD上实现显示。详细说明见博客:http://t.csdn.cn/SofN7-Linux文档类资源-CSDN文库 纸上得来终觉浅,绝知此事要躬行! 马上下载测试代码,看看效果。 |
|
嵌入式 最新文章 |
基于高精度单片机开发红外测温仪方案 |
89C51单片机与DAC0832 |
基于51单片机宠物自动投料喂食器控制系统仿 |
《痞子衡嵌入式半月刊》 第 68 期 |
多思计组实验实验七 简单模型机实验 |
CSC7720 |
启明智显分享| ESP32学习笔记参考--PWM(脉冲 |
STM32初探 |
STM32 总结 |
【STM32】CubeMX例程四---定时器中断(附工 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 | -2024/12/28 17:23:22- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |
数据统计 |