全网疯传!树莓派GUI开发竟然可以这么轻量?

描述

嘿,各位树莓派的发烧友们!今天我要和大家分享一个超酷的技巧——如何在没有桌面环境的情况下,在树莓派上开发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应用,而且不会拖慢它的速度。这就像给你的树莓派装上了一个轻便的“面甲”,让它既能跑得快,又能看起来很酷!如果你有任何问题或者想法,欢迎在评论区留言,我们一起探讨!

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分