3、开发环境搭建:交叉编译工具链配置、嵌入式Linux系统移植、依赖库安装

说实话,搭建开发环境这事儿,看着简单,但坑是真不少。我见过太多人卡在这一步,一卡就是好几天。你想想看,工具链版本不对、系统移植少了个驱动、依赖库编译不过……任何一个环节出问题,后面全白干。

这一章,我就把我在多个项目里踩过的坑、总结的经验,一次性讲清楚。咱们按顺序来:先搞定交叉编译工具链,再做系统移植,最后装好依赖库。

3.1 交叉编译工具链配置

嵌入式开发,说白了就是在PC上写代码,编译出能在ARM板子上跑的程序。这个编译工具,就是交叉编译工具链。

我个人习惯用Linaro提供的工具链,稳定、更新快。当然,你也可以用芯片厂商自带的,比如NXP的、TI的。但不管用哪个,核心就三点:版本匹配、路径正确、环境变量设对。

3.1.1 下载与安装

以ARM Cortex-A72为例,我常用的是gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu。下载后解压到 /opt 目录:

tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz -C /opt/

然后配置环境变量。我个人习惯写在 ~/.bashrc 里:

export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin
export CROSS_COMPILE=aarch64-linux-gnu-
export ARCH=arm64

记得 source ~/.bashrc 让它生效。

小技巧: 我习惯在项目根目录下放一个 setenv.sh 脚本,每次开新终端就 source 一下。这样不同项目用不同工具链,互不干扰。

3.1.2 验证工具链

配置完,跑一下这个命令:

aarch64-linux-gnu-gcc --version

能看到版本信息,就说明装好了。然后写个简单的hello world测试:

#include <stdio.h>
int main() {
    printf("Hello, Embedded World!\n");
    return 0;
}
aarch64-linux-gnu-gcc -o hello hello.c
file hello

输出应该是 ELF 64-bit LSB executable, ARM aarch64。嗯,看到这个就放心了。

注意: 我曾经遇到过工具链版本太高,导致编译出来的程序在旧版内核上跑不起来。所以,工具链版本最好和内核版本匹配。一般芯片厂商的SDK里会推荐具体版本。

3.2 嵌入式Linux系统移植

系统移植,说白了就是让Linux内核在你的板子上跑起来。这步涉及的东西比较多,我挑最关键的讲:内核配置、设备树、根文件系统。

3.2.1 内核编译

从芯片厂商或内核官网下载源码。以Linux 5.10为例:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j4

这里 defconfig 用的是默认配置。但实际项目中,你得根据板子硬件来裁剪。比如你的板子没有WiFi模块,就把WiFi驱动去掉,能省不少空间。

我个人习惯先编译一次默认配置,确保工具链没问题。然后再慢慢裁剪。

3.2.2 设备树(Device Tree)

设备树是描述硬件信息的文件。说白了,就是告诉内核:你的板子上有哪些外设、它们连在哪个引脚上、用哪个中断号。

以我做过的一个项目为例,设备树里这样描述UART:

&uart0 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&uart0_pins>;
    clock-frequency = <24000000>;
};

这里 status = "okay" 表示启用这个UART。如果改成 "disabled",内核就不会去初始化它。

重点: 设备树写错了,板子可能起不来。我遇到过最坑的一次,是GPIO的pinmux配错了,导致SD卡识别不了。排查了整整两天……所以,设备树一定要对照原理图来写

3.2.3 根文件系统制作

根文件系统,就是板子启动后看到的 / 目录。里面要有 /bin/etc/lib 这些基本目录。

我推荐用Buildroot或Yocto来制作。Buildroot简单直接,适合快速验证。Yocto功能强大,但学习曲线陡。

用Buildroot的步骤:

make menuconfig
# 选择Target options -> Target Architecture = AArch64
# 选择Filesystem images -> ext4
make

编译完会在 output/images 下生成 rootfs.ext4。把这个文件烧到板子的存储设备上就行。

经验之谈: 刚开始做移植时,根文件系统尽量精简。只放必要的库和工具,比如 busyboxglibc。等系统跑起来了,再慢慢加东西。这样出了问题,排查范围小。

3.3 依赖库安装(Boost、Protobuf)

高精地图引擎离不开Boost和Protobuf。这两个库在嵌入式环境下编译,有点讲究。

3.3.1 Boost库交叉编译

Boost库很大,我们不需要全部编译。只编译用到的模块就行,比如 filesystemsystemthread

先下载源码,然后执行:

./bootstrap.sh --with-libraries=filesystem,system,thread
./b2 toolset=gcc-arm target-os=linux \
    include=/opt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/aarch64-linux-gnu/include \
    link=static threading=multi \
    --prefix=/opt/boost_arm install

这里 link=static 表示编译成静态库。我个人喜欢用静态库,部署时不用带一堆 .so 文件。

注意: Boost的编译选项很多,一定要指定 toolset=gcc-arm,否则它会用本机的gcc编译,出来的库在ARM板子上跑不了。我刚开始就犯过这个错。

3.3.2 Protobuf交叉编译

Protobuf分两部分:protoc(代码生成器)和 libprotobuf(运行时库)。protoc 在PC上运行,用本机gcc编译。libprotobuf 在板子上运行,用交叉编译器编译。

先编译本机的 protoc

./configure --prefix=/opt/protobuf_host
make -j4
make install

再交叉编译 libprotobuf

./configure --host=aarch64-linux-gnu \
    --with-protoc=/opt/protobuf_host/bin/protoc \
    --prefix=/opt/protobuf_arm
make -j4
make install

这里 --with-protoc 指定了刚才编译好的 protoc 路径。没有这个参数,交叉编译会失败。

关键点: 编译完成后,/opt/protobuf_arm/lib 下会有 libprotobuf.alibprotobuf.so。记得把它们拷贝到板子的 /usr/lib 目录下,或者编译你的应用时链接进去。

3.3.3 验证依赖库

写个简单的测试程序,验证Boost和Protobuf是否正常工作:

#include <boost/filesystem.hpp>
#include <google/protobuf/message.h>
#include <iostream>

int main() {
    // 测试Boost
    boost::filesystem::path p("/tmp");
    std::cout << "Boost works: " << p.string() << std::endl;

    // 测试Protobuf
    google::protobuf::Message *msg = nullptr;
    std::cout << "Protobuf works" << std::endl;

    return 0;
}

编译命令:

aarch64-linux-gnu-g++ -o test_deps test_deps.cpp \
    -I/opt/boost_arm/include \
    -I/opt/protobuf_arm/include \
    -L/opt/boost_arm/lib \
    -L/opt/protobuf_arm/lib \
    -lboost_filesystem -lboost_system -lprotobuf -lpthread

把编译好的 test_deps 拷贝到板子上运行。如果输出正常,说明环境搭建成功了。

避坑指南: 我曾经在链接时忘了加 -lpthread,结果程序一运行就崩溃。Boost和Protobuf都依赖pthread,链接时一定要加上

好了,到这里开发环境就搭好了。工具链能用、系统能跑、依赖库也装上了。接下来,就可以开始写高精地图引擎的核心代码了。

嗯,这一章内容不少,但每一步都很关键。你按照这个流程走,应该不会出大问题。如果遇到什么奇怪的现象,先检查工具链版本,再看设备树,最后看库的链接顺序。八成能解决。