【智能家居项目】裸机版本——字体子系统 | 显示子系统

news2024/11/16 9:47:33

🐱作者:一只大喵咪1201
🐱专栏:《智能家居项目》
🔥格言:你只管努力,剩下的交给时间!
图

图
今天实现上图整个项目系统中的字体子系统和显示子系统。

目录

  • 🀄设计思路
  • 🀄字体子系统
    • 🃏管理层
    • 🃏子系统层
    • 🃏字库层
  • 🀄显示子系统
    • 🃏编程
  • 🀄测试

🀄设计思路

在显示设备上显示字体其实也是比较复杂的,显示的字体有点阵字体,矢量字体等方式。

  • 使用点阵绘制文字时:每个文字的大小一样,前后文字互不影响:
    图
    如上图所示,点阵字体中的每个字体的点阵大小都是固定的,也就是需要的像素点个数是固定的,例如8*16就是宽用8个像素点,长用16个像素点,无论是汉字,字母,数字甚至是一个标点符合,都用8*16个像素点。
  • 点阵方式的字体并不连续,字体与字体之间分隔较远,看上去并不是那么美观。
  • 使用Freetype(矢量字体)绘制文字时:大小可能不同,前面文字会影响后面文字:

图
如上图所示,矢量字体中的每个字体的大小是不一样的,根据字体的类型,是汉字还是字母甚至是标点符合,以及前一个字体的位置,会对显示的字体做适当的调整。

  • 矢量字体的排列更加紧凑合理,看起来也更美观,符合我们的生活经验。

描述点阵字体:

图
对于普通的点阵字体,描述该字体需要:

  • X、Y方向大小及原点坐标
  • 每个像素点的值

所以可以用如下结构体来表示一个点阵字体:

struct dot_font
{
	int iX;	
	int iY;		//字体坐标
	int iWidth;//宽度
	int iHeight;//高度
	unsigned char* dots;//用于显示该字体的16字节数组(字模)
}

通过坐标以及长度和宽度可以确定字体的轮廓,将dots数组中的数据发送给显存,一个完整的字体就显示在屏幕上了。


描述矢量字体:

图

如上图所示,对于矢量字体,每个字体的大小可能不一样,前一个字体会影响下一个字体,其中有两黑点非常重要:

  • 左边的黑点:当前字符的原点。
  • 右边的黑点:下一个字符的原点。

下一个字符的位置和上一个字符息息相关,还有其他要素,如宽度,高度,绘制的起始点等等组合在一起才可以确定一个字符。

  • 绘制起点和原点不是一个,原点是相当于是整个字符的标点,字符的位置由原点决定。
  • 绘制起点也就是显示开始的位置,一般是字符的左上角。

所以可以用如下结构体来表示一个矢量字体:

typedef struct FontBitMap
{
	int iLeftUpX;		/* 位图左上角X坐标 */	
	int iLeftUpY;		/* 位图左上角Y坐标 */
	int iWidth;			/* 字体宽度 */
	int iHeight;		/* 字体高度 */
	int iCurOriginX;	/* 原点X坐标 */
	int iCurOriginY;	/* 原点Y坐标 */
	int iNextOriginX;	/* 下一个字符X坐标 */ 
	int iNextOriginY;	/* 下一个字符Y坐标 */ 
	unsigned char* Buffer;/* 字符点阵 */
}FontBitMap, *pFontBitMap;

包含矢量字体的绘制左边(左上角坐标),字体的宽度和高度,当前字体的原点,下一个字体的原点,以及一个字模数组。


现在我们要做的就是抽象出一个结构体,既能描述点阵字体,也能描述矢量字体。

  • 能用来描述矢量字体的结构体必然也能够描述点阵字体。

绘制起始坐标以及宽度和高度和点阵字体中的坐标位置以及x和y方向的长度一样,当前字符原点和下一个字符原点,点阵字体也可以通过计算得到,所以无论是点阵字体还是矢量字体,都可以共用这一个结构体。

图
如上图,整个字体系统并不涉及到内核或者芯片,它是属于软件层面的,所以并不需要分那么多层,都放在一层中即可。

无论是点阵字体还是矢量字体,它们都必须有字库,将字符在字库中对应的数据发送给显示设备才能显示出相应字符,常见的字库有ASCII码字库,GBK字符,以及FreeType字库。

  • 字库也要被描述,也要被管理起来。

