STM32CubeMX配置指南:嵌入式深度学习开发环境搭建

1. 为什么需要STM32CubeMX来搭建嵌入式深度学习环境

在嵌入式设备上运行深度学习模型,很多人第一反应是"这怎么可能?"——毕竟我们习惯了在服务器或高性能GPU上训练和运行AI模型。但现实是,越来越多的智能终端设备需要在本地完成推理任务:工业传感器要实时识别异常振动,农业无人机要现场判断作物病害,医疗设备要即时分析心电图波形。这些场景无法依赖云端,必须在资源受限的MCU上实现。

STM32CubeMX正是解决这个矛盾的关键工具。它不是传统意义上的"深度学习框架",而是一个智能配置引擎,把复杂的硬件初始化工作变成了可视化操作。想象一下,如果每次都要手动编写时钟树配置、外设寄存器设置、中断向量表,光是让一个ADC正常采集数据就可能耗费半天时间。而STM32CubeMX能让你在几分钟内完成整个微控制器的初始化配置,生成可直接编译的C代码框架。

更重要的是,它与主流嵌入式AI工具链天然兼容。当你用TensorFlow Lite for Microcontrollers训练好模型后,STM32CubeMX生成的工程结构恰好符合其要求;当你要集成CMSIS-NN优化库时,它能自动配置所需的DSP指令集支持;甚至在调试阶段,它生成的调试配置也能无缝对接ST-Link和J-Link等常用调试器。

这不是一个替代编程的工具,而是把工程师从重复性硬件配置中解放出来,让你真正聚焦在AI算法与嵌入式系统融合的核心问题上——如何让有限的RAM跑通神经网络,如何在毫秒级响应中完成推理,如何平衡功耗与精度。接下来的内容,我会带你一步步走过这个过程,不讲抽象概念,只说具体操作。

2. 环境准备与基础配置

2.1 安装STM32CubeMX与配套工具

首先确认你的开发环境已经准备好。STM32CubeMX本身是跨平台的,Windows、macOS和Linux都能运行,但后续的编译和烧录环节会有些许差异。

在Windows系统上,我推荐使用最新稳定版(目前是6.12.0)。安装过程非常简单,下载官方安装包后一路点击"Next"即可。安装完成后,软件会自动检查并提示你安装必要的Java运行环境——这是STM32CubeMX的运行基础,务必确保安装成功。

安装完成后不要急着打开软件,先配置好两个关键组件:

STM32Cube MCU包:这是设备支持的核心。打开STM32CubeMX,点击"Help" → "Check for Updates",软件会自动检测并列出所有可用的MCU系列包。对于深度学习应用,重点关注以下几类芯片:

  • STM32H7系列:拥有双核Cortex-M7,主频高达480MHz,配备大容量SRAM(1MB以上),是目前嵌入式AI的首选
  • STM32U5系列:超低功耗设计,特别适合电池供电的AI终端,内置专用AI加速器
  • STM32F7系列:性价比高,适合入门级AI项目

选择你需要的目标芯片系列,点击"Install"等待下载完成。这个过程可能需要几分钟,取决于你的网络速度。

编译工具链:STM32CubeMX本身不包含编译器,需要额外安装。根据你的开发习惯选择:

  • 如果使用Keil MDK-ARM,确保已安装版本5.37或更高
  • 如果偏好GCC工具链,推荐安装STM32CubeIDE自带的ARM GCC(版本10.3.1)
  • 对于命令行爱好者,可以直接下载GNU Arm Embedded Toolchain

安装完成后,在STM32CubeMX中进入"Preferences" → "Tools"选项卡,确认编译器路径已正确识别。这一步看似简单,但很多初学者的编译失败都源于此。

2.2 创建第一个AI友好型工程

现在可以创建我们的第一个工程了。点击"File" → "New Project",在弹出的设备选择窗口中,输入芯片型号。如果你不确定选哪个,可以搜索"STM32H743"——这是目前最均衡的选择,既有足够算力又便于学习。

选择芯片后,软件会加载对应的引脚图。这时不要急于配置外设,先做一件重要的事:启用核心优化特性

