必须通过工具链文件显式指定:设CMAKE_SYSTEM_NAME Generic、CMAKE_SYSTEM_PROCESSOR arm、CMAKE_C_COMPILER arm-none-eabi-gcc等,并用-DCMAKE_TOOLCHAIN_FILE=...调用;仅靠CC环境变量不可靠。

怎么让CMake用arm-none-eabi-gcc而不是系统gcc
CMake默认找gcc,不会自动识别交叉编译工具链。必须显式告诉它用arm-none-eabi-gcc,否则编译会失败或生成x86代码。
最可靠的方式是写一个工具链文件(比如arm-toolchain.cmake),内容包括:
set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_C_COMPILER arm-none-eabi-gcc) set(CMAKE_CXX_COMPILER arm-none-eabi-g++) set(CMAKE_OBJCOPY arm-none-eabi-objcopy) set(CMAKE_SIZE arm-none-eabi-size)
调用时加参数:cmake -DCMAKE_TOOLCHAIN_FILE=arm-toolchain.cmake -G "Unix Makefiles" ..
- 不要只靠
export CC=arm-none-eabi-gcc,CMake可能忽略环境变量 -
CMAKE_SYSTEM_NAME Generic适用于裸机(无OS)项目;若用FreeRTOS等,可设为FreeRTOS,但Generic更通用 - 确保
arm-none-eabi-gcc已在$PATH中,运行arm-none-eabi-gcc --version能成功返回
怎么在CMake里生成.bin和.hex文件
CMake本身不内置.bin/.hex生成逻辑,得靠add_custom_command调用arm-none-eabi-objcopy完成转换。
立即学习“C++免费学习笔记(深入)”;
假设你的主可执行目标叫firmware.elf(由add_executable(firmware.elf ...)定义),在CMakeLists.txt末尾加:
add_custom_target(firmware.bin
COMMAND ${CMAKE_OBJCOPY} -O binary ${CMAKE_CURRENT_BINARY_DIR}/firmware.elf ${CMAKE_CURRENT_BINARY_DIR}/firmware.bin
DEPENDS firmware.elf
)
add_custom_target(firmware.hex
COMMAND ${CMAKE_OBJCOPY} -O ihex ${CMAKE_CURRENT_BINARY_DIR}/firmware.elf ${CMAKE_CURRENT_BINARY_DIR}/firmware.hex
DEPENDS firmware.elf
)
- 务必用
DEPENDS firmware.elf,否则firmware.elf还没生成就去跑objcopy,会报“no input files” -
-O binary输出纯二进制(适合烧录到Flash起始地址),-O ihex输出Intel HEX(带地址信息,兼容更多烧录器) - 别用
OUTPUT属性试图让CMake管理这些文件——CMake对裸机输出文件的依赖推导很弱,add_custom_target更可控
链接脚本和启动文件怎么接入CMake
裸机固件必须指定链接脚本(linker.ld)和启动代码(如startup_stm32f407xx.s),否则arm-none-eabi-gcc会链接失败或跳转到错误地址。
在CMakeLists.txt中:
target_link_options(firmware.elf PRIVATE
"-T${CMAKE_SOURCE_DIR}/ld/linker.ld"
"-nostdlib"
"-Wl,-Map=${CMAKE_CURRENT_BINARY_DIR}/firmware.map"
)
target_sources(firmware.elf PRIVATE
"${CMAKE_SOURCE_DIR}/src/startup_stm32f407xx.s"
"${CMAKE_SOURCE_DIR}/src/main.cpp"
)
-
-nostdlib必须加,否则链接器会尝试拉入glibc相关符号,裸机环境不存在 - 启动文件后缀如果是
.s(小写),CMake默认用C编译器处理;要确保它被arm-none-eabi-gcc -x assembler-with-cpp处理,可在target_sources后加:set_source_files_properties("${CMAKE_SOURCE_DIR}/src/startup_stm32f407xx.s" PROPERTIES LANGUAGE ASM) -
linker.ld路径必须是绝对路径(${CMAKE_SOURCE_DIR}/...),相对路径在out-of-source构建时容易出错
常见错误:size/objcopy找不到或权限拒绝
运行make firmware.bin时报arm-none-eabi-objcopy: command not found或Permission denied,本质不是CMake问题,而是工具链没装全或路径不对。
- 检查是否安装了
binutils-arm-none-eabi(Ubuntu/Debian)或gcc-arm-none-eabi包(macOS用brew install arm-gcc-bin) - 运行
which arm-none-eabi-objcopy,确认命令存在且可执行;如果路径含空格或中文,重装到纯英文路径 - 某些发行版把
objcopy装在/usr/lib/arm-none-eabi/bin/objcopy但没建软链——手动加软链:sudo ln -s /usr/lib/arm-none-eabi/bin/objcopy /usr/bin/arm-none-eabi-objcopy - Windows用户注意:MSYS2/Cygwin下路径分隔符不影响,但PowerShell中
cmake -G "MinGW Makefiles"可能冲突,建议统一用Unix Makefiles+mingw32-make
裸机CMake最难的不是语法,而是所有外部依赖(工具链、启动文件、链接脚本)必须严格匹配芯片型号和内存布局——差一个字节的地址偏移,.bin烧进去就跑飞。


