🀄字体子系统

🃏管理层

先来实现对字符位图以及字库的管理。

图
如上图头文件中代码所示,结构体FontBitLib是用来描述一个要显示字符的位图的,每一个字符都会创建一个结构体对象,而成员中的Buffer中存放的就是该字符的字母数据,将这些数据发送给显示设备就能显示出对应字符。

结构体FontLib是用来描述字库的,本喵这里只会实现ASCII码字库,其中的成员函数有很多在这里是用不到的,但是为了符合所有字库,这里本喵仍然写了,方便以后的扩展和维护。

还有一个注册字库的函数声明和一个获取字库的函数声明,获取字库的函数有__表明该函数在另一层被调用,这样也是为了避免重复包含的问题。

图
如上图所示源文件代码,创建了一个用来管理字库的全局链表,以及实现了注册字库和获取字库的函数。


🃏子系统层

字库的管理已经实现了,下面该实现一下子系统层调用这些管理函数的字体系统了:

图
如上图所示头文件代码,提供了对字库进行一系列操作的函数声明。

图
如上图源文件代码所示,由于在同一时刻只能使用一个字库,所以创建了一个全局的默认字库变量,还提供了操作字库所用方法的具体实现,在初始化默认字库的时候,需要判断该字库的初始化方法是否为空,为空说明不用初始化。


🃏字库层

此时对字库的管理以及各种操作都已经实现了,但是字库还没有,所以接下来就需要实现ASCII码字库:

图
如上图所示是ASCII字库的头文件,只有一个增加ASCII码字库的函数声明。

图

如上图所示源文件中代码,包含一个ASCII码字库,这是一个全局的二维数组,字库中的数据是通过字模制作软件生成的,在本喵的文章I2C通信协议 | OLED屏中详细讲解过,有兴趣的小伙伴可以移步。

创建了全局的ASCII码字库结构体变量并进行了初始化,本喵这里的ASCII码大小是固定的,就是8*16的,所以就在函数中就直接给了定值,对于获取字符的位图函数本喵单独讲解一下:

tu
仍然是这个图,在显示该字符的时候,是通过原点坐标来确定位置的,也就是图中坐标的黑点,这个黑点的坐标是由用户指定的,所以在函数中该坐标是已知的。

左上角的绘画起始坐标和原点在x方向上相同,在y方向上相差字体的高度,ASCII码字符中就是16,所以可以通过当前原点坐标计算出左上角坐标。

下一个字符的原点坐标,在x方向上和当前字符原点坐标相差字体的宽度,ASCII码字符中就是8,所以也可以通过当前原点坐标计算出下一个字符的原点坐标。

字符的高度和宽度是固定的,也就是8*16的,最重要的字模数组Buffer中的内容就来自前面的字库ascii_font二维数组,如果用户没有向Buffer中存放数据,那么就直接返回字库中对应的字模数据,如果用户存放了,那么就将字库中的字模数据复制到Buffer中。

图
如上图代码就是用来从字库中获取指定字符的位图数据的。


虽然将字体系统分为了三层,但是它们仍然属于系统层,只是在系统层中又细分出来的三层。

图

如上图所示便是这三小层各种的功能和互相之间的调用关系,子系统层只负责使用字库,并不关心字库的维护和管理,管理层则要做到细节处的管理,管理多个字库,但是并不用知道每个字库中的内容,字库层则需要详细实现自己所代表的字库,包括所有字模数据,以及字库结构体中的那些成员方法。

🀄显示子系统

显示子系统和设备子系统中的显示设备并不是一回事。

图
如上图,文字子系统会将从字体子系统中取出的点阵发送给设备子系统中的显示设备绘制点阵从而显示出来。

编码集:

图
如上图所示,字符串“ABC中国”使用不同的编码集在内存中的数据不一样,使用Unicode编码集中的UTF-8(左边内存窗口),每一个汉字占用3个字节,使用GB2312编码集(右边内存窗口),每一个汉字占用2个字节。

  • 无论什么编码集,对ASCII码都是用一个字节表示。

编码格式:

拿常用的Unicode编码集举例,它包含三种编码格式,其中最常用的就是UTF-8编码格式:

