切换语言
EN CN
论坛首页 / 树莓派开发者 / 3.5秒极速启动!树莓派 Zero 2 W 的极端启动优化技巧! / 应用软件 / 3.5秒极速启动!树莓派 Zero 2 W 的极端启动优化技巧!

3.5秒极速启动!树莓派 Zero 2 W 的极端启动优化技巧!

admin发布于 2025-01-24
5992 浏览

不久前,我们构建了一个名为 SolarCamPi 的项目,这是一个离网太阳能供电的WiFi摄像头。

SolarCamPi:https://kittenlabs.de/solarcampi/

在这个项目中,我们使用了Raspberry Pi Zero 2 W,它在启动时进入Linux系统,拍摄一张照片,建立WiFi连接,然后再次关闭(以节省电量)。这个过程每几分钟重复一次,以便持续向云服务发送最新图像

Raspberry Pi Zero 每开机一秒钟都会消耗宝贵的电力,这对于太阳能供电的设备来说是一种稀缺资源(至少在西欧的冬天是这样......)。用户空间的应用程序(服务器连接、图片上传等)已经尽可能进行了优化。电子设备的设置也特意设计为在休眠时尽可能少地消耗电力。

有两种方法可以进一步降低总能耗:

1.降低功耗/电流

2.减少运行时间

然而,在某些情况下,需要在两者之间找到平衡。例如,仅仅为了节省一些电流而禁用CPU涡轮加速并不是一个好主意,因为由此产生的额外时间将消耗比快速完成任务并关闭更多的能量。我们想要的是电流与时间图下的面积尽可能小。

硬件设置

在优化嵌入式启动过程时,能够在做出更改后迅速看到效果至关重要。在工作中频繁更换SD卡、摆弄读卡器和电源供应器既分散注意力又令人厌烦。

为了避免这种情况,存在一些有用的工具:

1.Nordic Power Profiler 套件 II

https://www.nordicsemi.com/Products/Development-hardware/Power-Profiler-Kit-2?lang=zh-CN

2.USB-SD-Mux Fast

https://github.com/linux-automation/usbsdmux

3.USB-UART转换器

Power Profiler 套件

Power Profiler Kit II(现在称为PPK)可以为被测设备(DUT)供电,并随时间准确测量其功耗。您可以启用/禁用DUT,查看任何时间点的功耗,以及查看8个数字输入的状态!我们将其中一个数字输入连接到Raspberry Pi的GPIO引脚上。

这样,“我们的应用程序”的第一个动作(即终点线)将是切换GPIO引脚。然后,我们只需测量从开机到GPIO切换之间的时间。

USB-SD-多路复用器

USB-SD-Mux是硬件黑客们非常有用的工具,它是microSD卡和带有USB-C接口的DUT之间的转换器。计算机可以从DUT“窃取”microSD卡,重写其内容,然后将microSD卡插回DUT,而无需触摸设备。

这大大简化了测试更改的工作流程,避免了拔下卡片、将其插入microSD读卡器、刷新、将卡片插回DUT等繁琐步骤。它甚至可以使用板载GPIO来自动重置或供电DUT。

USB-UART转换器

几乎需要某种形式的UART接口。这些更改将在某个时刻破坏系统启动、WiFi连接等,而如果没有UART控制台,我们将无法看到发生了什么。标准的CP2102、FTDI等转换器都能很好地工作。

测量/测试设置

在干净的Debian 12(bookworm)arm64 Lite映像上,修改了/boot/firmware/cmdline.txt 文件以包含 init=/init.sh。这意味着内核将在用户空间的第一件事就是执行/init.sh 脚本(在运行systemd或任何其他内容之前)。

这样的 init.sh 脚本可能如下所示:


#!/bin/bash
gpioset 0 4=0
sleep 1
gpioset 0 4=1
sleep 1
gpioset 0 4=0
exec /sbin/init

这将切换GPIO4,然后用/sbin/init(即systemd)替换自己以恢复正常启动。

在Nordic的Power Profiler软件中,您可以看到Raspberry Pi在启动过程中的电流消耗(以5V计算)。大约12秒后,数字输入0变为低电平,表明我们的 init.sh 已执行。

在此过程中,总共使用了1.90库仑(库仑和安培秒是等价的)的电量。计算1.9As * 5.0V得出此启动过程的能耗为9.5Ws。

作为参考:一节AA碱性电池可以提供约13500Ws的能量。

降低电流

首先,我们来做简单的事情,尽可能降低工作电流。

禁用HDMI

我们可以完全禁用HDMI编码器。由于我们需要GPU来编码摄像头数据,因此无法禁用GPU。如果您的应用程序不需要摄像头/GPU支持,请尝试完全禁用GPU。

