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

这篇具有很好参考价值的文章主要介绍了【智能家居项目】裸机版本——字体子系统 | 显示子系统。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🐱作者:一只大喵咪1201
🐱专栏:《智能家居项目》
🔥格言:你只管努力,剩下的交给时间!
【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
今天实现上图整个项目系统中的字体子系统和显示子系统。

🀄设计思路

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

  • 使用点阵绘制文字时:每个文字的大小一样,前后文字互不影响:
    【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
    如上图所示,点阵字体中的每个字体的点阵大小都是固定的,也就是需要的像素点个数是固定的,例如8*16就是宽用8个像素点,长用16个像素点,无论是汉字,字母,数字甚至是一个标点符合,都用8*16个像素点。
  • 点阵方式的字体并不连续,字体与字体之间分隔较远,看上去并不是那么美观。
  • 使用Freetype(矢量字体)绘制文字时:大小可能不同,前面文字会影响后面文字:

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
如上图所示,矢量字体中的每个字体的大小是不一样的,根据字体的类型,是汉字还是字母甚至是标点符合,以及前一个字体的位置,会对显示的字体做适当的调整。

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

描述点阵字体:

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
对于普通的点阵字体,描述该字体需要:

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

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

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

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


描述矢量字体:

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网

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

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

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

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

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

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方向的长度一样,当前字符原点和下一个字符原点,点阵字体也可以通过计算得到,所以无论是点阵字体还是矢量字体,都可以共用这一个结构体。

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
如上图,整个字体系统并不涉及到内核或者芯片,它是属于软件层面的,所以并不需要分那么多层,都放在一层中即可。

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

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

🀄字体子系统

🃏管理层

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

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
如上图头文件中代码所示,结构体FontBitLib是用来描述一个要显示字符的位图的,每一个字符都会创建一个结构体对象,而成员中的Buffer中存放的就是该字符的字母数据,将这些数据发送给显示设备就能显示出对应字符。

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

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

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
如上图所示源文件代码,创建了一个用来管理字库的全局链表,以及实现了注册字库和获取字库的函数。


🃏子系统层

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

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
如上图所示头文件代码,提供了对字库进行一系列操作的函数声明。

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
如上图源文件代码所示,由于在同一时刻只能使用一个字库,所以创建了一个全局的默认字库变量,还提供了操作字库所用方法的具体实现,在初始化默认字库的时候,需要判断该字库的初始化方法是否为空,为空说明不用初始化。


🃏字库层

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

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
如上图所示是ASCII字库的头文件,只有一个增加ASCII码字库的函数声明。

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网

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

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

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
仍然是这个图,在显示该字符的时候,是通过原点坐标来确定位置的,也就是图中坐标的黑点,这个黑点的坐标是由用户指定的,所以在函数中该坐标是已知的。

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

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

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

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
如上图代码就是用来从字库中获取指定字符的位图数据的。


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

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网

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

🀄显示子系统

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

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
如上图,文字子系统会将从字体子系统中取出的点阵发送给设备子系统中的显示设备绘制点阵从而显示出来。

编码集:

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
如上图所示,字符串“ABC中国”使用不同的编码集在内存中的数据不一样,使用Unicode编码集中的UTF-8(左边内存窗口),每一个汉字占用3个字节,使用GB2312编码集(右边内存窗口),每一个汉字占用2个字节。

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

编码格式:

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

Unicode数值范围(16进制) UTF-8编码方式(二级制)
0000 0000-0000 007F 0xxxxxxx
0000 0080-0000 07FF 110xxxxx 10xxxxxx
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF 11110xxx 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上显示。

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
如上图所示便是整个显示的过程,先确定编码集为Unicode,再确定编码格式为UTF-8,然后算出编码值,取出对应的点阵数据,最后在显示设备上显示。

🃏编程

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

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
如上图,要想在显示设备上显示字符,有三要素,分别是具体的显示设备,字符的坐标,已经要显示的字符。

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
如上图代码便是整个显示系统中的核心代码,里面有几个被调用的函数本喵会单独拿出来讲解。


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

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网

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

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


得到字符的编码值以后,就要获取该编码值对应的点阵数据。
【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
如上图,根据编码值获取点阵数据是从字体子系统中获取,该函数在ascii_font.c中已经实现了,调用默认字库中的GetFontBitMap即可从默认字库(ASCII码字库)中获得点阵数据。


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

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网

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


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

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网

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

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
如上图代码所示,获取(iX,iY)坐标像素点的像素值时,按照OLED显示方式图,通过横坐标x确定像素点所在字节在Buffer中的地址,然后根据像素点的y坐标确定是该字节的哪个比特位,然后通过按位与和左移操作返回该像素点的值。


【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网

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

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

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

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

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

🀄测试

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

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网

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

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

【智能家居项目】裸机版本——字体子系统 | 显示子系统,智能家居项目,智能家居,单片机,stm32,物联网
可以看到,成功显示字符,源代码中的字符串太长,涉及到了换行,所以本喵在仅显示了A Big MiaoMi字符串,没有实现换行。文章来源地址https://www.toymoban.com/news/detail-713339.html