Unicode数值范围(16进制)UTF-8编码方式(二级制)
0000 0000-0000 007F0xxxxxxx
0000 0080-0000 07FF110xxxxx 10xxxxxx
0000 0800-0000 FFFF1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

第一列表示Unicode编码集支持的编码范围,第二列表示UTF-8编码格式,该编码格式中每个字节中包含有其他信息:

  • 每个字节中,从高位到地位,遇到第一个0之前,有几个1就表示这个字符有几个字节共同表示,如果是10xxxxxx10表示当前字节和前一个字节共同表示一个字符。
  • 去掉表示字节个数的位以后,将剩余的位放在一起,组成的数值就是UTF-8编码值。

1110xxxx 10yyyyyy 10zzzzzz这三个字节表示的字符,这串二进制序列表示该字符用3个字节表示,组合成UTF-8编码值为xxxxyyyy yyzzzzzz两个字节大小。

除了UTF-8编码格式外,还有UTF-16LE,UTF-16BE等编码格式,不同的编码格式,表示同一个字符的数据也会不一样。

点阵:

从默认字库中取出字符的点阵数据,然后再选择显示设备,将字符在LCD或者OLED上显示。

图
如上图所示便是整个显示的过程,先确定编码集为Unicode,再确定编码格式为UTF-8,然后算出编码值,取出对应的点阵数据,最后在显示设备上显示。

🃏编程

显示子系统是在使用字体子系统和设备子系统中的显示设备,所以它并不用分很多层,只工作在显示子系统层(应用层)。

tu
如上图,要想在显示设备上显示字符,有三要素,分别是具体的显示设备,字符的坐标,已经要显示的字符。

图
如上图代码便是整个显示系统中的核心代码,里面有几个被调用的函数本喵会单独拿出来讲解。


调用该函数显示字符串的时候,首先做的第一步就是获得字符的编码值,这里调用了GetCodeForStr函数来获得编码值:

图

如上图代码所示,专门创建一个文件encoding来处理编码值,在该函数中,可以获取指定字符不同编码集下的编码值,这里本喵就只用ASCII编码集。

使用该函数获取的是一个字符的编码值,所以对于ASCII编码集来说,直接返回该字符的ASCII码值作为该字符的编码值即可,因为ASCII编码集默认就是支持的。


得到字符的编码值以后,就要获取该编码值对应的点阵数据。
图
如上图,根据编码值获取点阵数据是从字体子系统中获取,该函数在ascii_font.c中已经实现了,调用默认字库中的GetFontBitMap即可从默认字库(ASCII码字库)中获得点阵数据。


点阵数据有了以后,就需要将点阵数据写入到显示设备在RAM中的显存中去:

图

如上图代码所示,该函数的就是将点阵数据写入到RAM中的显存中的,点阵数据是在FontBitMap对象中的,将该字符的所有像素点数据都获取到并写入到显存中。


本喵这里使用的OLED显示,所以按照OLED显示方式来分析如何获取像素点的位置:

图

如上图所示,OLED显示的字符是8*16的,前一页显示一个字符点阵的前八8字节,后一也显示后8个字节,每个字节中的一个比特位就是一个像素点,所以要获取的是每一个比特位中的值。

tu
如上图代码所示,获取(iX,iY)坐标像素点的像素值时,按照OLED显示方式图,通过横坐标x确定像素点所在字节在Buffer中的地址,然后根据像素点的y坐标确定是该字节的哪个比特位,然后通过按位与和左移操作返回该像素点的值。


图

如上图所示,整个显示子系统的调用关系,首先调用显示子系统中的ShowTextInDisplayDevice函数去显示字符str,应用层只用关心这一个函数怎么调用,不比管它的实现,站在应用层的角度,此时就可以显示出指定字符了。

显示函数又调用显示子系统中编码集层中的GetCodeStr得到该字符的编码值,再用编码值去字体子系统中的默认字库中得到点阵数据,数据放在FontBitMap结构体对象中。

显示子系统再调用DrawBitMapOnFrameBuffer将点阵数据写到设备子系统中显示设备的RAM显存中,在这个函数内部,显示子系统会先调用GetPixelColorFromBitMap函数获取像素点的颜色数据,然后再调用显示设备自带的SetPixel方法将颜色数据写到RAM显存中。

