你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32N6的开发日记(3):如何利用CubeMX快速部署我们的神经网络模型

[复制链接]
攻城狮Melo 发布时间:2026-3-23 11:06

前言

STM32N6作为意法半导体推出的首款集成自研神经处理单元的STM32产品以“MCU+NPU”的异构架构重新定义了边缘AI的算力边界,是意法半导体的MCU最前沿技术栈,不过由于其高难度技术应用以及需要的极其深厚的STM32使用经验以及神经网络基础概念,因此上手难度非常的高。 image.png

自从STM32N6发布以来,博主有幸获得一块STM32N6570-DK开发板,闲暇之余陆陆续续折腾如何开发。因此将会陆陆续续发表一些使用STM32N6的使用笔记,以供将来的使用者参考。

回顾学习历程,踩了很多很多的坑,在后续使用STM32N6的文章中也会向大家陆续介绍这些点。

使用这块芯片最大的亮点就是在其上运行神经网络单元,本期我们将介绍如何在导入并初步运行和验证我们的神经网络模型。 1

准备工作

在开始之前,先介绍一下我们需要准备哪些东西:首先是一个神经网络模型,这里我选择使用ST提供的目标检测模型(单目标:人)

image.png

image.png

明确其网络架构,例如选用yolov8n_320_quant_pc_uf_od_coco-person.tflite模型,则代表着模型输入为3203203的RGB888类型图片,输出为单目标模型,F=2100,因此维度信息为:(1,5,2100),模型总大小为2.96MB。

image.png

开发工具为STM32CubeIDE用于代码编程,STM32CubeMX用于初始化配置和STM32CubeProgram用于程序烧录。

image.png

同时意法半导体还提供了ST Edge AI工具用来帮助神经网络模型量化和辅助部署在STM32N6中,但是操作起来有点麻烦还要去找NPU的驱动库,官方在某一次更新的时候将其功能整合在了STM32CubeMX中,因此可以直接在CubeMX中进行模型处理和部署。 2

STM32CubeMX配置

在上一期的基础上,我们完成了如何在STM32N6中实现FSBL_XIP模式,配置了外部Flash和SPRAM来存放我们的代码。

image.png

image.png

外部Flash和SPRAM配置如上图所示,具体的配置信息可以参考上一期文章。 神经网络部署

原本在N6中使用NPU和神经网络模型的处理需要依靠ST EdgeAI-Core工具:

image.png

操作起来比较繁琐,并且没有没有特别多的社区资料用于参考。但是在某一次CubeMX的更新中,STM32CubeMX中的CubeAI插件也继承了ST Edge AI Core的功能,专用于STM32N6系列模型分析和部署。

image.png

首先点击X-CUBE-AI将软件包切换到Application工程,因为我们的模型大小为2.96MB而FSBL空间仅为511K,不足以存放模型,因此要放在更大的Application空间。

image.png

Profile用于规定RAM和ROM的使用大小,例如在n6-allmems-O3这个方案中,点击右边的齿轮可以查看当前方案的分配情况:

image.png

例如规定外部RAM使用地址从0x90000000开始,外部Flash从0x7100000开始,注意这个信息很重要。外部RAM从0x90000000开始的话,我们在使用的时候就注意,不要造成地址复用,比方说用户在采集摄像头图像的时候,不要存在这个地方,方式和神经网络运算过程中造成内存污染。

外部Flash从0x71000000开始,则代表着神经网络模型权重的下载地址为0x71000000,这个信息后面要用!

image.png

根据情况修改内存分配情况,我们暂时不改。

image.png

点击分析,STM32CubeMX会调用STEdgeAI的命令来帮助我们部署模型并生成处理报告。 处理报告信息

image.png

报告中这部分信息交代了我们的模型使用内存情况,例如该模型使用了npuRAM3,RAM4部分还有2.905MB大小的权重文件位于外部FLASH,首地址为0x71000000。

image.png

image.png

