当前位置: 首页 > news >正文

第四篇:交叉编译与工具链文件——一套代码,多个平台

📌 前言

前几篇文章我们写了CMakeLists.txt,但里面有一些硬编码的内容:

  • arm-none-eabi-gcc(编译器名称)

  • -mcpu=cortex-m3(CPU 型号)

  • stm32f103.ld(链接脚本)

如果换一个芯片(比如从 STM32F103 换到 STM32F407),这些内容都要改。有没有办法让 CMakeLists.txt 完全不用动,只改一个文件就能切换芯片呢?

答案是:工具链文件(Toolchain File)

🧠 一、什么是交叉编译?

在继续之前,先理解一个概念:交叉编译

  • 本地编译:在 PC 上编译,在 PC 上运行(比如你用 VS Code 写了一个 Hello World,在电脑上运行)

  • 交叉编译:在 PC 上编译,在另一个平台上运行(比如在 PC 上编译出单片机可以运行的机器码)

单片机开发就是典型的交叉编译。我们需要告诉 CMake:

  1. 编译器不是系统的gcc,而是arm-none-eabi-gcc

  2. 目标系统不是 Windows/Linux,而是裸机

  3. 编译选项要根据芯片型号来定

这些信息,都可以放在工具链文件里。

📝 二、工具链文件长什么样?

工具链文件本质上就是一个 CMake 脚本,里面set()了一系列变量。

cmake/arm-none-eabi-gcc.cmake

# ============================================================ # 工具链文件:ARM GCC 交叉编译配置 # ============================================================ # 1. 目标系统 set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR arm) # 2. 编译器 set(CMAKE_C_COMPILER arm-none-eabi-gcc) set(CMAKE_CXX_COMPILER arm-none-eabi-g++) set(CMAKE_ASM_COMPILER arm-none-eabi-gcc) # 3. 编译选项(可由用户覆盖) set(MCU_FLAGS "-mcpu=cortex-m3 -mthumb") set(CMAKE_C_FLAGS "${MCU_FLAGS} -Wall -Wextra -std=c11" CACHE STRING "" FORCE) set(CMAKE_CXX_FLAGS "${MCU_FLAGS} -Wall -Wextra -std=c++14" CACHE STRING "" FORCE) set(CMAKE_ASM_FLAGS "${MCU_FLAGS}" CACHE STRING "" FORCE) set(CMAKE_EXE_LINKER_FLAGS "${MCU_FLAGS} -T ${CMAKE_SOURCE_DIR}/stm32f103.ld" CACHE STRING "" FORCE) # 4. 调试信息 set(CMAKE_C_FLAGS_DEBUG "-O0 -g3" CACHE STRING "" FORCE) set(CMAKE_C_FLAGS_RELEASE "-Os" CACHE STRING "" FORCE)

注意CACHE STRING "" FORCE的写法,它让这些变量可以被用户在命令行覆盖。

🚀 三、如何使用工具链文件?

使用工具链文件非常简单,在cmake配置时通过-DCMAKE_TOOLCHAIN_FILE参数指定:

cmake -B build -DCMAKE_TOOLCHAIN_FILE=cmake/arm-none-eabi-gcc.cmake cmake --build build

如果你的工程支持多个芯片平台,可以准备多个工具链文件:

cmake/ ├── arm-none-eabi-gcc-stm32f1.cmake # STM32F1 ├── arm-none-eabi-gcc-stm32f4.cmake # STM32F4 ├── arm-none-eabi-gcc-stm32l4.cmake # STM32L4 └── esp32.cmake # ESP32

切换芯片,只需要换一个工具链文件。CMakeLists.txt 完全不用动。

🔄 四、实战:同一份代码编译到不同芯片

4.1 项目结构
my_firmware/ ├── CMakeLists.txt # 一份!完全不用动 ├── cmake/ │ ├── stm32f103.cmake # STM32F103 工具链 │ └── stm32f407.cmake # STM32F407 工具链 ├── src/ │ └── main.c # 一份!完全不用动 └── ld/ ├── stm32f103.ld # F103 链接脚本 └── stm32f407.ld # F407 链接脚本
4.2 CMakeLists.txt(不再包含硬件相关的硬编码)
cmake_minimum_required(VERSION 3.20) project(MyFirmware C ASM) # 编译选项和链接脚本都在工具链文件中定义 # CMakeLists.txt 只关注“有哪些源文件” add_executable(my_firmware src/main.c src/system.c ) target_include_directories(my_firmware PRIVATE inc/ drivers/inc/ )
4.3 编译到 STM32F103
cmake -B build_f103 -DCMAKE_TOOLCHAIN_FILE=cmake/stm32f103.cmake cmake --build build_f103
4.4 编译到 STM32F407
cmake -B build_f407 -DCMAKE_TOOLCHAIN_FILE=cmake/stm32f407.cmake cmake --build build_f407

