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。嗯,看到这个就放心了。
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",内核就不会去初始化它。
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。把这个文件烧到板子的存储设备上就行。
busybox、glibc。等系统跑起来了,再慢慢加东西。这样出了问题,排查范围小。
3.3 依赖库安装(Boost、Protobuf)
高精地图引擎离不开Boost和Protobuf。这两个库在嵌入式环境下编译,有点讲究。
3.3.1 Boost库交叉编译
Boost库很大,我们不需要全部编译。只编译用到的模块就行,比如 filesystem、system、thread。
先下载源码,然后执行:
./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 文件。
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.a 和 libprotobuf.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,链接时一定要加上。
好了,到这里开发环境就搭好了。工具链能用、系统能跑、依赖库也装上了。接下来,就可以开始写高精地图引擎的核心代码了。
嗯,这一章内容不少,但每一步都很关键。你按照这个流程走,应该不会出大问题。如果遇到什么奇怪的现象,先检查工具链版本,再看设备树,最后看库的链接顺序。八成能解决。