嘿,各位樹莓派的發(fā)燒友們!今天我要和大家分享一個超酷的技巧——如何在沒有桌面環(huán)境的情況下,在樹莓派上開發(fā)GUI應(yīng)用。想象一下,你的樹莓派就像一個超級英雄,而我們要給它裝上一個炫酷的“面甲”,讓它不僅能跑,還能飛!而且,這個“面甲”不會拖慢它的速度,因為我們不裝笨重的桌面環(huán)境!

framebuffer:樹莓派的“魔法畫布”
首先,得聊聊framebuffer這個神奇的東西。framebuffer就像是Linux系統(tǒng)給我們的一個魔法畫布,你可以直接在上面畫畫。在樹莓派上,HDMI接口對應(yīng)的是/dev/fb0這個魔法畫布。如果你接的是普通的顯示器或者電視,直接往這個文件里寫數(shù)據(jù),屏幕上就會出現(xiàn)你畫的內(nèi)容,簡直不要太神奇!
如果你用的是SPI接口的屏幕,那你就有了第二個魔法畫布/dev/fb1。這時候,你需要在/boot/config.txt文件里設(shè)置一下分辨率,讓這兩個畫布的大小一樣。不然的話,就像給一個方形的畫布硬塞進一個圓形的框里,畫面會變形,看著就別扭!
framebuffer_width=480framebuffer_height=320
在/boot/config.txt中,我們創(chuàng)建一個自定義的HDMI模式并設(shè)置分辨率。同樣,為你的屏幕選擇合適的分辨率。如果你希望同時在LCD屏幕和外部顯示器上以不同分辨率顯示,請不要這樣做。
# 當LCD連接時,有兩個framebuffer:## - /dev/fb0 - 代表HDMI輸出。這個輸出支持硬件加速,# 因此應(yīng)該是GUI的目標。# - /dev/fb1 - 代表LCD輸出。## 以下設(shè)置啟用了一個自定義模式(模式87),將HDMI# 輸出設(shè)置為與LCD相同的分辨率。這很有用,因為應(yīng)用程序# 可以以硬件加速的輸出為目標,然后使用raspi2fb工具將輸出鏡像到LCD。## HDMI組強制DMT輸出,即使在HDMI輸出上也是如此。DMT是顯示器的標準,# 而CEA是電視的標準。hdmi_cvt480320601000hdmi_mode=87hdmi_group=2
低級操作:直接往framebuffer上畫畫
如果你是編程大神,可以直接用C語言操作framebuffer。想象一下,你手里拿著一支魔法畫筆,直接在畫布上畫畫。不過,這個過程有點復(fù)雜,需要你用ioctl這個魔法咒語來獲取畫布的信息,比如寬度和高度。然后,你就可以用魔法畫筆在畫布上涂顏色了。不過,這個過程對新手來說有點難,別擔心,后面我會介紹一個更簡單的工具。

