嘿,各位树莓派的发烧友们!今天我要和大家分享一个超酷的技巧——如何在没有桌面环境的情况下,在树莓派上开发GUI应用。想象一下,你的树莓派就像一个超级英雄,而我们要给它装上一个炫酷的“面甲”,让它不仅能跑,还能飞!而且,这个“面甲”不会拖慢它的速度,因为我们不装笨重的桌面环境!

framebuffer:树莓派的“魔法画布”
首先,得聊聊framebuffer这个神奇的东西。framebuffer就像是Linux系统给我们的一个魔法画布,你可以直接在上面画画。在树莓派上,HDMI接口对应的是/dev/fb0这个魔法画布。如果你接的是普通的显示器或者电视,直接往这个文件里写数据,屏幕上就会出现你画的内容,简直不要太神奇!
如果你用的是SPI接口的屏幕,那你就有了第二个魔法画布/dev/fb1。这时候,你需要在/boot/config.txt文件里设置一下分辨率,让这两个画布的大小一样。不然的话,就像给一个方形的画布硬塞进一个圆形的框里,画面会变形,看着就别扭!
framebuffer_width=480framebuffer_height=320
在/boot/config.txt中,我们创建一个自定义的HDMI模式并设置分辨率。同样,为你的屏幕选择合适的分辨率。如果你希望同时在LCD屏幕和外部显示器上以不同分辨率显示,请不要这样做。
# 当LCD连接时,有两个framebuffer:## - /dev/fb0 - 代表HDMI输出。这个输出支持硬件加速,# 因此应该是GUI的目标。# - /dev/fb1 - 代表LCD输出。## 以下设置启用了一个自定义模式(模式87),将HDMI# 输出设置为与LCD相同的分辨率。这很有用,因为应用程序# 可以以硬件加速的输出为目标,然后使用raspi2fb工具将输出镜像到LCD。## HDMI组强制DMT输出,即使在HDMI输出上也是如此。DMT是显示器的标准,# 而CEA是电视的标准。hdmi_cvt 480 320 60 1 0 0 0hdmi_mode=87hdmi_group=2
低级操作:直接往framebuffer上画画
如果你是编程大神,可以直接用C语言操作framebuffer。想象一下,你手里拿着一支魔法画笔,直接在画布上画画。不过,这个过程有点复杂,需要你用ioctl这个魔法咒语来获取画布的信息,比如宽度和高度。然后,你就可以用魔法画笔在画布上涂颜色了。不过,这个过程对新手来说有点难,别担心,后面我会介绍一个更简单的工具。

通过framebuffer显示图形的步骤如下:
1. 打开对应于你想要使用的显示设备的/dev/fbX文件。
2. 使用ioctl(这是一个允许与某些设备通信的Linux系统调用)来获取有关framebuffer的信息。具体方式是设备特定的,但framebuffer允许获取诸如宽度和高度等信息。
3. 写入字节到文件。确切的字节序列取决于framebuffer的颜色深度。
有一个关于使用framebuffer的绝佳指南,我强烈推荐阅读。在按照示例操作时,我需要记住以下几点:
要访问framebuffer,你需要以root身份运行。这并不理想,我们可以通过放松这一要求来解决,但目前请使用sudo运行代码示例。我将在下一节讨论替代方案。
该指南假设你正在显示到/dev/fb0。因为我正在使用基于SPI的屏幕,所以我实际上想要显示到/dev/fb1,所以我相应地修改了代码示例。
framebuffer的颜色深度将取决于你正在使用的屏幕,但通常HDMI输出(/dev/fb0)将运行在32bpp(每像素位数),而许多(所有?)SPI屏幕将运行在16bpp。
镜像魔法:让两个framebuffer同步显示
如果你有两个framebuffer,比如一个HDMI和一个SPI屏幕,你可以用raspi2fb这个魔法工具,把一个画布上的内容复制到另一个画布上。就像用魔法镜子把一个画布上的画面反射到另一个画布上一样。不过,别忘了先设置好分辨率,否则画面会像被挤扁的气球一样变形。
如果你尝试运行raspi2fb,你会遇到权限错误。原因是当前用户没有权限与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运行。然而,这存在安全问题,因为你不想让你的应用程序以root身份运行!幸运的是,framebuffer文件的组是video,所以你只需要将用户添加到该组。例如,对于我的用户:
usermod -a -G video avik
现在你可以无需root权限运行raspi2fb。
如上所述,如果你设置了SPI屏幕,并将系统配置为将控制台显示到fb1。当raspi2fb将HDMI屏幕镜像到fb1时,控制台和raspi2fb(以及因此在HDMI屏幕上运行的任何内容)都在更新同一个framebuffer。这会导致文本光标出现在屏幕上。
一个framebuffer感知的应用程序应该禁用文本光标,但它只能在控制台显示在与应用程序相同的framebuffer上时这样做。但是,如果应用程序显示在fb0,而控制台显示在fb1,文本光标仍然存在。解决方法是将控制台显示在fb0,与应用程序一起。
你可以通过编辑/boot/cmdline.txt来实现这一点,但我想只在运行raspi2fb时切换控制台的输出。幸运的是,你可以使用con2fbmap在系统运行时切换控制台显示。因此,你可能会执行以下操作:
con2fbmap 1 0 # 将控制台切换到fb0raspi2fb # 将fb0镜像到fb1# 在单独的窗口或SSH会话中,运行一个显示到fb0的应用程序。# 应用程序完成后,停止raspi2fb,然后:con2fbmap 1 1 # 将控制台切换回fb1
注意,con2fbmap也需要访问framebuffer,因此要么你需要以root身份运行,要么你需要是video组的成员。
Raylib:让GUI开发变得简单又有趣
说到这儿,我得隆重介绍一下Raylib这个神器。它就像一个魔法工具箱,让你不用直接操作framebuffer,也能画出漂亮的图形界面。而且,它支持硬件加速,就像给你的树莓派装上了火箭助推器,速度超快!
它具有以下优势:
Raylib支持多个平台。这意味着我可以在笔记本电脑上开发应用程序,渲染到屏幕上的窗口,然后在树莓派上部署该应用程序,无需任何更改。
没有外部依赖项。这使得在树莓派上编译变得容易,也可以理解库代码本身,因为该库非常自包含。
Raylib支持硬件加速,提供出色的性能。
Raylib包含功能,例如文本渲染支持,而这些功能我本来需要自己开发。