同一份代码,同一个 CMakeLists.txt,两个不同的芯片,两套独立的编译产物。

⚙️ 五、CMake Presets:更优雅的切换方式

如果你的项目支持多个平台,每次都要输入长长的命令有点麻烦。CMake 3.19+ 支持CMake Presets,可以把配置写在CMakePresets.json里。

CMakePresets.json

{ "version": 3, "configurePresets": [ { "name": "stm32f103", "displayName": "STM32F103", "generator": "Ninja", "binaryDir": "${sourceDir}/build_f103", "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/cmake/stm32f103.cmake" } }, { "name": "stm32f407", "displayName": "STM32F407", "generator": "Ninja", "binaryDir": "${sourceDir}/build_f407", "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/cmake/stm32f407.cmake" } } ] }

之后只需要:

cmake --preset stm32f103 cmake --build build_f103

📝 六、总结

工具链文件的核心价值:把“硬件相关”的配置从 CMakeLists.txt 中抽离出来

放在 CMakeLists.txt 中放在工具链文件中
项目名称编译器路径
源文件列表CPU 型号
头文件路径编译选项
模块依赖链接脚本

这样做的好处:

  • CMakeLists.txt 只关注“代码组织”,不关注“硬件差异”

  • 换芯片 = 换工具链文件,业务代码完全不动

  • 同一份代码,可以在 CI 中并行编译到多个平台

http://www.jsqmd.com/news/1402936/

相关文章:

  • PDF怎么解密,删除保护?我又找到了一款免费办公神器!内置几百种功能,可离线使用,赶紧冲!
  • ros2 从零开始zed双目相机在rviz显示出图像和深度信息
  • Java顺序表实现:从静态数组到动态扩容的底层原理与实战
  • PyCharm中利用Mermaid与PlantUML实现Markdown流程图绘制全攻略
  • 代码随想录day13
  • Grok 4.6智能体长时运行能力解析与工程实践指南
  • OpenClaw技能系统实战:构建可扩展、安全的AI智能体
  • ARCH与交叉编译器的关系
  • Day5语法:循环-分支语句
  • 南宁防水修缮品牌怎么选?漏水维修科普、避坑与靠谱服务商参考 - 收录优先
  • PCB镀铜均匀性怎么做?工程师揭秘关键工艺
  • 停车场道闸系统选购避坑指南:选对设备,大幅降低后期运维难题
  • 纯WINDOWS API的C++2D小游戏Subterra(目前还未开发,实时更新)
  • 从PID到模型预测:管道小球摆杆控制的核心难点与工程实现
  • Linux vsftpd匿名FTP服务器搭建:安全配置与被动模式故障排查
  • OpenClaw像素风赛博办公室:AI Agent工作流可视化与实时监控实践
  • 华为eNSP设备启动失败:从虚拟化冲突到权限配置的完整排错指南
  • 工业陶瓷零件的材料选型与精度控制:从氧化锆到氮化硅的几个技术点
  • 【无标题】买房收房别只看颜值,这些坑你根本看不见 在本地房产交易活跃的当下,越来越多人意识到:房子表面光鲜不代表住得安心。尤其是二手房翻新或精装交付,墙面平整、地板漂亮背后,可能藏着水电隐患、防水失效
  • Quartus内置ModelSim‑Altera报错Error: Failure to obtain a Verilog simulation license. Unable to checkout
  • Cursor 修复循环校验翻车:3次追问后账单超预算200%,我这样设熔断
  • 居家如何养护艾草暖宫精油日常养护感受海艾独有的温润体感
  • PDF 临时处理总要四处找工具?常用操作放进一个工具箱
  • 脱皮腰果色选怎样减少黑斑、碎仁与异色料混入
  • 上海市口碑好的防水补漏维修公司怎么找_屋面防水本地维修队伍水准盘点,挑选要点深度剖析 - 雨婺虹修缮
  • 网络安全入门:小白必学的极简框架与实战指南
  • OpenClaw部署运维指南:从安装到稳定运行的AI智能体实践
  • Windows鼠标异常唤醒电脑的排查与解决全攻略
  • IDEA代码报红但能运行?深度解析索引缓存机制与系统化解决方案
  • Java枚举类深度解析:从常量管理到状态机设计的实战指南