(主机环境:Ubuntu 20.04.2 LTS 、树莓派:Raspberry Pi 4 Model B)
树莓派官网:Teach, Learn, and Make with Raspberry Pi
树莓派Github:https://github.com/raspberrypi
树莓派配置:Raspberry Pi Documentation - Configuration
树莓派论坛:Raspberry Pi Forums - Index page
树莓派镜像烧录工具 (Windows):https://downloads.raspberrypi.org/imager/imager_latest.exe
树莓派镜像:Operating system images – Raspberry Pi
一、获取Raspberry Kernel源码
cd ~/raspberry/linux #选择源码下载位置(自定义)
sudo apt-get install git #安装git
#官方的github地址,获取源码
git clone --depth=1 --branch rpi-5.13.y https://github.com/raspberrypi/linux
# --depth=1 获取最近的一次commit
# --branch 指定分支
二、构建 Raspberry Pi 的交叉编译器
(文件夹名称和路径有些是自定义的,注意区分)
1、首先,确保系统已更新并安装所需的依赖包:
sudo apt update
sudo apt upgrade
sudo apt install build-essential gawk git texinfo bison file wget
sudo apt-get install git bison flex libssl-dev zip libncurses-dev make
2、我测试时,Raspbian 自带 GCC 8.3.0、Binutils 2.31.1 和 Glibc 2.28。所以使用相同的 Glibc 版本构建我们的交叉编译器,避免出现其他问题。查看版本的命令:
gcc --version
ld -v
ldd --version
3、在用户目录下创建一个文件夹,下载 binutils-2.31.tar.bz2、gcc-8.3.0.tar.gz、glibc-2.28.tar.bz2
cd ~
mkdir gcc_all
cd gcc_all
wget https://ftpmirror.gnu.org/binutils/binutils-2.31.tar.bz2
wget https://ftpmirror.gnu.org/glibc/glibc-2.28.tar.bz2
wget https://ftpmirror.gnu.org/gcc/gcc-8.3.0/gcc-8.3.0.tar.gz
4、提取所有压缩文件,下载 GCC8.3 需要的一些资源文件到源码中,删除没用的压缩包
tar xf binutils-2.31.tar.bz2
tar xf glibc-2.28.tar.bz2
tar xf gcc-8.3.0.tar.gz
rm *.tar.*
cd gcc-8.3.0
contrib/download_prerequisites
rm *.tar.*
5、创建交叉编译的安装文件夹,并添加到 PATH 路径中
cd ~/gcc_all
mkdir cross-pi-gcc
# 这里的文件夹路径可以自定义,但是要把路径添加到环境变量里
export PATH=/home/lodge/gcc_all/cross-pi-gcc/bin:$PATH
6、复制 Linux 内核头文件到交叉编译安装文件夹,并安装
cd ~/raspberry/linux
KERNEL=kernel7
make ARCH=arm INSTALL_HDR_PATH=/home/lodge/gcc_all/cross-pi-gcc/arm-linux-gnueabihf headers_install
7、编译安装
cd ~/gcc_all
# 安装 binutils
mkdir build-binutils && cd build-binutils
../binutils-2.31/configure --prefix=/home/lodge/gcc_all/cross-pi-gcc --target=arm-linux-gnueabihf --with-arch=armv6 --with-fpu=vfp --with-float=hard --disable-multilib
make -j8
make install
# 安装 GCC
mkdir build-gcc && cd build-gcc
../gcc-8.3.0/configure --prefix=/home/lodge/gcc_all/cross-pi-gcc --target=arm-linux-gnueabihf --enable-languages=c,c++,fortran --with-arch=armv6 --with-fpu=vfp --with-float=hard --disable-multilib
make -j8 all-gcc
make install-gcc
# 安装 glibc
mkdir build-glibc && cd build-glibc
../glibc-2.28/configure --prefix=/home/lodge/gcc_all/cross-pi-gcc/arm-linux-gnueabihf --build=$MACHTYPE --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf --with-arch=armv6 --with-fpu=vfp --with-float=hard --with-headers=/home/lodge/gcc_all/cross-pi-gcc/arm-linux-gnueabihf/include --disable-multilib libc_cv_forced_unwind=yes
make install-bootstrap-headers=yes install-headers
make -j8 csu/subdir_lib
install csu/crt1.o csu/crti.o csu/crtn.o /home/lodge/gcc_all/cross-pi-gcc/arm-linux-gnueabihf/lib
arm-linux-gnueabihf-gcc -nostdlib -nostartfiles -shared -x c /dev/null -o /home/lodge/gcc_all/cross-pi-gcc/arm-linux-gnueabihf/lib/libc.so
touch /home/lodge/gcc_all/cross-pi-gcc/arm-linux-gnueabihf/include/gnu/stubs.h
# 退回到gcc,完成安装libgcc
cd ../build-gcc
make -j8 all-target-libgcc
make install-target-libgcc
# 退回到glibc,完成安装Glibc
cd ../build-glibc
make -j8
make install
# 退回到gcc,完成安装gcc
cd ../build-gcc
make -j8
make install
cd ..
8、编译安装完成,可以在系统中查看
arm-linux-gnueabihf-gcc --version
9、测试
9.1、编写一个简单的程序 test.cpp
#include <iostream>
using namespace std;
int main(void)
{
cout << "Hello Raspberry Pi" << endl;
cout << "test arm-linux-gnueabihf cross-compile-tools" << endl;
return 0;
}
9.2、交叉编译
arm-linux-gnueabihf-g++ test.cpp -o test
9.3、把编译后的程序拷贝到树莓派中运行
三、编译内核源码
(尽量不要用 root 用户来执行)
1、安装所需依赖包
sudo apt-get install bc bison flex libssl-dev make libc6-dev libncurses5-dev
安装 32 位工具链
sudo apt install crossbuild-essential-armhf
安装 64 位工具链
sudo apt install crossbuild-essential-arm64
2、构建配置
cd linux
make kernelversion #查看源码内核版本
# Raspberry Pi 2, Pi 3, Pi 3+
KERNEL=kernel7
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig
# Raspberry Pi 4 32位
KERNEL=kernel7l
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2711_defconfig
# Raspberry Pi 4 64位
KERNEL=kernel8
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig
3、编译内核、模块、设备树
# 32位
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs
# 64位
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image modules dtbs
4、安装内核、模块、设备树 到 SD 卡
将 SD 卡从树莓派中取出,连接到 Ubuntu 机器上,SD 卡中的两个分区,boot 和 rootfs 会自动挂载。
# 挂载路径要以实际为准
# 安装模块 32位
sudo env PATH=$PATH make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=/media/lodge/rootfs modules_install
# 安装模块 64位
sudo env PATH=$PATH make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_MOD_PATH=/media/lodge/rootfs modules_install
# 安装内核镜像和设备树 32位
sudo cp /media/lodge/boot/$KERNEL.img /media/lodge/boot/$KERNEL-backup.img
sudo cp arch/arm/boot/zImage /media/lodge/boot/$KERNEL.img
sudo cp arch/arm/boot/dts/*.dtb /media/lodge/boot
sudo cp arch/arm/boot/dts/overlays/*.dtb* /media/lodge/boot/overlays/
sudo cp arch/arm/boot/dts/overlays/README /media/lodge/boot/overlays/
# 安装内核镜像和设备树 64位
sudo cp /media/lodge/boot/$KERNEL.img /media/lodge/boot/$KERNEL-backup.img
sudo cp arch/arm64/boot/zImage /media/lodge/boot/$KERNEL.img
sudo cp arch/arm64/boot/dts/*.dtb /media/lodge/boot
sudo cp arch/arm64/boot/dts/overlays/*.dtb* /media/lodge/boot/overlays/
sudo cp arch/arm64/boot/dts/overlays/README /media/lodge/boot/overlays/
为什么这里还要安装模块呢?
硬件驱动有许许多多,如果把所有的驱动都在编译内核的时候就指定编译到内核里面那么内核会非常大。所以编译内核的时候只编译一部分非常常用的驱动,这些驱动在Linux内核配置的时候指定为 “*”。还有一些不怎么常用的与平台相关的驱动被指定为 “M” 编译,它们可以放到文件系统根分区下,等真正需要的时候再加载。
对于这种驱动模块,可以用 make modules 编译。不过直接 make,不加任何参数,就是make all,包含 make modules。虽然编译了,但并没有被安装到文件系统根分区下,内核启动的时候还是找不到这些模块。make modules_install 就是起这个作用的,把模块放到lib/modules 目录中一个和内核名一样的目录下,并且运行 depmod 生成模块依赖关系文件,系统启动时加载模块就是从dep里面读取信息加载模块。由于是交叉编译,不是要将模块安装到本机,所以需要使用INSTALL_MOD_PATH参数,指定安装的根目录是 SD 卡的 rootfs 分区。
5、取下 SD 卡,插入树莓派中开机,进入系统后可以查看内核版本,看是否更新成功
uname -a
四、Pi4更新内核可能出现的问题
1、开机无法启动,卡死在 bcm2835-dma unable to set DMA mask
参考:https://github.com/raspberrypi/linux/issues/3057
https://github.com/raspberrypi/linux/commit/278f37a1cbb70636a6eb000c1c9fd56bf7097f6c
原因:bcm2835 DMA只能寻址 1GB 的问题
解决:在 arch/arm/mach-bcm/board_bcm2835.c 文件中 DT_MACHINE_START 处添加以下片段
DT_MACHINE_START(BCM2835, "BCM2835")
+ #if defined(CONFIG_ZONE_DMA) && defined(CONFIG_ARM_LPAE)
+ .dma_zone_size = SZ_1G,
+ #endif
.map_io = bcm2835_map_io,
.init_machine = bcm2835_init,
.dt_compat = bcm2835_compat,
.smp = smp_ops(bcm2836_smp_ops),
MACHINE_END
然后重新编译内核。
五、动态设备树
1、介绍
一个常规的 Linux 设备树,主要是由源文件.dts 和头文件.dtsi 共同编译生成 .dtb 二进制文件,内核在初始化后会加载这个 dtb 文件,并把相关设备都注册好,然后才可以愉快地使用了。
这对于大部分硬件产品来说这没什么问题,例如一部手机在出厂以后,它的硬件几乎是不会变的。但对于树莓派这种开发板来说,尤其是它的 40pin 扩展引脚,外围电路的变动可就是无限可能了,而内核加载 dtb 后是不能变的,所以需要一种动态覆盖配置的设备树机制,这就是树莓派的dtoverlay(设备树覆盖)。
dtoverlay 同样是由 dts 源文件编译而来,语法几乎和设备树一样,不过输出文件扩展名为 .dtbo。树莓派提供了两种方式加载 dtbo:
a)将编译好的 dtbo 放到 /boot/overlays 下,并由 /boot/config.txt 配置和使能;
b)通过命令 dtoverlay <dtbo_file> 动态覆盖设备树;
2、操作过程(本例在树莓派上操作)
a)安装设备树编译器
sudo apt-get install device-tree-compiler
b)新建一个设备树文件 (test_led.dts),并定义一个 led_type 的 LED 设备,并将其命名为 led_1 。
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target-path = "/";
__overlay__ {
led_1 {
compatible = "led_type";
};
};
};
};
fragment 和 __overlay__ 非常重要!!如果不这么写会导致动态加载失败,其实以上的代码转化为标准的设备树语法为:
/led_1 {
compatible = "led_type";
};
c)编译生成 .dtbo 文件
sudo dtc -I dts test_led.dts -o test_led.dtbo
d)将 test_led.dtbo 文件拷贝到 /boot/overlays/
sudo cp test_led.dtbo /boot/overlays/
e)在 /boot/config.txt 最后一行添加 dtoverlay=test_led,重启 reboot…
f)可以在 /sys/device/platform 或者 /proc/device-tree 下查看到设备 led_1 已经注册
六、更改 DSI 输出分辨率
参考:RPi 树莓派 DSI 接口研究 MIPI raspberry pi
树莓派内核版本:5.10.52
因为官方镜像里的 dsi 驱动,默认输出分辨率为 800*480,如果要更改的话,就需要使用开源的树莓派 KMS 驱动。
在 /boot/config.txt 文件中加入下面三行:
dtoverlay=rpi-ft5406 # 树莓派原生触摸文件;
dtoverlay=vc4-kms-v3d # 加载kms驱动;
dtoverlay=vc4-kms-dsi-7inch
hdmi_force_hotplug=1 # Pi3上要把这个开启;
显示:修改 drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c 下的 rpi_touchscreen_modes 结构体参数;
触摸:修改 arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts 下的 touchscreen 节点参数;
编译烧录到 SD 卡,重启~
七、系统自定义
1、关闭屏保
参考:树莓派关闭屏保
在 /etc/lightdm/lightdm.conf 文件中添加一行
xserver-command=X -s 0 dpms
2、屏蔽彩虹屏
树莓派开机会自检GPU,在屏幕中央会出现彩虹色的方块。在 /boot/config.txt 中添加一行
disable_splash=1
意思就是取消启动界面显示。
3、屏蔽开机滚动 log
修改文件: /boot/cmdline.txt
将 console=tty1 修改成 console=tty3(是将开机的滚动代码指向 tty3,从而开机时的 tty1 没有代码),并在最后添加一句 loglevel=3。
4、开启 SSH 和 串口登录
在 /boot 分区下创建一个 ssh 空文件;
在 /boot/config.txt 文件中添加
dtoverlay=pi3-miniuart-bt
替换 cmdline.txt 内容为
dwc_otg.lpm_enable=0 console=tty1 console=serial0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
注:原内容要做好备份
5、设置开机界面 (有点问题,偶尔开机不显示)
首先安装 fbi
sudo apt-get install fbi
准备一张不超过屏幕分辨率的图片,重命名为 splash.png,存放在 /home/pi 目录下。
手动写一个服务
sudo nano /etc/systemd/system/splashscreen.service
内容如下
[Unit]
Description=Splash Screen
DefaultDependencies=no
After=local-fs.target
[Service]
ExecStart=/usr/bin/fbi -d /dev/fb0 --noverbose -a /home/pi/splash.png
StandardInput=tty
StandardOutput=tty
[Install]
WantedBy=sysinit.target
参数说明
-d /dev/fb0 告诉fbi命令输出的设备
/dev/fb0 是系统中 framebuffer 设备
--noverbose 禁用 fbi 命令默认会在屏幕底部显示的状态条
-a 自动缩放图像适应显示设备
要显示的图片指向 /home/pi/splash.png
设置服务开机启动并重启看效果
sudo systemctl enable splashscreen
sudo reboot
八、系统镜像打包制作
1、使用 gparted 工具分区
安装
sudo apt install gparted
启动
gparted /dev/sdc
这里的 sdc 是 sd 卡在 Ubuntu 系统下显示的设备节点,以设备显示的为准,可用输入命令查看
ls /dev/sd*
启动后界面如下:
-
第一个 /dev/sdc1 是 /boot 的 FAT32 分区,第二个是文件系统根分区。可以看出来,第二分区有 26.19GiB 是没有使用的,这部分不需要备份到镜像中
-
首先,卸载 /dev/sdc2,因为挂载的分区不能修改,鼠标右键选中 sdc2,点击卸载(Unmount)
-
然后,右键选中 sdc2,点击更改大小,界面如下
-
可以随意更改镜像大小,也可拖动上方的滑块进行设置,设置完成后预览可以看到有26.25GiB 没有分区,这部分就是不会打包进镜像的
-
最后点击工具栏上方的 ✓ ,确定执行操作;注意分区的时候,最好给 /dev/sdc2 分区后的未使用空间留有一些余地,以免执行分区的时候出现问题。我这里预留的是 41.68MiB。
2、镜像制作
查看分区情况
sudo fdisk -l /dev/sdc
从显示信息可以看出,扇区大小是 512,扇区末尾是 7290879
运行 dd 命令打包镜像, 因为扇区是从 0 计数,所以 count 要加 1,of 参数后面是镜像保存路径,可以自定义
sudo dd if=/dev/sdc of=/home/lodge/raspberrypi/backup-img/myImage.img bs=512 count=7290880
等待执行结束,就得到我们想要的镜像包了,然后就可以烧录到 sd 卡使用了。
九、64位系统兼容32位软件
开启armhf架构支持
sudo dpkg --add-architecture armhf
更新源
sudo apt-get update
安装32位兼容库
sudo apt-get install libc6:armhf
Useful reference:
1、Building GCC as a cross compiler for Raspberry Pi | Solarian Programmer
2、详细介绍git clone --depth=1的用法_白马金羁侠少年的博客-CSDN博客
3、Raspberry Pi Documentation - The Linux kernel
4、树莓派(Raspberry Pi 4 Model B)编译64位内核Kernel_杨-CSDN博客 (64位系统制作)
5、https://www.raspberrypi.org/forums/viewtopic.php1900946&hilit=icn6211#p1899593