2. 开发环境搭建:交叉编译工具链配置、内核源码树构建、QEMU模拟器与树莓派真实硬件环境搭建
嵌入式Linux驱动开发与普通应用程序开发最大的区别在于“交叉编译”与“目标硬件”的分离。本章将系统搭建三套可用的开发环境:纯QEMU模拟环境(用于快速验证)、树莓派真实硬件环境(用于最终部署)以及交叉编译工具链(连接宿主机与目标机的桥梁)。完成本章后,你将拥有一个可编译、可加载、可调试的完整驱动开发实验平台。
2.1 交叉编译工具链配置
交叉编译工具链是驱动开发的核心工具。它运行在x86_64宿主机上,但生成的二进制代码只能在ARM目标机上执行。我们选择Linaro提供的GCC工具链,它针对ARM Cortex-A系列处理器做了优化,且与主线内核兼容性最好。
2.1.1 工具链版本选择与下载
| 目标平台 | 工具链前缀 | 推荐版本 | 下载源 |
|---|---|---|---|
| 树莓派2/3 (ARMv7) | arm-linux-gnueabihf- | gcc-linaro-7.5.0-2019.12 | Linaro官方Release |
| 树莓派4 (ARMv8 32位兼容) | aarch64-linux-gnu- | gcc-arm-10.3-2021.07 | ARM官方GNU-A工具链 |
| QEMU模拟 (ARMv7) | arm-linux-gnueabihf- | 同上 | 同上 |
/opt/toolchains/ 目录,并将 bin/ 子目录加入 $PATH。建议在 ~/.bashrc 中永久添加:
export PATH=$PATH:/opt/toolchains/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
2.1.2 验证工具链可用性
配置完成后,执行以下命令验证工具链是否正常工作:
$ arm-linux-gnueabihf-gcc --version
arm-linux-gnueabihf-gcc (Linaro GCC 7.5-2019.12) 7.5.0
Copyright (C) 2017 Free Software Foundation, Inc.
$ arm-linux-gnueabihf-objdump --version
GNU objdump (GNU Binutils) 2.31.1
编写一个简单的C程序测试交叉编译:
// test.c
#include <stdio.h>
int main(void) {
printf("Hello from ARM!\n");
return 0;
}
// 交叉编译
$ arm-linux-gnueabihf-gcc -static -o test_arm test.c
$ file test_arm
test_arm: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, not stripped
file 命令显示 ELF 64-bit LSB x86-64,说明你使用了宿主机的gcc而非交叉编译器。请检查 $PATH 中交叉编译器路径是否在系统gcc之前。
2.2 内核源码树构建
驱动模块必须与目标内核版本完全匹配才能加载。我们采用“独立内核源码树”方式:下载与目标机相同版本的内核源码,配置并编译出内核模块编译所需的头文件与配置文件,但不编译完整内核镜像(QEMU环境除外)。
2.2.1 获取内核源码
从 kernel.org 获取主线内核,或从树莓派官方GitHub获取带树莓派补丁的内核:
# 主线内核(用于QEMU)
$ wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.76.tar.xz
$ tar -xf linux-6.1.76.tar.xz
$ cd linux-6.1.76
# 树莓派内核(用于真实硬件)
$ git clone --depth=1 --branch rpi-6.1.y https://github.com/raspberrypi/linux.git
$ cd linux
2.2.2 内核配置与编译
内核配置是构建过程中最容易出错的环节。我们使用 defconfig 生成基础配置,然后手动启用驱动开发所需的选项:
# 对于QEMU ARM环境
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- vexpress_defconfig
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
# 必须启用的选项(在menuconfig中搜索并设置)
# CONFIG_MODULES=y —— 模块支持
# CONFIG_MODULE_UNLOAD=y —— 模块卸载
# CONFIG_DEVTMPFS=y —— 设备节点自动管理
# CONFIG_DEBUG_FS=y —— 调试文件系统
# 编译内核与设备树(QEMU需要完整内核镜像)
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage -j$(nproc)
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
make modules_prepare。这会生成 Module.symvers 和必要的头文件,耗时仅为完整编译的1/10。
2.2.3 内核源码树结构验证
编译完成后,确认以下关键文件存在:
$ ls -l arch/arm/boot/zImage
-rwxr-xr-x 1 user user 4.5M Mar 15 10:30 arch/arm/boot/zImage
$ ls -l arch/arm/boot/dts/vexpress-v2p-ca9.dtb
-rw-r--r-- 1 user user 12K Mar 15 10:30 arch/arm/boot/dts/vexpress-v2p-ca9.dtb
$ ls -l include/generated/autoconf.h
-rw-r--r-- 1 user user 58K Mar 15 10:30 include/generated/autoconf.h
下图展示了内核源码树中与驱动开发相关的核心目录结构:
2.3 QEMU模拟器环境搭建
QEMU模拟器可以在没有真实硬件的情况下运行ARM Linux内核,是驱动开发初期调试和验证的利器。我们使用QEMU的“versatile express”开发板模拟(Cortex-A9 MPCore),它支持SMP、GIC中断控制器和足够的外设。
2.3.1 安装QEMU与依赖
$ sudo apt-get update
$ sudo apt-get install qemu-system-arm qemu-utils libncurses5-dev \
bison flex libssl-dev bc
2.3.2 制作根文件系统
使用Buildroot制作最小根文件系统,包含busybox和必要的动态库:
# 下载Buildroot
$ wget https://buildroot.org/downloads/buildroot-2023.11.tar.gz
$ tar -xf buildroot-2023.11.tar.gz
$ cd buildroot-2023.11
# 配置为ARM vexpress目标
$ make qemu_arm_vexpress_defconfig
$ make -j$(nproc)
# 生成的根文件系统位于 output/images/rootfs.ext2
$ wget https://people.linaro.org/~alex.bennee/images/vexpress64-openembedded_minimal-armv8-20160311-447.img.xz
$ unxz vexpress64-openembedded_minimal-armv8-20160311-447.img.xz
注意此镜像为ARMv8,需使用 qemu-system-aarch64。
2.3.3 启动QEMU并验证
使用以下命令启动QEMU,挂载内核镜像、设备树和根文件系统:
$ qemu-system-arm \
-M vexpress-a9 \
-kernel linux-6.1.76/arch/arm/boot/zImage \
-dtb linux-6.1.76/arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
-drive file=buildroot-2023.11/output/images/rootfs.ext2,if=sd,format=raw \
-append "console=ttyAMA0,115200 root=/dev/mmcblk0 rw" \
-serial stdio \
-net nic -net user,hostfwd=tcp::2222-:22
启动成功后,QEMU终端会显示Linux启动日志,最终出现登录提示符(Buildroot默认用户root,无密码)。
# lsmod
Module Size Used by Not tainted
# uname -r
6.1.76
# cat /proc/devices | head -5
Character devices:
1 mem
2 pty
3 ttyp
2.4 树莓派真实硬件环境搭建
树莓派环境用于最终部署和真实硬件验证。与QEMU不同,树莓派需要处理SD卡启动、UART串口调试和GPIO外设交互。
2.4.1 烧录系统与配置
使用Raspberry Pi Imager或手动烧录官方Raspberry Pi OS Lite(无桌面版,节省资源):
# 查看SD卡设备(通常是 /dev/sdX 或 /dev/mmcblkX)
$ lsblk
# 烧录系统镜像
$ sudo dd if=2024-03-15-raspios-bookworm-armhf-lite.img of=/dev/sdX bs=4M status=progress
$ sync
烧录完成后,在SD卡的boot分区创建 ssh 空文件以启用SSH,并编辑 config.txt 启用UART串口:
# 在 config.txt 末尾添加
enable_uart=1
dtoverlay=disable-bt # 禁用蓝牙,释放UART0给GPIO 14/15
2.4.2 串口调试连接
使用USB转TTL串口线连接树莓派的GPIO引脚:
| 树莓派引脚 | 功能 | 串口线颜色(常见) |
|---|---|---|
| GPIO 14 (Pin 8) | TXD (发送) | 绿色 |
| GPIO 15 (Pin 10) | RXD (接收) | 白色 |
| GND (Pin 6) | 地线 | 黑色 |
宿主机使用minicom或screen连接:
$ sudo apt-get install minicom
$ sudo minicom -D /dev/ttyUSB0 -b 115200
2.4.3 编译树莓派内核模块
在宿主机上使用交叉编译器编译驱动模块,然后通过SCP传输到树莓派:
# 在宿主机内核源码目录中编译模块
$ cd linux-rpi-6.1.y
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules
# 假设驱动源码在 drivers/char/ 下
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- M=drivers/char/
# 传输到树莓派
$ scp drivers/char/my_driver.ko pi@192.168.1.100:/home/pi/
uname -r 确认内核版本,然后检查模块的 vermagic 是否一致:
# 树莓派上
$ uname -r
6.1.21-v7+
# 宿主机上检查模块
$ modinfo my_driver.ko | grep vermagic
vermagic: 6.1.21-v7+ SMP mod_unload modversions ARMv7
如果版本不匹配,模块加载时会报 Invalid module format 错误。
2.5 环境验证清单
完成所有搭建后,使用以下清单逐项确认环境就绪:
2.5.1 逐项验证命令
- 工具链:
arm-linux-gnueabihf-gcc -v显示版本信息,无“command not found”错误。 - 内核编译:
ls -l arch/arm/boot/zImage文件存在且大小大于2MB。 - QEMU启动:执行QEMU命令后,终端输出内核启动日志,最终出现
buildroot login:提示。 - 树莓派启动:串口终端显示树莓派启动日志,登录后执行
uname -a确认内核版本。 - 模块加载:编写一个最简单的“hello world”模块,交叉编译后分别在QEMU和树莓派上执行
insmod和rmmod,无错误输出。
// hello.c
#include <linux/module.h>
#include <linux/kernel.h>
static int __init hello_init(void) {
printk(KERN_INFO "Hello, Linux Driver World!\n");
return 0;
}
static void __exit hello_exit(void) {
printk(KERN_INFO "Goodbye, Linux Driver World!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
// Makefile
obj-m := hello.o
KDIR := /path/to/your/kernel/tree
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
2.6 本章小结
本章完成了驱动开发环境的全链路搭建,包括:
- 交叉编译工具链:Linaro GCC 7.5,支持ARMv7硬浮点,配置了
$ARCH和$CROSS_COMPILE环境变量。 - 内核源码树:主线6.1.76(QEMU)和树莓派6.1.y分支,均已完成配置和编译。
- QEMU模拟器:vexpress-a9开发板模拟,Buildroot根文件系统,支持网络转发和串口交互。
- 树莓派真实硬件:系统烧录、UART串口调试、内核模块交叉编译与部署流程。