构建Raylib:魔法工具箱的组装
要使用Raylib,你需要先在树莓派上构建它。想象一下,你正在组装一个魔法工具箱,需要按照说明书一步一步来。构建过程中,你可能需要修改一些配置文件,比如把SUPPORT_BUSY_WAIT_LOOP这行注释掉,这样你的应用就不会占用100%的CPU,而是像一个聪明的魔法师,只在需要的时候才使用魔法。
使用Raylib开发应用
默认情况下,Raylib使用“忙等待”循环。这意味着,在你的应用程序完成渲染后,对于该帧的其余时间,Raylib会不断检查是否已经过去足够的时间以开始下一帧。这会导致高CPU使用率。
多个GitHub作者提到现在支持“睡眠”等待。在这种实现中,库设置一个计时器,并要求操作系统在经过一定时间后唤醒应用程序。
需要注意的是,这种实现不是默认的,你必须注释掉一行并重新构建库:
// 在raylib/src/config.c中注释掉以下行:#define SUPPORT_BUSY_WAIT_LOOP
(但如果你使用CMake而不是Make,你可能需要在src/config.c.in中进行此更改。)
通过此更改,我发现我的应用程序在没有进行任何密集计算的情况下,CPU使用率为1-3%。
如果你查看用于构建Raylib示例应用程序的Makefile,你会发现许多平台特定的选项。因为Raylib支持如此多的平台,Makefile相当大。
当你构建自己的应用程序时,你需要指定几个选项。因为我没有运行任何make install类型的命令来构建Raylib,所以库的必要文件位于我下载Raylib的目录中。
我从官方Makefile开始,移除了我不需要的平台支持,只剩下以下必要的选项。
首先,我们定义一些编译器标志:
-std=c99:定义C语言模式(1999年修订版的标准C)
-D_DEFAULT_SOURCE:与-std=c99一起在Linux上使用,用于timespec,这是用于与时间相关的功能。
CFLAGS = -std=c99 -D_DEFAULT_SOURCE
接下来,我们需要告诉编译器在哪里找到定义库的数据结构和函数的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发行版中的src目录,你会发现构建库已经创建了一个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
现在,你可以调用编译器:
gcc -o app app.c $CFLAGS $INCLUDE_PATHS $LDFLAGS $LDLIBS
实际上,我指定了更多的标志,比如-Wall以发出更多的警告,但上述是最低要求。
触摸屏校准:让触摸屏听话
如果你的树莓派有触摸屏,那你就需要校准它,让触摸屏知道你的手指在哪儿。这个过程有点像训练一只小狗,基本方法是使用tslib库来获取原始触摸事件,根据一些过滤器和校准设置进行转换,然后创建一个新的触摸事件流,Raylib可以从中读取。

在我们开始之前,注意tslib操作的是/dev/input/eventX文件,就像framebuffer文件一样,这些文件用于与触摸屏设备交互。与framebuffer文件一样,要与触摸事件文件交互,我们需要必要的权限:
$ ls -l /dev/input/event*crw-rw---- 1 root input 13, 64 Jan 1 22:48 /dev/input/event0
注意input组,所以我们将当前用户添加到该组。例如,对于我:
usermod -a -G input avik
接下来,tslib在Rasbian软件包仓库中可用,但版本太旧,无法满足我们的需求。因此,我们需要从源代码构建它:
sudo apt install automake libtoolgit clone git://github.com/kergoth/tslib.gitcd tslib./autogen.sh./configuremakesudo make install
最后,我们需要校准触摸屏,测试它并创建一个新的事件流,Raylib可以从中读取。我发现这些命令需要稍作修改,与上面链接的指南中的命令有所不同。
#校准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# 创建新的事件流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
最后一个命令将创建一个新的/dev/input/eventX文件,其编号比现有的文件更大。Raylib已经设置为从编号最大的文件读取,启动Raylib应用程序将开始从正确的事件流读取。
你还可以在运行最后一个命令时包含-d参数,以在后台以守护进程模式运行。
总之,通过这些魔法工具和技巧,你可以在树莓派上开发出轻量级的GUI应用,而且不会拖慢它的速度。这就像给你的树莓派装上了一个轻便的“面甲”,让它既能跑得快,又能看起来很酷!如果你有任何问题或者想法,欢迎在评论区留言,我们一起探讨!
全部0条评论
快来发表一下你的评论吧 !