这可以将电流消耗从136.7mA降低到122.6mA(超过10%!)。

相关的config.txt参数:

# disable HDMI (saves power)
dtoverlay=vc4-kms-v3d,nohdmi
max_framebuffers=1
disable_fw_kms_setup=1
disable_overscan=1
# disable composite video output
enable_tvout=0

禁用活动LED

仅通过禁用活动LED,我们就可以节省2mA(从122.6mA降低到120.6mA)。


dtparam=act_led_trigger=none
dtparam=act_led_activelow=on

禁用摄像头LED

对摄像头LED重复相同的操作(如果存在)。这还将减少LED反射回图像的机会。

disable_camera_led=1

涡轮调整

如前所述,在浪费时间的同时节省电流可能并不理想。

在当前的更改下,Pi可以在使用1.62As的情况下启动。

force_turbo=0
initial_turbo=10
arm_boost=0

在没有强制涡轮模式的情况下,使用了1.58As:

出于某种未知原因,禁用涡轮/增强模式也会反转GPIO4的默认状态(因此我在 init.sh 中切换了极性)。

减少时间

电流降低了约13%,这很有帮助,但仍有很长的路要走。

Pi在出现Linux控制台上的第一行输出之前需要8秒钟(同时消耗约1As)。

幸运的是,有多种方法可以获取有关这8秒钟的更多信息。

调试启动

在Raspberry Pi家族的启动过程中,GPU首先初始化。

它与SD卡通信并查找bootcode.bin文件(Pi 4及更新版本使用EEPROM代替)。

我们可以修改此bootcode.bin以启用详细的UART日志记录:

sed -i -e "s/BOOT_UART=0/BOOT_UART=1/" /boot/firmware/bootcode.bin

UART日志记录:https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#bootcode-bin-uart-enable

首先备份原始的bootcode.bin,因为此过程可能是破坏性的。

使用启用的BOOT_UART重启后,我们会获得大量有用的信息:


Raspberry Pi Bootcode
Found SD card, config.txt = 1, start.elf = 1, recovery.elf = 0, timeout = 0
Read File: config.txt, 1322 (bytes)
Raspberry Pi Bootcode
Read File: config.txt, 1322
Read File: start.elf, 2981376 (bytes)
Read File: fixup.dat, 7303 (bytes)
MESS:00:00:01.295242:0: brfs: File read: /mfs/sd/config.txt
MESS:00:00:01.300131:0: brfs: File read: 1322 bytes
MESS:00:00:01.335680:0: HDMI0:EDID error reading EDID block 0 attempt 0
[..]
MESS:00:00:01.392537:0: HDMI0:EDID error reading EDID block 0 attempt 9
MESS:00:00:01.398632:0: HDMI0:EDID giving up on reading EDID block 0
MESS:00:00:01.406335:0: brfs: File read: /mfs/sd/config.txt
MESS:00:00:01.411272:0: gpioman: gpioman_get_pin_num: pin LEDS_PWR_OK not defined
MESS:00:00:01.918176:0: gpioman: gpioman_get_pin_num: pin LEDS_PWR_OK not defined
MESS:00:00:01.923999:0: *** Restart logging
MESS:00:00:01.927872:0: brfs: File read: 1322 bytes
MESS:00:00:01.933328:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 0
[..]
MESS:00:00:01.995436:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 9
MESS:00:00:02.002052:0: hdmi: HDMI0:EDID giving up on reading EDID block 0
MESS:00:00:02.007955:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 0
[..]
MESS:00:00:02.070610:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 9
MESS:00:00:02.077225:0: hdmi: HDMI0:EDID giving up on reading EDID block 0
MESS:00:00:02.082840:0: hdmi: HDMI:hdmi_get_state is deprecated, use hdmi_get_display_state instead
MESS:00:00:02.091586:0: HDMI0: hdmi_pixel_encoding: 162000000
MESS:00:00:02.799203:0: brfs: File read: /mfs/sd/initramfs8
MESS:00:00:02.803082:0: Loaded 'initramfs8' to 0x0 size 0xb0898e
MESS:00:00:02.821799:0: initramfs loaded to 0x1b4e7000 (size 0xb0898e)
MESS:00:00:02.836318:0: dtb_file 'bcm2710-rpi-zero-2-w.dtb'
MESS:00:00:02.840194:0: brfs: File read: 11569550 bytes
MESS:00:00:02.849171:0: brfs: File read: /mfs/sd/bcm2710-rpi-zero-2-w.dtb
MESS:00:00:02.854262:0: Loaded 'bcm2710-rpi-zero-2-w.dtb' to 0x100 size 0x8258
MESS:00:00:02.876038:0: brfs: File read: 33368 bytes
MESS:00:00:02.892755:0: brfs: File read: /mfs/sd/overlays/overlay_map.dtb
MESS:00:00:02.927145:0: brfs: File read: 5255 bytes
MESS:00:00:02.933541:0: brfs: File read: /mfs/sd/config.txt
MESS:00:00:02.937568:0: dtparam: audio=on
MESS:00:00:02.948005:0: brfs: File read: 1322 bytes
MESS:00:00:02.971952:0: brfs: File read: /mfs/sd/overlays/vc4-kms-v3d.dtbo
MESS:00:00:03.023016:0: Loaded overlay 'vc4-kms-v3d'
MESS:00:00:03.026278:0: dtparam: nohdmi=true
MESS:00:00:03.031105:0: dtparam: act_led_trigger=none
MESS:00:00:03.048180:0: dtparam: act_led_activelow=on
MESS:00:00:03.149316:0: brfs: File read: 2760 bytes
MESS:00:00:03.154502:0: brfs: File read: /mfs/sd/cmdline.txt
MESS:00:00:03.158504:0: Read command line from file 'cmdline.txt':
MESS:00:00:03.164369:0: 'console=serial0,115200 console=tty1 root=PARTUUID=26bbce6b-02 rootfstype=ext4 fsck.repair=yes rootwait cfg80211.ieee80211_regdom=DE init=/init.sh'
MESS:00:00:03.195926:0: gpioman: gpioman_get_pin_num: pin EMMC_ENABLE not defined
MESS:00:00:03.269361:0: brfs: File read: 146 bytes
MESS:00:00:03.812401:0: brfs: File read: /mfs/sd/kernel8.img
MESS:00:00:03.816343:0: Loaded 'kernel8.img' to 0x200000 size 0x8d8bd7
MESS:00:00:05.364579:0: Device tree loaded to 0x1b4de900 (size 0x8605)
MESS:00:00:05.370571:0: uart: Set PL011 baud rate to 103448.300000 Hz
MESS:00:00:05.377080:0: uart: Baud rate change done...
MESS:00:00:05.380495:0: uart: Baud rate[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034]

