1. 驱动章节实验环境搭建¶
本章的主要目的是搭建驱动章节所需的实验环境,后续章节将不在实验环境搭建上耗费太多的版面, 而是主要讲解设备驱动的原理。本小节内容涉及的知识点较多,需要有一定的内核基础才可理解相关内容,若不理解部分知识点,跳过即可,可等到接触到相关知识再回头学习。
首先我们要明白程序最终是运行在板卡上,可以在板卡上编译或者在pc上使用交叉编译器进行编译, 需要下载内核源码或对应内核的头文件(Kernel Headers),之后编译源码、编译驱动模块以及设备树等,最终将驱动模块和设备树拷贝到开发板上运行。
另外,驱动模块是具有独立功能的程序,它可以被单独编译,但不能独立运行, 在运行时它被链接到内核作为内核的一部分在内核空间运行。因此我们写的内核模块想要在某个版本的内核上运行,那么就必须在该内核版本上编译它。
提示
本章节不需要烧录我们编译的内核,编译内核可以在板卡上或者个人PC上,编译内核是为了辅助编译驱动程序。 另外,也可以编译出内核头文件deb包,安装后同样可以编译驱动模块。如果有更新替换内核的需求请参考 镜像构建与部署的构建内核deb包 章节。
1.1. 搭建编译环境¶
编译内核可以在pc虚拟机上进行交叉编译也在板卡上进行本地编译。由于pc虚拟机性能远远强于板卡,一般建议在虚拟机上编译,如果不想用虚拟机,希望直接在板卡上开发,不介意编译时间过长也可以在板卡上编译。
在PC上,可以使用VirtualBox或者VMWare,搭建ubuntu虚拟机,建议使用Ubuntu18.04或者ubuntu20.04版本, 详细搭建请参考 LubanCat-RK系列板卡应用开发手册 系列章节, 建议使用配套的虚拟机镜像。
安装相关库和工具搭建编译环境,执行以下命令:
1 2 | sudo apt update
sudo apt install gcc make git bc libssl-dev liblz4-tool device-tree-compiler bison flex u-boot-tools gcc-aarch64-linux-gnu
|
1.2. 获取内核源码¶
确认板卡使用的内核版本,可以在板卡使用命令 uname -a 查看。
1 2 3 4 5 | #查看板卡内核版本
uname -a
#如RK3588的5.10.160内核镜像信息输出如下
Linux lubancat 5.10.160 #16 SMP Tue Jan 13 13:59:50 CST 2026 aarch64 GNU/Linux
|
获取内核源码,建议直接git克隆野火官方提供的内核源码,或者下载Lubancat-SDK源码,SDK源码中包含内核源码。
1.2.1. 直接克隆野火官方提供的内核源码¶
RK3528系列板卡用户执行以下命令获取内核源码:
1 2 | #-b参数指定lbc-develop-5.10分支
git clone -b lbc-develop-5.10 https://github.com/LubanCat/kernel.git
|
RK3562系列板卡用户执行以下命令获取内核源码:
1 2 | #-b参数指定lbc-develop-6.1分支
git clone -b lbc-develop-6.1 https://github.com/LubanCat/kernel.git
|
RK3566/rk3568系列板卡用户执行以下命令获取内核源码:
1 2 3 4 5 6 7 8 | #RK3566/rk3568系列板卡目前有两个版本的内核,具体版本可以在 板卡 使用uname -a命令查看,查看后获取对应的源码
uname -a
#-b参数指定stable-4.19-rk356x分支(4.19.232版本)
git clone -b stable-4.19-rk356x https://github.com/LubanCat/kernel.git
#-b参数指定lbc-develop-6.1分支(6.1.99版本)
git clone -b lbc-develop-6.1 https://github.com/LubanCat/kernel.git
|
RK3576系列板卡用户执行以下命令获取内核源码:
1 2 | #-b参数指定lbc-develop-6.1分支
git clone -b lbc-develop-6.1 https://github.com/LubanCat/kernel.git
|
RK3588系列板卡用户执行以下命令获取内核源码:
1 2 3 4 5 6 7 8 9 10 11 | #RK3588系列板卡目前有三个版本的内核,具体版本可以在 板卡 使用uname -a命令查看,查看后获取对应的源码
uname -a
#-b参数指定stable-5.10-rk3588分支(5.10.160版本)
git clone -b stable-5.10-rk3588 https://github.com/LubanCat/kernel.git
#-b参数指定lbc-develop-5.10分支(5.10.209版本)
git clone -b lbc-develop-5.10 https://github.com/LubanCat/kernel.git
#-b参数指定lbc-develop-6.1分支(6.1.99版本)
git clone -b lbc-develop-6.1 https://github.com/LubanCat/kernel.git
|
1.2.2. 通过SDK获取内核源码¶
访问百度网盘资源介绍页面获取SDK源码压缩包:资料网盘/8-SDK源码压缩包/LubanCat_Linux_SDK,查看目录下的“源码压缩包选择说明.txt”下载对应的SDK源码压缩包。
重要
由于源码压缩包体积很大,仅在有大量修改的稳定版本时更新,其发布日期可能和镜像发布日期无法对应。当我们在本地解压压缩包以后,只需要借助Github做少量更新,即可同步到最新版本。
以下过程以LubanCat_RK356x_Linux_SDK进行演示,对应4.19.232内核版本,实际文件名称以自己下载的SDK为准。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # 安装7z压缩工具
sudo apt install p7zip-full
# 在用户家目录创建LubanCat_SDK目录
mkdir ~/LubanCat_SDK
# 将下载的SDK源码移动到LubanCat_SDK目录下,xxx为日期
mv LubanCat_RK356x_Linux_SDK_xxx.7z ~/LubanCat_SDK
# 进入LubanCat_SDK目录
cd ~/LubanCat_SDK
# 解压SDK压缩包
7z x LubanCat_RK356x_Linux_SDK_xxx.7z
# 检出.repo目录下的git仓库
.repo/repo/repo sync -l
#查看内核源码目录
ls ~/LubanCat_SDK/kernel*
#进入内核目录
cd ~/LubanCat_SDK/kernel
#更新内核
git pull
#也可以将SDK全部更新
#将所有的源码仓库同步到最新版本
#网络不好则可忽略此步骤
cd ~/LubanCat_SDK/ && .repo/repo/repo sync -c
|
注意
如果git pull或者repo sync -c执行时提示网络连接超时,请检查并能否通畅访问github。 确认可以正常访问github的话,可以重复多次执行git pull或者repo sync -c命令来进行同步。 若无法访问github,可以忽略同步源码仓库到最新版本这一步骤。
注解
如果是Generic版本SDK,kernel-5.10目录对应5.10.209版本内核,kernel-6.1目录对应6.1.99版本内核。
1.3. 编译内核¶
1.3.1. 在PC上交叉编译内核(建议)¶
按照前面小节搭建编译环境并下载源码之后,进入内核源码根目录,根据具体的板卡设置配置文件。
RK3528系列板卡用户执行以下命令编译内核源码:
1 2 3 4 5 6 7 8 | #清除之前生成的所有文件和配置
make mrproper
# 加载lubancat_linux_rk3528_defconfig配置文件,rk3528系列均是该配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk3528_defconfig
# 编译内核,指定平台,指定交叉编译工具,使用8线程进行编译,线程可根据电脑性能自行确定
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8
|
RK3562系列板卡用户执行以下命令编译内核源码:
1 2 3 4 5 6 7 8 | #清除之前生成的所有文件和配置
make mrproper
# 加载lubancat_linux_rk3562_defconfig配置文件,rk3562系列均是该配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk3562_defconfig
# 编译内核,指定平台,指定交叉编译工具,使用8线程进行编译,线程可根据电脑性能自行确定
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8
|
RK3566/RK3568系列板卡用户执行以下命令编译内核源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 | #清除之前生成的所有文件和配置
make mrproper
#(4.19.232版本执行)
# 加载lubancat2_defconfig配置文件,rk3566/RK3568系列均是该配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat2_defconfig
#(6.1.99版本执行)
# 加载lubancat_linux_rk356x_defconfig配置文件,rk3566/RK3568系列均是该配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk356x_defconfig
# 编译内核,指定平台,指定交叉编译工具,使用8线程进行编译,线程可根据电脑性能自行确定
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8
|
RK3576系列板卡用户执行以下命令编译内核源码:
1 2 3 4 5 6 7 8 | #清除之前生成的所有文件和配置
make mrproper
# 加载lubancat_linux_rk3576_defconfig配置文件,rk3576系列均是该配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk3576_defconfig
# 编译内核,指定交叉编译工具,使用8线程进行编译,线程可根据电脑性能自行确定
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8
|
RK3588系列板卡用户执行以下命令编译内核源码:
1 2 3 4 5 6 7 8 | #清除之前生成的所有文件和配置
make mrproper
# 加载lubancat_linux_rk3588_defconfig配置文件,rk3588系列均是该配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk3588_defconfig
# 编译内核,指定交叉编译工具,使用8线程进行编译,线程可根据电脑性能自行确定
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8
|
如果没有交叉编译工具,或者编译工具版本不匹配也可以使用Lubancat-SDK源码的gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu版本的编译工具链。
执行以下命令获取并配置编译工具的环境变量:
RK356x系列板卡用户执行以下命令:
1 2 3 4 5 6 7 8 | #获取编译工具链
git clone https://github.com/LubanCat/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu.git
#导出环境变量,需要根据实际指定编译工具链的绝对路径
export PATH=/root/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin:$PATH
#查看编译工具链,如果COLLECT_LTO_WRAPPER变量为指定的路径,即配置成功
aarch64-linux-gnu-gcc -v
|
RK3528、RK3562、rk3576、3588系列板卡用户执行以下命令:
1 2 3 4 5 6 7 8 9 10 | #获取编译工具链
git clone https://github.com/LubanCat/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.git
#导出环境变量,需要根据实际指定编译工具链的绝对路径
export PATH=/root/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH
#查看编译工具链,如果COLLECT_LTO_WRAPPER变量为指定的路径,即配置成功
aarch64-none-linux-gnu-gcc -v
#注意名字不是aarch64-linux-gnu-gcc而是aarch64-none-linux-gnu-gcc,所以执行其他命令的时候要注意改为aarch64-none-linux-gnu-
|
以上配置为临时导出环境变量,打开其他终端或者重启都需要重新导出环境变量,如需永久保存需要将导出环境变量的命令写入~/.bashrc文件末尾,并执行source ~/.bashrc 重新加载配置。
SDK中也包含了交叉编译工具链,位置在SDK源码/prebuilts/gcc/linux-x86/aarch64/目录下,也可以使用以上方法导出环境变量。
1.3.2. 在板卡上本地编译内核¶
按照前面小节搭建编译环境并下载源码之后,进入内核源码根目录,根据具体的板卡设置配置文件。
RK3528系列板卡用户执行以下命令编译内核源码:
1 2 3 4 5 6 7 8 | #清除之前生成的所有文件和配置
make mrproper
# 加载lubancat_linux_rk3528_defconfig配置文件,rk3528系列均是该配置文件
make lubancat_linux_rk3528_defconfig
# 编译内核,使用4线程进行编译
make -j4
|
RK3562系列板卡用户执行以下命令编译内核源码:
1 2 3 4 5 6 7 8 | #清除之前生成的所有文件和配置
make mrproper
# 加载lubancat_linux_rk3562_defconfig配置文件,rk3562系列均是该配置文件
make lubancat_linux_rk3528_defconfig
# 编译内核,使用4线程进行编译
make -j4
|
RK3566/RK3568系列板卡用户执行以下命令编译内核源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 | #清除之前生成的所有文件和配置
make mrproper
#(4.19.232版本执行)
# 加载lubancat2_defconfig配置文件,rk356x系列均是该配置文件
make lubancat2_defconfig
#(6.1.99版本执行)
# 加载lubancat_linux_rk356x_defconfig配置文件,rk356x系列均是该配置文件
make lubancat_linux_rk356x_defconfig
# 编译内核,使用4线程进行编译
make -j4
|
RK3576系列板卡用户执行以下命令编译内核源码:
1 2 3 4 5 6 7 8 | #清除之前生成的所有文件和配置
make mrproper
# 加载lubancat_linux_rk3576_defconfig配置文件,rk3576系列均是该配置文件
make lubancat_linux_rk3576_defconfig
# 编译内核,使用8线程进行编译
make -j8
|
RK3588系列板卡用户执行以下命令编译内核源码:
1 2 3 4 5 6 7 8 | #清除之前生成的所有文件和配置
make mrproper
# 加载lubancat_linux_rk3588_defconfig配置文件,rk3588系列均是该配置文件
make lubancat_linux_rk3588_defconfig
# 编译内核,使用8线程进行编译
make -j8
|
板卡上本地编译需要的时间较长,rk3576、rk3588系列可能需要半个小时,rk3528、rk356x系列可能需要1个小时。
注意
如果是从sdk获取的内核,编译时报syntax error的错误,可以将内核目录单独复制到家目录或者其他目录再编译
内核编译成功后方可以继续学习后续内容。
1.4. 如何编译和加载内核驱动模块¶
1.4.1. 编译内核驱动模块¶
内核模块加载到内核,可以将内核模块编译成单独的模块,在内核启动后由用户手动动态加载, 也可以将模块直接编译进内核,在内核启动时就自动加载。测试一般是单独编译成内核模块,然后手动加载,方便调试,同时也节省时间。
野火提供了驱动教程的源码,可以执行以下命令获取:
1 2 3 4 5 | # 从github获取
git clone https://github.com/LubanCat/lubancat_rk_code_storage
# 或者从gitee获取
git clone https://gitee.com/LubanCat/lubancat_rk_code_storage
|
获取到源码后,源码目录下的linux_driver文件夹就是存放驱动教程的例程文件,将其配套驱动程序代码放置到 内核代码同级目录 ,原因是编译内核模块时,
驱动程序需要依赖编译好的Linux内核,驱动模块中的Makefile中指定了内核的路径,为方便使用例程,请放至同一目录结构下。
内核驱动模块对象所需的构建步骤和编译很复杂,它利用了linux内核构建系统的强大功能, 目前我们还不需要深入了解这部分知识,利用简单的Make工具就能编译出我们想要的内核驱动模块。
这里以编译helloworld内核模块为例,使用命令进入01_helloworld目录,然后使用make:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #进入helloworld例程源码目录
cd linux_driver/01_helloworld
#编译驱动模块
make
#信息输出如下
make -C ../../kernel/ M=/home/guest/linux_driver/01_helloworld modules
make[1]: Entering directory '/home/guest/kernel'
CC [M] /home/guest/linux_driver/01_helloworld/helloworld.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/guest/linux_driver/01_helloworld/helloworld.mod.o
LD [M] /home/guest/linux_driver/01_helloworld/helloworld.ko
make[1]: Leaving directory '/home/guest/kernel'
|
生成helloworld.ko文件,就是本例程编译生成的内核驱动模块。
其中Makefile如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #指定内核路径,可以是相对路径或绝对路径
KERNEL_DIR=../../kernel/
#KERNEL_DIR=/home/guest/LubanCat_Linux_rk356x_SDK/kernel/
#指定目标架构为arm64
ARCH=arm64
#指定交叉编译工具链的前缀
CROSS_COMPILE=aarch64-linux-gnu-
#导出为环境变量
export ARCH CROSS_COMPILE
#指定要编译的内核模块目标文件
obj-m := helloworld.o
#all :默认目标,执行时会编译驱动模块
#$(MAKE) :调用make工具
#-C $(KERNEL_DIR) :指定的内核源码目录
#M=$(CURDIR) :模块的源码位于当前目录
#modules :编译模块
all:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules
.PHONE:clean
#清理编译生成的文件
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean
|
重要
Makefile中指定的目录 “KERNEL_DIR=../../kernel/”要和实际编译内核时指定的输出目录一致,如果编译内核时没有指定特定输出目录,那么就将这个变量指定到内核源码的根目录,可以用绝对路径或者相对路径。 这里的环境是在PC上,使用交叉编译工具编译内核模块,在板卡上编译内核模块类似,使用板卡系统的gcc工具,Makefile不指定CROSS_COMPILE 、ARCH也可以。
1.4.2. 加载内核驱动模块¶
编译好内核驱动模块,可以通过多种方式将helloworld.ko拷贝到开发板,我们可以使用 NFS网络文件系统 、 scp命令 、 sftp命令 等。
可参考 文件传输与NFS网络文件系统 章节。
scp 命令用于 Linux 之间复制文件和目录,该命令基于ssh,需要搭建好ssh环境,scp命令格式如下:
1 | scp local_file remote_username@remote_ip:remote_folder
|
例如:
1 2 3 4 5 6 7 8 9 10 | #传输helloworld.ko文件到板卡
scp helloworld.ko cat@192.168.103.111:/home/cat
#信息输出如下
The authenticity of host '192.168.103.111 (192.168.103.111)' can't be established.
ECDSA key fingerprint is SHA256:6ABxo/ZefrBRZCo6gI0UvupADVVRQvYLfYePr+rWx5s.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.103.111' (ECDSA) to the list of known hosts.
cat@192.168.103.111's password:
helloworld.ko 100% 159KB 26.8MB/s 00:00
|
将helloworld.ko发送到192.168.103.111的/home/cat/目录下,192.168.103.111是开发板ip,需根据实际而定,开发板用户名为cat, 输入yes,然后输入密码进行验证,等待传输完成,这个时候我们开发板就有了helloworld.ko 这个文件。如果是在开发板进行本地编译则不需要再进行传输。
安装卸载内核驱动模块使用insmod和rmmod命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #进入家目录
cd /home/cat/
# 加载内核模块
sudo insmod helloworld.ko
#查看当前加载的内核模块
lsmod
#信息输出如下
Module Size Used by
helloworld 16384 0
# 卸载内核模块
sudo rmmod helloworld.ko
|
查看加载的内核模块,可以使用命令lsmod,其他信息也可以到/sys/module目录下查看,例如:加载成功helloworld.ko模块后, 可以到/sys/module/helloworld下查看。
1.5. 如何编译和加载设备树¶
1.5.1. 编译设备树¶
Linux 3.x 之后的版本引入了设备树(Device Tree)的概念和机制。设备树是一种用于描述硬件平台的静态数据结构,包含了有关硬件设备、总线、中断控制器等信息的详细描述。 后面我们写的驱动需要依赖设备树,所以在这里先介绍如何编译设备树、加载设备树。 这里不做代码讲解,具体原理请参考后续 Linux设备树 章节
1.5.1.1. 使用内核工具编译设备树¶
在编译 Linux 内核时,会生成名为 dtc(Device Tree Compiler)的工具,该工具用于自动编译设备树源文件(.dts 或 .dtsi 文件)为二进制的设备树文件(.dtb 文件)。我们也可以使用命令 sudo apt install device-tree-compiler 下载dtc编译工具。
dtc工具使用示例如下:
1 2 3 4 5 6 | # 编译 dts 为 dtb
内核目录/scripts/dtc/dtc -I dts -O dtb -o xxx.dtb xxx.dts
#也可以下载dtc工具进行编译
sudo apt install device-tree-compiler
dtc -I dts -O dtb -o xxx.dtb xxx.dts
|
内核使用dtc工具的命令大致如上所示,实际上设备树中有非常多的依赖关系,所以一般情况下, 设备树不仅仅只是通过一个dtc命令就能将编译出来的。以上为伪代码,仅供参考使用,了解即可。
1.5.1.2. 使用内核的构建脚本编译设备树¶
我们可以尝试着通过内核的构建脚本去编译设备树,我们所要用到的设备树文件都存放在 内核源码/arch/arm64/boot/dts/rockchip 里面。
前面提到了编译内核时会自动去编译设备树,但是编译内核很耗时,所以我们推荐使用如下命令只编译设备树。
RK3528系列板卡执行以下命令:
1 2 3 4 | #加载配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk3528_defconfig
#使用dtbs参数单独编译设备树
make ARCH=arm64 -j4 CROSS_COMPILE=aarch64-linux-gnu- dtbs
|
RK3562系列板卡执行以下命令:
1 2 3 4 | #加载配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk3562_defconfig
#使用dtbs参数单独编译设备树
make ARCH=arm64 -j4 CROSS_COMPILE=aarch64-linux-gnu- dtbs
|
rk3566/rk3568系列板卡执行以下命令:
1 2 3 4 5 6 7 8 9 10 | #加载配置文件
#(4.19.232版本执行)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat2_defconfig
#(6.1.99版本执行)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk356x_defconfig
#使用dtbs参数单独编译设备树
make ARCH=arm64 -j4 CROSS_COMPILE=aarch64-linux-gnu- dtbs
|
rk3576系列板卡执行以下命令:
1 2 3 4 | #加载配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk3576_defconfig
#使用dtbs参数单独编译设备树
make ARCH=arm64 -j4 CROSS_COMPILE=aarch64-linux-gnu- dtbs
|
rk3588系列板卡执行以下命令:
1 2 3 4 | #加载配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk3588_defconfig
#使用dtbs参数单独编译设备树
make ARCH=arm64 -j4 CROSS_COMPILE=aarch64-linux-gnu- dtbs
|
前面我们提到了编译内核时会去编译设备树,那么此时在没有修改、添加设备树的情况下使用构建脚本单独编译设备树是不会有任何输出的,我们可以手动修改设备树文件再进行编译测试。
例如修改 内核源码/arch/arm64/boot/dts/rockchip 下的 xxx.dts 文件再进行编译。找到已经编译成dtb文件对应的dts源文件,可简单在dts文件中加个空格,以防错误修改导致编译报错。以下以修改rk3568-lubancat-2.dts为例。
执行命令后会编译修改过的设备树,编译成功后生成的设备树文件(.dtb)位于源码目录下的 内核源码/arch/arm64/boot/dts/rockchip 。
1.5.2. 加载设备树¶
加载设备树,将编译好的新设备树文件,替换对应板卡的设备树,替换 /boot/dtb/ 目录下的设备树文件即可。
1.5.2.1. 确定板卡使用的设备树文件¶
由于rk-lubancat系列板卡有众多型号,不同型号使用的设备树很可能不同,并且同一型号也可能有不同的版本,导致设备树很可能不同。我们可以启动并登录板卡,查看 /boot/ 目录下的软连接从而确认当前板卡使用的设备树文件。
在板卡中执行以下命令:
1 2 3 4 5 6 7 8 9 | #查看设备树软连接
ls -l /boot/
#信息输出如下
...
-rw-rw-r-- 1 root root 1215954 Jul 11 2025 logo_kernel.bmp
drwx------ 2 root root 16384 Jul 11 2025 lost+found
lrwxrwxrwx 1 root root 28 Jun 18 04:29 rk-kernel.dtb -> dtb/rk3566-lubancat-1n.dtb
drwxrwxr-x 2 root root 4096 Mar 19 2026 uEnv
|
笔者此处使用的板卡为鲁班猫1N,可以从以上信息中看到rk-kernel.dtb链接到了dtb/rk3566-lubancat-1n.dtb, 在系统启动过程中会读取rk-kernel.dtb作为系统设备树, 那么笔者使用的鲁班猫1N板卡实际读取的设备树为/boot/dtb/rk3566-lubancat-1n.dtb 。
因此,如果需要修改和替换系统加载的设备树,那么就要修改rk-kernel.dtb软链接的设备树。
1.5.2.2. 替换设备树¶
以鲁班猫1N板卡为例,该板卡的设备树文件是 rk3566-lubancat-1n.dtb,通过SCP或NFS将 内核源码/arch/arm64/boot/dts/rockchip/ 目录下编译生成的设备树拷贝到开发板上,替换/boot/dtb/目录下的 rk3566-lubancat-1n.dtb。
1.5.2.3. 查看设备树节点¶
在系统上查看设备树加载情况,比如我们设备树根目录下有设备树节点 leds,我们可以通过以下的方式加载并查看新的设备树是否生效了。
设备树中的设备树节点在文件系统中有与之对应的文件,位于“/proc/device-tree”目录。查看“/proc/device-tree”目录如下所示。
接着进入led文件夹,可以发现led节点中定义的属性以及它的子节点,如下所示。
在节点属性中有一个name属性,我们查看dts源码并没有发现leds节点中定义了name属性,这个name属性自动生成的,为保存节点名。
这里的属性是一个文件,而子节点是一个文件夹,我们进入“sys-status-led”文件夹。 里面有compatible、name、status等属性文件。 我们可以使用“cat”命令查看这些属性文件,如下所示。
1.6. 如何编译和加载设备树插件¶
1.6.1. 编译设备树插件¶
Linux4.4以后引入了动态设备树(Dynamic DeviceTree)。设备树插件可以被动态的加载到系统中,供被内核识别。
重要
设备树插件和设备树不是互相替代的关系,而是互补的关系。设备树插件可以在主设备树定型的情况下, 再对主设备树未描述的功能进行动态的拓展。 比如A板的设备树没有开启串口1的功能,但B板需要开启串口1的功能,那么可以直接沿用A板的设备树, 并用设备树插件拓展出串口1,满足B板的需求。
1.6.1.1. 使用内核工具编译设备树插件¶
编译设备树插件和编译设备树类似,这里介绍内核中的dtc工具编译编译设备树插件的过程。
内核中将xxx.dts 编译为 xxx.dtbo的过程示例,仅供参考:
1 | 内核构建目录/scripts/dtc/dtc -I dts -O dtb -o xxx.dtbo xxx.dts
|
例如,将内核源码arch/arm64/boot/dts/rockchip/overlay/目录下的rk356x-lubancat-uart3-m0-overlay.dts编译为rk356x-lubancat-uart3-m0-overlay.dtbo
1 | scripts/dtc/dtc -I dts -O dtb -o arch/arm64/boot/dts/rockchip/overlay/rk356x-lubancat-uart3-m0-overlay.dtbo arch/arm64/boot/dts/rockchip/overlay/rk356x-lubancat-uart3-m0-overlay.dts
|
执行编译命令后可以内核源码arch/arm64/boot/dts/rockchip/overlay/找到相应的dtbo文件。
当然和编译设备树一样,设备树插件的编译也涉及到依赖关系,所以编译过程也比较复杂。 不仅仅是使用一条命令就可以完成编译的,一般我们在内核目录下,执行make指定dtb选项,即可自动编译添加的设备树插件。
1.6.1.2. 使用内核的构建脚本编译设备树插件¶
设备树插件与设备树一样都是使用DTC工具编译,只不过设备树编译为.dtb。而设备树插件需要编译为.dtbo。 我们可以使用DTC编译命令编译生成.dtbo,但是这样比较繁琐、容易出错。
鲁班猫系列开发板许多外设硬件描述都是以dtbo插件的形式提供的,使用设备树插件配置硬件外设使用起来非常灵活。
如上图,这是一个设备树插件文件列表,用于指定在编译内核时要编译的设备树插件文件。在条件CONFIG_CPU_RK3568被启用时,鲁班猫系列板卡,将编译CONFIG_CPU_RK3568包含的设备树插件。
野火rk356x系列板卡的设备树插件均添加到CONFIG_CPU_RK3568的条件判断中,野火rk3588系列板卡的设备树插件均添加到CONFIG_CPU_RK3588的条件判断中,野火rk3528系列板卡的设备树插件均添加到CONFIG_CPU_RK3528的条件判断中。
当大家尝试写设备树插件的时候,可以将自己的设备树插件添加到内核源码 arch/arm64/boot/dts/rockchip/overlays 目录下,
例如添加lubancat-test-overlay.dts对应的dts源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /dts-v1/;
/plugin/;
/ {
fragment@0 {
target-path = "/";
__overlay__ {
test-node {
status = "okay";
};
};
};
};
|
以上作用是将在根节点”/”添加一个名为test-node的节点,节点的status属性值为”okay”。
修改 arch/arm64/boot/dts/rockchip/overlays/Makefile 文件,添加编译选项:
1 2 3 | #以CONFIG_CPU_RK3568为例
dtbo-$(CONFIG_CPU_RK3568) += \
lubancat-test-overlay.dtbo \
|
添加设备树插件源文件到overlays目录下并修改Makefile添加编译配置后,执行设备树的编译命令。
rk3528系列板卡执行以下命令:
1 2 3 4 | #加载配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk3528_defconfig
#使用dtbs参数单独编译设备树
make ARCH=arm64 -j4 CROSS_COMPILE=aarch64-linux-gnu- dtbs
|
RK3562系列板卡执行以下命令:
1 2 3 4 | #加载配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk3562_defconfig
#使用dtbs参数单独编译设备树
make ARCH=arm64 -j4 CROSS_COMPILE=aarch64-linux-gnu- dtbs
|
rk3566/rk3568系列板卡执行以下命令:
1 2 3 4 5 6 7 8 9 10 | #加载配置文件
#(4.19.232版本执行)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat2_defconfig
#(6.1.99版本执行)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk356x_defconfig
#使用dtbs参数单独编译设备树
make ARCH=arm64 -j4 CROSS_COMPILE=aarch64-linux-gnu- dtbs
|
rk3576系列板卡执行以下命令:
1 2 3 4 | #加载配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk3576_defconfig
#使用dtbs参数单独编译设备树
make ARCH=arm64 -j4 CROSS_COMPILE=aarch64-linux-gnu- dtbs
|
rk3588系列板卡执行以下命令:
1 2 3 4 | #加载配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat_linux_rk3588_defconfig
#使用dtbs参数单独编译设备树
make ARCH=arm64 -j4 CROSS_COMPILE=aarch64-linux-gnu- dtbs
|
如果修改或添加了新的设备树插件,在执行命令后可以在终端看到有相应的编译输出,修改或添加的dts编译为dtbo文件。
1 2 3 | #编译输出如下
CALL scripts/checksyscalls.sh
DTCO arch/arm64/boot/dts/rockchip/overlay/lubancat-test-overlay.dtbo
|
lubancat-test-overlay.dtbo就是编译生成的设备树插件文件。
1.6.2. 加载设备树插件¶
野火Lubancat_RK系列板卡支持通过uboot加载设备树插件,供内核识别使用。
通过SCP或NFS将.dtbo设备树插件拷贝到开发板 /boot/dtb/overlays/ 目录下,下面操作都在开发板上进行。
以LubanCat-2为例,如下图所示:
和设备树一样,设备树插件的配置文件也进行了软连接,方便不同板卡的配置文件进行加载,可以执行以下命令确认系统实际加载的配置文件。
1 2 3 4 5 6 7 8 9 10 11 12 | #查看设备树软连接
ls -l /boot/uEnv/
#信息输出如下
total 84
lrwxrwxrwx 1 root root 20 Jun 18 2024 uEnv.txt -> uEnvLubanCat2.txt
-rw-rw-r-- 1 root root 302 Jul 11 2025 uEnvLubanCat-series.txt
-rw-rw-r-- 1 root root 3444 Jul 11 2025 uEnvLubanCat1.txt
-rw-rw-r-- 1 root root 2203 Jul 11 2025 uEnvLubanCat1H.txt
-rw-rw-r-- 1 root root 1241 Jul 11 2025 uEnvLubanCat1IO-test.txt
-rw-rw-r-- 1 root root 2317 Jul 11 2025 uEnvLubanCat1IO.txt
....
|
从以上信息可以看到,当前系统实际加载的配置文件为/boot/uEnv/uEnvLubanCat2.txt。
将要加载的设备树插件写入到/boot/uEnv/uEnvLubanCat2.txt配置文件中,系统启动过程中会自动读取uEnv.txt文件,从而加载指定的设备树插件, 打开位于“/boot/uEnv/”目录下的uEnvLubanCat2.txt文件,如下所示:
要将设备树插件写入uEnvLubanCat2.txt,使用vim或者nano编辑器打开文件,参照着红框内容写即可,书写格式为“dtoverlay=<设备树插件路径>”。
修改完成后保存、退出,执行reboot命令重启系统。 重启后正常情况下我们可以在“/proc/device-tree”或者“/sys/firmware/devicetree/base/”目录下找到设备节点同名的文件夹,证明设备树插件已经加载成功。
1 2 3 4 5 | #查看节点信息
cat /proc/device-tree/test-node/status
#信息输出如下
okay
|