驱动开发环境搭建:交叉编译工具链安装、Linux内核源码获取与配置、设备树基础与编译、TFTP/NFS网络文件系统搭建

说实话,座舱芯片驱动开发的第一步,往往不是写代码,而是搭环境。我见过太多新手一上来就急着敲键盘,结果编译报错、烧录失败、板子起不来,折腾半天才发现是工具链没装对。嗯,咱们先把地基打牢。

交叉编译工具链安装

座舱芯片通常是ARM或RISC-V架构,你的PC是x86架构。说白了,你得在PC上编译出能在芯片上跑的程序。这就是交叉编译。

工具链的选择,我个人习惯用Linaro提供的GCC工具链,稳定且社区活跃。比如针对ARM Cortex-A系列:

# 下载ARM64工具链
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz

# 解压到指定目录
tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz -C /opt/

# 配置环境变量
export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin
小提示:建议把环境变量写入 ~/.bashrc/etc/profile,避免每次开终端都要重新 export。我一般写在 /etc/profile.d/cross_compile.sh 里,方便多用户使用。

验证安装是否成功:

aarch64-linux-gnu-gcc --version

如果看到版本号输出,恭喜你,第一步走通了。

注意:工具链版本要和内核版本匹配。我曾经遇到过用太新的GCC编译旧内核,结果一堆语法报错。反过来,太旧的GCC不支持新内核的某些特性。一般芯片厂商的SDK里会推荐具体版本,照着来就行。

Linux内核源码获取与配置

内核源码怎么拿?我建议直接从官方仓库clone,或者用芯片厂商提供的BSP包。以高通SA8155为例:

# 从官方仓库获取
git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
cd linux
git checkout v5.10.120  # 选择一个LTS版本

# 或者使用厂商BSP
# 通常厂商会提供一个 .tar.gz 包,解压即可
tar -xvf sa8155_linux_kernel_5.10.tar.gz

内核配置是重头戏。你想想看,一个完整的内核有上万种配置选项,但座舱芯片只需要其中一小部分。怎么做?

  1. 使用默认配置:芯片厂商通常会提供 defconfig 文件
  2. 基于默认配置修改:运行 make menuconfig 进行图形化配置
  3. 保存配置:修改后保存为 .config 文件
# 加载厂商默认配置
make ARCH=arm64 defconfig  # 或者使用厂商提供的配置文件
# 例如:make ARCH=arm64 sa8155_defconfig

# 图形化配置
make ARCH=arm64 menuconfig

# 编译内核
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)

核心要点:配置内核时,重点关注这几类选项:

  • CPU架构和特性(ARMv8、NEON、GIC等)
  • 内存管理(DMA、CMA、ION等)
  • 显示驱动(DRM/KMS、MIPI DSI、HDMI等)
  • 音频驱动(ALSA、I2S、TDM等)
  • 网络协议栈(TCP/IP、CAN、Ethernet等)
  • 电源管理(PM、DVFS、Suspend/Resume等)

我在项目中遇到过最坑的事:忘了开启CMA(连续内存分配器),结果显示驱动申请不到大块连续内存,屏幕死活点不亮。排查了两天才找到原因。所以,配置完一定要仔细检查关键选项。

设备树基础与编译

设备树(Device Tree)是什么?说白了,它就是描述硬件信息的配置文件。以前的内核把硬件信息硬编码在代码里,换一块板子就要改代码。设备树把硬件描述和内核代码解耦了。

一个简单的设备树节点示例:

/dts-v1/;

/ {
    model = "Qualcomm SA8155 EVB";
    compatible = "qcom,sa8155";

    chosen {
        bootargs = "console=ttyMSM0,115200n8 root=/dev/nfs rw";
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x0 0x80000000 0x0 0x80000000>;
    };

    soc {
        #address-cells = <2>;
        #size-cells = <2>;

        uart@a84000 {
            compatible = "qcom,msm-uart";
            reg = <0x0 0x00a84000 0x0 0x1000>;
            interrupts = <0 113 4>;
            clock-frequency = <7372800>;
            status = "okay";
        };

        i2c@a88000 {
            compatible = "qcom,i2c-qup";
            reg = <0x0 0x00a88000 0x0 0x1000>;
            interrupts = <0 114 4>;
            #address-cells = <1>;
            #size-cells = <0>;
            clock-frequency = <400000>;
            status = "okay";

            touch@38 {
                compatible = "goodix,gt9271";
                reg = <0x38>;
                interrupt-parent = <&tlmm>;
                interrupts = <67 2>;
                reset-gpios = <&tlmm 66 0>;
            };
        };
    };
};