通過framebuffer顯示圖形的步驟如下:
1. 打開對應(yīng)于你想要使用的顯示設(shè)備的/dev/fbX文件。
2. 使用ioctl(這是一個允許與某些設(shè)備通信的Linux系統(tǒng)調(diào)用)來獲取有關(guān)framebuffer的信息。具體方式是設(shè)備特定的,但framebuffer允許獲取諸如寬度和高度等信息。
3. 寫入字節(jié)到文件。確切的字節(jié)序列取決于framebuffer的顏色深度。
有一個關(guān)于使用framebuffer的絕佳指南,我強烈推薦閱讀。在按照示例操作時,我需要記住以下幾點:
要訪問framebuffer,你需要以root身份運行。這并不理想,我們可以通過放松這一要求來解決,但目前請使用sudo運行代碼示例。我將在下一節(jié)討論替代方案。
該指南假設(shè)你正在顯示到/dev/fb0。因為我正在使用基于SPI的屏幕,所以我實際上想要顯示到/dev/fb1,所以我相應(yīng)地修改了代碼示例。
framebuffer的顏色深度將取決于你正在使用的屏幕,但通常HDMI輸出(/dev/fb0)將運行在32bpp(每像素位數(shù)),而許多(所有?)SPI屏幕將運行在16bpp。
鏡像魔法:讓兩個framebuffer同步顯示
如果你有兩個framebuffer,比如一個HDMI和一個SPI屏幕,你可以用raspi2fb這個魔法工具,把一個畫布上的內(nèi)容復(fù)制到另一個畫布上。就像用魔法鏡子把一個畫布上的畫面反射到另一個畫布上一樣。不過,別忘了先設(shè)置好分辨率,否則畫面會像被擠扁的氣球一樣變形。
如果你嘗試運行raspi2fb,你會遇到權(quán)限錯誤。原因是當前用戶沒有權(quán)限與framebuffer交互。我們可以通過查看framebuffer文件的所有者和組來查看這一點:
$ls-l /dev/fb*crw-rw---- 1 root video 29, 0 Jan 1 22:48 /dev/fb0crw-rw---- 1 root video 29, 1 Jan 13 21:52 /dev/fb1
文件由root擁有,因此需要使用sudo運行。然而,這存在安全問題,因為你不想讓你的應(yīng)用程序以root身份運行!幸運的是,framebuffer文件的組是video,所以你只需要將用戶添加到該組。例如,對于我的用戶:
usermod -a-Gvideoavik
現(xiàn)在你可以無需root權(quán)限運行raspi2fb。
如上所述,如果你設(shè)置了SPI屏幕,并將系統(tǒng)配置為將控制臺顯示到fb1。當raspi2fb將HDMI屏幕鏡像到fb1時,控制臺和raspi2fb(以及因此在HDMI屏幕上運行的任何內(nèi)容)都在更新同一個framebuffer。這會導致文本光標出現(xiàn)在屏幕上。
一個framebuffer感知的應(yīng)用程序應(yīng)該禁用文本光標,但它只能在控制臺顯示在與應(yīng)用程序相同的framebuffer上時這樣做。但是,如果應(yīng)用程序顯示在fb0,而控制臺顯示在fb1,文本光標仍然存在。解決方法是將控制臺顯示在fb0,與應(yīng)用程序一起。
你可以通過編輯/boot/cmdline.txt來實現(xiàn)這一點,但我想只在運行raspi2fb時切換控制臺的輸出。幸運的是,你可以使用con2fbmap在系統(tǒng)運行時切換控制臺顯示。因此,你可能會執(zhí)行以下操作:
con2fbmap10 # 將控制臺切換到fb0raspi2fb # 將fb0鏡像到fb1# 在單獨的窗口或SSH會話中,運行一個顯示到fb0的應(yīng)用程序。# 應(yīng)用程序完成后,停止raspi2fb,然后:con2fbmap11 # 將控制臺切換回fb1
注意,con2fbmap也需要訪問framebuffer,因此要么你需要以root身份運行,要么你需要是video組的成員。
Raylib:讓GUI開發(fā)變得簡單又有趣
說到這兒,我得隆重介紹一下Raylib這個神器。它就像一個魔法工具箱,讓你不用直接操作framebuffer,也能畫出漂亮的圖形界面。而且,它支持硬件加速,就像給你的樹莓派裝上了火箭助推器,速度超快!
它具有以下優(yōu)勢:
Raylib支持多個平臺。這意味著我可以在筆記本電腦上開發(fā)應(yīng)用程序,渲染到屏幕上的窗口,然后在樹莓派上部署該應(yīng)用程序,無需任何更改。
沒有外部依賴項。這使得在樹莓派上編譯變得容易,也可以理解庫代碼本身,因為該庫非常自包含。
Raylib支持硬件加速,提供出色的性能。
Raylib包含功能,例如文本渲染支持,而這些功能我本來需要自己開發(fā)。