最后会调用Flush函数将RAM显存中的数据发送到显示设备自带的显存中,至此整个显示流程就结束了。

  • 在获取编码值的时候,根据不同的编码集和编码方式获得编码值,这里可以进行扩展维护。
  • 在获取像素点颜色数据的时候,显示设备的显示规则不同,获取编码值的方式也就不同,这里也可以扩展维护。

🀄测试

最后就是测试显示子系统,设备子系统和字体子系统三个系统能否成功配合在OLED上显示字符了:

图

如上图代码所示,在测试函数中,先将字体系统中的字库初始化完毕,包括添加字库设置默认字库等等,然后再初始化显示设备,包括指定显示设备,初始化等等步骤。

  • 显示字符只用调用一个函数ShowTextInDidsplayDevice(ptDev,16,16,str)即可在作为为(16,16)处显示指定字符str

图
可以看到,成功显示字符,源代码中的字符串太长,涉及到了换行,所以本喵在仅显示了A Big MiaoMi字符串,没有实现换行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1063681.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

无法启动此程序,因为计算机中“找不到msvcp140.dll”的解决方法

msvcp140.dll是Microsoft Visual C 2015 Redistributable的一个动态链接库文件,它是许多基于Visual Studio开发的应用程序和游戏的必要组件。当计算机上缺失msvcp140.dll文件时,可能会导致以下问题: 1. 程序无法启动,提示“找不到…

【多线程安全】死锁 锁竞争总结

下面有两段代码&#xff1a; public class test {private static int count 0;public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(() -> {for (int i 0; i < 10000; i) {count;}});Thread t2 new Thread(() -> {for (i…

速通Redis基础(一):掌握Redis的字符串类型和命令

目录 字符串&#xff08;String&#xff09; 常见命令 SET GET MSET&MGET SETNX INCR INCRBY DECR DECRBY INCRBYFLOAT APPEND GETRANGE SETRANGE STRLEN Redis字符串类型命令总结 Redis&#xff08;Remote Dictionary Server&#xff09;是一个高性能的…

机器学习基础之《分类算法(8)—随机森林》

一、什么是集成学习方法 1、定义 集成学习通过建立几个模型组合的来解决单一预测问题。它的工作原理是生成多个分类器/模型&#xff0c;各自独立地学习和作出预测。这些预测最后结合成组合预测&#xff0c;因此优于任何一个单分类的做出预测 谚语&#xff1a;三个臭皮匠顶个诸…

论文悦读(6)——PM操作系统之TreeSLS单级存储

TreeSLS &#xff08;SOSP23&#xff09; 1. 背景 (Background)1.1 内存-存储二级架构 (1, 2.1)1.2 单级架构 (2.2)1.3 总结 2. 动机 (Motivation)2.1 现有SLS性能低下 (2.3)2.2 现有SLS难以支持External Synchrony (1, 2.4)2.3 高速PM出现为SLS带来新的机遇与挑战 (1, 2.5) 3.…

CDN是什么?(网络零基础入门篇)

1.CDN的全称 是 Content Delivery Network&#xff0c;即内容分发网络。 其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节&#xff0c;使内容传输得更快、更稳定。 通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网…

探索古彝文的秘密,AI实现古籍传承

陈老老老板&#x1f934; &#x1f9d9;‍♂️本文专栏&#xff1a;生活&#xff08;主要讲一下自己生活相关的内容&#xff09;生活就像海洋,只有意志坚强的人,才能到达彼岸。 &#x1f9d9;‍♂️本文简述&#xff1a;最新资讯&#xff0c;合合信息扫描全能王实现古彝文识别&…

sigmoid和softmax函数有什么区别

Sigmoid函数和Softmax函数都是常用的激活函数&#xff0c;但它们的主要区别在于应用场景和输出结果的性质。 Sigmoid函数&#xff08;也称为 Logistic函数&#xff09;&#xff1a; Sigmoid函数将输入值映射到0到1之间的连续实数范围&#xff0c;通常用于二元分类问题。 Si…

U-Net神经网络简明教程

推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 在我们进入技术细节之前&#xff0c;考虑一下&#xff0c;为什么要使用 U-Net&#xff1f; 你可能在搜索语义分割时遇到过这一点&#xff0c;我很快就会写一篇博客&#xff0c;然后再看看这个。 制作此架构的初衷是用于生物…