在左侧项目导航栏中找到"System Core" → "SYS",在右侧配置面板中找到"Debug"选项,将其设置为"Serial Wire"。这确保了后续调试功能的可用性。

然后点击"System Core" → "RCC"(复位和时钟控制),这是整个系统的心脏。在"High Speed Clock(HSE)"中选择"Crystal/Ceramic Resonator",并设置频率为8MHz——这是大多数开发板的标准晶振频率。

最关键的一步来了:点击"System Core" → "NVIC"(嵌套向量中断控制器),勾选"Enable all interrupts by default"。深度学习推理过程中会频繁使用DMA传输数据,中断配置不当会导致数据丢失或程序崩溃。

完成这些基础配置后,点击左上角的"Project Manager"标签页。在这里设置项目基本信息:

  • Project Name:输入有意义的名称,比如"AI_Sensor_Node"
  • Project Folder Location:选择一个不含中文和空格的路径
  • Toolchain / IDE:根据你安装的编译器选择,如"MDK-ARM v5"或"SW4STM32"

特别注意"Code Generator"选项卡中的设置:

  • 勾选"Generate peripheral initialization as a pair of '.c/.h' files per peripheral"——这会让代码结构更清晰
  • 在"Librarys"部分,确保"CMSIS"和"CMSIS-NN"被选中。CMSIS-NN是ARM专门为嵌入式AI优化的数学库,能大幅提升神经网络运算效率

点击"Generate Code",STM32CubeMX会自动生成完整的工程框架。此时不要关闭软件,我们还有更重要的配置要做。

3. 深度学习关键外设配置

3.1 时钟树配置:为AI计算提供稳定动力

时钟配置是嵌入式AI性能的基石。STM32H7系列芯片的时钟树极其复杂,有多个PLL(锁相环)和分频器,但不必被吓到——STM32CubeMX的图形化界面让这一切变得直观。

在"Clock Configuration"标签页中,你会看到一个清晰的时钟树示意图。我们的目标是让CPU获得尽可能高的稳定频率,同时确保内存和外设时钟满足AI计算需求。

首先关注"HCLK"(AHB总线时钟),这是CPU和内存访问的关键。对于STM32H743,最大支持400MHz,但实际使用中建议设置为280-320MHz。过高的频率会增加功耗,且对电源稳定性要求极高。

具体操作步骤:

  • 在"PLL1"区域,将"VCO Input"设置为"HSI/2"(4MHz)
  • 将"VCO Output"设置为"800MHz"(这是PLL1的推荐工作点)
  • 设置"PLL1 Q"分频器为"2",这样HCLK就是400MHz
  • 但为了留有余量,将"HCLK"分频器设置为"2",最终得到200MHz的系统时钟

为什么不是直接400MHz?因为深度学习推理需要大量内存访问,过高的HCLK可能导致SRAM访问不稳定。200MHz是一个经过验证的平衡点,在保证性能的同时确保系统可靠性。

接下来配置"ADC"时钟。如果你计划用ADC采集传感器数据进行AI分析,需要特别注意。在"ADC"区域,将"ADC12"时钟源设置为"PLL2 P",分频系数设为"2"。这样ADC可以获得100MHz的采样时钟,支持最高1MSPS的采样率。

最后别忘了配置"RNG"(随机数发生器)时钟。虽然看起来与AI无关,但在某些量化算法和数据增强过程中会用到真随机数。将"RNG"时钟源设置为"PLL1 Q",分频系数设为"16",确保其稳定工作。

完成配置后,观察右下角的"Clock Configuration Summary",确认所有时钟频率都在芯片规格范围内。如果有红色警告,说明配置超出了硬件限制,需要调整分频系数。

3.2 内存管理:为神经网络分配合适空间

嵌入式AI最大的挑战之一就是内存限制。一个简单的CNN模型可能就需要几百KB的权重参数,而STM32H743的内部SRAM只有1MB。STM32CubeMX提供了精细的内存管理能力,让我们能够合理规划这片宝贵的空间。