構(gòu)建Raylib:魔法工具箱的組裝
要使用Raylib,你需要先在樹莓派上構(gòu)建它。想象一下,你正在組裝一個魔法工具箱,需要按照說明書一步一步來。構(gòu)建過程中,你可能需要修改一些配置文件,比如把SUPPORT_BUSY_WAIT_LOOP這行注釋掉,這樣你的應(yīng)用就不會占用100%的CPU,而是像一個聰明的魔法師,只在需要的時候才使用魔法。
使用Raylib開發(fā)應(yīng)用
默認情況下,Raylib使用“忙等待”循環(huán)。這意味著,在你的應(yīng)用程序完成渲染后,對于該幀的其余時間,Raylib會不斷檢查是否已經(jīng)過去足夠的時間以開始下一幀。這會導致高CPU使用率。
多個GitHub作者提到現(xiàn)在支持“睡眠”等待。在這種實現(xiàn)中,庫設(shè)置一個計時器,并要求操作系統(tǒng)在經(jīng)過一定時間后喚醒應(yīng)用程序。
需要注意的是,這種實現(xiàn)不是默認的,你必須注釋掉一行并重新構(gòu)建庫:
// 在raylib/src/config.c中注釋掉以下行:#defineSUPPORT_BUSY_WAIT_LOOP
(但如果你使用CMake而不是Make,你可能需要在src/config.c.in中進行此更改。)
通過此更改,我發(fā)現(xiàn)我的應(yīng)用程序在沒有進行任何密集計算的情況下,CPU使用率為1-3%。
如果你查看用于構(gòu)建Raylib示例應(yīng)用程序的Makefile,你會發(fā)現(xiàn)許多平臺特定的選項。因為Raylib支持如此多的平臺,Makefile相當大。
當你構(gòu)建自己的應(yīng)用程序時,你需要指定幾個選項。因為我沒有運行任何make install類型的命令來構(gòu)建Raylib,所以庫的必要文件位于我下載Raylib的目錄中。
我從官方Makefile開始,移除了我不需要的平臺支持,只剩下以下必要的選項。
首先,我們定義一些編譯器標志:
-std=c99:定義C語言模式(1999年修訂版的標準C)
-D_DEFAULT_SOURCE:與-std=c99一起在Linux上使用,用于timespec,這是用于與時間相關(guān)的功能。
CFLAGS= -std=c99 -D_DEFAULT_SOURCE
接下來,我們需要告訴編譯器在哪里找到定義庫的數(shù)據(jù)結(jié)構(gòu)和函數(shù)的Raylib頭文件。這里,RAYLIB_PATH指的是你下載Raylib的位置。
INCLUDE_PATHS=-isystem$RAYLIB_PATH/src \ -isystem$RAYLIB_PATH/src/external# 在樹莓派上,你還需要指定在哪里找到一些頭文件# 這些頭文件在不同位置。這些頭文件被Raylib使用。INCLUDE_PATHS+=-I/opt/vc/includeINCLUDE_PATHS+=-I/opt/vc/include/interface/vmcs_host/linuxINCLUDE_PATHS+=-I/opt/vc/include/interface/vcos/pthreads
然后,我們需要告訴編譯器在哪里找到編譯后的庫。如果你查看Raylib發(fā)行版中的src目錄,你會發(fā)現(xiàn)構(gòu)建庫已經(jīng)創(chuàng)建了一個libraylib.a文件。這就是我們將鏈接的文件。
不同平臺的附加依賴庫有所不同。例如,在桌面Linux上,我們是使用X,但在樹莓派上則不是。
告訴編譯器在哪里找到libraylib.aLDFLAGS = -L$RAYLIB_PATH/srcLDFLAGS += -L/opt/vc/lib # 在樹莓派上需要# 在桌面Linux上LDLIBS = -lraylib -lm -lpthread -ldl -lrt -lX11# 在樹莓派上LDLIBS = -lraylib -lbrcmGLESv2 -lbrcmEGL -lpthread -lrt -lm -lbcm_host -ldl
現(xiàn)在,你可以調(diào)用編譯器:
gcc-o app app.c$CFLAGS$INCLUDE_PATHS$LDFLAGS$LDLIBS
實際上,我指定了更多的標志,比如-Wall以發(fā)出更多的警告,但上述是最低要求。
觸摸屏校準:讓觸摸屏聽話
如果你的樹莓派有觸摸屏,那你就需要校準它,讓觸摸屏知道你的手指在哪兒。這個過程有點像訓練一只小狗,基本方法是使用tslib庫來獲取原始觸摸事件,根據(jù)一些過濾器和校準設(shè)置進行轉(zhuǎn)換,然后創(chuàng)建一個新的觸摸事件流,Raylib可以從中讀取。

