第一章 开发环境搭建:交叉编译工具链配置、TFTP/NFS网络文件系统搭建、串口终端与调试工具
各位同学,大家好。我是老李,在车载Linux这行摸爬滚打了十多年。今天咱们正式开始《车载Linux内核移植与驱动开发实战》的第一课。
做嵌入式开发,尤其是车载Linux,第一步不是写代码,而是搭环境。这就像盖房子,地基没打好,后面全是坑。我见过太多新手,甚至有些老手,因为环境问题浪费好几天时间。所以,这一章咱们把基础打牢。
1.1 交叉编译工具链配置
先说说交叉编译。你想想看,你的电脑是x86架构,但车机芯片通常是ARM架构。你不能直接在电脑上编译出ARM能跑的程序,对吧?这就需要交叉编译工具链。
说白了,交叉编译就是在你的PC上,用一套特殊的编译器,生成目标板(比如ARM)能执行的二进制文件。
1.1.1 工具链的选择
我个人习惯用Linaro提供的工具链,稳定且社区支持好。当然,芯片厂商也会提供自己的工具链,比如NXP、TI、瑞萨。我的建议是:
- 前期学习:用Linaro的gcc-arm-linux-gnueabihf,通用性强。
- 项目实战:用芯片厂商官方提供的工具链,针对性更强,性能优化更好。
重要提醒:工具链的版本一定要和你的内核版本匹配。版本差太多,编译会报各种莫名其妙的错误。
1.1.2 安装与配置
安装其实很简单,解压就行。但配置环境变量这一步,很多人会栽跟头。
# 下载工具链(以Linaro 7.5为例)
wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/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/
# 配置环境变量(写入~/.bashrc)
export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH
export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm
# 使配置生效
source ~/.bashrc
# 验证安装
arm-linux-gnueabihf-gcc --version
我的小技巧:我习惯在项目根目录下创建一个 setenv.sh 脚本,把环境变量写进去。每次打开新终端,直接 source setenv.sh,干净利落。这样不同项目之间不会互相干扰。
1.1.3 避坑指南
我曾经在一个项目里,编译内核时报错说找不到 gnueabihf 相关的头文件。查了半天,发现是 CROSS_COMPILE 变量末尾忘了加短横线 -。嗯,这里要注意,这个短横线不能少。
还有一次,同事把32位和64位的工具链搞混了,编译出来的内核在板子上直接起不来。所以,下载前一定看清楚是 arm-linux-gnueabihf(32位)还是 aarch64-linux-gnu(64位)。
1.2 TFTP/NFS网络文件系统搭建
开发阶段,你不可能每次修改都重新烧写整个系统。太慢了。所以我们需要网络文件系统。TFTP用来传输内核和设备树,NFS用来挂载根文件系统。
为什么这么做?因为这样你修改了驱动或者应用程序,只需要重新编译,然后重启板子,就能通过网络加载新的文件。开发效率提升不止一个档次。
1.2.1 TFTP服务器搭建
TFTP很简单,就是用来传文件的。我们用它来传内核镜像(zImage)和设备树(.dtb)。
# 安装TFTP服务器(Ubuntu)
sudo apt-get install tftpd-hpa
# 配置TFTP
sudo vim /etc/default/tftpd-hpa
# 内容如下:
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/tftpboot" # 你的tftp根目录
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="-l -c -s"
# 创建目录并设置权限
sudo mkdir -p /tftpboot
sudo chmod 777 /tftpboot
# 重启服务
sudo systemctl restart tftpd-hpa
注意:防火墙要放行UDP 69端口。我遇到过好几次,服务器搭好了,板子就是连不上,最后发现是防火墙没关。
1.2.2 NFS服务器搭建
NFS用来共享根文件系统。板子启动时,通过网络挂载你PC上的文件系统目录。
# 安装NFS服务器
sudo apt-get install nfs-kernel-server
# 配置导出目录
sudo vim /etc/exports
# 添加一行(假设你的根文件系统在 /home/li/rootfs)
/home/li/rootfs *(rw,sync,no_subtree_check,no_root_squash)
# 重启NFS服务
sudo systemctl restart nfs-kernel-server
# 验证
showmount -e localhost
我的经验:no_root_squash 这个选项很重要。不加的话,板子上的root用户会被映射成nobody,很多操作会权限不足。我曾经因为这个,调试一个驱动时,死活写不了设备节点,折腾了一下午。
1.2.3 U-Boot中的网络启动设置
板子启动时,需要在U-Boot里设置好网络参数。这里给个典型配置:
# 设置IP地址(板子IP)
setenv ipaddr 192.168.1.100
# 设置服务器IP(你的PC)
setenv serverip 192.168.1.10
# 设置网关
setenv gatewayip 192.168.1.1
# 设置网络掩码
setenv netmask 255.255.255.0
# 从TFTP下载内核和设备树
tftp 0x82000000 zImage
tftp 0x83000000 board.dtb
# 设置NFS根文件系统启动参数
setenv bootargs 'console=ttyS0,115200 root=/dev/nfs nfsroot=192.168.1.10:/home/li/rootfs rw ip=192.168.1.100:192.168.1.10:192.168.1.1:255.255.255.0::eth0:off'
# 启动内核
bootz 0x82000000 - 0x83000000
你想想看,有了这套流程,你改完代码,编译,然后重启板子,几秒钟就能看到效果。比起烧写Flash,快太多了。
1.3 串口终端与调试工具
串口是嵌入式开发者的眼睛。没有串口,你根本不知道板子启动到哪一步了,内核为什么崩溃。
1.3.1 串口终端配置
我推荐用 minicom 或者 screen。我个人更习惯用 screen,因为它轻量,而且可以轻松回滚。
# 安装screen
sudo apt-get install screen
# 连接串口(假设串口设备是 /dev/ttyUSB0,波特率115200)
screen /dev/ttyUSB0 115200
# 退出screen:Ctrl+A,然后按K,再按Y确认
关键参数:波特率115200,数据位8,停止位1,无校验。这是嵌入式Linux的标配。如果板子输出乱码,先检查波特率对不对。
1.3.2 调试工具
除了串口终端,还有几个工具是必备的:
| 工具 | 用途 | 我的评价 |
|---|---|---|
| dmesg | 查看内核日志 | 调试驱动时,这是第一手信息 |
| strace | 跟踪系统调用 | 应用程序出问题时,用它看卡在哪 |
| gdb | 调试应用程序 | 配合gdbserver做远程调试,很强大 |
| perf | 性能分析 | 找性能瓶颈时,用它准没错 |
| devmem2 | 读写物理内存 | 调试寄存器时,我经常用它 |
举个例子,调试一个I2C驱动时,我会先用 devmem2 直接读写I2C控制器的寄存器,确认硬件没问题。然后再看驱动代码。这样能快速定位是硬件问题还是软件问题。
警告:用 devmem2 操作物理地址时,一定要小心。写错了寄存器,可能导致系统死机甚至硬件损坏。我在项目中就见过有人不小心把DDR配置寄存器改乱了,板子直接变砖。
1.4 本章小结
好了,这一章的内容就这些。我们搭建了交叉编译环境,配好了TFTP和NFS服务器,也搞定了串口终端。这些是后续所有实验的基础。
说实话,这些步骤看起来琐碎,但每一步都很关键。我刚开始做车载项目时,也是从这些基础环境一步步走过来的。环境搭好了,后面写驱动、移植内核,才能事半功倍。
下一章,我们会正式开始内核的编译和启动流程分析。到时候,这些环境就派上用场了。
课后练习:按照本章步骤,在你的PC上搭建好完整的开发环境。然后用串口连接你的开发板,确保能从U-Boot启动到NFS根文件系统。这是后续所有实验的入场券。