在"Pinout & Configuration"视图中,点击左侧导航栏的"System Core" → "MCU",然后切换到"Memory"选项卡。这里显示了芯片的所有内存区域:

  • D1 domain: AXI SRAM (512KB) - 高速,适合存放权重参数
  • D2 domain: SRAM1 (128KB) - 中速,适合存放中间激活值
  • D3 domain: SRAM3 (64KB) - 低速,适合存放临时变量

关键配置在于"Memory Assignment"部分。默认情况下,所有内存都被分配给堆栈,这对AI应用是灾难性的。我们需要重新分配:

  • 将"AXI SRAM"的"Data Memory"分配比例设为"70%"——这部分将用于存放神经网络权重
  • 将"SRAM1"的"Data Memory"设为"20%"——用于存放推理过程中的特征图
  • 将"SRAM3"的"Stack and Heap"设为"90%"——确保系统有足够的运行时内存

更进一步,点击"Project Manager" → "Advanced Settings",在"User Labels"中添加自定义内存段:

  • 添加名为"AI_WEIGHTS"的段,地址指向AXI SRAM起始地址(0x24000000),大小设为384KB
  • 添加名为"AI_ACTIVATIONS"的段,地址指向SRAM1起始地址(0x30000000),大小设为96KB

这样做的好处是,后续在C代码中可以使用__attribute__((section("AI_WEIGHTS")))直接将模型权重放入指定内存区域,避免运行时动态分配带来的不确定性。

3.3 外设接口配置:连接AI数据源

深度学习不是孤立的计算,它需要数据输入和结果输出。根据你的应用场景,选择合适的外设接口至关重要。

传感器数据采集(ADC + DMA): 如果处理温度、湿度、振动等模拟信号,配置ADC是第一步。在"Analog" → "ADC1"中:

  • 启用ADC1,模式选择"Independent mode"
  • 设置分辨率"16 Bits"(高精度对AI特征提取很重要)
  • 采样时间选择"640.5 Cycles"(平衡精度和速度)
  • 最关键的是启用"DMA Continuous Requests",这样ADC可以在不占用CPU的情况下持续采集数据

然后配置DMA:在"Connectivity" → "DMA1"中,为ADC1配置一个通道,数据宽度设为"Half Word",优先级设为"High"。这样采集的数据会自动流入预分配的缓冲区,为AI推理做好准备。

图像数据输入(DCMI): 如果处理摄像头数据,DCMI(数字摄像头接口)是关键。在"Connectivity" → "DCMI"中:

  • 启用DCMI,设置"Embedded Synchronisation"为"Hardware"
  • 数据格式选择"RGB565"(平衡带宽和色彩信息)
  • 在"DMA"配置中,为DCMI分配一个独立的DMA通道,缓冲区大小设为"64KB"(足够存储一帧QVGA图像)

模型输出接口(UART + USB): 推理结果需要输出到上位机或显示屏。我通常配置两个输出通道:

  • UART1:用于调试输出,波特率设为"115200",启用硬件流控
  • USB_DEVICE:配置为CDC类,这样可以在PC端虚拟出一个串口,无需额外驱动

在"Connectivity" → "USB_DEVICE"中,选择"Device Only"模式,Class For Interface 1选择"CDC"。这样生成的代码会自动包含USB CDC驱动,你可以像使用UART一样发送推理结果。

4. AI中间件集成与代码生成

4.1 CMSIS-NN库集成配置

CMSIS-NN是ARM官方为Cortex-M系列处理器优化的神经网络库,它包含了针对不同架构高度优化的卷积、池化、激活函数等基本操作。STM32CubeMX能自动集成这个关键组件。

在"Project Manager" → "Code Generator"选项卡中,确保"CMSIS"和"CMSIS-NN"复选框已被勾选。但这只是第一步,还需要进行针对性配置。

点击左侧导航栏的"Middleware" → "CMSIS",在右侧配置面板中:

  • "CMSIS Version"选择最新稳定版(目前是5.9.0)
  • "CMSIS-NN"部分,勾选"Enable CMSIS-NN"和"Enable CMSIS-NN Examples"
  • 在"Target Processor"中,根据你的芯片选择对应架构,如"ARM Cortex-M7 with FPU"

