3、交叉编译环境搭建:交叉编译工具链的选择与安装、CMake交叉编译配置、常用编译选项详解
好,咱们进入正题。嵌入式开发里,交叉编译环境搭建是绕不开的第一道坎。说白了,就是你在PC上写代码,但最终要在ARM、MIPS或者RISC-V的板子上跑。你PC上的gcc编译出来的东西,板子不认识。反过来,板子上的编译器又太慢,没法搞大型项目。所以,我们需要一套「交叉编译工具链」。
3.1 交叉编译工具链的选择与安装
工具链的选择,其实挺有讲究的。我个人习惯先看芯片厂商。比如你用全志、瑞芯微的芯片,它们官方SDK里通常会自带一套工具链。用官方的,最省心。但如果你用的是比较通用的ARM Cortex-A系列,比如树莓派,那Linaro出品的工具链就是首选。
常见的工具链命名规则,你得看懂:
| 前缀 | 含义 | 我遇到过的场景 |
|---|---|---|
| arm-linux-gnueabihf- | ARM架构,Linux系统,glibc库,硬浮点 | 树莓派、大多数Cortex-A7/A53 |
| aarch64-linux-gnu- | 64位ARM架构,Linux系统 | 树莓派64位、RK3399 |
| arm-none-eabi- | ARM架构,无操作系统,嵌入式裸机 | STM32、单片机 |
| mipsel-linux-gnu- | MIPS小端架构,Linux系统 | 一些老的路由器、安防芯片 |
安装步骤其实很简单,以我常用的Linaro工具链为例:
# 下载工具链(以ARM 32位为例)
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/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/
# 配置环境变量
export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH
# 验证是否安装成功
arm-linux-gnueabihf-gcc --version
3.2 CMake交叉编译配置
OpenCV用的是CMake构建系统。要交叉编译,就得写一个工具链文件(toolchain.cmake)。这个文件告诉CMake:别用你本机的gcc,用我指定的交叉编译器。
嗯,这里要注意。很多新手直接改CMakeLists.txt,结果一塌糊涂。正确的做法是写一个独立的工具链文件,然后通过 -DCMAKE_TOOLCHAIN_FILE 参数传入。
我分享一个我常用的模板:
# 文件名: arm-linux-gnueabihf.toolchain.cmake
# 指定目标系统
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
# 指定交叉编译器
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
# 指定目标系统的根文件系统路径
# 这里指向你板子的sysroot,里面包含了板子的库和头文件
set(CMAKE_SYSROOT /home/user/sysroot/arm-linux-gnueabihf)
# 设置查找策略:先找sysroot,再找系统路径
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
然后编译OpenCV时,这样调用:
mkdir build_arm && cd build_arm
cmake .. \
-DCMAKE_TOOLCHAIN_FILE=../arm-linux-gnueabihf.toolchain.cmake \
-DCMAKE_INSTALL_PREFIX=./install \
-DBUILD_opencv_world=ON \
-DWITH_GTK=OFF \
-DWITH_QT=OFF \
-DWITH_V4L=ON \
-DWITH_FFMPEG=ON \
-DWITH_TBB=ON \
-DWITH_OPENMP=ON
make -j4
make install
CMAKE_SYSROOT 指向一个完整的根文件系统。比如从板子上把 /lib、/usr/lib、/usr/include 拷贝出来。这样CMake就能自动找到板子上的依赖库,省去很多手动指定路径的麻烦。
3.3 常用编译选项详解
编译选项这东西,说白了就是告诉编译器怎么优化你的代码。嵌入式平台资源有限,选对了选项,性能能翻倍。选错了,可能连编译都过不去。
我重点讲几个常用的:
- -march=armv7-a:指定CPU架构。比如你的板子是Cortex-A7,就写armv7-a。别偷懒写armv5,那样编译器会生成兼容性代码,性能大打折扣。
- -mfpu=neon:开启NEON指令集。OpenCV的图像处理大量依赖NEON加速。不开启的话,很多函数会退化成普通C代码,慢得你想哭。
- -mfloat-abi=hard:硬浮点。如果你的板子有硬件浮点单元,一定要用这个。软浮点模拟浮点运算,性能差好几倍。
- -O2 / -O3:优化等级。我一般用-O2,兼顾性能和代码大小。O3有时会引入一些奇怪的bug,我在项目中遇到过两次,后来就不太敢用了。
- -pipe:编译时用管道而不是临时文件。能稍微加快编译速度,尤其是在多核机器上。
举个例子,一个完整的编译选项组合:
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv7-a -mfpu=neon -mfloat-abi=hard -O2 -pipe")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv7-a -mfpu=neon -mfloat-abi=hard -O2 -pipe")
-mfpu=neon。结果在板子上跑人脸检测,一帧要2秒。加上之后,直接降到0.3秒。你想想看,这差距有多大。所以,编译选项一定要根据你的板子硬件来配置,别图省事。
另外,链接选项也别忽视。比如 -Wl,--gc-sections 可以去除未使用的代码段,减小可执行文件体积。对于Flash空间紧张的嵌入式设备,这个选项很实用。
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
好了,交叉编译环境搭建这块,核心就是三件事:选对工具链、写好CMake工具链文件、配好编译选项。这三步走稳了,后面OpenCV的移植就成功了一半。