第二章:嵌入式Linux开发环境搭建

说实话,搭建开发环境这件事,看着琐碎,但却是整个嵌入式Linux学习的基石。我见过太多新手,一上来就急着写代码,结果编译报错半天找不到原因——最后发现是工具链没装对。嗯,咱们今天就把这事一次性搞定。

2.1 交叉编译工具链安装

先说说交叉编译。你想想看,你的PC是x86架构,但嵌入式板子通常是ARM架构。PC上编译的程序,ARM板子跑不了。怎么办?就得用交叉编译工具链。

我个人习惯用Linaro提供的工具链,稳定且社区活跃。安装其实就三步:

  1. 下载对应架构的压缩包(比如gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz)
  2. 解压到指定目录,比如/opt/toolchains/
  3. 配置环境变量
# 解压
sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/toolchains/

# 配置环境变量(加到~/.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-

# 验证
arm-linux-gnueabihf-gcc --version
小提示: 我曾经在项目里遇到过工具链版本不匹配的问题,导致内核编译到一半就崩了。建议用Linaro 7.5.0或更高版本,兼容性最好。

2.2 TFTP/NFS服务配置

开发过程中,你肯定不想每次改个驱动都重新烧写整个系统。TFTP和NFS就是干这个的——一个用来快速下载内核和设备树,一个用来挂载根文件系统。

2.2.1 TFTP服务器配置

TFTP说白了就是个轻量级的文件传输协议。U-Boot里用它来加载内核镜像。

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

# 配置文件 /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/tftpboot"
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端口。我曾经在客户现场调试时,死活连不上TFTP,最后发现是防火墙没关。嗯,这种低级错误犯一次就够了。

2.2.2 NFS服务器配置

NFS用来共享根文件系统。开发阶段把根文件系统放在主机上,板子通过网络挂载,改完代码直接生效,不用反复烧写。

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

# 编辑 /etc/exports,添加共享目录
/opt/rootfs *(rw,sync,no_subtree_check,no_root_squash)

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

板子端U-Boot里设置启动参数:

setenv bootargs 'console=ttyS0,115200 root=/dev/nfs nfsroot=192.168.1.100:/opt/rootfs rw ip=192.168.1.101:192.168.1.100:192.168.1.1:255.255.255.0::eth0:off'

2.3 U-Boot与内核编译

U-Boot和内核的编译,流程其实差不多。我建议你养成一个好习惯:每次编译前先清理,避免旧文件干扰。

2.3.1 U-Boot编译

# 下载源码
git clone https://github.com/u-boot/u-boot.git
cd u-boot

# 配置(以STM32MP157为例)
make stm32mp15_defconfig

# 编译
make -j4

# 编译完成后,会在当前目录生成u-boot.bin

这里有个坑:不同板子的配置文件不一样。你最好先查一下板子的官方文档,确认defconfig的名字。我刚开始做的时候,随便选了个配置,结果U-Boot起不来,折腾了两天。

2.3.2 Linux内核编译

# 下载内核源码
git clone https://github.com/torvalds/linux.git
cd linux

# 配置内核
make ARCH=arm stm32mp1_defconfig

# 如果需要自定义配置
make ARCH=arm menuconfig

# 编译内核和设备树
make ARCH=arm zImage -j4
make ARCH=arm dtbs -j4

# 编译内核模块
make ARCH=arm modules -j4
make ARCH=arm modules_install INSTALL_MOD_PATH=/opt/rootfs
核心要点: 编译时一定要指定ARCH和CROSS_COMPILE。我见过有人忘了设置,结果编译出来的内核在板子上跑不起来,报了一堆非法指令的错误。

2.4 根文件系统制作

根文件系统,说白了就是Linux启动后挂载的第一个文件系统。它里面包含了init程序、库文件、配置文件等。制作根文件系统,主流有两种方式:Buildroot和Yocto。

2.4.1 Buildroot方式

Buildroot适合快速出系统。配置简单,编译快,适合中小型项目。

# 下载Buildroot
git clone https://github.com/buildroot/buildroot.git
cd buildroot

# 配置
make stm32mp157_dk_defconfig

# 自定义配置
make menuconfig

# 编译(第一次编译会比较慢)
make -j4

# 编译完成后,output/images/目录下会生成rootfs.tar

我个人习惯用Buildroot做原型验证。有一次客户要三天出demo,我用Buildroot一天就把系统跑起来了,剩下两天调应用。效率确实高。

2.4.2 Yocto方式

Yocto功能更强大,但学习曲线陡峭。适合需要高度定制化的产品级项目。

# 下载Yocto
git clone git://git.yoctoproject.org/poky.git
cd poky

# 初始化构建环境
source oe-init-build-env

# 配置
bitbake-layers add-layer ../meta-openembedded/meta-oe
bitbake-layers add-layer ../meta-st-stm32mp

# 编译
bitbake core-image-minimal

# 编译完成后,tmp/deploy/images/目录下会生成根文件系统镜像
经验之谈: Yocto第一次编译可能要几个小时,建议用一台性能好点的机器。我当年用笔记本编译Yocto,风扇狂转,差点没把电脑烧了。后来换了台服务器,世界清净了。

2.5 开发环境验证

环境搭好了,怎么验证?我一般会写个简单的Hello World程序,交叉编译后放到板子上跑一下。

// hello.c
#include <stdio.h>

int main() {
    printf("Hello, Embedded Linux!\n");
    return 0;
}

// 交叉编译
arm-linux-gnueabihf-gcc -o hello hello.c

// 通过TFTP传到板子
tftp 192.168.1.101 -c put hello

// 板子上执行
./hello

如果能看到"Hello, Embedded Linux!"输出,恭喜你,开发环境搭建成功了。

最后提醒: 开发环境搭建是嵌入式Linux开发的第一步,也是最容易出问题的一步。遇到问题别慌,先检查环境变量、权限、网络配置这些基础项。我遇到过最离谱的一次,折腾了半天发现是网线没插好。嗯,有时候问题就是这么简单。