更关键的是"Optimization Level"设置:

  • "Basic Functions":选择"Optimized"——这是必须的,包含基础数学运算
  • "Convolution Functions":选择"Highly Optimized"——卷积是CNN最耗时的操作
  • "Pooling Functions":选择"Optimized"
  • "Activation Functions":选择"Optimized"

完成这些配置后,STM32CubeMX会在生成的工程中自动包含CMSIS-NN头文件和库文件,并在main.c中添加相应的初始化代码。

但要注意一个常见陷阱:CMSIS-NN默认使用ARM Compiler 6,如果你使用GCC工具链,需要在生成的Makefile或IDE设置中添加编译选项"-mfloat-abi=hard -mfpu=fpv5-d16",否则浮点运算会异常缓慢。

4.2 TensorFlow Lite Micro集成

TensorFlow Lite Micro是Google专为微控制器设计的轻量级推理引擎,与STM32CubeMX配合得天衣无缝。

在"Middleware" → "Third Party"中,你会看到"TensorFlow Lite Micro"选项(如果未显示,可能需要更新STM32CubeMX版本)。启用它后,软件会自动下载并集成TFLM核心库。

配置要点:

  • "Model Format"选择"FlatBuffer"——这是TFLM的标准模型格式
  • "Memory Allocation"选择"Heap Based"——更适合动态模型加载
  • "Debug Features"中,只勾选"Error Reporting",避免调试信息占用过多内存

生成代码后,你会在Inc/目录下看到tflite_micro.h头文件,在Src/目录下看到tflite_micro.c实现文件。更重要的是,STM32CubeMX会在main.c中自动生成TFLM初始化代码框架:

// 在main()函数开始处
TfLiteStatus init_status = tflite_micro_init();
if (init_status != kTfLiteOk) {
    Error_Handler(); // 处理初始化失败
}

但这里有个重要提醒:TFLM本身不包含模型,你需要自己准备。通常的做法是:

  1. 在PC端用TensorFlow训练好模型
  2. 使用TFLite Converter转换为.tflite格式
  3. 使用xxd工具将二进制模型转换为C数组
  4. 将生成的C数组文件添加到STM32CubeMX工程中

这样,模型就作为常量数据编译进固件,避免了运行时加载的复杂性。

4.3 生成并验证AI工程框架

现在到了最关键的一步:生成完整工程。点击"Project Manager" → "Generate Code",STM32CubeMX会创建一个结构清晰的工程目录。

生成的工程包含几个重要部分:

  • Core/Inc/:所有头文件,包括stm32h7xx_hal.h和cmsis_nn.h
  • Core/Src/:HAL库和中间件源文件
  • Drivers/:底层驱动代码
  • Middlewares/Third_Party/:TFLM和CMSIS-NN库
  • Src/:你的应用代码,包括main.c和freertos.c(如果启用了FreeRTOS)

打开main.c文件,你会看到自动生成的初始化代码。重点关注以下几段:

系统时钟初始化

/* Configure the system clock */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLL1;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2; // 注意这里是2分频
RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

AI相关外设初始化

/* Initialize ADC1 */
MX_ADC1_Init();

/* Initialize DMA for ADC */
MX_DMA_Init();

/* Initialize CMSIS-NN */
arm_cmsis_nn_init();

/* Initialize TensorFlow Lite Micro */
tflite_micro_init();

编译前,还有一个重要检查:在"Project Manager" → "Settings"中,确认"Code Generation"选项卡下的"Generate peripheral initialization as a pair of '.c/.h' files per peripheral"已被勾选。这确保了每个外设都有独立的初始化文件,便于后续维护和修改。

生成工程后,用你的IDE(如STM32CubeIDE或Keil)打开,编译整个项目。第一次编译可能会有一些警告,但不应该有错误。如果出现链接错误,通常是内存分配问题,回到"Memory"配置页面调整各内存段大小。

5. 实战:部署一个简单的图像分类模型

5.1 模型准备与转换

理论讲得再多不如一次实际部署。我们以经典的"Micro Speech"模型为蓝本,改造为一个简单的图像分类模型——识别手写数字(MNIST数据集的简化版)。

