资讯动态

嵌入式Linux启动画面psplash源码拆解与集成实战

发布时间:2026/10/21 13:02:26 来源:尧图企业网站定制
简介psplash源码是一套面向ARM架构嵌入式Linux开发者的开源启动画面工具用于在系统启动或更新阶段接管屏幕显示带进度条与动画效果的图形界面常见于树莓派、开发板等设备。资源包共79个文件约2.67MB以C源码与头文件为核心包含psplash.c、psplash-fb.c等实现文件以及Makefile、configure等构建脚本另有po本地化文件、png图片素材与sample示例覆盖framebuffer后端、颜色配置、图像头生成等模块。已有667人学习下载。通过研读源码读者可理解psplash与Linux内核协作获取加载进度、更新界面的机制掌握修改config.h定制颜色字体与进度条样式、交叉编译并集成到目标系统的方法同时获得调试优化与拓展动画效果的思路适合希望深入嵌入式启动流程与自定义用户界面的开发者参考。1. psplash 源码拆包嵌入式启动画面到底怎么跑起来的做过嵌入式 Linux 的同行大概都遇到过这个场景板子上电后屏幕黑着等根文件系统挂载、init 拉起一堆服务好几秒过去才看到 Qt 或 LVGL 的界面。中间这段黑屏时间客户看着像死机。psplash 就是填这段空白的那个东西——一个极轻量的启动画面程序直接往 framebuffer 上画图不依赖 X11、不依赖 Wayland甚至连图形库都不需要几百 KB 的源码包编译出来一个可执行文件加一张图片就完事。这份 psplash 源码包适合谁做 BSP 移植的、调启动时间的、给产品做开机 logo 的以及想搞清楚 framebuffer 直写这套机制的人。它不复杂但坑不少图片格式、颜色深度、旋转方向、和 init 脚本的配合每一项都能让你多烧几块板子。下面按「源码结构 → 编译配置 → 集成到启动流程 → 排错 → 进阶」的顺序拆一遍能抄的地方直接给命令和代码。2. 源码结构与 framebuffer 直写原理先看懂再动手2.1 目录里到底有什么拿到源码包先别急着 make。解压后大致是这么几类文件文件/目录作用psplash.c主程序初始化、绘制、进度条逻辑都在这psplash.h结构体与宏定义颜色、坐标、命令协议psplash-fb.cframebuffer 设备操作open/ioctl/mmap 全在这psplash-image.h由图片转换生成的 C 数组头文件psplash-poky-img.h默认示例图片数据Makefile.am/configure.acautotools 构建脚本psplash-write往 FIFO 写命令的小工具用于更新进度核心就三个psplash.c管流程psplash-fb.c管硬件psplash-image.h管画面。很多人编译报错八成是图片头文件没生成或者格式不对。2.2 framebuffer 直写是怎么工作的psplash 不走任何图形栈直接操作/dev/fb0。流程是标准的 Linux framebuffer 三步/* psplash-fb.c 中的关键逻辑简化后 */ int fd open(/dev/fb0, O_RDWR); /* 1. 打开 framebuffer 设备 */ ioctl(fd, FBIOGET_VSCREENINFO, vinfo); /* 2. 取可变屏幕信息 */ ioctl(fd, FBIOGET_FSCREENINFO, finfo); /* 取固定屏幕信息 */ /* 计算显存大小 行字节数 * 高度 */ size_t fb_size finfo.line_length * vinfo.yres; void *fb mmap(NULL, fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); /* 3. 映射显存 */映射完成后fb指针就指向了屏幕显存。往这块内存写像素屏幕立刻变化。psplash 把图片数据按行拷贝进去再在底部画一条进度条。这里有个关键参数vinfo.bits_per_pixel。它决定每个像素占几个字节——16 位是 2 字节RGB56524 位是 3 字节32 位是 4 字节ARGB8888。图片头文件里的数据必须和这个值匹配否则画出来是花屏或者颜色错乱。我一般先在板子上跑一句cat /sys/class/graphics/fb0/bits_per_pixel拿到实际值再去生成图片头文件能省掉一轮返工。2.3 图片是怎么变成 C 数组的psplash 不读 PNG/JPG它要的是原始像素数组。源码包里通常带一个转换脚本make-image-header.sh或类似名字逻辑是用工具把图片转成指定格式的原始数据再包成 C 数组。# 常见做法先把 PNG 转成 RGB565 原始数据再生成头文件 # 假设目标屏幕是 16 位色、480x272 pngtopnm logo.png | ppmquant 256 | pnmtoplainpnm logo.ppm # 用脚本转成 C 头文件脚本名以实际包内为准 ./make-image-header.sh logo.png POKY生成的psplash-logo-img.h里是一个uint8_t psplash_logo_img[]数组外加宽高宏定义。注意图片尺寸最好和屏幕分辨率一致或者至少宽度对齐。宽度不对齐会导致每行拷贝错位画面斜着撕裂——这个现象我第一次遇到时以为是屏幕坏了查了半天才发现是图片宽度不是 4 的倍数。提示转换前先确认屏幕的像素格式和字节序。RGB565 有大端小端之分板子和 PC 不一致时颜色会红蓝互换。3. 编译与配置autotools 交叉编译的完整走法3.1 交叉编译环境准备psplash 用 autotools 构建交叉编译需要指定 host 和编译器。假设工具链前缀是arm-linux-gnueabihf-标准流程# 1. 生成 configure 脚本如果源码包没带 ./autogen.sh # 2. 配置指定交叉编译器和安装路径 ./configure \ --hostarm-linux-gnueabihf \ --prefix/usr \ CCarm-linux-gnueabihf-gcc \ --disable-static \ --enable-shared # 3. 编译 make -j4 # 4. 安装到临时目录方便打包进根文件系统 make DESTDIR/path/to/rootfs install参数说明--host告诉 configure 这是交叉编译会去找arm-linux-gnueabihf-gcc--prefix/usr决定装到目标系统的/usr/binDESTDIR是暂存目录不污染宿主机。如果 configure 报找不到编译器检查工具链是否在 PATH 里或者用绝对路径写CC。3.2 关键编译选项与裁剪psplash 本身没几个开关但有几个地方值得注意--enable-shared/--disable-static嵌入式一般用动态链接省空间但如果根文件系统很小、不想带一堆 .so可以反过来编静态。图片头文件路径默认用包内自带的要换自己的 logo得改Makefile.am里的psplash-image.h依赖或者直接替换生成的头文件。进度条颜色在psplash.h里有一组宏定义比如PSPLASH_BAR_COLOR、PSPLASH_BG_COLOR改完重新编译即可。/* psplash.h 中可调的颜色定义格式随像素深度变化 */ #define PSPLASH_BAR_COLOR 0xffffffff /* 进度条颜色32位ARGB */ #define PSPLASH_BG_COLOR 0x00000000 /* 背景色 */ #define PSPLASH_BAR_WIDTH 8 /* 进度条高度像素 */改颜色时注意这些值的编码方式跟bits_per_pixel绑定。16 位色下写 32 位的值颜色会完全不对。稳妥做法是先用默认值跑通再逐项改。3.3 生成自己的启动 logo替换 logo 是最高频的需求。完整步骤# 1. 准备一张和屏幕同分辨率的 PNG去掉透明通道psplash 不处理 alpha 混合 # 2. 转成 PPM 中间格式 convert logo.png -alpha off -depth 8 logo.ppm # 需要 ImageMagick # 3. 用包内脚本生成头文件第二个参数是数组名前缀 ./make-image-header.sh logo.ppm LOGO # 4. 替换原头文件重新编译 cp psplash-logo-img.h psplash-image.h make clean make逻辑说明-alpha off是关键psplash 直接拷贝像素不做 alpha 混合带透明通道的 PNG 转出来会有黑边或杂色。-depth 8保证每通道 8 位避免高位深数据导致数组长度对不上。生成的头文件里数组长度必须等于宽 × 高 × 每像素字节数不等就是转换参数错了。注意如果屏幕是竖屏但图片是横屏别在图片里旋转用 psplash 的旋转参数或 framebuffer 的rotate属性处理否则进度条坐标会错位。4. 集成到启动流程和 init 脚本配合的三种方式4.1 最简集成init 里直接拉起psplash 编译好扔进/usr/bin在 init 脚本早期启动它#!/bin/sh # 在挂载完 /proc /sys、framebuffer 驱动就绪之后 /usr/bin/psplash # 后续启动服务期间可以更新进度 /usr/bin/psplash-write PROGRESS 30 /etc/init.d/start_network /usr/bin/psplash-write PROGRESS 60 /etc/init.d/start_app /usr/bin/psplash-write QUIT # 退出 psplash交还屏幕psplash-write通过 FIFO默认/tmp/psplash_fifo给主进程发命令。PROGRESS后跟 0-100 的整数QUIT让 psplash 退出并清屏。顺序很重要必须在 framebuffer 驱动加载之后启动否则 open/dev/fb0会失败。4.2 用 systemd 管理如果根文件系统用 systemd# /etc/systemd/system/psplash.service [Unit] DescriptionBoot Splash DefaultDependenciesno Aftersysinit.target Beforemulti-user.target [Service] Typesimple ExecStart/usr/bin/psplash ExecStop/usr/bin/psplash-write QUIT RemainAfterExityes [Install] WantedBymulti-user.target关键点是DefaultDependenciesno和Aftersysinit.target保证它在服务启动前就跑起来。ExecStop负责退出时清屏不然 psplash 的画面会一直留在屏幕上盖住后面的应用界面。4.3 进度条同步的坑很多人集成完发现进度条不动或者动一下就卡住。原因通常是psplash-write写 FIFO 时主进程没在读或者 FIFO 路径不一致。psplash 默认 FIFO 路径在编译时确定可以用--with-fifo之类的选项改具体看 configure 支持也可以直接改源码里的宏。# 确认 FIFO 是否存在、权限对不对 ls -l /tmp/psplash_fifo # 手动发一条命令测试 echo PROGRESS 50 /tmp/psplash_fifo如果手动发命令有效但脚本里无效多半是脚本执行时 FIFO 还没创建——psplash 启动到创建 FIFO 有几毫秒延迟脚本里加个sleep 0.2或者循环等待 FIFO 出现再继续。5. 避坑与排查五个真实翻车记录5.1 花屏、颜色错乱现象psplash 启动后屏幕是彩色雪花或者 logo 颜色完全不对红蓝互换。原因图片头文件的像素格式和 framebuffer 实际bits_per_pixel不匹配或者字节序反了。解决先读/sys/class/graphics/fb0/bits_per_pixel确认深度再用对应参数重新生成头文件。RGB565 大小端问题在转换脚本里加字节交换或者改psplash-fb.c里的写入逻辑。5.2 画面撕裂、斜着错位现象图片能显示但每行往右偏移整体斜着。原因图片宽度和 framebuffer 的line_length不匹配。framebuffer 每行有对齐填充图片宽度不是对齐值的整数倍时拷贝就错位。解决把 logo 宽度改成 4 或 8 的倍数或者改psplash-fb.c里的拷贝逻辑按finfo.line_length逐行处理而不是整块 memcpy。5.3 启动后黑屏psplash 没反应现象执行 psplash 后没有任何输出屏幕全黑。原因/dev/fb0不存在或权限不对或者 framebuffer 驱动还没加载。解决ls -l /dev/fb0确认设备节点存在检查内核配置里 framebuffer 驱动是否编入确认启动顺序在驱动加载之后。用strace /usr/bin/psplash能看到卡在哪个系统调用。5.4 进度条不动现象psplash 画面正常但psplash-write PROGRESS 50没效果。原因FIFO 路径不匹配或者主进程没在监听。解决确认 psplash 启动时打印的 FIFO 路径可以加调试输出和psplash-write用的路径一致。检查 FIFO 文件权限非 root 用户可能写不进去。5.5 退出后屏幕残留现象psplash-write QUIT后画面还在应用界面被盖住。原因psplash 退出时没有清屏或者应用没有重新初始化 framebuffer。解决确认 QUIT 命令被正确处理在应用启动前手动清一次 framebuffer或者让 psplash 退出时把屏幕填成背景色。有些方案是 psplash 退出后由应用接管 framebuffer这时要保证应用重新 mmap。6. 进阶把 psplash 改成你自己的启动信息面板跑通基本流程后psplash 还能干更多事。它的源码足够小改起来没有心理负担。我一般会做两件事一是把进度条换成文字状态提示二是加一个简单的动画效果。6.1 加文字渲染psplash 本身不带字体渲染但可以内嵌一个点阵字库。思路是在psplash-image.h旁边再加一个font.h存 ASCII 字符的点阵数据然后在psplash.c的绘制函数里加一个draw_text()/* 简化示例在指定位置画一个字符 */ static void draw_char(uint32_t *fb, int x, int y, char c, uint32_t color) { const uint8_t *glyph font8x16[(int)c]; /* 每个字符 16 字节点阵 */ for (int row 0; row 16; row) { for (int col 0; col 8; col) { if (glyph[row] (0x80 col)) { fb[(y row) * fb_width (x col)] color; } } } }参数说明fb是 mmap 后的显存指针x/y是字符左上角坐标color按当前像素深度编码。字库可以用现成的 8x16 点阵也可以用工具从 TTF 生成。注意坐标不要超出屏幕范围否则会写越界。6.2 进度条动画与平滑过渡默认进度条是跳变的PROGRESS 30直接跳到 30% 位置。想要平滑动画可以在主循环里加插值/* 在 psplash 主循环中逐步逼近目标进度 */ static int current_progress 0; static int target_progress 0; while (current_progress ! target_progress) { if (current_progress target_progress) current_progress; else current_progress--; draw_progress_bar(current_progress); usleep(10000); /* 10ms 一步100 步约 1 秒 */ }这样进度条会以每步 10ms 的速度平滑移动视觉上舒服很多。代价是主循环被占用期间不处理 FIFO 命令——所以插值步长别太小或者用非阻塞方式读 FIFO。6.3 验证方法改完之后怎么确认没问题我的习惯是三步在 PC 上用qemu模拟 framebuffer 跑一遍确认逻辑正确板子上先不接屏幕用cat /dev/fb0 /tmp/fb.raw抓一帧在 PC 上用工具转成 PNG 看效果接上屏幕从冷启动到应用界面全程录像逐帧检查过渡是否自然。# 抓取 framebuffer 当前内容假设 480x27216位色 cat /dev/fb0 /tmp/fb.raw # 在 PC 上转换查看需要知道分辨率和像素格式 # 用 Python PIL 或 ffmpeg 都可以从那以后我每次改 psplash 的绘制逻辑都强制走一遍「qemu 验证 → 抓帧比对 → 实机录像」再也没出现过上线后花屏的尴尬。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