2. 开发环境搭建:MDK/STM32CubeIDE安装、CMSIS-NN软件包获取、工程模板创建
好,咱们正式开始动手。这一章说白了就是“把家伙事儿备齐”。我见过不少新手,兴致勃勃想跑神经网络,结果卡在环境配置上大半天,最后连个LED都没点着。咱们别走弯路,一步到位。
2.1 选择你的武器:MDK 还是 STM32CubeIDE?
先聊个实在问题:用哪个IDE?我个人习惯是两套都备着,但根据项目场景选主工具。下面这张表能帮你快速决策:
| 对比项 | MDK (Keil μVision) | STM32CubeIDE |
|---|---|---|
| 编译器 | ARM Compiler 6 (基于Clang) | GCC for ARM |
| 调试器 | ULINK / J-Link 支持好 | ST-Link 原生集成 |
| CMSIS-NN 优化 | ARM Compiler 6 对 CMSIS-NN 内联汇编支持极佳 | GCC 下部分内联汇编需手动调整 |
| 许可证 | 商用收费(32KB代码限制免费版) | 完全免费 |
| 上手难度 | 传统,资料多 | Eclipse风格,现代 |
我的建议: 如果你刚入门,用 STM32CubeIDE 起步,零成本、开箱即用。等做到性能调优阶段,再切到 MDK + ARM Compiler 6,你会发现推理速度能再快个 10%-15%。我在一个口罩检测项目里实测过,同样代码,MDK 编译出来的二进制比 GCC 小 8%,推理帧率高了 12%。
2.2 安装与验证:别光点“下一步”
2.2.1 MDK 安装要点
- 下载:去 Keil 官网下最新版 MDK-Arm。别下成旧版,CMSIS-NN 5.9.0 以上才完整支持。
- 安装路径:我个人习惯装到
C:\Keil_v5,别带中文和空格。 - Pack Installer:装完后打开,搜索你的芯片型号。比如 STM32F407,点 Install。这里有个坑——一定要装 CMSIS 包,版本选 5.9.0 或更高。
- 验证:新建一个空工程,编译通过就算成了。
我曾经踩过的坑: 装完 MDK 后直接打开旧工程,结果编译器报错说找不到 arm_math.h。后来发现是 CMSIS 包版本太老,CMSIS-NN 的头文件路径没被包含。解决办法:在 Pack Installer 里把 CMSIS 更新到最新,然后在工程选项的 C/C++ Include Paths 里手动加上
CMSIS\NN\Include。
2.2.2 STM32CubeIDE 安装要点
- 下载:ST官网,选对应操作系统版本。注意区分安装版和绿色版,我推荐安装版,省心。
- 首次启动:会提示选工作空间(Workspace)。路径同样不要中文。
- 安装固件包:在 Help → Manage Embedded Software Packages 里,找到你的 MCU 系列,比如 STM32F4,点 Install。这一步会下载 HAL 库和 CMSIS 包。
- 验证:新建一个 STM32 工程,选芯片,生成代码,编译通过。
2.3 获取 CMSIS-NN 软件包
CMSIS-NN 不是单独下载的,它藏在 CMSIS 这个大包里。你想想看,ARM 把 DSP、NN、Core 这些库都打包在一起了。获取方式有三种:
- 方式一(推荐):通过 IDE 的 Pack Manager 直接装。MDK 的 Pack Installer 里搜 CMSIS,选 5.9.0+;STM32CubeIDE 的固件包管理器里自带。
- 方式二:去 GitHub 上搜
ARM-software/CMSIS_5,下载 Releases 里的 zip。解压后把CMSIS\NN文件夹拷出来。 - 方式三:如果你用 STM32CubeMX 生成工程,在 Middleware 里勾选 CMSIS,会自动包含 NN 库。
关键文件清单: 拿到 CMSIS-NN 包后,确认以下文件存在:
Include/arm_nnfunctions.h—— 主头文件Include/arm_nnsupportfunctions.h—— 辅助函数Source/下的所有 .c 文件 —— 实现代码
2.4 工程模板创建:从零搭一个 CMSIS-NN 项目
这一步我手把手带你走一遍。咱们以 STM32F407 + STM32CubeIDE 为例,MDK 的步骤大同小异。
2.4.1 新建基础工程
- 打开 STM32CubeIDE,File → New → STM32 Project。
- 选芯片:STM32F407VGT6,Next。
- 工程名:
cmsis_nn_template,Finish。 - 初始化代码生成后,你会看到一堆 .c 和 .h 文件。别慌,这是 HAL 库的骨架。
2.4.2 添加 CMSIS-NN 源码
嗯,这里要注意:CMSIS-NN 的源码需要手动添加到工程里,IDE 不会自动帮你做。
- 在工程根目录下新建文件夹:
Middlewares/CMSIS/NN。 - 把
CMSIS/NN/Include和CMSIS/NN/Source拷进去。 - 在 IDE 里右键工程 → Properties → C/C++ General → Paths and Symbols。
- 在 Includes 标签页,Add 路径:
Middlewares/CMSIS/NN/Include。 - 在 Source Location 标签页,Link Folder,选
Middlewares/CMSIS/NN/Source。
小技巧: 如果你嫌手动添加 Source 文件麻烦,可以直接在工程目录下建一个
CMSIS 文件夹,把整个 CMSIS 包(包括 Core、DSP、NN)都放进去。然后在工程设置里一次性 Include 所有路径。我自己的模板就是这么干的,换芯片时只需要改芯片型号,CMSIS 路径不用动。
2.4.3 配置编译器选项
这一步容易忘,但忘了就编译不过。打开工程设置:
- C/C++ Build → Settings → Tool Settings → MCU GCC Compiler → Optimization:选
-O2或-O3。CMSIS-NN 在优化开启下才能发挥性能。 - MCU GCC Compiler → Preprocessor:添加宏定义
ARM_MATH_CM4(根据你的内核选,CM7 就写ARM_MATH_CM7)。这个宏告诉 CMSIS-DSP 库你的芯片内核版本。 - MCU GCC Linker → Miscellaneous:勾选
Use float with printf (-u _printf_float),方便调试时打印浮点结果。
2.4.4 写一个测试函数
模板搭好了,总得跑一下验证吧?在 main.c 里加一段最简单的卷积测试:
#include "arm_nnfunctions.h"
#include "arm_math.h"
// 定义一个简单的 3x3 卷积核,输入 5x5,输出 3x3
#define INPUT_CH 1
#define OUTPUT_CH 1
#define KERNEL_DIM 3
#define INPUT_DIM 5
#define OUTPUT_DIM 3
q7_t input[INPUT_CH * INPUT_DIM * INPUT_DIM];
q7_t kernel[OUTPUT_CH * INPUT_CH * KERNEL_DIM * KERNEL_DIM];
q7_t bias[OUTPUT_CH];
q7_t output[OUTPUT_CH * OUTPUT_DIM * OUTPUT_DIM];
int main(void)
{
// 初始化数据(略,实际可填随机数)
// 调用 CMSIS-NN 的卷积函数
arm_convolve_HWC_q7_basic(input, INPUT_DIM, INPUT_CH,
kernel, OUTPUT_CH, KERNEL_DIM,
bias, 0, 0,
output, 0, 0,
OUTPUT_DIM, 0, NULL);
while(1);
}
编译,下载到板子,用调试器看 output 数组的值。如果非零,恭喜你,环境搭好了!
2.5 知识体系总览
下面这张图帮你理清本章的核心逻辑:从 IDE 选择到工程跑通,每一步的依赖关系。
2.6 常见问题与避坑
- 编译报错:arm_math.h 找不到 —— 检查 CMSIS 包是否安装,Include 路径是否添加了
CMSIS/DSP/Include。 - 链接报错:undefined reference to arm_convolve_HWC_q7_basic —— 确认 NN 的 Source 文件已添加到工程,并且被编译了。在 STM32CubeIDE 里,有时需要手动把 .c 文件拖到 src 文件夹下。
- 运行死机 —— 检查堆栈大小。CMSIS-NN 的临时缓冲区可能很大,在启动文件里把堆设到 0x2000 以上。
- 性能不如预期 —— 确认优化等级开了 -O2 或 -O3,并且定义了正确的
ARM_MATH_CMx宏。
我曾经遇到的诡异问题: 有一次在 STM32CubeIDE 里编译通过,下载后程序跑飞。查了两天,最后发现是 CMSIS-NN 的 Source 文件被 IDE 的“排除构建”选项给勾掉了。右键每个 .c 文件 → Resource Configuration → Exclude from Build,确保没打勾。这种低级错误,犯一次就长记性了。