设备树编译

# 编译设备树源文件(.dts)为二进制文件(.dtb)
dtc -I dts -O dtb -o sa8155-evb.dtb sa8155-evb.dts

# 反编译查看
dtc -I dtb -O dts -o sa8155-evb.dts sa8155-evb.dtb
经验之谈:调试设备树时,我习惯先用 dtc -I dtb -O dts 反编译看看实际加载的设备树是什么样。有时候你写的dts和实际加载的不一样,可能是bootloader做了修改。

设备树的核心语法要点:

  • /dts-v1/; 是版本声明,必须写在文件开头
  • 根节点 / 包含所有硬件信息
  • compatible 属性用于匹配驱动
  • reg 属性描述地址和大小
  • interrupts 属性描述中断信息
  • status 属性控制设备是否启用(okay/disabled)

TFTP/NFS网络文件系统搭建

开发阶段,你不可能每次修改都烧录整个镜像。太慢了。我一般用TFTP下载内核和设备树,用NFS挂载根文件系统。这样改完代码,编译、重启、测试,几分钟一轮。

TFTP服务器搭建

# 安装TFTP服务器
sudo apt-get install tftpd-hpa

# 配置TFTP目录
sudo mkdir -p /srv/tftp
sudo chmod 777 /srv/tftp

# 修改配置文件 /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="--secure --create"

# 重启服务
sudo systemctl restart tftpd-hpa

NFS服务器搭建

# 安装NFS服务器
sudo apt-get install nfs-kernel-server

# 创建根文件系统目录
sudo mkdir -p /srv/nfs/rootfs

# 解压根文件系统
sudo tar -xvf sa8155_rootfs.tar.gz -C /srv/nfs/rootfs

# 配置 /etc/exports
/srv/nfs/rootfs *(rw,sync,no_subtree_check,no_root_squash)

# 重启NFS服务
sudo systemctl restart nfs-kernel-server

U-Boot启动参数设置

# 设置IP地址
setenv ipaddr 192.168.1.100
setenv serverip 192.168.1.10

# 通过TFTP下载内核和设备树
tftp 0x80000000 Image
tftp 0x83000000 sa8155-evb.dtb

# 设置NFS根文件系统
setenv bootargs console=ttyMSM0,115200n8 root=/dev/nfs rw nfsroot=192.168.1.10:/srv/nfs/rootfs,v3,tcp ip=192.168.1.100:192.168.1.10:192.168.1.1:255.255.255.0::eth0:off

# 启动内核
booti 0x80000000 - 0x83000000
避坑指南:我曾经在NFS上栽过大跟头。根文件系统里缺少 busybox 的某些命令,或者 /dev 目录下没有必要的设备节点,导致系统启动到一半就卡住了。建议先用完整的根文件系统验证网络启动流程,再逐步裁剪。

网络文件系统搭建完成后,你的开发效率会提升一个量级。改驱动 -> 编译 -> 重启板子 -> 看效果,整个过程不超过5分钟。这才是座舱芯片驱动开发的正确节奏。

本章小结:

  • 交叉编译工具链是基础,版本匹配很重要
  • 内核配置要关注关键子系统,不要贪多
  • 设备树是硬件描述语言,学会看和改dts是基本功
  • TFTP+NFS是开发阶段的利器,能大幅缩短迭代周期
驱动开发环境搭建知识体系 驱动开发环境搭建 交叉编译工具链 Linux内核源码 设备树 TFTP/NFS网络文件系统 Linaro GCC 环境变量 源码获取 menuconfig dts语法 dtc编译 TFTP服务器 NFS服务器 四大模块相互配合,构成完整的驱动开发环境
专注资料整理