第四章 开发环境搭建:交叉编译工具链配置、IDE设置、版本控制与代码仓库初始化

好,咱们进入第四章。这一章说实话,是很多新手容易栽跟头的地方。我见过不少同学,代码写得挺溜,结果卡在环境搭建上,一卡就是半天。说白了,开发环境就是你的武器库,武器没磨好,上战场肯定吃亏。

嵌入式开发和PC端开发最大的区别,就是交叉编译。你想想看,你的电脑是x86架构,而售货机的主控芯片很可能是ARM Cortex-M系列。电脑上写的C代码,得编译成ARM能跑的机器码,这个过程就叫交叉编译。

4.1 交叉编译工具链配置

工具链是什么?就是一套工具的组合。包括编译器、链接器、汇编器、调试器等等。对于ARM Cortex-M系列,我个人习惯用GNU Arm Embedded Toolchain

4.1.1 下载与安装

去ARM官网下载最新版本。注意选对操作系统版本。我建议下载gcc-arm-none-eabi-xxx-linux.tar.bz2(Linux)或.exe(Windows)。

安装步骤很简单,但有个坑我要提醒你:

路径中不要有中文和空格! 我曾经在一个项目里,因为路径带了中文,折腾了整整一个下午,最后发现是工具链不认中文字符。从那以后,我的所有工具链都装在纯英文路径下。

Linux下解压后,配置环境变量:

# 编辑 ~/.bashrc 或 ~/.zshrc
export PATH=$PATH:/opt/gcc-arm-none-eabi-xxx/bin
# 验证是否安装成功
arm-none-eabi-gcc --version

Windows下更简单,安装时勾选“Add to PATH”就行。不过我还是建议手动检查一下环境变量。

4.1.2 验证工具链

写个最简单的程序测试一下:

// test.c
#include <stdio.h>

int main() {
    printf("Hello, Vending Machine!\n");
    return 0;
}

然后编译:

arm-none-eabi-gcc -c test.c -o test.o
arm-none-eabi-objdump -d test.o

如果能看到ARM汇编指令输出,说明工具链工作正常。嗯,这里要注意,printf在裸机环境下是不能直接用的,这里只是测试编译流程。

4.2 IDE设置:Keil / IAR / STM32CubeIDE

工具链配好了,接下来是IDE。这三款我都用过,各有千秋。我简单说说我的感受:

IDE 优点 缺点 适用场景
Keil MDK 编译快,调试稳定,生态好 收费,代码量有限制 商业项目、小规模开发
IAR EWARM 优化能力强,代码密度高 界面老旧,学习曲线陡 对代码大小有严格要求的项目
STM32CubeIDE 免费,集成CubeMX,开源 启动慢,插件多 个人学习、中小团队

我个人建议,如果是做售货机这种量产产品,用Keil或者IAR。为什么?因为稳定。我在项目中遇到过CubeIDE在调试时突然崩溃的情况,当时现场演示,场面一度很尴尬。

4.2.1 Keil配置要点

安装Keil后,第一件事是安装对应的器件包(Device Pack)。比如STM32F103系列,需要安装Keil.STM32F1xx_DFP.xxx.pack。

新建项目时,注意以下几点:

  • 选择正确的芯片型号:别选错了,否则外设驱动全废
  • 配置时钟源:外部晶振还是内部RC,决定了系统时钟精度
  • 设置优化等级:调试阶段用-O0,发布阶段用-Os
小技巧:在Keil的Options for Target -> C/C++里,加上-Wall -Wextra编译选项。这样编译器会给出更多警告,帮你提前发现潜在问题。

4.2.2 IAR配置要点

IAR的配置稍微复杂一点。它的项目选项层级很深,新手容易找不到地方。我一般这样设置:

  • Project -> Options -> General Options -> Target:选择对应芯片
  • Compiler -> Optimizations:选High (Balanced)
  • Linker -> Output:勾选Generate extra output file,格式选Intel extended HEX

为什么要生成HEX文件?因为生产烧录时,工厂通常只认HEX或者BIN格式。我曾经吃过这个亏,给工厂的烧录文件格式不对,导致整批板子都烧录失败,返工花了两天时间。

4.2.3 STM32CubeIDE配置要点