禁用HDMI探测

在启动过程中,引导加载程序会花费大量时间尝试自动检测可能连接的HDMI显示器的视频参数。然而,由于我们没有HDMI(而且它已经被禁用了),因此等待I2C响应以获取EDID(包括分辨率、帧率等信息)信息并不明智。

通过简单地硬编码一个EDID字符串,我们可以禁用任何探测:


# don't try to read HDMI eeprom
hdmi_blanking=2
hdmi_ignore_edid=0xa5000080
hdmi_ignore_cec_init=1
hdmi_ignore_cec=1

禁用HAT、PoE和LCD探测

启动过程还会尝试检测HAT上的I2C EEPROM,尝试检测需要风扇的PoE HAT以及其他一些内容。我们可以安全地禁用这些探测:


# all these options cause a wait for an I2C bus response, we don't need any of them, so let's disable them.
force_eeprom_read=0
disable_poe_fan=1
ignore_lcd=1
disable_touchscreen=1
disable_fw_kms_setup=1

禁用摄像头和显示器探测

探测连接的MIPI摄像头或显示器也会花费一些时间。我们知道连接了哪个摄像头(在这个案例中是HQ Camera,IMX477),因此我们可以硬编码这个信息:

# no autodetection for anything (will wait for I2C answers)
camera_auto_detect=0
display_auto_detect=0
# load HQ camera IMX477 sensor manually
dtoverlay=imx477

禁用 initramfs

上述更改将(自报告的)启动时间从5.38秒缩短到4.75秒。我们可以通过移除auto_initramfs=1来完全禁用initramfs,这取决于initramfs的大小,但可以将启动时间缩短到4.47秒。

经过测试,没有显著差异

尽管网上经常推荐将SD外设超频到100 MHz,但这在启动性能上并没有产生可测量的差异

# not recommended! data corruption risk!
dtoverlay=sdtweak,overclock_50=100

而且,在高速下操作SD外设还存在数据损坏的风险(在写入访问时),这在远程物联网设备中是非常不希望的。

内核加载

此时,加载内核是最慢的操作之一。

MESS:00:00:03.816343:0: Loaded 'kernel8.img' to 0x200000 size 0x8d8bd7
MESS:00:00:05.364579:0: Device tree loaded to 0x1b4de900 (size 0x8605)

加载9276375字节大约需要1.54秒,即大约6 MiB/s的传输速度

