2、开发环境搭建:Keil MDK / STM32CubeIDE安装、ARM GCC工具链配置、串口调试工具准备

好,咱们正式开始动手了。

做单片机深度学习推理,说白了就是让一颗Cortex-M内核的芯片去跑神经网络。你想想看,这活儿对工具链的要求其实挺高的——既要能编译C/C++,又要能链接浮点库,还得方便调试。我个人习惯把环境分成三块:IDE(集成开发环境)、编译器工具链、以及串口调试工具。这三样配齐了,后面写代码、调bug才能顺风顺水。

2.1 Keil MDK 安装与配置

Keil MDK,老牌工具了。我在项目中用过很多次,尤其是做STM32F4/F7系列的时候。它的编译器效率高,调试器也好用。

2.1.1 下载与安装

去ARM官网或者Keil官网下载MDK-ARM。注意版本——我个人建议用5.38或更新的版本,对Cortex-M7和M33支持更好。安装时一路Next就行,但有个坑:安装路径不要有中文。我曾经因为路径带了个“项目”二字,结果编译死活报错,查了半天才发现是路径编码问题。

⚠️ 避坑指南: 安装完成后,记得用管理员权限运行一次Keil uVision。否则后面加载Pack包时可能会权限不足。

2.1.2 Pack包管理

Keil的Pack包是灵魂。打开Pack Installer,搜索你的芯片型号。比如我用STM32F407,就搜“STM32F4xx_DFP”。下载安装后,才能在Device列表里选到具体型号。

嗯,这里要注意:Pack版本不是越新越好。我遇到过某个新版本Pack改了外设寄存器地址映射,导致老项目跑飞。所以,如果项目稳定,就别轻易升级Pack。

2.1.3 工程模板配置

新建工程时,选好芯片后,会弹出一个Manage Run-Time Environment窗口。这里我一般只勾选:

  • CMSIS-CORE:必须的,Cortex-M内核抽象层
  • Device-Startup:启动文件,芯片上电第一段代码
  • Compiler-I/O:如果你要用printf串口输出,这里选“STDERR”和“STDIN”

其他像USB、文件系统之类的,等用到再勾,别一股脑全选上——编译慢,还容易冲突。

2.2 STM32CubeIDE 安装与配置

如果你喜欢开源、免费,或者做HAL库开发,那STM32CubeIDE是首选。它基于Eclipse,集成了CubeMX图形化配置工具。说白了,你点点鼠标就能生成初始化代码,省去手写寄存器配置的麻烦。

2.2.1 下载与安装

去ST官网下载。注意区分Windows版和Linux版。安装时同样注意路径无中文。我第一次用CubeIDE时,直接默认路径装在了C盘,结果后来项目多了,C盘爆红……建议装到D盘或E盘。

2.2.2 首次启动与工作空间

启动时会让你选Workspace(工作空间)。我习惯把每个大项目单独建一个Workspace,比如“STM32_AI_Project”。这样不同项目的配置不会互相干扰。

进入界面后,点“Help -> Install New Software”,可以装一些插件。不过说实话,做嵌入式深度学习,默认的就够了。别装太多花里胡哨的插件,容易卡。

2.2.3 导入CubeMX工程

CubeIDE可以直接打开.ioc文件。双击.ioc文件,会自动启动图形化配置界面。配置好时钟、外设后,点“Generate Code”,代码就生成了。

这里有个小技巧:生成代码时,把“Generate peripheral initialization as a pair of .c/.h files per peripheral”勾上。这样每个外设单独一个文件,后期维护方便。我刚开始做项目时没勾,所有外设初始化挤在一个main.c里,那叫一个乱……

2.3 ARM GCC 工具链配置

Keil和CubeIDE都自带编译器,但有时候我们需要用ARM GCC。为什么?因为GCC是开源的,而且可以精细控制编译优化选项。比如做量化推理时,我经常用GCC的“-O3 -ffast-math”来压榨性能。

2.3.1 下载ARM GCC

去ARM官网下载“GNU Arm Embedded Toolchain”。选最新稳定版就行。我目前用的是10.3-2021.10版本,稳定且对Cortex-M7支持好。

下载后解压到一个目录,比如:C:\arm-gcc-toolchain。然后把这个目录下的bin文件夹路径加到系统环境变量PATH里。

💡 验证方法: 打开命令行,输入 arm-none-eabi-gcc --version。如果显示版本号,说明配置成功。