如果你用STM32CubeIDE,那配置就简单多了。它集成了CubeMX,图形化配置外设,自动生成初始化代码。但要注意:

  • 生成代码后,不要手动修改main.c中由CubeMX管理的部分
  • 自己的业务逻辑写在/* USER CODE BEGIN *//* USER CODE END */之间
  • 每次重新生成代码前,备份自己的修改
切记:CubeMX重新生成代码时,会覆盖main.c中非用户区域的代码。我见过有人把整个业务逻辑写在CubeMX的代码区,结果一重新生成,全没了。那种感觉,就像写了一个月的论文没保存一样。

4.3 版本控制(Git)与代码仓库初始化

说到版本控制,这是每个工程师的必修课。不管你是一个人开发还是团队协作,Git都是标配。我见过太多人用“最终版v1”、“最终版v2”、“打死不改版”来命名文件,结果最后自己都分不清哪个是最新的。

4.3.1 Git安装与配置

安装Git就不多说了,官网下载一路Next。安装完后,先配置用户信息:

git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
git config --global core.autocrlf input  # Linux/Mac
git config --global core.autocrlf true   # Windows

这里有个细节:core.autocrlf是为了解决Windows和Linux换行符不一致的问题。Windows用CRLF,Linux用LF。如果不设置,你从Windows提交的代码,在Linux上打开会多出^M字符。

4.3.2 创建.gitignore文件

嵌入式项目有很多编译中间文件,比如.o.hex.map.uvguix.*等。这些文件不应该提交到仓库。我建议在项目根目录创建.gitignore

# 编译输出
*.o
*.hex
*.bin
*.elf
*.map
*.lst

# IDE配置
*.uvguix.*
*.uvoptx
*.uvprojx
*.ewp
*.eww
.cproject
.project

# 调试文件
Debug/
Release/
*.bak
*.swp

# 系统文件
.DS_Store
Thumbs.db

为什么要忽略IDE配置文件?因为每个人的IDE版本、插件可能不同,提交这些文件会导致冲突。我建议只提交源码和项目描述文件。

4.3.3 初始化仓库

在项目目录下执行:

git init
git add .
git commit -m "Initial commit: 售货机嵌入式软件项目初始化"

然后关联远程仓库(比如GitHub、GitLab、Gitee):

git remote add origin https://github.com/yourname/vending-machine.git
git push -u origin master
建议:使用main分支作为主分支,而不是master。这是近年来的行业惯例。创建仓库时,可以设置默认分支为main

4.3.4 分支策略

对于售货机这种嵌入式项目,我推荐用Git Flow的简化版:

  • main:稳定版本,只合入经过测试的代码
  • develop:日常开发分支
  • feature/xxx:新功能分支,从develop分出
  • hotfix/xxx:紧急修复分支,从main分出

举个例子,你要开发一个硬币识别功能:

git checkout -b feature/coin-detection develop
# 开发完成后
git checkout develop
git merge --no-ff feature/coin-detection
git branch -d feature/coin-detection

为什么要用--no-ff?这样能保留分支历史,方便以后回溯。我在项目中就靠这个参数,快速定位过几次bug。

4.4 环境验证:跑一个LED闪烁程序

环境搭好了,总得验证一下。我习惯用最简单的LED闪烁程序来测试整个工具链是否通畅。

以STM32F103C8T6为例:

#include "stm32f10x.h"

void delay(void) {
    for(uint32_t i = 0; i < 500000; i++);
}

int main(void) {
    // 使能GPIOC时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
    
    // 配置PC13为推挽输出
    GPIOC->CRH &= ~(GPIO_CRH_CNF13 | GPIO_CRH_MODE13);
    GPIOC->CRH |= GPIO_CRH_MODE13_1;  // 输出模式,最大2MHz
    
    while(1) {
        GPIOC->BSRR = GPIO_BSRR_BR13;  // 低电平,LED亮
        delay();
        GPIOC->BSRR = GPIO_BSRR_BS13;  // 高电平,LED灭
        delay();
    }
}

编译通过后,用ST-Link下载到板子上。如果LED闪烁,恭喜你,整个开发环境搭建成功!

嗯,这里有个小插曲。我第一次用STM32时,怎么调LED都不亮。查了半天,发现是GPIO时钟没使能。你看,越是基础的东西,越容易忽略。

好了,第四章就到这里。环境搭建是磨刀不误砍柴工,花点时间把基础打牢,后面写代码会顺畅很多。下一章我们开始讲项目结构设计,那才是真正考验架构能力的地方。