到了这里,关于【智能家居项目】裸机版本——字体子系统 | 显示子系统的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【智能家居项目】FreeRTOS版本——将裸机程序改造成FreeRTOS程序 | DHT11温湿度传感器

    🐱作者:一只大喵咪1201 🐱专栏:《智能家居项目》 🔥格言: 你只管努力,剩下的交给时间! 如上图所示是裸机版本的智能家居项目总体框架结构,这篇文章开始,本喵要带着大家逐渐将智能家居项目从逻辑版本改为FreeRTOS版本,并且要增加温湿度显示和网络时间获取两个

    2024年02月05日
    浏览(42)
  • 【智能家居项目】FreeRTOS版本——多任务系统中使用DHT11 | 获取SNTP服务器时间 | 重新设计功能框架

    🐱作者:一只大喵咪1201 🐱专栏:《智能家居项目》 🔥格言: 你只管努力,剩下的交给时间! 在上篇文章中,本喵仅进行了单任务的DHT11温湿度传感器使用,相当于裸机使用。 根据上面时序图计算接收一次数据(5个字节)的耗时,不考虑主机发送起始信号的耗时: 最小时间

    2024年02月05日
    浏览(43)
  • 【智能家居项目】RT-Thread版本——DHT11获取温湿度 | MQTT上传到服务器 | 服务器控制外设

    🐱作者:一只大喵咪1201 🐱专栏:《智能家居项目》 🔥格言: 你只管努力,剩下的交给时间! 这篇文章中,本喵将使用 RT-Thread Studio 来实现这个智能家居的项目,最终效果和前面的裸机版本以及 FreeRTOS 版本一样。在这里本喵将让大家体验到 RT-Thread Studio 的便利。 首先打开

    2024年04月17日
    浏览(48)
  • Linux MQTT智能家居项目(智能家居界面布局)

    1.选择工程名称和项目保存路径 2.选择QWidget 3.添加保存图片的资源文件: 在工程目录下添加Icon文件夹保存图片: 将文件放入目录中: 将图片添加进入资源文件中: 这里我们一共显示4个界面:LED控制界面,温度湿度显示界面,光照强度显示界面,摄像头监测界面。 所以这里

    2024年02月13日
    浏览(51)
  • 【OrangePi Zero2 智能家居】智能家居项目的软件实现

    一、项目整体设计 二、项目代码的前期准备 三、实现语音监听接口 四、实现socket监听接口 五、实现烟雾报警监听接口 六、实现设备节点代码 七、实现接收消息处理接口 整体的软件框架大致如下: 整个项目开启4个监听线程, 分别是: 语音监听线程:用于监听语音指令,

    2024年02月22日
    浏览(59)
  • 项目名称:智能家居边缘网关项目

    软件环境: C语言 硬件环境: STM32G030C8TX单片机开发板 开发工具: Linux平台GCC交叉编译环境以及ukeil   边缘网关是部署在网络边缘侧的网关,通过网络联接、协议转换等功能联接物理和数字世界,提供轻量化的联接管理、实时数据分析及应用管理功能。比较常见的就是智能家居中

    2024年02月16日
    浏览(45)
  • QT 项目 智能家居系统 上位机

    有哪些文件  : 头文件: 1. auidio .h  语音识别 模块 2. camera.h  人脸识别登录模块 3. chooselevelscene.h  翻金币游戏的  4. dataconfig.h   翻金币游戏的 关卡 的数据  5.entry_mode.h 登录方式的界面类 6.luck_draw.h   开心一天的界面类 7.mainsence.h 翻金币游戏的背景图片的类  8.mainwindow

    2024年02月12日
    浏览(50)
  • Linux嵌入式项目-智能家居

    一、资料下载  二、框架知识  三、MQTT通信协议 1、上位机APP主要工作        1.wait for msg  / while(1)订阅等待消息        2.处理消息 客户端创建了两个线程,一个线程用于发布消息,一个线程用于监听订阅消息 (那我的仿真系统也可以啊,一个发送处理数据线程。一个监听

    2024年02月16日
    浏览(47)
  • 入门级带你实现一个安卓智能家居APP(1)java版本

    ?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"? 以上是比较重要的UI控件,大家了解一下,其他的一些属性我就不多说了,你们可以自己尝试修改一下参数,就会发现具体的对应的作用是什么了。 我们的ui已经做好了,现在就去写主活动类的逻辑代码!!! 我主要说说主要逻辑吧。 首先,当然是

    2024年04月27日
    浏览(39)
  • 基于树莓派的智能家居项目及代码

    基于树莓派的智能家居。智能家居用到的硬件有:树莓派3B+、SU-03语音识别模块、pi 摄像头、继电器组、小灯、火焰传感器、蜂鸣器、电磁锁、超声波测距模块、DHT11温湿度检测模块,433M射频编解码模块或者红外模块,面包板等。 采用了简单工厂模式的一个设计方式。稳定,

    2024年02月08日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包