1. 项目概述与核心价值
给嵌入式设备换上一个专属的开机画面,这事儿听起来像是锦上添花,但对于产品化开发来说,却是塑造品牌形象、提升用户体验非常关键的一步。想象一下,用户按下电源键,映入眼帘的不再是千篇一律的企鹅或者枯燥的命令行滚动,而是你精心设计的Logo和进度条,那种专业感和归属感立刻就上来了。这次我拿瑞萨的RZ/G2L系列HD-G2L-IOT开发板开刀,目标就是把那个默认的、略显“朴素”的psplash开机画面,替换成我们自己的品牌标识。
psplash是Yocto项目里一个非常经典的开机动画程序,它轻量、高效,专门用于在Linux内核启动后、系统服务完全起来之前,显示一个图形化的启动界面。它通常包含一个静态的背景图和一个动态的进度条。我们的任务,就是深入这个流程,从源码开始,完成交叉编译,最终让开发板在启动时展示我们自定义的图片。整个过程会涉及到开源工具链的使用、交叉编译环境的配置、以及根文件系统的替换,算是一次比较完整的嵌入式Linux用户空间开发实战。
2. 开发环境搭建与源码获取
工欲善其事,必先利其器。自定义psplash的第一步,就是准备好它的“原料”——源代码,并搭建好可以加工这些原料的“车间”。
2.1 获取psplash源码
psplash的官方源码仓库托管在Yocto Project的Git服务器上。获取它非常简单,只需要一条git clone命令。这里有一个细节需要注意:虽然我们可以直接克隆主分支,但为了更好的兼容性,特别是与我们使用的Yocto版本(如文中的3.1.14)匹配,有时需要切换到特定的标签(tag)或提交(commit)。不过对于基础的自定义图片功能,主分支通常就是可用的。
git clone git://git.yoctoproject.org/psplash执行这条命令后,当前目录下就会生成一个名为psplash的文件夹,里面包含了程序所有的源代码、脚本和资源文件。
2.2 理解源码目录结构
进入psplash目录,我们先花几分钟看看里面的关键文件,这对后续操作至关重要:
psplash.c:主程序文件,包含了图形绘制、进度更新、事件处理的核心逻辑。psplash.h:头文件,定义了各种数据结构和函数声明。psplash-write.c:一个辅助工具的程序文件,用于在系统运行时(如从另一个终端)向psplash进程发送命令,例如更新进度条或显示文本信息。这个工具在我们调试时非常有用。psplash-default.h:这是关键文件之一。它默认包含了使用C语言数组定义的、内置的启动图片数据。我们要替换的,主要就是这里面的图片数据。make-image-header.sh:这是另一个关键脚本。它的作用就是将一张普通的PNG格式图片,转换成一个C语言头文件(.h文件),这个头文件里就以数组的形式存储了图片的每一个像素数据。autogen.sh:通常是一个用于生成configure脚本的辅助脚本。如果源码包里没有configure文件,我们就需要用它来生成。
搞清楚这些文件的作用,后面修改的时候就不会迷路了。
3. 核心原理:图片如何被嵌入程序
在开始动手之前,我们有必要搞清楚一个核心问题:一张普通的图片,是怎么变成程序的一部分,并在屏幕上显示出来的?这涉及到两个关键步骤:数据转换和编译链接。
3.1 图片数据的转换与嵌入
计算机屏幕显示图形,本质上是在控制每个像素点的颜色。一张PNG图片是经过压缩的二进制数据,程序不能直接理解。make-image-header.sh脚本的工作,就是充当一个“翻译官”。
- 读取与解码:脚本使用图像处理库(如libpng)读取你提供的
logo.png文件,将其解码为原始的像素数据,通常每个像素包含R(红)、G(绿)、B(蓝)三个通道的值。 - 数据格式化:脚本将这些像素数据,按照行优先的顺序,排列成一个巨大的、一维的数组。每个数组元素就是一个像素的颜色值。
- 生成C代码:脚本创建一个C语言头文件(例如
logo-img.h),在这个文件里,它会定义一个静态的、常量类型的数组(比如static const char),数组的名字是预定义好的(如psplash_logo_rgb)。刚才整理好的像素数据,就被逐字节地写入这个数组的初始化器中。 - 头文件包含:最后,我们需要让主程序
psplash.c知道这个新数组的存在。所以要去修改源码,让它#include我们新生成的logo-img.h文件,并且通常会注释掉或不再包含原来那个定义默认图片的psplash-default.h。
这样,在编译程序时,编译器就会把logo-img.h里的数组数据直接编译进最终的可执行文件二进制码中。程序运行时,直接从自己的内存空间里读取这些数据,渲染到帧缓冲(Framebuffer)设备上,图片就显示出来了。
注意:这种将资源“硬编码”进程序的方式,优点是加载速度快、不依赖外部文件系统,非常适合在启动初期、文件系统还未挂载时使用。缺点是一旦图片需要更换,就必须重新编译整个程序。
3.2 交叉编译的必要性
我们的开发环境(比如一台x86_64架构的Ubuntu PC)和运行环境(ARM64架构的RZ/G2L开发板)是不同的CPU架构。在PC上直接gcc编译出来的程序,是只能在PC上运行的机器码,放到ARM板子上是无法执行的。
因此,我们需要交叉编译。交叉编译器的名字通常带有前缀,比如aarch64-poky-linux-gcc。这个前缀指明了目标架构(aarch64)、工具链供应商或系统(poky-linux)。使用这个编译器,我们在x86的PC上编译,生成的就是能在ARM64板子上运行的二进制文件。后续的./configure和make步骤,都必须在这个交叉编译环境生效的前提下进行。
4. 逐步实操:定制你的开机画面
理论铺垫完毕,现在开始动手。请确保你已经在Linux开发主机上,并且能够访问到RZ/G2L开发板的根文件系统(例如通过NFS挂载或直接修改SD卡镜像)。
4.1 准备自定义图片
首先,你需要准备一张想要显示的PNG图片。这里有几个必须严格遵守的要点,否则图片可能无法显示或显示异常:
- 图片尺寸:必须与开发板屏幕的分辨率严格一致。例如,如果你的HD-G2L-IOT开发板配套的屏幕是800x480,那么你的图片就应该是800像素宽,480像素高。使用
identify命令(来自ImageMagick包)可以查看图片尺寸:identify logo.png。 - 颜色格式:psplash默认期望图片是RGB格式(24位色,即R、G、B各8位)。避免使用带Alpha通道的PNG(RGBA),除非你确认psplash版本支持并做了相应处理。简单的图标或Logo,用RGB格式即可。
- 文件命名:为了脚本兼容性,建议使用英文、数字、下划线组成的文件名,避免空格和中文。这里我们按示例使用
logo.png。
将准备好的图片,复制到psplash源码目录下。
4.2 执行图片转换脚本
进入psplash源码目录,执行转换命令。命令的格式需要特别注意:
./make-image-header.sh logo.png POKY这个命令有三个参数:
- 第一个参数(
logo.png):输入的图片文件名。 - 第二个参数(
POKY):这是一个图片标识符。它会被用来生成头文件中的数组变量名。例如,使用POKY,生成的数组名可能就是psplash_poky_rgb。这个标识符最好与你的图片内容或品牌相关,且需要大写。 - 第三个参数(示例中空缺):这个参数用于指定输出头文件的路径。示例命令没有写,意味着默认输出到当前目录,并自动命名为类似
logo-img.h的文件(具体名字取决于脚本内部逻辑,可能是psplash-poky-img.h)。为了清晰,我建议明确指定输出文件名:
这样就会生成一个名为./make-image-header.sh logo.png MYLOGO ./my-logo-img.hmy-logo-img.h的头文件。
执行成功后,用ls命令查看,应该能看到新生成的.h文件。可以用文本编辑器打开看一眼,里面应该有一个巨大的、名为psplash_mylogo_rgb的char数组。
4.3 修改源代码以使用新图片
现在,我们需要告诉psplash程序:“别用你自带的那个默认图片了,用我这个新的。”
备份原始文件:这是一个好习惯。先备份
psplash.c文件:cp psplash.c psplash.c.backup。编辑psplash.c:使用
vi或你喜欢的文本编辑器打开psplash.c。定位头文件包含位置:在文件开头的
#include区域,找到类似下面这行:#include "psplash-default.h"注释旧引用,添加新引用:将这行注释掉,并在其旁边或下方添加对新生成头文件的引用。同时,我们还需要注释掉另一个可能存在的、对默认背景图片的引用(如果它单独存在的话)。修改后的片段应该类似这样:
//#include "psplash-default.h" // 注释掉默认图片头文件 //#include "psplash-bg.h" // 如果存在,也注释掉 #include "my-logo-img.h" // 添加我们自定义图片的头文件实操心得:不同版本的psplash源码,头文件引用方式可能有细微差别。有些版本可能将背景和Logo分开定义。最稳妥的方法是,在
psplash.c里搜索psplash_default_rgb或psplash_logo_rgb这样的变量名,找到它们是在哪个头文件里声明的,然后去替换对应的#include语句。保存并退出。
4.4 生成构建配置脚本
如果源码目录下没有configure这个可执行文件,我们就需要生成它。通常开源项目使用GNU Autotools工具链(autoconf, automake, libtool)来生成可移植的构建脚本。
- 创建
autogen.sh脚本:touch autogen.sh chmod +x autogen.sh - 编辑
autogen.sh,写入以下内容:
这个脚本依次调用了几个工具:#!/bin/bash aclocal autoheader automake --add-missing autoconfaclocal收集宏定义,autoheader生成配置头文件模板,automake生成Makefile.in模板,autoconf生成configure脚本。 - 运行脚本:
执行成功后,你应该能看到./autogen.shconfigure脚本被生成出来。
4.5 配置交叉编译环境并编译
这是将我们的代码“翻译”成开发板能听懂的语言的关键一步。
激活交叉编译环境:你需要先获取并运行Yocto SDK的环境设置脚本。这个脚本的路径根据你的Yocto SDK安装位置而定。示例中给出的路径是
/opt/poky/3.1.14/environment-setup-aarch64-poky-linux。source /opt/poky/3.1.14/environment-setup-aarch64-poky-linux执行这条命令后,当前终端会话的环境变量(如
CC,CXX,PATH,CFLAGS,LDFLAGS等)都会被修改,指向交叉编译工具链。你可以用echo $CC命令验证,应该输出aarch64-poky-linux-gcc。重要提示:这个环境设置是“会话级”的。如果你关闭了这个终端,或者新开了一个终端窗口,都需要重新执行一次
source命令。运行configure脚本:告诉构建系统我们使用交叉编译器。
./configure --host=aarch64-poky-linux参数
--host=aarch64-poky-linux指明了我们编译的目标平台。configure脚本会检查依赖,并生成针对目标平台的Makefile。执行编译:
make如果一切顺利,编译过程不会有错误。最终会在当前目录下生成两个可执行文件:
psplash(主程序)和psplash-write(辅助工具)。
4.6 部署到开发板
编译出的二进制文件需要在开发板的根文件系统中替换原有的文件。
确定根文件系统位置:你的开发板根文件系统可能位于:
- 一个已经挂载在开发主机上的NFS目录。
- 一个SD卡镜像文件,你可以通过
loop设备挂载它。 - 一张已经插在读卡器里的SD卡,其分区被挂载在主机上。 假设你已将其挂载在
/mnt/rootfs。
备份并替换:
# 备份原来的文件(如果存在) sudo cp /mnt/rootfs/usr/bin/psplash /mnt/rootfs/usr/bin/psplash.backup sudo cp /mnt/rootfs/usr/bin/psplash-write /mnt/rootfs/usr/bin/psplash-write.backup # 复制新编译的文件 sudo cp psplash psplash-write /mnt/rootfs/usr/bin/ # 确保文件权限正确(通常可执行文件是755) sudo chmod 755 /mnt/rootfs/usr/bin/psplash /mnt/rootfs/usr/bin/psplash-write同步缓存:使用
sync命令确保所有数据都写入存储设备,避免缓存导致文件未实际更新。sync
4.7 重启验证
将SD卡插入开发板,或者重启NFS启动的开发板。观察启动过程,你应该能看到自定义的图片取代了原来的默认开机画面。
注意事项:psplash的启动是由系统初始化系统(如systemd或SysV init)调用的,通常在
/etc/init.d/或/lib/systemd/system/下有对应的服务脚本(如psplash.sh或psplash.service)。只要我们的可执行文件路径和名称没变,服务就会自动调用新的程序。如果开机画面没有变化,请检查第4.6步的替换是否成功,以及文件权限是否正确。
5. 进阶调整与深度定制
仅仅替换一张静态图片可能还不够。psplash还支持进度条、颜色、文本显示等元素的定制。这些通常需要通过修改源码中的常量定义来实现。
5.1 修改进度条样式
进度条的颜色、位置、大小都在psplash.c中通过常量定义。你可以搜索以下关键词进行修改:
PROGRESS_BAR_HEIGHT:进度条高度。PROGRESS_BAR_WIDTH:进度条宽度。PROGRESS_BAR_X,PROGRESS_BAR_Y:进度条左上角的坐标位置。PROGRESS_BAR_COLOR_R,PROGRESS_BAR_COLOR_G,PROGRESS_BAR_COLOR_B:进度条的颜色(RGB值,0-255范围)。
例如,想把蓝色进度条改成红色,可以找到并修改:
#define PROGRESS_BAR_COLOR_R 255 #define PROGRESS_BAR_COLOR_G 0 #define PROGRESS_BAR_COLOR_B 0修改后,需要重新执行make编译,并重新部署到开发板。
5.2 添加或修改启动文本
psplash可以在屏幕特定位置显示启动状态文本,比如“Starting system...”。这些字符串定义在psplash.c的psplash_state结构体初始化附近,或者以#define的形式存在。例如,搜索"Starting system"字符串,你可以将其替换为你想要的文字,比如"Initializing MyProduct v1.0..."。
注意:修改文本后,如果文本长度变化很大,可能需要调整文本显示的位置坐标(TEXT_X,TEXT_Y等宏定义),以避免文本显示到屏幕外或被其他元素遮挡。
5.3 使用psplash-write进行运行时调试
psplash-write工具非常有用。当psplash作为守护进程运行后,你可以在另一个终端(通过SSH或串口登录到开发板)使用它来动态控制开机画面。
- 更新进度:
psplash-write "PROGRESS 50"会将进度条设置为50%。 - 显示消息:
psplash-write "MSG Loading kernel modules..."会在屏幕上显示一条消息。 - 退出psplash:
psplash-write "QUIT"会让psplash进程退出,通常系统服务会在启动完成后执行这个命令来关闭开机画面。
你可以在自己的启动脚本中集成这些命令,实现更精细的启动状态提示。
6. 常见问题排查与解决实录
在实际操作中,你可能会遇到一些问题。下面是我在多次实践中总结的一些常见坑点及其解决方案。
6.1 图片转换失败或编译报错
问题现象:执行./make-image-header.sh时报错,提示找不到命令或图片格式错误。或者在make时编译报错,提示找不到图片数组的定义。
排查思路与解决:
- 依赖缺失:
make-image-header.sh脚本依赖imagemagick工具包中的convert和identify命令。确保你的开发主机上已安装:sudo apt-get install imagemagick # 对于Ubuntu/Debian - 图片格式问题:确保图片是标准的PNG格式,并且不是索引颜色模式(Indexed Color)。可以使用GIMP或在线转换工具将其转换为“RGB颜色”模式的PNG。
- 头文件引用错误:编译报错“undefined reference to
psplash_xxx_rgb”。这几乎肯定是psplash.c中没有正确包含你生成的头文件,或者头文件中的数组名与代码中引用的名字不匹配。- 检查:打开
psplash.c,搜索psplash_logo_rgb或psplash_default_rgb,看它被声明在哪个头文件里。确保你#include的是正确的、新生成的头文件。 - 核对数组名:打开你生成的
.h文件(如my-logo-img.h),查看数组的实际名称(如static const char psplash_mylogo_rgb[])。然后去psplash.c里搜索,确保所有使用到这个数组的地方,名字都一致。有时脚本生成的数组名会包含你传入的标识符(如POKY或MYLOGO),而源码中可能硬编码了另一个名字(如psplash_default_rgb)。如果不一致,你需要修改源码中的变量名,或者修改转换脚本的调用方式以生成正确的名字。
- 检查:打开
6.2 开机画面无变化或显示异常
问题现象:程序替换成功,但重启后开机画面还是老的,或者是黑屏、花屏、颜色错乱。
排查思路与解决:
- 文件未成功替换:这是最常见的原因。确保你替换的是开发板实际运行的根文件系统中的文件。如果你有多个SD卡或启动方式,务必确认修改的是正确的那一份。替换后务必执行
sync命令。 - 图片尺寸不匹配:这是导致黑屏或花屏的元凶。必须100%确认你的图片分辨率与开发板FB(帧缓冲)设备的分辨率完全相同。你可以通过以下命令在开发板上查询:
然后根据查询到的分辨率(如cat /sys/class/graphics/fb0/modes # 查看支持的模式 cat /sys/class/graphics/fb0/virtual_size # 查看当前虚拟分辨率U:800x480p-60)来制作图片。 - 颜色深度不匹配:开发板的FB可能运行在16位色(RGB565)或32位色(ARGB8888)模式下,而psplash默认可能处理的是24位色(RGB888)。如果出现严重的颜色错乱(比如一片洋红或绿色),可能需要修改
psplash.c中的绘图函数,使其与FB的颜色格式对齐。这涉及到更深的源码修改,通常Yocto的psplash配方会处理好这些适配。对于标准RGB888的图片,在常见的32位色FB上通常能正常显示。 - psplash服务未启动或启动失败:检查系统日志。
或者查看journalctl -u psplash # 如果使用systemd/var/log/messages、dmesg,看是否有psplash启动失败的错误信息。可能是依赖的FB设备节点(如/dev/fb0)权限问题,或者缺少某些动态库。
6.3 编译时configure或make报错
问题现象:运行./configure或make时,提示找不到编译器、库或头文件。
排查思路与解决:
- 交叉编译环境未生效:这是首要怀疑对象。确保你已经在当前终端执行了
source /path/to/environment-setup-xxx命令。用echo $CC和which aarch64-poky-linux-gcc验证交叉编译器是否已在路径中。 - 依赖库缺失:psplash编译可能依赖一些开发库,如
libpng、zlib。在Yocto SDK环境中,这些库通常已经以交叉编译的形式存在,但需要确保SDK安装完整。如果报错提示找不到png.h等,可能需要检查SDK的sysroot目录下是否有对应的头文件和库。一个变通方法是,在configure时通过CFLAGS和LDFLAGS明确指定sysroot路径(但环境设置脚本通常已做好这些)。 - 使用clean slate:如果之前编译失败过,残留的中间文件可能导致奇怪问题。尝试:
然后重新执行make distclean # 或 make clean./configure和make。
6.4 进度条不更新或与系统启动不同步
问题现象:图片显示了,但进度条不动,或者很快就跑满然后长时间等待才进入系统。
排查思路与解决:
- 进度更新机制:psplash的进度更新不是自动的,它通过读取一个FIFO管道(默认
/tmp/progress_fifo)来接收外部命令。系统启动脚本(如psplash.sh或psplash-systemd服务)会负责在启动的不同阶段向这个管道写入PROGRESS命令。 - 检查启动脚本:查看开发板根文件系统中
/etc/init.d/psplash或/lib/systemd/system/psplash.service以及相关的psplash.sh脚本。确保这些脚本在适当的时机(如启动服务前、后)执行了psplash-write “PROGRESS XX”命令。 - 手动测试:系统启动后,通过串口或SSH登录,尝试手动执行
psplash-write “PROGRESS 30”,看进度条是否会更新。如果会,说明psplash本身工作正常,问题出在启动脚本的进度控制逻辑上。你需要调整启动脚本中进度更新的时机和数值,使其与你的实际启动流程匹配。
自定义开机画面是嵌入式产品开发中一个能立刻提升产品质感的环节。从获取源码、理解原理、转换图片、交叉编译到部署调试,整个过程串联了嵌入式Linux开发的多个基础技能点。最关键的是图片规格必须严格匹配显示设备,以及交叉编译环境的正确设置。当看到自己的Logo随着设备启动而亮起时,那种成就感是对这些繁琐步骤的最好回报。如果第一次没有成功,请耐心按照排查步骤逐一检查,问题往往就出在某个细节上。