2. 开发环境搭建:交叉编译工具链配置、Linux内核源码获取与编译、设备树(Device Tree)基础、QEMU模拟环境搭建与测试

说实话,做嵌入式Linux驱动开发,环境搭建这一步要是没走稳,后面全是坑。我见过太多新手一上来就急着写代码,结果编译报错半天找不到原因——最后发现是工具链版本不对。嗯,咱们先把地基打牢。

本章核心目标:搭建一套完整的嵌入式Linux驱动开发实验环境,包括交叉编译工具链、内核源码树、设备树编译工具以及QEMU模拟器。这套环境将贯穿整个课程。

2.1 交叉编译工具链配置

交叉编译,说白了就是在一台电脑(宿主机)上编译出能在另一台不同架构的电脑(目标机)上运行的程序。我们做ARM驱动开发,宿主机通常是x86_64的PC,目标机是ARM开发板或QEMU模拟的ARM系统。

我个人习惯使用Linaro提供的GCC工具链,稳定且社区支持好。以ARM 32位为例,我们选用gcc-arm-linux-gnueabihf。

2.1.1 下载与安装

# 下载Linaro ARM GCC工具链
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz

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

# 添加环境变量
export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH

验证是否安装成功:

arm-linux-gnueabihf-gcc --version

如果看到版本信息,说明工具链已经就绪。

我的经验:建议把工具链路径永久写入 ~/.bashrc 或 ~/.zshrc,避免每次打开终端都要手动 export。我曾经因为忘记 export,浪费了整整一个下午排查一个“编译器找不到”的问题。

2.1.2 工具链组件说明

组件 用途
arm-linux-gnueabihf-gcc C语言交叉编译器
arm-linux-gnueabihf-g++ C++语言交叉编译器
arm-linux-gnueabihf-ld 链接器
arm-linux-gnueabihf-objdump 反汇编工具,调试驱动时非常有用
arm-linux-gnueabihf-objcopy 格式转换工具,用于生成裸机镜像

2.2 Linux内核源码获取与编译

驱动开发离不开内核源码。你想想看,驱动本质上就是内核的一部分,你得知道内核怎么管理设备、怎么调度中断、怎么管理内存。

2.2.1 获取内核源码

我建议直接从kernel.org获取主线内核,或者使用LTS版本。本课程以Linux 5.10 LTS为例。

# 从kernel.org下载
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.100.tar.xz

# 解压
tar -xvf linux-5.10.100.tar.xz
cd linux-5.10.100

2.2.2 配置内核

内核配置是门学问。对于驱动开发,我们通常不需要编译整个内核,但需要内核源码树来编译驱动模块。

# 使用默认的ARM配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- vexpress_defconfig

# 如果需要自定义配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

注意: menuconfig 需要安装 ncurses-dev 库。如果报错,执行 sudo apt-get install libncurses-dev。

2.2.3 编译内核

# 编译内核镜像
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 zImage

# 编译设备树
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs

# 编译内核模块
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules

编译完成后,你会得到以下关键文件:

  • arch/arm/boot/zImage — 内核镜像
  • arch/arm/boot/dts/vexpress-v2p-ca9.dtb — 设备树二进制文件
  • drivers/ 目录下的 .ko 文件 — 内核模块

2.3 设备树(Device Tree)基础

设备树,说白了就是描述硬件信息的配置文件。以前的内核把硬件信息硬编码在代码里,换一块板子就得改代码。设备树把硬件描述和内核代码分开了,换板子只需要换一个 .dts 文件。

2.3.1 设备树文件结构

一个典型的设备树文件包含以下部分:

/dts-v1/;

/ {
    model = "V2P-CA9";
    compatible = "arm,vexpress-v2p-ca9", "arm,vexpress";
    
    #address-cells = <1>;
    #size-cells = <1>;
    
    memory@60000000 {
        device_type = "memory";
        reg = <0x60000000 0x40000000>;
    };
    
    chosen {
        bootargs = "console=ttyAMA0,115200 root=/dev/mmcblk0 rw";
    };
    
    amba {
        #address-cells = <1>;
        #size-cells = <1>;
        
        uart@160000 {
            compatible = "arm,pl011", "arm,primecell";
            reg = <0x160000 0x1000>;
            interrupts = <0 5 4>;
        };
    };
};

2.3.2 关键概念

概念 说明
compatible 匹配驱动的关键字符串,驱动通过它找到对应的设备
reg 描述设备的地址范围和大小
interrupts 描述设备使用的中断号、触发方式等
status 设备状态,常用 okay 或 disabled

避坑指南:我曾经在调试一个I2C驱动时,设备树里写错了 reg 的地址,结果驱动死活加载不上。后来用 dtc -I dtb -O dts 反编译设备树,才发现地址少写了一个0。所以,写完设备树一定要反编译检查一遍。

2.3.3 编译设备树

# 将.dts编译为.dtb
dtc -I dts -O dtb -o myboard.dtb myboard.dts

# 反编译.dtb为.dts(调试用)
dtc -I dtb -O dts -o myboard.dts myboard.dtb

2.4 QEMU模拟环境搭建与测试

没有开发板怎么办?QEMU就是你的救星。它可以在PC上模拟一个完整的ARM系统,非常适合驱动开发的初期调试。

2.4.1 安装QEMU

sudo apt-get install qemu-system-arm

2.4.2 启动模拟系统

我们使用QEMU模拟vexpress-a9开发板,配合刚才编译的内核和设备树。

qemu-system-arm \
    -M vexpress-a9 \
    -kernel linux-5.10.100/arch/arm/boot/zImage \
    -dtb linux-5.10.100/arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
    -append "console=ttyAMA0,115200 root=/dev/mmcblk0 rw" \
    -sd rootfs.ext3 \
    -nographic

如果一切正常,你会看到内核启动日志,最后进入一个shell提示符。

验证驱动开发环境:进入系统后,执行 ls /dev/ 查看设备节点,执行 cat /proc/devices 查看已注册的设备驱动。这就是我们后续开发驱动的“试验田”。

2.4.3 常见问题与解决

  • QEMU启动后黑屏:检查是否使用了 -nographic 参数,或者串口控制台参数是否正确。
  • 内核崩溃:通常是设备树与内核版本不匹配,重新编译dtb即可。
  • 文件系统挂载失败:确认rootfs镜像路径正确,且格式为ext3。

2.5 本章知识体系

下面这张图展示了我们搭建的开发环境各组件之间的关系:

嵌入式Linux驱动开发环境架构 宿主机(x86_64 Ubuntu) 交叉编译工具链 Linux内核源码树 设备树(.dts/.dtb) QEMU模拟器(vexpress-a9) ARM Linux目标系统 你的驱动模块(.ko) 编译流程: 源码 → 交叉编译 → .ko

重要提醒:环境搭建完成后,建议创建一个快照或写一份环境配置文档。我见过太多同学在课程中途重装系统,结果环境配置全部丢失,又得从头再来。花10分钟记录一下,能省你一天的时间。

好了,环境搭建就到这里。工具链、内核、设备树、QEMU,这四个组件构成了我们驱动开发的基础设施。后面的章节,我们会在这个环境里一步步实现从最简单的字符设备驱动,到复杂的显示驱动。