MacLodge's Blogsince 2022
Linux / 树莓派

树莓派调试记录

(主机环境:Ubuntu 20.04.2 LTS 、树莓派:Raspberry Pi 4 Model B)

(主机环境: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

1. 获取Raspberry Kernel源码

cd ~/RaspBerryPi/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 指定分支

2. 构建 Raspberry Pi 的交叉编译器

  • 本小节是安装指定版本的交叉编译器,如不需要,可直接运行以下命令安装最新版

    • Install the 32-bit Toolchain for a 32-bit Kernel

    sudo apt install crossbuild-essential-armhf

    • Install the 64-bit Toolchain for a 64-bit Kernel

    sudo apt install crossbuild-essential-arm64

!> 文件夹名称和路径有些是自定义的,注意区分

1、首先,确保系统已更新并安装所需的依赖包:

sudo apt update
sudo apt upgrade
sudo apt install build-essential gawk git texinfo bison file wget
sudo apt 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

20210812111731846

3、在用户目录下创建一个文件夹,下载 binutils-2.31.tar.bz2、gcc-8.3.0.tar.gz、glibc-2.28.tar.bz2

cd ~
mkdir gcc_tools 
cd gcc_tools
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_tools
mkdir cross_pi_gcc

cd cross_pi_gcc/bin
# 这里的文件夹路径可以自定义,但是要把路径添加到环境变量里
export PATH=$(pwd):$PATH

6、复制 Linux 内核头文件到交叉编译安装文件夹,并安装

cd ~/RaspBerryPi/linux
KERNEL=kernel7
 
make ARCH=arm INSTALL_HDR_PATH=/home/lodge/gcc_tools/cross_pi_gcc/arm-linux-gnueabihf headers_install

7、编译安装

cd ~/gcc_tools
 
# 安装 binutils
mkdir build-binutils && cd build-binutils

../binutils-2.31/configure --prefix=/home/lodge/gcc_tools/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_tools/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_tools/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_tools/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_tools/cross_pi_gcc/arm-linux-gnueabihf/lib

arm-linux-gnueabihf-gcc -nostdlib -nostartfiles -shared -x c /dev/null -o /home/lodge/gcc_tools/cross_pi_gcc/arm-linux-gnueabihf/lib/libc.so

touch /home/lodge/gcc_tools/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

20210812143319945

9、测试

a) 编写一个简单的程序 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;
}

b) 交叉编译

arm-linux-gnueabihf-g++ test.cpp -o test

c) 把编译后的程序拷贝到树莓派中运行

20210812144947249

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

3.1 编译可直接安装的驱动模块

通过模块加载的方式替换驱动,必须编译的内核版本和树莓派当前运行的版本号完全一样, 并且内核配置也要相同,否则在安装驱动的时候,会出现格式错误的问题。

  1. 从github官网上获取相同版本的内核源码

    在树莓派上运行 uname -a 可查看内核版本号,源码里的makefile也可以查看到版本号,要保证这两个完全一样。

  2. 获取树莓派当前运行系统的内核配置

    • 先加载模块:sudo modprobe configs

    • 把 config.gz 内容复制到要编译的电脑上

    • 解压:zcat config.gz > .config

    • 把 .config 复制到内核源码目录下

    • 编译时不要再使用默认配置,即不用执行类似这种操作

      ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make bcm2709_defconfig

4. 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

然后重新编译内核。

5. 动态设备树

  1. 介绍

一个常规的 Linux 设备树,主要是由源文件.dts 和头文件.dtsi 共同编译生成 .dtb 二进制文件; 内核在初始化后会加载这个 dtb 文件,并把相关设备都注册好,然后才可以愉快地使用了。

这对于大部分硬件产品来说这没什么问题,例如一部手机在出厂以后,它的硬件几乎是不会变的。 但对于树莓派这种开发板来说,尤其是它的 40pin 扩展引脚,外围电路的变动可就是无限可能了,而内核加载 dtb 后是不能变的,所以需要一种动态覆盖配置的设备树机制,这就是树莓派的dtoverlay(设备树覆盖)。

dtoverlay 同样是由 dts 源文件编译而来,语法几乎和设备树一样,不过输出文件扩展名为 .dtbo。 树莓派提供了两种方式加载 dtbo:

  • 将编译好的 dtbo 放到 /boot/overlays 下,并由 /boot/config.txt 配置和使能;
  • 通过命令 dtoverlay <dtbo_file> 动态覆盖设备树;
  1. 操作过程(本例在树莓派上操作)
  • 安装设备树编译器
sudo apt-get install device-tree-compiler
  • 新建一个设备树文件 (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";
};
  • 编译生成 .dtbo 文件
sudo dtc -I dts test_led.dts -o test_led.dtbo
  • 将 test_led.dtbo 文件拷贝到 /boot/overlays/
sudo cp test_led.dtbo /boot/overlays/
  • 在 /boot/config.txt 最后一行添加 dtoverlay=test_led,重启 reboot…

  • 可以在 /sys/device/platform 或者 /proc/device-tree 下查看到设备 led_1 已经注册

20210812180140895

6. 更改 DSI 输出分辨率

参考:RPi 树莓派 DSI 接口研究 MIPI raspberry pi

树莓派内核版本:5.10.52

因为官方镜像里的 dsi 驱动,默认输出分辨率为 800*480,如果要更改的话,就需要使用开源的树莓派 KMS 驱动。

修改 drivers/input/touchscreen/edt-ft5x06.c 下的分辨率参数;

然后按照这里的说明加载修改后的驱动

7. 系统自定义

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 vim /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

8. 系统镜像打包制作

1、使用 gparted 工具分区

安装

sudo apt install gparted
``

启动

``` bash
gparted /dev/sdc

这里的 sdc 是 sd 卡在 Ubuntu 系统下显示的设备节点,以设备显示的为准,可用输入命令查看

ls /dev/sd*

启动后界面如下:

333

第一个 /dev/sdc1 是 /boot 的 FAT32 分区,第二个是文件系统根分区。可以看出来,第二分区有 26.19GiB 是没有使用的,这部分不需要备份到镜像中

首先,卸载 /dev/sdc2,因为挂载的分区不能修改,鼠标右键选中 sdc2,点击卸载(Unmount)

然后,右键选中 sdc2,点击更改大小,界面如下

543

可以随意更改镜像大小,也可拖动上方的滑块进行设置,设置完成后预览可以看到有26.25GiB 没有分区,这部分就是不会打包进镜像的

1dab914d97a34a74b6e84355a9a6eb0d

最后点击工具栏上方的 ✓ ,确定执行操作;注意分区的时候,最好给 /dev/sdc2 分区后的未使用空间留有一些余地,以免执行分区的时候出现问题。我这里预留的是 41.68MiB。

2、镜像制作

查看分区情况

sudo fdisk -l /dev/sdc

66ca3d77f3d24af496b82791397ca8e9

从显示信息可以看出,扇区大小是 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 卡使用了。

9. 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

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

6、树莓派驱动之设备树覆盖_年少生而为人-CSDN博客_dtoverlay

7、树莓派镜像制作(图文)

8、树莓派替换开机树莓logo

署名-非商业性使用-相同方式共享 4.0 国际(CC BY-NC-SA 4.0)
本文可自由转载与修改,但需保留作者署名与出处链接,不得用于商业用途,且衍生作品需以相同方式共享。 转载请注明:MacLodge's Blog · 树莓派调试记录
Esc