该部分交代了这个模型的各个部分分别是利用什么来运算,HW为硬件NPU运算,SW为软件CPU运算。 内存管理

image.png

在内存配置中开启AXISRAM3和AXISRAM4(我们使用的RAM) 串口调试

image.png

开启系统串口1用于调试,注意STM32N6570-DK的USART1分别是PE5和PE6。 3

代码测试

接下来生成我们的工程,在正式进行测试之前,我们要先进行一些准备工作: 串口重定向

#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)  PUTCHAR_PROTOTYPE  {  HAL_UART_Transmit(&huart1, (uint8_t *)ch, 1, 0xFFFF);return ch;  }int _write(int fd, char * ptr, int len){  HAL_UART_Transmit(&huart1, (uint8_t *) ptr, len, HAL_MAX_DELAY);return len;  }

image.png

首先是串口重定向,串口重定向让我们可以用printf来输出USART1的内容,方便我们待会打印内容,注意该步骤需要包含头文件stdio.h。 RIF安全配置

/* USER CODE BEGIN SysInit */  __HAL_RCC_RIFSC_CLK_ENABLE();  RIMC_MasterConfig_t RIMC_master = {0};  RIMC_master.MasterCID = RIF_CID_1;  RIMC_master.SecPriv = RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV;  HAL_RIF_RIMC_ConfigMasterAttributes(RIF_MASTER_INDEX_NPU, &RIMC_master);  HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_NPU, RIF_ATTRIBUTE_PRIV | RIF_ATTRIBUTE_SEC);
  /* USER CODE END SysInit */
  /* Initialize all configured peripherals */  MX_GPIO_Init();  MX_CACHEAXI_Init();  MX_USART1_UART_Init();  MX_RAMCFG_Init();  MX_X_CUBE_AI_Init();  SystemIsolation_Config();  /* USER CODE BEGIN 2 */

在Application工程中,添加NPU的安全权限,否则系统会直接卡死的。 内存使能

voidMX_X_CUBE_AI_Init(void){    set_clk_sleep_mode();    __HAL_RCC_NPU_CLK_ENABLE();    __HAL_RCC_NPU_FORCE_RESET();    __HAL_RCC_NPU_RELEASE_RESET();    npu_cache_init();/* USER CODE BEGIN 5 */    __HAL_RCC_AXISRAM1_MEM_CLK_ENABLE();    __HAL_RCC_AXISRAM2_MEM_CLK_ENABLE();    __HAL_RCC_AXISRAM3_MEM_CLK_ENABLE();    __HAL_RCC_AXISRAM4_MEM_CLK_ENABLE();    __HAL_RCC_AXISRAM5_MEM_CLK_ENABLE();    __HAL_RCC_AXISRAM6_MEM_CLK_ENABLE();
    RAMCFG_SRAM2_AXI->CR &= ~RAMCFG_CR_SRAMSD;    RAMCFG_SRAM3_AXI->CR &= ~RAMCFG_CR_SRAMSD;    RAMCFG_SRAM4_AXI->CR &= ~RAMCFG_CR_SRAMSD;    RAMCFG_SRAM5_AXI->CR &= ~RAMCFG_CR_SRAMSD;    RAMCFG_SRAM6_AXI->CR &= ~RAMCFG_CR_SRAMSD;/* USER CODE END 5 */}

在MX_X_CUBE_AI_Init初始化函数中,为内存管理使能。 4

NPU运行代码