在PC端准备模型的步骤:

  1. 使用TensorFlow训练一个小型CNN模型(3层卷积+1层全连接)
  2. 转换为TFLite格式:
tflite_convert \
  --saved_model_dir=./saved_model \
  --output_file=./model.tflite \
  --input_shapes=1,28,28,1 \
  --input_arrays=conv2d_input \
  --output_arrays=dense_1/BiasAdd \
  --inference_type=FLOAT \
  --experimental_enable_resource_variables
  1. 量化模型以减小体积(可选但推荐):
tflite_convert \
  --saved_model_dir=./saved_model \
  --output_file=./model_quant.tflite \
  --input_shapes=1,28,28,1 \
  --input_arrays=conv2d_input \
  --output_arrays=dense_1/BiasAdd \
  --inference_type=QUANTIZED_UINT8 \
  --std_dev_values=127.5 \
  --mean_values=127.5
  1. 转换为C数组:
xxd -i model_quant.tflite > model_data.cc

将生成的model_data.cc文件添加到STM32CubeMX工程的Src/目录下,并在main.c中包含:

#include "model_data.cc"

5.2 编写AI推理代码

现在在main.c中添加推理逻辑。在while(1)循环中,我们模拟从摄像头获取一帧图像(实际项目中这里会是DCMI DMA回调):

// 假设我们有一帧28x28的灰度图像数据
uint8_t input_image[28*28];
// 这里填充实际图像数据,例如从DCMI DMA缓冲区复制

// 创建TFLite解释器
static tflite::MicroInterpreter* interpreter;
static tflite::ErrorReporter* error_reporter;
static uint8_t tensor_arena[10*1024]; // 10KB内存用于TFLite张量

// 初始化解释器
tflite::MicroMutableOpResolver<10> resolver;
resolver.AddConv2D();
resolver.AddDepthwiseConv2D();
resolver.AddFullyConnected();
resolver.AddSoftmax();

interpreter = new tflite::MicroInterpreter(
    tflite::GetModel(g_model_data), resolver, tensor_arena, sizeof(tensor_arena));
error_reporter = new tflite::MicroErrorReporter();

// 分配张量
if (interpreter->AllocateTensors() != kTfLiteOk) {
    Error_Handler();
}

// 获取输入张量
TfLiteTensor* input = interpreter->input(0);
// 将图像数据复制到输入张量
for (int i = 0; i < 28*28; i++) {
    input->data.uint8[i] = input_image[i];
}

// 执行推理
if (interpreter->Invoke() != kTfLiteOk) {
    Error_Handler();
}

// 获取输出结果
TfLiteTensor* output = interpreter->output(0);
// 寻找最大概率的类别
int max_index = 0;
uint8_t max_value = 0;
for (int i = 0; i < 10; i++) {
    if (output->data.uint8[i] > max_value) {
        max_value = output->data.uint8[i];
        max_index = i;
    }
}

// 通过UART输出结果
char result_str[32];
sprintf(result_str, "Predicted: %d, Confidence: %d%%\r\n", 
        max_index, max_value);
HAL_UART_Transmit(&huart1, (uint8_t*)result_str, strlen(result_str), HAL_MAX_DELAY);

这段代码展示了嵌入式AI的核心流程:模型加载→内存分配→数据输入→推理执行→结果解析。关键点在于tensor_arena的大小需要根据模型复杂度仔细调整,太小会导致分配失败,太大会浪费宝贵的SRAM。

5.3 性能调优与调试技巧

部署完成后,性能测试必不可少。在STM32H743上运行这个MNIST模型,典型性能数据是:

  • 推理时间:约85ms(200MHz主频)
  • 内存占用:约8KB权重 + 12KB激活值 + 10KB运行时内存
  • 功耗:约45mA(3.3V供电)

如果性能不达标,有以下几个调优方向:

时钟优化:尝试将HCLK提升到280MHz,观察是否显著提升性能而不影响稳定性。注意监测芯片温度,过热会导致自动降频。

内存优化:如果RAM不足,可以将部分权重放在外部QSPI Flash中,使用XIP(eXecute In Place)方式直接执行。在STM32CubeMX中配置QSPI外设,然后在链接脚本中将权重段映射到外部存储器。

