1.本文目的
2.資源評估
3.顯示原理
4.嵌入式上漢字處理
5.結果驗證與展示
6.總結
做嵌入式圖形開發,我們往往都會利用到各種GUI進行互動設計,但是對於GUI的字符串處理與中文字庫顯示,也許並不會特別關注,因為GUI已經幫助我們封裝了一些通用的API,在調用相對應的API就可以顯示想要的圖像和字符串了。那麼這些底層原理到底是什麼呢?
正好和朋友討論,我們做嵌入式開發都是將輸出信息定位到串口,那能不能定位到I2C、SPI、網絡等各種協議上去呢?這個確實是有意思的事情,那正好手上有個樹莓派,可以通過DSI或者HDMI來顯示,那就把rt-thread的console重定位到LCD上去吧,讓樹莓派的屏幕代替我們的串口調試助手控制臺,這樣就不用接上串口看輸出信息了。
有了這個想法,於是立即付諸行動。需求很明確,開發平臺也已經確定,樹莓派4+HDMI屏(解析度1280x800)。或者接DSI的MIPI屏,我發現樹莓派的HDMI驅動原來和DSI的MIPI屏的驅動一樣,所以兩者沒有區別。為了簡化驗證的操作流程,可以選擇rtos,這裡我就用比較熟悉的rt-thread。因為rt-thread有著和Linux類似的控制終端,這樣更加方便對接。
2.資源評估有了想法,若要想進行下去,必須評估一下手上的資源是否齊全。下面列出必要的資源
1.樹莓派4
選擇樹莓派4作為驗證平臺,是我因為現在手上環境搭建已經很方便了。嵌入式開發的痛點和難點就是在環境搭建上,一個好的的開發環境可以達到事半功倍的效果。環境搭建值得好好整理,對於驗證各種功能,實現各種特性的驗證都十分的好用。
2.hdmi屏
由於已經完成樹莓派4的hdmi屏驅動的研究工作,並且hdmi驅動和最後抽象出來的就是FrameBuffer。操作起來不用管底層的實現,只需要向這個Framebuffer的地址處寫數據,會自動將這個數據顯示到LCD的屏上,十分的方便。並不用關心x,y坐標,像素等等。
3.字庫
這裡先通過英文字庫進行演示,後面再談中文字庫。目前抽取的是開源的GUI中的font_dejavu_40字庫進行研究。後面的40表示每個字符高度為40個像素,因為屏的解析度為1280x800。如果每個字符的高度太小則看起來文字非常的小,在大屏上看起來十分不友好,所以這裡選擇40個高度的字體,而寬度不定是因為字符的寬度是不是確定的,每個字符有著自己的寬度比如L和l兩個數字的寬度就不一樣。
有了上述資源,就可以進行後面的探究了,下面來梳理一下顯示原理。
3.顯示原理計算機圖形本質上就是像素點的集合,更加具體的就是紅黃藍三原色的組合。
三原色的排布組成了一個像素點。實際LCD放大後像素點看起來如下圖所示。
而這些像素的亮度決定了最後顯示在屏幕上的效果。像素深度(bits per pixel,簡稱bpp) 描述了每個像素的位數,比如32位則是RGBA8888,24位常見的RGB565和16位常見的565等等。這些都是一個像素所能夠表示的信息。
而多個像素可以表示一個圖像信息。像素是圖像操作的最小單位,所以下面暫時不要考慮顏色信息。
來看一個字庫中一個字符的信息
0x00, 0x00, //....
0x00, 0x00, //....
0x00, 0x00, //....
0x00, 0x00, //....
0x00, 0x00, //....
0x00, 0x00, //....
0x1f, 0x80, //..+%@@@@@
0xff, 0x80, //@@@@@@@@@
0xff, 0x80, //@@@@@@@@@
0xe3, 0x80, //@%%+..@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x03, 0x80, //.@@@
0x7f, 0xfc, //.@@@@@@@@@@@@@
0x7f, 0xfc, //.@@@@@@@@@@@@@
0x7f, 0xfc, //.@@@@@@@@@@@@@
0x00, 0x00, //....
0x00, 0x00, //....
0x00, 0x00, //....
0x00, 0x00, //....
0x00, 0x00, //....
0x00, 0x00, //....
0x00, 0x00, //....
0x00, 0x00, //....
0x00, 0x00, //....上述就是字符1在字符中的存放信息,每個字節按位展開,.表示0,@表示1。則右邊的注釋展示了該串字符的信息。如果我們將上述信息告訴給cpu,然後CPU處理這些信息放到LCD上顯示則可以顯示字符串1。
如果把上面的數組用程序解析交給LCD該如何設計。
1.拷貝上述數組到程序裡,作為只有一個字符的字庫數組array
2.申請一塊和framebuff一樣大的內存palette,作為圖像輸入的畫板
3.讀取array第一和第二個元素,將第一個元素按高位解析,如果是0向palette填充黑色像素點,1向palette填充白色像素點。
字庫中的每一位對應LCD的一個像素,如果對於RGB565來說,則表示2個字節。那我們可以做這樣的理解。字庫中1的寬度是32個像素,高度是40個像素。
於是可以做標準字庫的解析了。一個標準字庫是包含多個這樣的字符串結構的,所以字符串需要一張表記錄這些信息,根據asiic碼錶排序,字庫的存放順序也可如此,然後一個索引表記錄著每個字符串的寬度,數組所在的起始地址信息,有了這些信息,就可以依次做解析然後轉換成像素進行顯示了。
4.嵌入式上漢字處理嵌入式上受到資源限制,漢字字庫一直都不好解決,不能為了顯示漢字把2500~7000個漢字都收錄進去,這樣需要的內存資源和flash資源十分龐大。為了解決這個問題,一般都是自定義字庫,就是首先列出該項目中實際會用到的所有漢字,然後利用特定的軟體生成對應的像素字符數組,生成的同時,也會對應這一張map表,方便查找具體漢字的位置。
在處理英文的時候,由於所需的字符很少,可以通過ASCII碼進行索引,漢字則可以自定義索引規則,這些都是需要自己設計處理的。但是原理是一樣的。
5.結果驗證與展示經過上述的操作,已經完成了lcd console的任務,可以給自己交差了。
其實現的代碼也已經放到百度網盤上可以供參考。
連結:https://pan.baidu.com/s/17A37ISKT0tW3WWq2oXJASw
提取碼:dgr6上述代碼僅供參考,優化部分還需完善。
6.總結需要注意的是,對於不同的LCD,需要自己找到合適的大小的字體,這樣才能看起來清楚。另外在實現的細節上需要注意的是最好不要在framebuffer上直接繪圖,可以放到一個與framebuffer大小一樣的數組中,叫做palette,也就是畫板。當繪製一幀畫面完成後,再刷新到framebuffer中,這是因為framebuffer是非cache的,操作起來會影響刷屏的幀率,看起來幀率會很低。
對於英文字庫的顯示、中文漢字的處理,有很多東西需要去拓展。其中漢字的抗鋸齒問題就很值得研究學習,漢字模糊,漢字的銳化等等,萬變不離其宗,其核心都是對像素的處理。LCD繪圖,理解像素處理流程,所有的上層應用實現都非常好理解。