在我們開始之前,注意tslib操作的是/dev/input/eventX文件,就像framebuffer文件一樣,這些文件用于與觸摸屏設(shè)備交互。與framebuffer文件一樣,要與觸摸事件文件交互,我們需要必要的權(quán)限:
$ls-l /dev/input/event*crw-rw---- 1 root input 13, 64 Jan 1 22:48 /dev/input/event0
注意input組,所以我們將當前用戶添加到該組。例如,對于我:
usermod -a-Ginputavik
接下來,tslib在Rasbian軟件包倉庫中可用,但版本太舊,無法滿足我們的需求。因此,我們需要從源代碼構(gòu)建它:
sudo apt install automake libtoolgitclonegit://github.com/kergoth/tslib.gitcdtslib./autogen.sh./configuremakesudo make install
最后,我們需要校準觸摸屏,測試它并創(chuàng)建一個新的事件流,Raylib可以從中讀取。我發(fā)現(xiàn)這些命令需要稍作修改,與上面鏈接的指南中的命令有所不同。
#校準sudo \ LD_LIBRARY_PATH=/usr/local/lib \ TSLIB_FBDEVICE=/dev/fb1 \ TSLIB_TSDEVICE=/dev/input/event0 \ TSLIB_CALIBFILE=/etc/pointercal\ TSLIB_CONFFILE=/etc/ts.conf \ TSLIB_PLUGINDIR=/usr/local/lib/ts\ ts_calibrate# 測試sudo \ LD_LIBRARY_PATH=/usr/local/lib \ TSLIB_FBDEVICE=/dev/fb1 \ TSLIB_TSDEVICE=/dev/input/event0 \ TSLIB_CALIBFILE=/etc/pointercal\ TSLIB_CONFFILE=/etc/ts.conf \ TSLIB_PLUGINDIR=/usr/local/lib/ts\ ts_test# 創(chuàng)建新的事件流sudo \ LD_LIBRARY_PATH=/usr/local/lib \ TSLIB_FBDEVICE=/dev/fb1 \ TSLIB_TSDEVICE=/dev/input/event0 \ TSLIB_CALIBFILE=/etc/pointercal\ TSLIB_CONFFILE=/etc/ts.conf \ TSLIB_PLUGINDIR=/usr/local/lib/ts\ ts_uinput -v
最后一個命令將創(chuàng)建一個新的/dev/input/eventX文件,其編號比現(xiàn)有的文件更大。Raylib已經(jīng)設(shè)置為從編號最大的文件讀取,啟動Raylib應(yīng)用程序?qū)㈤_始從正確的事件流讀取。
你還可以在運行最后一個命令時包含-d參數(shù),以在后臺以守護進程模式運行。
總之,通過這些魔法工具和技巧,你可以在樹莓派上開發(fā)出輕量級的GUI應(yīng)用,而且不會拖慢它的速度。這就像給你的樹莓派裝上了一個輕便的“面甲”,讓它既能跑得快,又能看起來很酷!如果你有任何問題或者想法,歡迎在評論區(qū)留言,我們一起探討!
-
GUI
+關(guān)注
關(guān)注
3文章
671瀏覽量
40570 -
樹莓派
+關(guān)注
關(guān)注
120文章
1906瀏覽量
106697
發(fā)布評論請先 登錄
相關(guān)推薦
樹莓派可以做什么?
樹莓派驅(qū)動程序開發(fā)求解
樹莓派基礎(chǔ)功能設(shè)置
樹莓派可以做什么?
淺談香蕉派與樹莓派的不同
Ros(樹莓派)& STM32通訊的實現(xiàn)

評論