1. 为什么用 STM32N647 学嵌入式
很多人学嵌入式的路径是:点亮一个 LED → 跑通串口 → 学 RTOS → 然后卡住,不知道下一步该做什么。而 STM32N647 这类带 NPU 的 MCU 恰好提供了一个"有终点"的学习目标:让一个神经网络模型在单片机上跑起来。
这个目标的好处是,它把嵌入式开发中几乎所有核心能力都串了起来:
- 工具链与工程构建(Keil / STM32CubeIDE / CMake)
- 时钟、电源、外设配置(CubeMX 图形化配置)
- 存储与内存布局(内部 SRAM、外部 PSRAM、Flash 分区)
- 中断与 DMA(摄像头、麦克风数据搬运)
- 实时性(推理耗时、帧率、功耗)
- 调试与性能分析(SWD、ITM、性能计数器)
STM32N6 系列是 ST 首款集成自研 Neural-ART NPU 的 MCU 家族,N647 属于其中的高配型号,主打边缘视觉与音频推理。相比"跑个 FreeRTOS 点灯"的练习,它能让你的学习成果看得见、摸得着。
说明:本文以学习路线为主线,涉及的具体型号参数、工具版本请以 ST 官方最新数据手册与软件包为准,文中示例代码用于说明思路,实际使用时需按你的开发板与 SDK 版本调整。
2. 先搞清楚 STM32N647 的硬件底子
在写第一行代码之前,先花半天时间把芯片的"家底"摸清楚,这比盲目抄例程有用得多。
2.1 核心架构
STM32N6 系列的基本构成可以概括为:
- Cortex-M55 应用核:带 Helium(MVE)向量扩展,负责控制逻辑与部分算子加速
- Neural-ART NPU:专用神经网络加速器,负责卷积、全连接等主要算子
- 大容量片上 SRAM:用于模型权重、激活值、图像缓冲区
- 丰富的外设:DCMI/CSI 摄像头接口、SAI/I2S 音频、SDMMC、以太网、USB 等
2.2 内存是嵌入式 AI 的第一道坎
学嵌入式 AI,最先撞上的墙不是算法,而是内存。一个在 PC 上只有几 MB 的模型,量化后放到 MCU 上,权重、激活、输入输出缓冲区加起来很容易超过片上 SRAM。
所以你需要尽早建立这几个概念:
| 概念 | 含义 | 学习要点 |
|---|---|---|
| 权重存储 | 模型参数放在哪 | Flash / 外部存储 / 加载到 SRAM |
| 激活缓冲区 | 每层中间结果 | 由工具链自动分配,需关注峰值 |
| 输入输出张量 | 图像/音频数据 | 格式、通道数、量化参数 |
| 内存对齐 | NPU 访问要求 | 通常要求特定字节对齐 |
理解了这张表,后面看 ST 的 AI 工具链报告时就不会一头雾水。
3. 学习路线:分四个阶段推进
下面这条路线是我建议的推进顺序,每个阶段都有明确的"验收标准",避免学成"半吊子"。
#mermaid-svg-YUWUOIfPnYWgveXy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YUWUOIfPnYWgveXy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YUWUOIfPnYWgveXy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YUWUOIfPnYWgveXy .error-icon{fill:#552222;}#mermaid-svg-YUWUOIfPnYWgveXy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YUWUOIfPnYWgveXy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YUWUOIfPnYWgveXy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YUWUOIfPnYWgveXy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YUWUOIfPnYWgveXy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YUWUOIfPnYWgveXy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YUWUOIfPnYWgveXy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YUWUOIfPnYWgveXy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YUWUOIfPnYWgveXy .marker.cross{stroke:#333333;}#mermaid-svg-YUWUOIfPnYWgveXy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YUWUOIfPnYWgveXy p{margin:0;}#mermaid-svg-YUWUOIfPnYWgveXy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YUWUOIfPnYWgveXy .cluster-label text{fill:#333;}#mermaid-svg-YUWUOIfPnYWgveXy .cluster-label span{color:#333;}#mermaid-svg-YUWUOIfPnYWgveXy .cluster-label span p{background-color:transparent;}#mermaid-svg-YUWUOIfPnYWgveXy .label text,#mermaid-svg-YUWUOIfPnYWgveXy span{fill:#333;color:#333;}#mermaid-svg-YUWUOIfPnYWgveXy .node rect,#mermaid-svg-YUWUOIfPnYWgveXy .node circle,#mermaid-svg-YUWUOIfPnYWgveXy .node ellipse,#mermaid-svg-YUWUOIfPnYWgveXy .node polygon,#mermaid-svg-YUWUOIfPnYWgveXy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YUWUOIfPnYWgveXy .rough-node .label text,#mermaid-svg-YUWUOIfPnYWgveXy .node .label text,#mermaid-svg-YUWUOIfPnYWgveXy .image-shape .label,#mermaid-svg-YUWUOIfPnYWgveXy .icon-shape .label{text-anchor:middle;}#mermaid-svg-YUWUOIfPnYWgveXy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YUWUOIfPnYWgveXy .rough-node .label,#mermaid-svg-YUWUOIfPnYWgveXy .node .label,#mermaid-svg-YUWUOIfPnYWgveXy .image-shape .label,#mermaid-svg-YUWUOIfPnYWgveXy .icon-shape .label{text-align:center;}#mermaid-svg-YUWUOIfPnYWgveXy .node.clickable{cursor:pointer;}#mermaid-svg-YUWUOIfPnYWgveXy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YUWUOIfPnYWgveXy .arrowheadPath{fill:#333333;}#mermaid-svg-YUWUOIfPnYWgveXy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YUWUOIfPnYWgveXy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YUWUOIfPnYWgveXy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YUWUOIfPnYWgveXy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YUWUOIfPnYWgveXy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YUWUOIfPnYWgveXy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YUWUOIfPnYWgveXy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YUWUOIfPnYWgveXy .cluster text{fill:#333;}#mermaid-svg-YUWUOIfPnYWgveXy .cluster span{color:#333;}#mermaid-svg-YUWUOIfPnYWgveXy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YUWUOIfPnYWgveXy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YUWUOIfPnYWgveXy rect.text{fill:none;stroke-width:0;}#mermaid-svg-YUWUOIfPnYWgveXy .icon-shape,#mermaid-svg-YUWUOIfPnYWgveXy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YUWUOIfPnYWgveXy .icon-shape p,#mermaid-svg-YUWUOIfPnYWgveXy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YUWUOIfPnYWgveXy .icon-shape .label rect,#mermaid-svg-YUWUOIfPnYWgveXy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YUWUOIfPnYWgveXy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YUWUOIfPnYWgveXy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YUWUOIfPnYWgveXy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 阶段一:裸机基础
阶段二:外设与数据通路
阶段三:模型转换与量化
阶段四:板端部署与调优
进阶:多模型与低功耗
3.1 阶段一:裸机基础(约 2~3 周)
目标:能独立新建工程、配置时钟、点亮外设。
- 安装 STM32CubeIDE 或配置好 Keil + STM32CubeMX
- 用 CubeMX 生成一个最小工程,理解
main.c里的初始化顺序 - 手写 GPIO 翻转,用示波器或逻辑分析仪测一下实际频率
- 跑通串口 printf(重定向
_write或使用 ITM)
验收标准:能不看教程,从零新建工程并让串口每秒打印一次心跳。
3.2 阶段二:外设与数据通路(约 3~4 周)
目标:把"数据从传感器搬进内存"这件事做扎实,这是 AI 推理的前置条件。
- DMA:配置 DCMI 或 ADC 的 DMA 传输,理解双缓冲(double buffer)
- 中断优先级:搞清 NVIC 分组,避免推理任务被高频中断打断
- 定时器:用 TIM 做帧率统计
- 缓存与一致性:如果用到带 Cache 的配置,注意 DMA 与 CPU 的数据一致性
验收标准:摄像头(或模拟数据源)能稳定输出固定尺寸的帧到指定缓冲区,且不丢帧。
3.3 阶段三:模型转换与量化(约 2~3 周)
目标:把一个训练好的小模型变成 MCU 能吃的格式。
推荐从最简单的模型开始,比如 MNIST 手写数字识别或关键词唤醒(KWS),不要一上来就搞 YOLO。
典型流程:
- 用 PyTorch / TensorFlow 训练或直接拿现成模型
- 导出为 ONNX
- 用 ST 的 AI 工具链(STM32Cube.AI 相关工具)做转换与量化
- 查看生成的报告:Flash 占用、RAM 占用、每层算子映射情况
python
import torch
import torch.nn as nn
class TinyCNN(nn.Module):
def __init__(self):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(1, 8, 3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(8, 16, 3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2),
)
self.classifier = nn.Sequential(
nn.Flatten(),
nn.Linear(16 * 7 * 7, 10),
)
def forward(self, x):
return self.classifier(self.features(x))
model = TinyCNN().eval()
dummy = torch.randn(1, 1, 28, 28)
torch.onnx.export(
model, dummy, "tiny_cnn.onnx",
input_names=["input"], output_names=["output"],
opset_version=13,
)
print("exported tiny_cnn.onnx")
验收标准:转换工具不报错,报告中的 RAM 峰值在你板子的可用范围内。
3.4 阶段四:板端部署与调优(约 3~4 周)
目标:模型真正跑起来,并且知道它为什么快/慢。
- 把生成的 C 代码集成进工程,注意内存段放置(
.ai_ram之类的自定义段) - 用 DWT 周期计数器或 GPIO 翻转测量单次推理耗时
- 对比 CPU 推理与 NPU 推理的差异
- 尝试调整量化方式(int8 / int16)、输入分辨率,观察精度与速度的权衡
c
#include "stm32n6xx.h"
static volatile uint32_t cycles = 0;
static inline void dwt_start(void)
{
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
static inline uint32_t dwt_stop(void)
{
cycles = DWT->CYCCNT;
return cycles;
}
void run_inference_once(void)
{
dwt_start();
/* 这里调用 AI 工具链生成的推理接口 */
/* ai_run(...); */
uint32_t c = dwt_stop();
printf("inference cycles: %lu\r\n", (unsigned long)c);
}
验收标准:能给出"输入尺寸 → 推理耗时 → 内存占用"的实测表格,并能解释瓶颈在哪。
4. 学习过程中最容易踩的五个坑
- 内存不够就怪芯片:先看工具链报告里的峰值 RAM,往往是缓冲区开太大或没复用。
- 忽略量化误差:int8 量化后精度掉几个点是正常的,要学会看混淆矩阵而不是只看准确率。
- 中断里做重活:推理千万别放中断里,用标志位 + 主循环或 RTOS 任务。
- 不做基线对比:没有 CPU 版本的耗时做参照,就无法证明 NPU 的价值。
- 只跑 demo 不改参数:改输入尺寸、改量化方式、改模型结构,才是真正在"学"。
5. 推荐的学习资源与工具
- 官方文档:STM32N6 系列参考手册、数据手册、应用笔记(AN 系列)
- 软件包:STM32CubeMX、STM32CubeIDE、STM32CubeProgrammer、AI 相关扩展包
- 模型侧:ONNX 官方文档、PyTorch 量化教程
- 硬件侧:一块带摄像头与外部存储的官方或第三方开发板,一个逻辑分析仪
学习顺序建议:先文档后代码,先小模型后大模型,先 CPU 后 NPU。
6. 总结
用 STM32N647 学嵌入式,本质上是给自己找一个"必须打通全链路"的项目。你会被迫理解时钟、内存、DMA、中断、量化、性能分析------这些恰恰是嵌入式工程师的核心能力。
不要急着让模型跑出高帧率,先让它正确地跑起来 ,再让它跑得快 ,最后让它跑得省。这三步走完,你对嵌入式的理解就已经超过大多数只会点灯的人了。