3、嵌入式系统与微控制器基础:MCU架构、内存与存储限制、外设接口、实时操作系统

TinyML 的部署目标通常不是云端的 GPU 或服务器 CPU,而是功耗受限、资源受限的嵌入式设备,其核心计算载体是微控制器(MCU)。理解 MCU 的架构特点、内存与存储的硬性约束、外设接口的工作方式以及实时操作系统(RTOS)的调度逻辑,是进行模型压缩、算子优化和推理加速的前提。本章将系统梳理这些基础概念,并重点剖析它们与 TinyML 推理引擎(如 TensorFlow Lite Micro)之间的直接关联。

MCU 核心资源与 TinyML 部署链路 ARM Cortex-M 系列 MCU CPU 内核 (FPU/DSP) TinyML 推理引擎 (TFLite Micro / CMSIS-NN) 内存与存储 • Flash: 256KB - 2MB • SRAM: 64KB - 512KB • 模型权重存储 • 激活张量缓冲区 → 容量硬约束 外设接口 • I2C / SPI / UART • GPIO / DMA • 传感器数据采集 • 推理结果输出 → 数据通路 实时操作系统 (RTOS) 任务调度 / 中断管理 / 信号量 推理任务优先级与时间片控制 图:MCU 内部资源与 TinyML 部署的关键交互关系

上图展示了 MCU 内部三大核心模块(内存、外设、RTOS)与 TinyML 推理引擎之间的关系。在实际部署中,模型权重必须存放在 Flash 中,而推理过程中的中间张量则必须动态分配在 SRAM 中。外设接口负责将传感器数据送入内存,RTOS 则确保推理任务在严格的时序约束下完成。下面我们将逐一深入这些模块。

3.1 MCU 架构核心原理:从冯·诺依曼到哈佛架构

绝大多数 TinyML 目标芯片(如 STM32F4 系列、Nordic nRF52 系列、GAP8 等)采用 哈佛架构 或 改进型哈佛架构。与 PC 中常见的冯·诺依曼架构不同,哈佛架构将指令总线与数据总线物理分离,这意味着 CPU 可以在读取指令(从 Flash)的同时,并行访问数据(从 SRAM),从而显著提高吞吐量。对于 TinyML 而言,这意味着卷积层的权重加载与输入特征图的读取可以部分重叠,减少流水线停顿。

另一个关键架构特征是 SIMD(单指令多数据) 与 DSP 扩展。ARM Cortex-M4/M7 内核支持 16 位和 32 位 SIMD 指令,而 Cortex-M33 增加了针对 ML 的 Helium 技术(M-Profile Vector Extension)。这些指令集允许一条指令同时处理多个数据,是 CMSIS-NN 软件库能够实现 4-5 倍推理加速的硬件基础。在评估 MCU 时,不能只看主频(MHz),更要关注其是否有 FPU(浮点单元)以及 DSP 扩展指令。

核心要点: 在 TinyML 选型时,优先选择带有 FPU 和 DSP/SIMD 指令的 Cortex-M4/M7/M33 内核。如果仅使用 M0+ 内核,即使主频相同,卷积运算的耗时也可能相差 3 倍以上,因为 M0+ 没有硬件乘法器加速且缺乏 SIMD 支持。

3.2 内存与存储限制:Flash 与 SRAM 的博弈

MCU 的内存资源是 TinyML 部署中最严苛的约束。通常,Flash(非易失性)用于存放代码、模型权重和常量表,容量在 256KB 到 2MB 之间;SRAM(易失性)用于存放栈、堆、全局变量和推理时的中间激活值,容量通常在 64KB 到 512KB 之间。TinyML 模型(如 MobileNetV1 量化版)的权重可能占用 300KB Flash,但推理时若需存储 224x224x3 的输入张量(约 150KB 浮点型),则 SRAM 会立即溢出。

因此,内存规划 是部署前的必修课。以下是判断与操作流程:

  1. 计算模型大小:使用 TensorFlow Lite Converter 导出 .tflite 文件,查看文件大小,该大小约等于 Flash 占用。
  2. 估算运行时内存:使用 interpreter->arena_allocator 或 TFLite Micro 的 MicroAllocator 打印内存规划详情,重点关注 tensor_arena_size。
  3. 调整张量缓冲区:在 C++ 代码中,通常需要手动分配一个全局字节数组作为 Tensor Arena。例如:static uint8_t tensor_arena[200 * 1024];。若不足,则需减少模型输入尺寸或改用更小的模型。
  4. 利用 Flash 映射:对于大权重,可将其声明为 const 数组,链接器会将其放入 Flash 而非 SRAM,从而节省 RAM。
资源类型典型容量TinyML 主要用途溢出风险
Flash512KB - 2MB模型权重、代码、查找表模型过大无法烧录
SRAM64KB - 512KB激活张量、中间结果、栈推理时崩溃或复位
EEPROM4KB - 64KB校准参数、设备配置较少用于模型存储
风险提示: 不要将大数组(如图像缓冲区)定义为局部变量,这会耗尽栈空间。务必使用静态全局分配或内存池。在 STM32 上,若发生 HardFault,优先检查是否因 SRAM 越界访问导致。