内核加载由GPU(使用其内部的VideoCoreIV处理器)完成,这可能是加载代码效率低下或使用了非常保守的设置。由于这是一个黑盒,我们无法直接操作寄存器或修改参数。

理论上,GPU处理器内核超频是可行的

# Overclock GPU VideoCore IV processor (not recommended!)
core_freq_min=500
core_freq=550

这确实减少了20%的内核加载时间。但是带来了未知的副作用(可靠性等。)

Buildroot/自定义内核

是时候将系统从Raspbian/Debian迁移到自定义构建的Buildroot发行版了(特别是为了获取自定义内核)。

使用 buildroot 2024.02.1,我们配置了一个非常精简的系统。

原生的 aarch64 工具链,仍然使用完整的 glibc 和 Raspberry Pi 用户区工具(如相机实用程序)。

内核已配置:

  • 无声音支持
  • 无大多数块设备和文件系统驱动(除了SD/MMC和ext4)
  • 无RAID支持
  • 无USB支持
  • 无HID支持
  • 无DVB支持
  • 无视频和帧缓冲支持(HDMI已被禁用)
  • 无高级网络功能(隧道、桥接、防火墙等)
  • 未压缩(不使用Gzip)
  • 模块未压缩(不使用Gzip)

测试表明,内核和模块均未压缩可以带来正的能量结果(即使GPU加载内核时花费了更多时间)。Gzip解压缩需要消耗大量能量(并且实际上涉及另一个重定位步骤)。

一个名为KASLR的安全功能也被禁用。

KASLR将内核在内存中的加载地址随机化,使得编写漏洞利用代码更加困难(因为内核的内存位置是未知的)。这要求内核在被GPU加载后重新定位。

在我们的用例中,网络攻击面非常有限,所以可以禁用KASLR(反正所有应用软件都以root身份运行)。投机性执行漏洞(如Spectre)的缓解也被禁用。

最终的内核大小为8.5兆字节(未压缩),4.1兆字节压缩为Gzip(这里没有使用,只是为了比较)。最初的Raspbian内核是25 MiB(未压缩),8.9 MiB压缩为Gzip

最终结果

现在,我们可以在不到3.5秒的时间内启动到Linux用户空间程序!

Linux内核占用时间约为400毫秒(从引脚0到引脚1的差值)。

总能耗为 0.364 As * 5.0 V = 1.82 Ws,与原始Debian相比,能耗降低了5倍(原始Debian直到用户空间需要9.5 Ws)。

降低输入电压

在发表这篇博文后,Graham Sutherland / Polynomial 指出,Pi Zero 中的调节器在5.0V输入下效率不是很高。

Graham Sutherland / Polynomial 指出:https://chaos.social/@gsuberland/113064738117907263

这可能不适用于所有情况,但在我们的测试场景和成品中,我们可以将输入电压降至4.0V。

在5.0V下运行:

好好注意这里正在进行的单元。通过切换到4.0V(因为电流更高),mC(毫库仑/毫安培秒)增加,但是总能量显著降低!

350.94 毫秒 * 5.0V = 1.754 瓦秒

在4.0V下运行:

390.77 毫安 * 4.0V = 1.563 瓦秒

我们可以更进一步:

在3.6V运行:

399.60 毫秒 * 3.6V = 1.438 瓦秒

我们刚刚又降低了20%的能耗,这仅仅是通过在更理想的工作点操作开关模式调节器实现的!这当然需要进一步测试稳定性/可靠性(因为这在技术上是不符合规格的),但这是一个非常令人印象深刻的结果。

链接

完整的config.txt配置文件:

https://github.com/Manawyrm/SolarCamPi-Buildroot/blob/v2/buildroot/board/raspberrypi0w/config.txt

精简的内核配置文件:

https://github.com/Manawyrm/SolarCamPi-Buildroot/blob/v2/buildroot/configs/linux_solarcampi_defconfig

完整的Buildroot树:

https://github.com/Manawyrm/SolarCamPi-Buildroot/tree/v2

原文链接: https://kittenlabs.de/blog/2024/09/01/extreme-pi-boot-optimization/

树莓派 教程 零2W 极速启动

上海晶珩

上海晶珩是树莓派官方设计合作伙伴和官方代理商,专注于推广树莓派技术在多个行业中的应用,并提供专业技术服务,以加速客户产品研发进程。上海晶珩为物联网(IoT)、工业自动化、人工智能(AI)、工业控制等领域提供全面的工业树莓派解决方案和可靠的工业计算机产品。

  • 上海市嘉罗公路1661号盛创企业家园29号楼
  • +86-159 2148 3028
  • sales@edatec.cn
提交您的留言

提示

提交即表示您同意 EDATEC 的隐私政策

请输入验证码

captcha
0.726206s