voidMX_X_CUBE_AI_Process(void){/* USER CODE BEGIN 6 */  LL_ATON_RT_RetValues_t ll_aton_rt_ret = LL_ATON_RT_DONE;const LL_Buffer_InfoTypeDef * ibuffersInfos = NN_Interface_Default.input_buffers_info();const LL_Buffer_InfoTypeDef * obuffersInfos = NN_Interface_Default.output_buffers_info();  buffer_in = (uint8_t *)LL_Buffer_addr_start(&ibuffersInfos[0]);  buffer_out = (uint8_t *)LL_Buffer_addr_start(&obuffersInfos[0]);
// Printing buffer start and end addresses.  LL_ATON_RT_RuntimeInit();// run 10 inferencesfor (int inferenceNb = 0; inferenceNb<10; ++inferenceNb) {/* ------------- *//* - Inference - *//* ------------- *//* Pre-process and fill the input buffer */// Fill input buffer with constant data.//_pre_process(buffer_in);/* Perform the inference */    LL_ATON_RT_Init_Network(&NN_Instance_Default);  // Initialize passed network instance objectdo {/* Execute first/next step */      ll_aton_rt_ret = LL_ATON_RT_RunEpochBlock(&NN_Instance_Default);/* Wait for next event */if (ll_aton_rt_ret == LL_ATON_RT_WFE) {        LL_ATON_OSAL_WFE();      }    } while (ll_aton_rt_ret != LL_ATON_RT_DONE);/* Post-process the output buffer *//* Invalidate the associated CPU cache region if requested *///_post_process(buffer_out);    LL_ATON_RT_DeInit_Network(&NN_Instance_Default);/* -------------------- *//* - End of Inference - *//* -------------------- */  }  LL_ATON_RT_RuntimeDeInit();/* USER CODE END 6 */}

MX_X_CUBE_AI_Process为神经网络推理部分代码,大致可以分为三个部分:输入数据处理、NPU运行、数据后处理。

typedefstruct  {    constchar *name;             /**< Buffer name. NULL if end of list */    __LL_address_t addr_base;     /**< Buffer base address */    uint32_t offset_start;        /**< Offset of the buffer start address from the base address */    uint32_t offset_end;          /**< Offset of the buffer end address from the base address                                   *   (first bytes address beyond buffer length) */    uint32_t offset_limit;        /**< Offset of the limiter address from the base address,                                   *   (needed for configuring streaming engines) */    uint8_t is_user_allocated;    /**< */    uint8_t is_param;             /**< */    uint16_t epoch;               /**< */    uint32_t batch;               /**< */    constuint32_t *mem_shape;    /**< shape as seen by the user in memory (only valid for input/output buffers) */    uint16_t mem_ndims;           /**< Number of dimensions of mem_shape (Length of mem_shape) */    Buffer_CHPos_TypeDef chpos;   /**< Position  of channels dimension in mem shape */    Buffer_DataType_TypeDef type; /**< */    int8_t Qm;                    /**< */    int8_t Qn;                    /**< */    uint8_t Qunsigned;            /**< */    uint8_t ndims;                /**< */    uint8_t nbits;                /**< */    uint8_t per_channel;          /**< */    constuint32_t *shape;        /**< */    constfloat *scale;           /**< */    constint16_t *offset;        /**< This can become int8 or uint8 based on the Qunsigned field.                                   *   (This field Must have the same format of the quantized value) */  } LL_Buffer_InfoTypeDef;

LL_Buffer_InfoTypeDef结构体包含了模型的基本信息,包括模型运行过程中各类数据缓冲区的核心属性与量化特征。完整记录了缓冲区的命名、基地址、有效地址偏移范围(起始 / 结束 / 限制偏移量)等内存布局信息,也通过is_user_allocated、is_param等标识区分缓冲区的分配归属(用户分配 / 系统分配)和用途类型(参数缓冲区 / 数据缓冲区);同时,epoch、batch字段适配了批量推理、多轮次执行的场景需求。

结构体通过mem_shape、mem_ndims、chpos定义了数据在内存中的实际存储维度、维度数量及通道维度位置,而type字段则标识了缓冲区的数据类型;对于量化模型,Qm、Qn、Qunsigned、nbits、per_channel等字段精准描述了量化参数(量化位数、符号类型、量化系数),scale(缩放因子)、offset(偏移量)字段,完整还原量化张量的数值映射关系,shape字段则补充了模型逻辑层面的张量维度信息。 代码测试

LL_ATON_RT_RetValues_t ll_aton_rt_ret = LL_ATON_RT_DONE;  const LL_Buffer_InfoTypeDef * ibuffersInfos = NN_Interface_Default.input_buffers_info();  const LL_Buffer_InfoTypeDef * obuffersInfos = NN_Interface_Default.output_buffers_info();  buffer_in = (uint8_t *)LL_Buffer_addr_start(&ibuffersInfos[0]);  buffer_out = (uint8_t *)LL_Buffer_addr_start(&obuffersInfos[0]);  // Printing buffer start and end addresses.  uint32_t buff_in_len, buff_out_len;  printf("输入地址偏移开始 = %lu, \n \r 输入地址偏移结束 = %lu \n \r",ibuffersInfos->offset_start,ibuffersInfos->offset_end);  printf("输出地址偏移开始 = %lu, \n \r 输出地址偏移结束 = %lu \n \r",obuffersInfos->offset_start,obuffersInfos->offset_end);  // Getting buffer size and printing it.  buff_in_len = ibuffersInfos->offset_end - ibuffersInfos->offset_start;  buff_out_len = obuffersInfos->offset_end - obuffersInfos->offset_start;  printf("输入大小= %lu \n\r 输出大小 size = %lu \n\r", buff_in_len, buff_out_len);  LL_ATON_RT_RuntimeInit();

我们打印一下输入缓存和输出缓存的信息内容查看一下结果:

image.png

image.png

因为我们的模型是3203203的图片,总计307200字节,完全对的上。 image.png

输出数据是121005的float类型,所以总大小为121005*4总计42000大小。 NPU运行测试

在进行测试之前,我们还需要把权重文件烧录到单片机的指定地址中(前面设置过的0x71000000) image.png

工程目录下随着STM32CubeMX的生成,有着一个raw格式的文件,我们将其后缀修改为.bin文件:

image.png

将其烧录到STM32N6的指定地址中。

for (int inferenceNb = 0; inferenceNb<10; ++inferenceNb) {    /* ------------- */    /* - Inference - */    /* ------------- */    /* Pre-process and fill the input buffer */    //_pre_process(buffer_in);    /* Perform the inference */    LL_ATON_RT_Init_Network(&NN_Instance_Default);  // Initialize passed network instance object    do {      /* Execute first/next step */      ll_aton_rt_ret = LL_ATON_RT_RunEpochBlock(&NN_Instance_Default);      /* Wait for next event */      if (ll_aton_rt_ret == LL_ATON_RT_WFE) {        LL_ATON_OSAL_WFE();      }    } while (ll_aton_rt_ret != LL_ATON_RT_DONE);    /* Post-process the output buffer */    /* Invalidate the associated CPU cache region if requested */    //_post_process(buffer_out);

    float *floatout = (float *)buffer_out;    for(int i = 0;i<2100;i++)    {      printf("index:%d [%.2f %.2f %.2f %.2f %.2f]\r\n",          i,floatout[i*5],floatout[i*5+1],floatout[i*5+2],floatout[i*5+3],floatout[i*5+4]);    }
    LL_ATON_RT_DeInit_Network(&NN_Instance_Default);    /* -------------------- */    /* - End of Inference - */    /* -------------------- */  }  LL_ATON_RT_RuntimeDeInit();

接着我们在代码中添加:把输出地址,按照152100的形状,按照float类型打印出来,模型的运行结果如下: image.png

这5个数据分别对应着Yolov8n模型输出的检测框的:中心横坐标x,中心纵坐标y,检测框宽还有检测框高以及各个类别的置信度,由于我们只有一个类别(人),因此输出就是5个一组。

由于我们的输入数据是随机的,因此输出数据也不可信,但是由于输出的所有结果都在0~1之间,符合Yolo格式的输出结果,因此可以证明神经网络的运行是完全正确的。

后面就是如何运行摄像头,摄像头图像到神经网络输入,输出后处理在屏幕中绘制:

文章出处:实在太懒于是不想取名

收藏 评论0 发布时间:2026-3-23 11:06

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版