第二章 嵌入式开发环境搭建
说实话,嵌入式开发最让人头疼的往往不是写代码本身,而是环境搭建。我见过太多新手,甚至有些老手,在环境配置上浪费一整天。今天我就把这几年的经验梳理一下,帮你少走些弯路。
2.1 交叉编译工具链安装
交叉编译,说白了就是在你的电脑上编译出能在ARM板子上跑的程序。为什么不能直接在板子上编译?因为板子性能太弱了,编译个Linux内核可能要等半天。
核心概念:交叉编译工具链 = 编译器 + 链接器 + 库文件 + 调试工具
我个人习惯用Linaro提供的工具链,稳定且更新及时。安装步骤其实很简单:
# 下载ARM GCC工具链
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 -xf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/
# 配置环境变量
export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin
小技巧:建议把环境变量写到 ~/.bashrc 里,这样每次打开终端就不用重新设置了。我刚开始时忘了这步,每次都要手动export,烦得很。
验证是否安装成功:
arm-linux-gnueabihf-gcc --version
# 如果看到版本信息,说明安装成功
写个简单的测试程序试试:
// hello.c
#include <stdio.h>
int main() {
printf("Hello, Embedded World!\n");
return 0;
}
// 编译
arm-linux-gnueabihf-gcc -o hello hello.c
// 查看文件信息
file hello
# 输出: hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV)
看到ARM字样,说明编译成功了。嗯,这里要注意:如果你的宿主机是64位,可能需要安装32位兼容库,否则会报错。
我曾经踩过的坑:有一次我下载了错误的架构版本,比如板子是armv7,我下了armv8的工具链。编译出来的程序在板子上直接段错误。所以一定要确认你的目标架构。
2.2 嵌入式Linux系统构建
构建嵌入式Linux系统,说白了就是给板子做一个最小化的操作系统。我推荐用Buildroot,它比Yocto简单太多了。
为什么选Buildroot?你想想看,Yocto第一次构建要下载几十G的东西,编译几个小时。Buildroot轻量多了,适合快速验证。
# 下载Buildroot
git clone git://git.buildroot.net/buildroot
cd buildroot
# 配置
make menuconfig
# 关键配置项:
# Target options -> Target Architecture -> ARM (little endian)
# Target options -> Target Architecture Variant -> cortex-a7
# Toolchain -> Toolchain type -> External toolchain
# System configuration -> Root filesystem overlay directory
# 开始构建
make -j4
构建完成后,在 output/images 目录下会生成:
| 文件 | 说明 |
|---|---|
| zImage | 压缩后的内核镜像 |
| rootfs.ext4 | 根文件系统 |
| vexpress-v2p-ca9.dtb | 设备树文件 |
个人经验:第一次构建时,建议只选最基本的包。我见过有人一股脑选了所有包,结果编译了3小时,最后发现很多都用不上。先跑起来,再慢慢加功能。
2.3 QEMU模拟器配置
没有板子怎么办?用QEMU模拟。我在项目中经常先用QEMU验证,没问题了再烧到真板子上。这样调试效率高很多。
# 安装QEMU
sudo apt-get install qemu-system-arm
# 启动模拟器
qemu-system-arm \
-M vexpress-a9 \
-kernel zImage \
-dtb vexpress-v2p-ca9.dtb \
-drive file=rootfs.ext4,if=sd,format=raw \
-append "console=ttyAMA0 root=/dev/mmcblk0" \
-nographic
启动后你会看到Linux启动日志,最后出现登录提示符。默认用户名root,密码空。
调试技巧:加上 -s 参数可以启动GDB调试服务,然后用 arm-linux-gnueabihf-gdb 远程连接。我调试驱动时经常这么干。
网络配置也很重要:
# 使用tap网络,让QEMU和宿主机互通
qemu-system-arm \
-M vexpress-a9 \
-kernel zImage \
-dtb vexpress-v2p-ca9.dtb \
-drive file=rootfs.ext4,if=sd,format=raw \
-append "console=ttyAMA0 root=/dev/mmcblk0" \
-net nic -net tap,ifname=tap0 \
-nographic
注意:使用tap网络需要root权限,而且要先创建tap接口。我建议用脚本自动化这个过程,不然每次都要手动敲命令。
2.4 版本控制工具Git与GitLab部署
版本控制是DevOps的基石。Git大家都熟,但嵌入式项目有它的特殊性——二进制文件多、编译产物大、依赖管理复杂。
我建议的Git工作流:
- 主分支(main):只放稳定版本,每次提交都要经过CI测试
- 开发分支(develop):日常开发,允许小问题
- 功能分支(feature/*):每个新功能一个分支
- 发布分支(release/*):准备发布时创建
# 初始化仓库
git init
git add .
git commit -m "Initial commit: embedded project skeleton"
# 添加.gitignore,忽略编译产物
echo "build/
*.o
*.elf
*.bin
output/" > .gitignore
# 创建开发分支
git checkout -b develop
GitLab部署我用Docker,简单快速:
# docker-compose.yml
version: '3'
services:
gitlab:
image: gitlab/gitlab-ce:latest
restart: always
hostname: 'gitlab.example.com'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://gitlab.example.com'
ports:
- '80:80'
- '443:443'
- '22:22'
volumes:
- './gitlab/config:/etc/gitlab'
- './gitlab/logs:/var/log/gitlab'
- './gitlab/data:/var/opt/gitlab'
部署经验:GitLab对内存要求不低,至少4G。我曾在2G的服务器上跑,结果动不动就502。建议至少8G内存,否则你会很痛苦。
部署完成后,配置CI/CD流水线:
# .gitlab-ci.yml
stages:
- build
- test
- deploy
build_job:
stage: build
script:
- make clean
- make all
artifacts:
paths:
- build/*.bin
test_job:
stage: test
script:
- qemu-system-arm -M vexpress-a9 -kernel build/zImage -dtb build/vexpress-v2p-ca9.dtb -drive file=build/rootfs.ext4,if=sd,format=raw -append "console=ttyAMA0 root=/dev/mmcblk0" -nographic -serial stdio
deploy_job:
stage: deploy
script:
- scp build/*.bin user@target-board:/tmp/
only:
- main
这个流水线实现了:代码提交后自动编译、在QEMU中运行测试、通过后自动部署到目标板。说白了,这就是嵌入式DevOps的核心。
总结一下:环境搭建是基础,但也是最容易出问题的地方。交叉编译工具链选对版本、Buildroot配置精简、QEMU验证先行、GitLab自动化流水线。把这四步走稳了,后面的开发就会顺畅很多。
我记得第一次搭建完整环境时,光工具链就折腾了两天。现在有了这些经验,半小时就能搞定。所以别怕踩坑,每个坑都是成长的机会。