算法优化:CMSIS-NN提供了多种优化级别。在代码中添加编译宏可以启用更高级优化:

#define ARM_NN_TRUNCATE // 启用截断除法,提升整数运算速度
#include "arm_nnfunctions.h"

调试方面,我推荐两个实用技巧:

  1. 使用STM32CubeMonitor工具实时监控内存使用情况,特别是堆栈溢出问题
  2. 在关键函数前后添加GPIO翻转代码,用示波器测量实际执行时间,比软件计时更准确

6. 常见问题与解决方案

6.1 编译与链接问题排查

在实际开发中,最常见的问题是编译通过但链接失败,错误信息往往晦涩难懂。以下是几个高频问题及其解决方案:

"undefined reference to `arm_convolve_s8'": 这表示CMSIS-NN的卷积函数未正确链接。检查两点:

  • 确认在"Project Manager" → "Code Generator"中已勾选"CMSIS-NN"
  • 检查链接器脚本中是否包含了CMSIS-NN库路径,通常在STM32CubeMX生成的.ld文件中应该有类似-larm_cmsisnn的链接选项

"region 'RAM_D1' overflowed by XXX bytes": 内存溢出是最常见的问题。解决方案不是简单增加内存大小,而是分析内存使用:

  • 使用arm-none-eabi-size工具查看各段大小:arm-none-eabi-size -A build/*.elf
  • 重点关注.bss(未初始化全局变量)和.data(已初始化全局变量)段
  • 将大型数组(如模型权重)移到特定内存段:static int8_t weights[1024] __attribute__((section(".ram_d1")));

**"multiple definition of HAL_TIM_PeriodElapsedCallback'"**: 这是由于多个文件定义了相同的回调函数。STM32CubeMX生成的代码中,HAL回调函数默认是弱定义(weak),但如果你在其他文件中也定义了同名函数,就会冲突。解决方案是在你的应用文件中使用extern "C"`声明,或者重命名你的回调函数。

6.2 运行时问题诊断

即使编译链接都成功,运行时问题依然可能悄然而至:

推理结果始终为0或固定值: 这通常意味着数据输入有问题。使用UART打印输入张量的前几个值,确认数据是否正确复制。特别注意数据类型匹配——如果模型是int8量化,输入也必须是int8,不能是uint8。

系统在推理过程中复位: 这很可能是堆栈溢出或内存越界。在main.c中添加堆栈检查:

// 在main()开始处
extern uint32_t _estack;
uint32_t *stack_ptr = (uint32_t*)&_estack;
while (*stack_ptr == 0xDEADBEEF) { // 堆栈保护区标记
    stack_ptr++;
}
// 计算剩余堆栈空间
int free_stack = (uint32_t)stack_ptr - (uint32_t)&_estack;
if (free_stack < 1024) { // 小于1KB触发警告
    HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
}

DMA传输数据不完整: 如果使用DMA采集传感器数据,确保在DMA完成回调中禁用中断,处理完数据后再重新使能。否则可能在处理过程中被新的DMA完成中断打断,导致数据错乱。

6.3 性能瓶颈分析方法

要真正理解系统瓶颈所在,不能只看整体推理时间。我推荐一个三步分析法:

第一步:粗略定位 使用HAL_GetTick()在推理前后打点:

uint32_t start = HAL_GetTick();
interpreter->Invoke();
uint32_t end = HAL_GetTick();
printf("Total inference time: %d ms\r\n", end - start);

第二步:模块分解 在CMSIS-NN函数调用前后添加更精确的计时:

// 使用DWT周期计数器(需要先使能)
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;

DWT->CYCCNT = 0;
arm_convolve_s8(...);
uint32_t conv_time = DWT->CYCCNT;

第三步:硬件验证 使用逻辑分析仪捕获GPIO信号,将不同处理阶段映射到不同引脚:

  • PA0:DMA数据准备完成
  • PA1:模型输入复制开始
  • PA2:CMSIS-NN卷积开始
  • PA3:推理完成

这样就能直观看到每个阶段的实际耗时,找出真正的瓶颈所在。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