??作者:一只大喵咪1201
??專欄:《智能家居項(xiàng)目》
??格言:你只管努力,剩下的交給時間!
今天實(shí)現(xiàn)上圖整個項(xiàng)目系統(tǒng)中的字體子系統(tǒng)和顯示子系統(tǒng)。
??設(shè)計思路
在顯示設(shè)備上顯示字體其實(shí)也是比較復(fù)雜的,顯示的字體有點(diǎn)陣字體,矢量字體等方式。
- 使用點(diǎn)陣?yán)L制文字時:每個文字的大小一樣,前后文字互不影響:
如上圖所示,點(diǎn)陣字體中的每個字體的點(diǎn)陣大小都是固定的,也就是需要的像素點(diǎn)個數(shù)是固定的,例如8*16
就是寬用8個像素點(diǎn),長用16個像素點(diǎn),無論是漢字,字母,數(shù)字甚至是一個標(biāo)點(diǎn)符合,都用8*16
個像素點(diǎn)。
- 點(diǎn)陣方式的字體并不連續(xù),字體與字體之間分隔較遠(yuǎn),看上去并不是那么美觀。
- 使用Freetype(矢量字體)繪制文字時:大小可能不同,前面文字會影響后面文字:
如上圖所示,矢量字體中的每個字體的大小是不一樣的,根據(jù)字體的類型,是漢字還是字母甚至是標(biāo)點(diǎn)符合,以及前一個字體的位置,會對顯示的字體做適當(dāng)?shù)恼{(diào)整。
- 矢量字體的排列更加緊湊合理,看起來也更美觀,符合我們的生活經(jīng)驗(yàn)。
描述點(diǎn)陣字體:
對于普通的點(diǎn)陣字體,描述該字體需要:
- X、Y方向大小及原點(diǎn)坐標(biāo)
- 每個像素點(diǎn)的值
所以可以用如下結(jié)構(gòu)體來表示一個點(diǎn)陣字體:
struct dot_font
{
int iX;
int iY; //字體坐標(biāo)
int iWidth;//寬度
int iHeight;//高度
unsigned char* dots;//用于顯示該字體的16字節(jié)數(shù)組(字模)
}
通過坐標(biāo)以及長度和寬度可以確定字體的輪廓,將dots
數(shù)組中的數(shù)據(jù)發(fā)送給顯存,一個完整的字體就顯示在屏幕上了。
描述矢量字體:
如上圖所示,對于矢量字體,每個字體的大小可能不一樣,前一個字體會影響下一個字體,其中有兩黑點(diǎn)非常重要:
- 左邊的黑點(diǎn):當(dāng)前字符的原點(diǎn)。
- 右邊的黑點(diǎn):下一個字符的原點(diǎn)。
下一個字符的位置和上一個字符息息相關(guān),還有其他要素,如寬度,高度,繪制的起始點(diǎn)等等組合在一起才可以確定一個字符。
- 繪制起點(diǎn)和原點(diǎn)不是一個,原點(diǎn)是相當(dāng)于是整個字符的標(biāo)點(diǎn),字符的位置由原點(diǎn)決定。
- 繪制起點(diǎn)也就是顯示開始的位置,一般是字符的左上角。
所以可以用如下結(jié)構(gòu)體來表示一個矢量字體:
typedef struct FontBitMap
{
int iLeftUpX; /* 位圖左上角X坐標(biāo) */
int iLeftUpY; /* 位圖左上角Y坐標(biāo) */
int iWidth; /* 字體寬度 */
int iHeight; /* 字體高度 */
int iCurOriginX; /* 原點(diǎn)X坐標(biāo) */
int iCurOriginY; /* 原點(diǎn)Y坐標(biāo) */
int iNextOriginX; /* 下一個字符X坐標(biāo) */
int iNextOriginY; /* 下一個字符Y坐標(biāo) */
unsigned char* Buffer;/* 字符點(diǎn)陣 */
}FontBitMap, *pFontBitMap;
包含矢量字體的繪制左邊(左上角坐標(biāo)),字體的寬度和高度,當(dāng)前字體的原點(diǎn),下一個字體的原點(diǎn),以及一個字模數(shù)組。
現(xiàn)在我們要做的就是抽象出一個結(jié)構(gòu)體,既能描述點(diǎn)陣字體,也能描述矢量字體。
- 能用來描述矢量字體的結(jié)構(gòu)體必然也能夠描述點(diǎn)陣字體。
繪制起始坐標(biāo)以及寬度和高度和點(diǎn)陣字體中的坐標(biāo)位置以及x和y方向的長度一樣,當(dāng)前字符原點(diǎn)和下一個字符原點(diǎn),點(diǎn)陣字體也可以通過計算得到,所以無論是點(diǎn)陣字體還是矢量字體,都可以共用這一個結(jié)構(gòu)體。
如上圖,整個字體系統(tǒng)并不涉及到內(nèi)核或者芯片,它是屬于軟件層面的,所以并不需要分那么多層,都放在一層中即可。
無論是點(diǎn)陣字體還是矢量字體,它們都必須有字庫,將字符在字庫中對應(yīng)的數(shù)據(jù)發(fā)送給顯示設(shè)備才能顯示出相應(yīng)字符,常見的字庫有ASCII
碼字庫,GBK
字符,以及FreeType
字庫。
- 字庫也要被描述,也要被管理起來。
??字體子系統(tǒng)
??管理層
先來實(shí)現(xiàn)對字符位圖以及字庫的管理。
如上圖頭文件中代碼所示,結(jié)構(gòu)體FontBitLib
是用來描述一個要顯示字符的位圖的,每一個字符都會創(chuàng)建一個結(jié)構(gòu)體對象,而成員中的Buffer
中存放的就是該字符的字母數(shù)據(jù),將這些數(shù)據(jù)發(fā)送給顯示設(shè)備就能顯示出對應(yīng)字符。
結(jié)構(gòu)體FontLib
是用來描述字庫的,本喵這里只會實(shí)現(xiàn)ASCII碼字庫,其中的成員函數(shù)有很多在這里是用不到的,但是為了符合所有字庫,這里本喵仍然寫了,方便以后的擴(kuò)展和維護(hù)。
還有一個注冊字庫的函數(shù)聲明和一個獲取字庫的函數(shù)聲明,獲取字庫的函數(shù)有__
表明該函數(shù)在另一層被調(diào)用,這樣也是為了避免重復(fù)包含的問題。
如上圖所示源文件代碼,創(chuàng)建了一個用來管理字庫的全局鏈表,以及實(shí)現(xiàn)了注冊字庫和獲取字庫的函數(shù)。
??子系統(tǒng)層
字庫的管理已經(jīng)實(shí)現(xiàn)了,下面該實(shí)現(xiàn)一下子系統(tǒng)層調(diào)用這些管理函數(shù)的字體系統(tǒng)了:
如上圖所示頭文件代碼,提供了對字庫進(jìn)行一系列操作的函數(shù)聲明。
如上圖源文件代碼所示,由于在同一時刻只能使用一個字庫,所以創(chuàng)建了一個全局的默認(rèn)字庫變量,還提供了操作字庫所用方法的具體實(shí)現(xiàn),在初始化默認(rèn)字庫的時候,需要判斷該字庫的初始化方法是否為空,為空說明不用初始化。
??字庫層
此時對字庫的管理以及各種操作都已經(jīng)實(shí)現(xiàn)了,但是字庫還沒有,所以接下來就需要實(shí)現(xiàn)ASCII碼字庫:
如上圖所示是ASCII字庫的頭文件,只有一個增加ASCII碼字庫的函數(shù)聲明。
如上圖所示源文件中代碼,包含一個ASCII碼字庫,這是一個全局的二維數(shù)組,字庫中的數(shù)據(jù)是通過字模制作軟件生成的,在本喵的文章I2C通信協(xié)議 | OLED屏中詳細(xì)講解過,有興趣的小伙伴可以移步。
創(chuàng)建了全局的ASCII碼字庫結(jié)構(gòu)體變量并進(jìn)行了初始化,本喵這里的ASCII碼大小是固定的,就是8*16
的,所以就在函數(shù)中就直接給了定值,對于獲取字符的位圖函數(shù)本喵單獨(dú)講解一下:
仍然是這個圖,在顯示該字符的時候,是通過原點(diǎn)坐標(biāo)來確定位置的,也就是圖中坐標(biāo)的黑點(diǎn),這個黑點(diǎn)的坐標(biāo)是由用戶指定的,所以在函數(shù)中該坐標(biāo)是已知的。
左上角的繪畫起始坐標(biāo)和原點(diǎn)在x方向上相同,在y方向上相差字體的高度,ASCII碼字符中就是16,所以可以通過當(dāng)前原點(diǎn)坐標(biāo)計算出左上角坐標(biāo)。
下一個字符的原點(diǎn)坐標(biāo),在x方向上和當(dāng)前字符原點(diǎn)坐標(biāo)相差字體的寬度,ASCII碼字符中就是8,所以也可以通過當(dāng)前原點(diǎn)坐標(biāo)計算出下一個字符的原點(diǎn)坐標(biāo)。
字符的高度和寬度是固定的,也就是8*16
的,最重要的字模數(shù)組Buffer
中的內(nèi)容就來自前面的字庫ascii_font
二維數(shù)組,如果用戶沒有向Buffer
中存放數(shù)據(jù),那么就直接返回字庫中對應(yīng)的字模數(shù)據(jù),如果用戶存放了,那么就將字庫中的字模數(shù)據(jù)復(fù)制到Buffer
中。
如上圖代碼就是用來從字庫中獲取指定字符的位圖數(shù)據(jù)的。
雖然將字體系統(tǒng)分為了三層,但是它們?nèi)匀粚儆谙到y(tǒng)層,只是在系統(tǒng)層中又細(xì)分出來的三層。
如上圖所示便是這三小層各種的功能和互相之間的調(diào)用關(guān)系,子系統(tǒng)層只負(fù)責(zé)使用字庫,并不關(guān)心字庫的維護(hù)和管理,管理層則要做到細(xì)節(jié)處的管理,管理多個字庫,但是并不用知道每個字庫中的內(nèi)容,字庫層則需要詳細(xì)實(shí)現(xiàn)自己所代表的字庫,包括所有字模數(shù)據(jù),以及字庫結(jié)構(gòu)體中的那些成員方法。
??顯示子系統(tǒng)
顯示子系統(tǒng)和設(shè)備子系統(tǒng)中的顯示設(shè)備并不是一回事。
如上圖,文字子系統(tǒng)會將從字體子系統(tǒng)中取出的點(diǎn)陣發(fā)送給設(shè)備子系統(tǒng)中的顯示設(shè)備繪制點(diǎn)陣從而顯示出來。
編碼集:
如上圖所示,字符串“ABC中國”
使用不同的編碼集在內(nèi)存中的數(shù)據(jù)不一樣,使用Unicode
編碼集中的UTF-8(左邊內(nèi)存窗口),每一個漢字占用3個字節(jié),使用GB2312編碼集(右邊內(nèi)存窗口),每一個漢字占用2個字節(jié)。
- 無論什么編碼集,對ASCII碼都是用一個字節(jié)表示。
編碼格式:
拿常用的Unicode
編碼集舉例,它包含三種編碼格式,其中最常用的就是UTF-8
編碼格式:
Unicode數(shù)值范圍(16進(jìn)制) | 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
編碼格式,該編碼格式中每個字節(jié)中包含有其他信息:
- 每個字節(jié)中,從高位到地位,遇到第一個0之前,有幾個1就表示這個字符有幾個字節(jié)共同表示,如果是
10xxxxxx
,10
表示當(dāng)前字節(jié)和前一個字節(jié)共同表示一個字符。- 去掉表示字節(jié)個數(shù)的位以后,將剩余的位放在一起,組成的數(shù)值就是
UTF-8
編碼值。
如1110xxxx 10yyyyyy 10zzzzzz
這三個字節(jié)表示的字符,這串二進(jìn)制序列表示該字符用3個字節(jié)表示,組合成UTF-8
編碼值為xxxxyyyy yyzzzzzz
兩個字節(jié)大小。
除了UTF-8編碼格式外,還有UTF-16LE,UTF-16BE等編碼格式,不同的編碼格式,表示同一個字符的數(shù)據(jù)也會不一樣。
點(diǎn)陣:
從默認(rèn)字庫中取出字符的點(diǎn)陣數(shù)據(jù),然后再選擇顯示設(shè)備,將字符在LCD或者OLED上顯示。
如上圖所示便是整個顯示的過程,先確定編碼集為Unicode
,再確定編碼格式為UTF-8
,然后算出編碼值,取出對應(yīng)的點(diǎn)陣數(shù)據(jù),最后在顯示設(shè)備上顯示。
??編程
顯示子系統(tǒng)是在使用字體子系統(tǒng)和設(shè)備子系統(tǒng)中的顯示設(shè)備,所以它并不用分很多層,只工作在顯示子系統(tǒng)層(應(yīng)用層)。
如上圖,要想在顯示設(shè)備上顯示字符,有三要素,分別是具體的顯示設(shè)備,字符的坐標(biāo),已經(jīng)要顯示的字符。
如上圖代碼便是整個顯示系統(tǒng)中的核心代碼,里面有幾個被調(diào)用的函數(shù)本喵會單獨(dú)拿出來講解。
調(diào)用該函數(shù)顯示字符串的時候,首先做的第一步就是獲得字符的編碼值,這里調(diào)用了GetCodeForStr
函數(shù)來獲得編碼值:
如上圖代碼所示,專門創(chuàng)建一個文件encoding
來處理編碼值,在該函數(shù)中,可以獲取指定字符不同編碼集下的編碼值,這里本喵就只用ASCII編碼集。
使用該函數(shù)獲取的是一個字符的編碼值,所以對于ASCII編碼集來說,直接返回該字符的ASCII碼值作為該字符的編碼值即可,因?yàn)锳SCII編碼集默認(rèn)就是支持的。
得到字符的編碼值以后,就要獲取該編碼值對應(yīng)的點(diǎn)陣數(shù)據(jù)。
如上圖,根據(jù)編碼值獲取點(diǎn)陣數(shù)據(jù)是從字體子系統(tǒng)中獲取,該函數(shù)在ascii_font.c
中已經(jīng)實(shí)現(xiàn)了,調(diào)用默認(rèn)字庫中的GetFontBitMap
即可從默認(rèn)字庫(ASCII碼字庫)中獲得點(diǎn)陣數(shù)據(jù)。
點(diǎn)陣數(shù)據(jù)有了以后,就需要將點(diǎn)陣數(shù)據(jù)寫入到顯示設(shè)備在RAM中的顯存中去:
如上圖代碼所示,該函數(shù)的就是將點(diǎn)陣數(shù)據(jù)寫入到RAM中的顯存中的,點(diǎn)陣數(shù)據(jù)是在FontBitMap
對象中的,將該字符的所有像素點(diǎn)數(shù)據(jù)都獲取到并寫入到顯存中。
本喵這里使用的OLED顯示,所以按照OLED顯示方式來分析如何獲取像素點(diǎn)的位置:
如上圖所示,OLED顯示的字符是8*16
的,前一頁顯示一個字符點(diǎn)陣的前八8字節(jié),后一也顯示后8個字節(jié),每個字節(jié)中的一個比特位就是一個像素點(diǎn),所以要獲取的是每一個比特位中的值。
如上圖代碼所示,獲取(iX,iY)坐標(biāo)像素點(diǎn)的像素值時,按照OLED顯示方式圖,通過橫坐標(biāo)x確定像素點(diǎn)所在字節(jié)在Buffer
中的地址,然后根據(jù)像素點(diǎn)的y坐標(biāo)確定是該字節(jié)的哪個比特位,然后通過按位與和左移操作返回該像素點(diǎn)的值。
如上圖所示,整個顯示子系統(tǒng)的調(diào)用關(guān)系,首先調(diào)用顯示子系統(tǒng)中的ShowTextInDisplayDevice
函數(shù)去顯示字符str
,應(yīng)用層只用關(guān)心這一個函數(shù)怎么調(diào)用,不比管它的實(shí)現(xiàn),站在應(yīng)用層的角度,此時就可以顯示出指定字符了。
顯示函數(shù)又調(diào)用顯示子系統(tǒng)中編碼集層中的GetCodeStr
得到該字符的編碼值,再用編碼值去字體子系統(tǒng)中的默認(rèn)字庫中得到點(diǎn)陣數(shù)據(jù),數(shù)據(jù)放在FontBitMap
結(jié)構(gòu)體對象中。
顯示子系統(tǒng)再調(diào)用DrawBitMapOnFrameBuffer
將點(diǎn)陣數(shù)據(jù)寫到設(shè)備子系統(tǒng)中顯示設(shè)備的RAM顯存中,在這個函數(shù)內(nèi)部,顯示子系統(tǒng)會先調(diào)用GetPixelColorFromBitMap
函數(shù)獲取像素點(diǎn)的顏色數(shù)據(jù),然后再調(diào)用顯示設(shè)備自帶的SetPixel
方法將顏色數(shù)據(jù)寫到RAM顯存中。
最后會調(diào)用Flush
函數(shù)將RAM顯存中的數(shù)據(jù)發(fā)送到顯示設(shè)備自帶的顯存中,至此整個顯示流程就結(jié)束了。
- 在獲取編碼值的時候,根據(jù)不同的編碼集和編碼方式獲得編碼值,這里可以進(jìn)行擴(kuò)展維護(hù)。
- 在獲取像素點(diǎn)顏色數(shù)據(jù)的時候,顯示設(shè)備的顯示規(guī)則不同,獲取編碼值的方式也就不同,這里也可以擴(kuò)展維護(hù)。
??測試
最后就是測試顯示子系統(tǒng),設(shè)備子系統(tǒng)和字體子系統(tǒng)三個系統(tǒng)能否成功配合在OLED上顯示字符了:
如上圖代碼所示,在測試函數(shù)中,先將字體系統(tǒng)中的字庫初始化完畢,包括添加字庫設(shè)置默認(rèn)字庫等等,然后再初始化顯示設(shè)備,包括指定顯示設(shè)備,初始化等等步驟。文章來源:http://www.zghlxwxcb.cn/news/detail-713339.html
- 顯示字符只用調(diào)用一個函數(shù)
ShowTextInDidsplayDevice(ptDev,16,16,str)
即可在作為為(16,16)處顯示指定字符str
。
可以看到,成功顯示字符,源代碼中的字符串太長,涉及到了換行,所以本喵在僅顯示了A Big MiaoMi
字符串,沒有實(shí)現(xiàn)換行。文章來源地址http://www.zghlxwxcb.cn/news/detail-713339.html
到了這里,關(guān)于【智能家居項(xiàng)目】裸機(jī)版本——字體子系統(tǒng) | 顯示子系統(tǒng)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!