1、自动化基石:嵌入式开发环境搭建与工具链配置(GCC、Makefile、CMake)
说实话,我见过太多工程师在开发环境上栽跟头了。明明代码逻辑没问题,编译就是过不去。最后发现,是工具链没配好。嵌入式开发的第一步,就是把你的「武器库」收拾利索。今天我们就聊聊 GCC、Makefile 和 CMake 这三样东西。
1.1 为什么非得自己搭环境?
你想想看,嵌入式开发不像写个 Python 脚本。你的代码最终要跑在单片机或者 ARM 芯片上。那芯片的指令集跟你的电脑不一样。所以你需要一个「翻译官」——交叉编译器。
IDE 虽然方便,但隐藏了太多细节。我在项目中遇到过好几次,IDE 升级后,原来的工程就编译不过了。你根本不知道它背后调了哪些参数。自己搭环境,说白了就是掌握主动权。
1.2 GCC:编译器界的瑞士军刀
GCC(GNU Compiler Collection)是嵌入式开发的核心。它能把 C/C++ 代码变成机器码。我个人习惯用 arm-none-eabi-gcc 这个版本,专门针对 ARM Cortex-M 系列芯片。
安装其实不复杂。以 Ubuntu 为例:
sudo apt-get update
sudo apt-get install gcc-arm-none-eabi
装完验证一下:
arm-none-eabi-gcc --version
看到版本号就说明成了。嗯,这里要注意:Windows 用户建议用 MSYS2 或者 WSL。别在 Windows 原生 cmd 里折腾,坑太多。
1.3 Makefile:自动化编译的基石
有了编译器,你总不能每次都手动敲一长串命令吧?
arm-none-eabi-gcc -c main.c -o main.o
arm-none-eabi-gcc -c uart.c -o uart.o
arm-none-eabi-gcc main.o uart.o -o firmware.elf
项目小还好说。一旦有几十个源文件,手动编译会疯掉的。Makefile 就是来解决这个问题的。
一个最简单的 Makefile 长这样:
CC = arm-none-eabi-gcc
CFLAGS = -Wall -O2 -mcpu=cortex-m4
firmware.elf: main.o uart.o
$(CC) $^ -o $@
main.o: main.c
$(CC) $(CFLAGS) -c $< -o $@
uart.o: uart.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f *.o firmware.elf
这里有几个关键点:
$@表示目标文件$^表示所有依赖文件$<表示第一个依赖文件
你只要在终端敲 make,它就会自动检查哪些文件改了,只重新编译改过的文件。这就是增量编译,省时间。
flash 目标,直接调用 OpenOCD 或者 JLink 烧录。这样一条 make flash 就搞定编译+烧录,省心。
1.4 CMake:跨平台的构建系统
Makefile 虽然好用,但跨平台就麻烦了。Linux 用一套路径,Windows 用另一套。这时候 CMake 就派上用场了。
CMake 不直接编译代码。它先生成 Makefile(或者 Ninja 文件),然后再调用 make 去编译。说白了,它是个「构建系统的构建系统」。
一个典型的 CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
project(Firmware)
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
set(CMAKE_C_FLAGS "-Wall -O2 -mcpu=cortex-m4")
add_executable(firmware.elf
main.c
uart.c
gpio.c
)
target_include_directories(firmware.elf PRIVATE
inc
)
使用方式:
mkdir build
cd build
cmake ..
make
为什么要先建一个 build 目录?这叫「外部构建」。所有编译产生的中间文件都在 build 里,不会污染源码目录。我刚开始用 CMake 时没注意这点,源码目录乱成一锅粥。
1.5 三者的配合关系
我画个简单的对应关系:
| 工具 | 作用 | 适用场景 |
|---|---|---|
| GCC | 编译代码成机器码 | 所有嵌入式项目 |
| Makefile | 自动化编译流程 | 中小型项目,Linux 环境 |
| CMake | 跨平台生成构建文件 | 大型项目,需要跨平台 |
实际项目中,我通常这样用:
- 个人小项目:直接手写 Makefile
- 团队协作:用 CMake + Makefile 组合
- 商业产品:CMake + Ninja(比 Make 更快)
1.6 实战:从零搭建一个 STM32 项目
光说不练假把式。我们动手搭一个完整的 STM32F4 项目。
第一步:准备启动文件和链接脚本。这些一般芯片厂商会提供。STM32 的话,从 ST 官网下载 HAL 库,里面就有。
第二步:写 CMakeLists.txt:
cmake_minimum_required(VERSION 3.15)
project(STM32F4_Demo)
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
# 芯片型号
set(MCU_FLAGS "-mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16")
set(CMAKE_C_FLAGS "${MCU_FLAGS} -Wall -O2 -g")
# 源文件
file(GLOB SOURCES
"src/*.c"
"startup/startup_stm32f407xx.s"
)
add_executable(${PROJECT_NAME}.elf ${SOURCES})
# 链接脚本
target_link_options(${PROJECT_NAME}.elf PRIVATE
-T "STM32F407VGTx_FLASH.ld"
)
# 生成 hex 和 bin
add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -O ihex ${PROJECT_NAME}.elf ${PROJECT_NAME}.hex
COMMAND ${CMAKE_OBJCOPY} -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin
)
第三步:编译:
mkdir build && cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/gcc-arm-none-eabi.cmake
make
看到生成了 .hex 和 .bin 文件,就说明环境搭好了。
1.7 总结
环境搭建这东西,说白了就是一次配置,长期受益。你花半天时间把工具链理清楚,后面写代码、调试、烧录都会顺畅很多。
我个人建议:别急着写业务代码。先把 Makefile 或者 CMake 的模板准备好。以后每个新项目,直接复制模板,改改芯片型号和源文件列表就行。这才是自动化该有的样子。
下一章,我们会聊聊版本控制。Git 在嵌入式开发中怎么用?分支策略怎么定?到时候再细说。