Spring MVC程序开发(JavaEE进阶系列3)

目录 前言&#xff1a; 1.什么是Spring MVC 1.1MVC的定义 1.2MVC和Spring MVC的关系 1.3为什么要学习Spring MVC 2.Spring MVC项目的创建 3.Spring MVC框架的使用 3.1连接的功能 3.1.1RequestMapping 3.1.2GetMapping 3.1.3PostMapping 3.2获取参数的功能 3.2.1获…

罗彻斯特大学探讨ChatGPT等人工智能将如何影响高等教育

人工智能聊天机器人ChatGPT持续引起互联网用户的热议&#xff0c;它能够回答关于各个领域的问题&#xff0c;创作歌曲、食谱&#xff0c;起草电子邮件等等。罗切斯特的教职员工和管理人员就他们如何处理 ChatGPT 以及它如何影响未来的教学和学习提出了他们的想法。 “让这项技…

基于Java的新能源汽车在线租赁平台设计与实现(源码+lw+ppt+部署文档+视频讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

基于ChatGPT快速入门体验NLP词云

基于ChatGPT快速入门体验NLP词云 一、什么是自然语言处理二、自然语言处理和词云的关系三、Python环境准备四、基于ChatGpt制作词云4.1 ChatGPT生成初级词云代码4.2 ChatGPT生成进阶词云代码4.3 基于ChatGPT解决代码问题4.4 基于ChatGPT建议修改问题代码 一、什么是自然语言处理…

c语言进阶部分详解(详细解析字符串常用函数,并进行模拟实现)

前段时间也是把指针较为详细系统的讲解完毕&#xff0c;接下来介绍一个全新的知识点&#xff0c;就是字符函数和字符串函数 前几期文章可进我主页观看&#xff1a;总之就是非常唔姆_Matlab,经验分享,c语言题目分享-CSDN博客 想要源代码可以去我的github看看&#xff1a;Neros…

教你拥有一个自己的QQ机器人!0基础超详细保姆级教学!基于NoneBot2 Windows端搭建QQ机器人

0.序言 原文链接&#xff1a;教你本地化部署一个QQ机器人本教程主要面向Windows系统用户教程从0开始全程详细指导&#xff0c;0基础萌新请放心食用&#x1f355;如果你遇到了问题&#xff0c;请仔细检查是否哪一步有遗漏。如果你确定自己的操作没问题&#xff0c;可以到原文链…

苹果macbook电脑磁盘满了怎么清理内存

如果你是苹果macbook用户&#xff0c;可能会面临一个常见但又令人头疼的问题——磁盘空间不足。这不仅影响了你的电脑性能&#xff0c;还可能导致新的软件无法安装&#xff0c;甚至影响到文件的保存。好消息是&#xff0c;有多种方法可以有效地解决这个问题。下面就一起来看看吧…

【100个 Unity实用技能】☀️ | UGUI Text中加入超链接文本,可直接点击跳转

Unity 小科普 老规矩&#xff0c;先介绍一下 Unity 的科普小知识&#xff1a; Unity是 实时3D互动内容创作和运营平台 。包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者&#xff0c;借助 Unity 将创意变成现实。Unity 平台提供一整套完善的软件解决方案&#xff…

unity脚本_Input鼠标键盘 c#

获取鼠标坐标 检测鼠标输入 如果在运行游戏场景中点击一下鼠标左键 检测鼠标抬起 选中即可 检测键盘按下 当前屏幕分辨率 注意&#xff1a;获取的是显示器的分辨率 获取设备屏幕宽高 屏幕休眠模式 窗口/全屏模式 移动设备屏幕转向

【C语言】字符函数和字符串函数(1)

#国庆发生的那些事儿# 大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解字符函数和字符串函数&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 1.本章重点2. strlen2.1函数介绍2.2 模拟实现 3. strcpy3…

第八章 排序 六、简单选择排序

目录 一、算法思想 二、例子 1、我们有以下序列要排序 2、首先从左往右扫描&#xff0c;在其中找到最小的一个数&#xff0c;让它与第一个数互换位置 3、此次扫描完成后&#xff0c;我们取新的子序列&#xff0c;并再次从左往右扫描&#xff0c;在其中找到最小的一个数&…