3.3 外设接口:数据采集与输出的通道

TinyML 系统的价值在于连接物理世界。MCU 通过丰富的外设接口与传感器和执行器交互。在推理加速场景中,最关键的接口是 DMA(直接内存访问) 和 I2C/SPI。

DMA 允许外设(如 ADC、SPI 外设)在无需 CPU 干预的情况下,直接将数据搬运到 SRAM 缓冲区。这对于连续采样麦克风数据(如关键词唤醒)至关重要。例如,在 STM32 上配置 SPI 与外部数字麦克风通信时,开启 DMA 循环模式,CPU 只需在缓冲区半满或全满时处理中断,从而将 CPU 资源留给推理计算。

SPI 通常用于连接高带宽传感器(如摄像头模块、外部 Flash 芯片),其时钟频率可达 40MHz 以上。而 I2C 则用于低速配置和控制(如设置传感器寄存器),速率通常在 400kHz。在部署推理模型时,需要根据数据吞吐量选择合适的接口:

  • 若输入为 1kHz 的振动信号(16bit),I2C 足够。
  • 若输入为 640x480 的灰度图像(30fps),则必须使用 DCMI(数字摄像头接口)或高速 SPI + DMA。
实战经验: 在编写外设驱动时,务必为 DMA 缓冲区设置 __attribute__((aligned(4))) 对齐,并确保缓冲区位于 SRAM 而非 CCM RAM(部分 STM32 的 CCM RAM 无法被 DMA 访问)。否则会导致 DMA 传输数据错乱。

3.4 实时操作系统(RTOS):任务调度与推理时序

虽然简单的 TinyML 应用可以在裸机(Super Loop)上运行,但当系统需要同时处理传感器采集、通信协议栈(如 BLE)、用户界面和推理任务时,引入 RTOS(如 FreeRTOS、Zephyr)是必要的。RTOS 的核心价值在于 可预测的调度延迟 和 任务隔离。

在 TinyML 推理场景中,通常将推理任务设置为 高优先级,但为了避免阻塞关键的中断响应,推理任务内部不应长时间关闭中断。推荐的做法是:

  1. 将传感器数据采集放在中断服务程序(ISR)中,仅通过信号量通知任务。
  2. 推理任务等待信号量,获取数据后执行 tflite::MicroInterpreter::Invoke()。
  3. 在 Invoke() 执行期间,RTOS 调度器可能被抢占,因此需要评估最坏情况下的推理延迟是否满足系统实时性要求。

例如,在一个电机故障预测系统中,振动数据以 8kHz 采样,每 256 个样本触发一次 FFT 和推理。若推理耗时 15ms,而采样缓冲区只能容纳 32ms 的数据,则必须确保推理任务在下一个缓冲区满之前完成。此时,RTOS 的时间片配置和优先级设置成为关键。

案例拆解:基于 STM32F746 + FreeRTOS 的语音关键词识别
背景与约束: 设备需在嘈杂环境中识别“Hi, MCU”关键词,使用 16-bit 16kHz 音频流。MCU 主频 216MHz,SRAM 320KB,Flash 1MB。模型为 DS-CNN 量化模型,权重 180KB,Tensor Arena 分配 120KB。
初始现象: 裸机环境下,音频中断频繁打断推理,导致识别率下降至 60%。
分析过程: 音频中断每 1ms 触发一次,中断服务程序耗时 20us,但频繁的上下文切换导致 CPU 流水线效率降低,且推理任务无法连续执行。
操作步骤: ① 引入 FreeRTOS,创建音频采集任务(优先级 3)和推理任务(优先级 2)。② 音频任务使用 DMA 双缓冲,每 16ms 通过队列发送数据块。③ 推理任务阻塞在队列上,收到数据后调用 Invoke()。④ 将推理任务内部的关键循环(如矩阵乘法)中的中断优先级设置为低于系统节拍。
结果对比: 识别率提升至 92%,CPU 占用率从 95% 降至 70%,且系统响应时间稳定。
复盘经验: 在 RTOS 中,推理任务不应被频繁的 ISR 打断,但也不应长时间屏蔽中断。利用 DMA 和队列机制,将数据采集与计算解耦,是 TinyML 实时性的关键。

3.5 常见误区与总结复盘

在嵌入式系统基础这一章,开发者常陷入以下误区:

  • 误区一:只关注主频,忽略总线架构。 例如,选择 100MHz 的 M0+ 与 80MHz 的 M4,在跑 8bit 定点模型时,M4 可能更快,因为其具备单周期乘法指令。
  • 误区二:内存分配使用 malloc。 在 MCU 上,动态内存分配容易产生碎片,且耗时不确定。TFLite Micro 要求使用静态分配的 Tensor Arena,这是硬性要求。
  • 误区三:忽视 Flash 读取速度。 某些 MCU 的 Flash 需要插入等待周期,导致代码执行变慢。将频繁调用的推理函数放入 RAM 执行(通过 __attribute__((section(".ramfunc"))))可提升速度。
  • 误区四:RTOS 使用不当。 在中断中调用 printf 或 Invoke() 是禁忌,会导致系统崩溃。