2.3.2 在Keil中使用GCC

Keil默认用ARMCC(ARM自家的编译器)。但你可以切换成GCC。方法:

  1. 打开工程选项(Project -> Options for Target)
  2. 在“Target”标签页,把“ARM Compiler”改成“Use GCC”
  3. 在“C/C++”标签页,设置GCC的路径和编译选项

不过说实话,我很少在Keil里用GCC。因为Keil的ARMCC对STM32优化更好。GCC更适合在命令行下用,配合Makefile做自动化构建。

2.3.3 在CubeIDE中配置GCC

CubeIDE默认用的是自带的GCC。如果你想换版本,可以在“Window -> Preferences -> C/C++ -> Build -> Settings”里,找到“Global Tools Paths”,把“GCC ARM”路径改成你自己的。

嗯,这里要注意:不同版本的GCC生成的浮点ABI可能不兼容。比如你用GCC 9编译的库,链接时用GCC 10,可能会报“conflicting CPU architectures”错误。所以,整个项目统一用一个版本的GCC。

2.4 串口调试工具准备

做嵌入式开发,串口就是你的眼睛。没有串口,你都不知道程序跑到哪一步了。我习惯用串口打印调试信息,尤其是打印神经网络的中间层输出——看看激活值是不是正常范围。

2.4.1 常用串口工具

工具名称 特点 推荐指数
PuTTY 轻量、稳定,支持SSH和串口 ⭐⭐⭐⭐
SecureCRT 功能强大,支持脚本、日志记录 ⭐⭐⭐⭐⭐
串口助手(野火/正点原子) 国产、免费、界面友好 ⭐⭐⭐⭐
MobaXterm 集成串口、SSH、SFTP,功能全面 ⭐⭐⭐⭐⭐

我个人最常用的是MobaXterm。因为它不仅能看串口,还能直接传文件、开SSH隧道。调试时一个窗口搞定所有。

2.4.2 串口参数配置

不管用哪个工具,参数必须和单片机端一致。常见的配置:

  • 波特率:115200(常用)或921600(高速调试)
  • 数据位:8
  • 停止位:1
  • 校验位:无
  • 流控:无

我曾经犯过一个低级错误:单片机端设的是115200,串口工具设的是9600,结果打印出来全是乱码。查了半小时才发现……所以,先确认波特率一致,这是最基本的。

2.4.3 串口打印重定向

在STM32上,我们通常用printf来打印。但printf默认输出到stdout,需要重定向到串口。方法是在代码里实现fputc函数:

#include <stdio.h>

int fputc(int ch, FILE *f) {
    // 假设串口1已经初始化
    while (!(USART1->SR & USART_SR_TXE)); // 等待发送缓冲区空
    USART1->DR = (uint8_t)ch;             // 发送一个字节
    return ch;
}

这样,你直接写printf("Hello, AI!\n");,数据就会从串口发出来。

🔑 关键点: 在Keil中,还需要勾选“Use MicroLIB”,否则printf会占用大量栈空间。CubeIDE中默认使用newlib-nano,不需要额外配置。

2.5 环境验证:跑一个Hello World

工具都装好了,咱们跑个最简单的程序验证一下。写一个main.c,内容如下:

#include "main.h"

int main(void) {
    HAL_Init();
    SystemClock_Config();  // 配置系统时钟,比如168MHz
    MX_USART1_UART_Init(); // 初始化串口1

    printf("STM32 AI Inference Framework Ready!\n");
    printf("System Clock: %d MHz\n", HAL_RCC_GetSysClockFreq() / 1000000);

    while (1) {
        // 空循环,后面会在这里跑神经网络
    }
}

编译、下载、打开串口工具。如果看到打印信息,说明环境搭建成功。

嗯,这里要注意:第一次下载时,记得检查BOOT0引脚电平。如果BOOT0拉高,芯片会进入系统存储器模式,你下载的程序不会运行。我刚开始学STM32时就被这个坑过,折腾了一下午。

2.6 小结

环境搭建这一步,说白了就是磨刀。刀磨好了,砍柴才快。Keil和CubeIDE各有优劣,我建议你两个都装——Keil做性能优化,CubeIDE做快速原型。ARM GCC作为备选,当你需要精细控制编译选项时再用。串口工具选一个顺手的,后面调试全靠它。

下一章,咱们开始搭建工程模板,把CMSIS-NN库集成进来。到时候,你就可以在STM32上跑一个真正的神经网络了。