2、开发环境搭建:交叉编译工具链安装、Makefile基础、CMake基础、版本控制(Git)入门
说实话,搭建开发环境这事儿,看着简单,但坑是真不少。我见过太多人卡在第一步——工具链装不上,或者装上了路径不对,编译出来跑不了。咱们今天就把这事儿捋清楚。
2.1 交叉编译工具链安装
什么叫交叉编译?说白了,就是在你的电脑上编译出能在另一个设备上跑的程序。比如你在 x86 的笔记本上编译 ARM 的固件。嗯,这里要注意,工具链的选择直接决定了你后面能不能顺利干活。
2.1.1 工具链的选择
我个人习惯用 Linaro 提供的工具链,稳定且社区活跃。当然,如果你用的是芯片厂商的 SDK,那直接用他们配好的也行。
- ARM 架构:推荐
gcc-arm-none-eabi(裸机)或gcc-arm-linux-gnueabihf(带 Linux 系统) - RISC-V 架构:推荐
riscv64-unknown-elf-gcc - MIPS 架构:推荐
mips-linux-gnu-gcc
2.1.2 安装步骤(以 ARM 为例)
下载解压,然后配置环境变量。就这么两步,但细节决定成败。
# 下载工具链(以 gcc-arm-10.3-2021.07-x86_64-arm-none-eabi.tar.xz 为例)
wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/10.3-2021.07/gcc-arm-none-eabi-10.3-2021.07-x86_64-linux.tar.bz2
# 解压到 /opt 目录
sudo tar -xjf gcc-arm-none-eabi-10.3-2021.07-x86_64-linux.tar.bz2 -C /opt/
# 配置环境变量(写入 ~/.bashrc 或 ~/.zshrc)
export PATH=$PATH:/opt/gcc-arm-none-eabi-10.3-2021.07/bin
export CROSS_COMPILE=arm-none-eabi-
export ARCH=arm
# 使配置生效
source ~/.bashrc
# 验证安装
arm-none-eabi-gcc --version
setenv.sh,每次开新终端时 source 一下。这样不同项目可以用不同版本的工具链,互不干扰。
2.2 Makefile 基础
Makefile 这东西,说白了就是告诉编译器怎么干活。你想想看,一个项目几十个源文件,总不能每次都手动敲 gcc 命令吧?
2.2.1 最简单的 Makefile
# 目标文件: 依赖文件
main: main.o utils.o
gcc -o main main.o utils.o
main.o: main.c
gcc -c main.c
utils.o: utils.c
gcc -c utils.c
clean:
rm -f *.o main
嗯,这里要注意,Makefile 里的缩进必须用 Tab 键,不能用空格。我刚开始学的时候被这个坑过,编译报错半天找不到原因。
2.2.2 变量与自动推导
实际项目中,我们不会这么傻写。用变量和自动推导规则,代码会清爽很多。
CC = gcc
CFLAGS = -Wall -O2
TARGET = main
OBJS = main.o utils.o
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJS) $(TARGET)
.PHONY: clean
$@ 表示目标文件,$^ 表示所有依赖文件,$< 表示第一个依赖文件。这三个自动变量,你写 Makefile 时天天都会用到。
2.3 CMake 基础
Makefile 虽然好用,但跨平台时就很头疼。CMake 的出现就是为了解决这个问题——你写一份 CMakeLists.txt,它能自动生成对应平台的 Makefile 或项目文件。
2.3.1 一个简单的 CMake 项目
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(MyProject)
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 11)
# 添加可执行文件
add_executable(main main.c utils.c)
# 添加头文件路径
target_include_directories(main PRIVATE include)
编译方式也很简单:
mkdir build
cd build
cmake ..
make
rm -rf build 就行,不用一个个删 .o 文件。
2.3.2 交叉编译的 CMake 配置
做嵌入式开发时,CMake 的交叉编译配置是必学的。我曾经在给一个 ARM 项目配 CMake 时,忘了设置 CMAKE_SYSROOT,结果链接器找不到标准库,折腾了一下午。
# 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_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
使用时加上 -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake 参数即可。
2.4 版本控制(Git)入门
Git 这东西,说实话,不会用的话项目根本没法管。我见过有人用「复制文件夹+改日期」来管理版本,结果出了 bug 根本不知道改了什么。
2.4.1 基本工作流
# 初始化仓库
git init
# 添加文件到暂存区
git add main.c utils.c
# 提交到本地仓库
git commit -m "初始提交:添加主程序与工具函数"
# 查看状态
git status
# 查看提交历史
git log --oneline
2.4.2 分支管理
我个人习惯用 main 分支作为稳定版本,dev 分支做开发。每次修 bug 或加功能,都从 dev 拉一个新分支,改完再合并回去。
# 创建并切换到 dev 分支
git checkout -b dev
# 在 dev 上创建功能分支
git checkout -b feature/add-uart-driver
# 开发完成后合并回 dev
git checkout dev
git merge feature/add-uart-driver
# 删除功能分支
git branch -d feature/add-uart-driver
git pull --rebase 一下,保持提交历史的整洁。
2.4.3 .gitignore 文件
编译生成的 .o 文件、build 目录、IDE 的配置文件,这些都不应该提交到仓库里。写一个 .gitignore 文件,省心很多。
# .gitignore
build/
*.o
*.elf
*.bin
*.hex
.vscode/
.idea/
*.swp