TaterLi 个人博客

STM32N6 NPU 上手

本文仅仅是上手记录,并且丢掉那烦人的CubeIDE(不喜欢Eclipse),可以用CubeCLT来代替,里面包含了CubeIDE除了那个UI外壳的所有东西.

本文也不会依赖正点自己的东西,因为他也没解释文件来源等,属于授人以鱼了,所以我现在就要授人以渔.

用到的几个库.

这里不得不吐槽下,STM32N6的资料分散且很乱,还有闭源边界很模糊的问题.

硬件是正点原子的STM32N647开发板,毕竟官方买不起,32M的片外Flash和32M的片外RAM,但是NPU是高带宽操作,其实也用不到片外RAM的,而且实际拓扑能看到,NPU访问RAM比MCU核心还近.

可以理解成3,4,5,6是给NPU专用的,如果不够,还可以借用其他片上SRAM,再考虑片外RAM,我量化时候猜测YOLO v8n是不够的,所以我还借用了AXISRAM2,等下会说到.

第一步是准备一个配置JSON.

{
  "Globals": {},
  "Profiles": {
    "default": {
      "memory_pool": "./my_mpools/stm32n647.mpool",
      "options": "-O3 --all-buffers-info --cache-maintenance --Oauto-sched --native-float --enable-virtual-mem-pools --Omax-ca-pipe 4 --Ocache-opt --Os --enable-epoch-controller"
    }
  }
}

上面的优化Flag不能说和GCC一样,只能说基本没相关性,具体参数要看NPU编译器.

编译器文档 https://stedgeai-dc.st.com/assets/embedded-docs/stneuralart_neural_art_compiler.html

然后创建一个Pool配置.

{
  "params": { "param": [{ "paramname": "max_onchip_sram_size", "value": "1024", "magnitude": "KBYTES" }] },
  "memory": {
    "cacheinfo": [{ "nlines": 512, "linesize": 64, "associativity": 8, "bypass_enable": 1,
      "prop": { "rights": "ACC_WRITE", "throughput": "MID", "latency": "MID", "byteWidth": 8, "freqRatio": 2.50, "read_power": 13.584, "write_power": 12.645 } }],
    "mem_file_prefix": "atonbuf",
    "mempools": [
      { "fname": "AXIFLEXMEM", "name": "flexMEM", "fformat": "FORMAT_RAW", "prop": { "rights": "ACC_WRITE", "throughput": "MID", "latency": "MID", "byteWidth": 8, "freqRatio": 2.50, "read_power": 9.381, "write_power": 8.569 }, "offset": { "value": "0x34000000", "magnitude": "BYTES" }, "size": { "value": "0", "magnitude": "KBYTES" } },
      { "fname": "AXISRAM1", "name": "cpuRAM1", "fformat": "FORMAT_RAW", "prop": { "rights": "ACC_WRITE", "throughput": "MID", "latency": "MID", "byteWidth": 8, "freqRatio": 2.50, "read_power": 16.616, "write_power": 14.522 }, "offset": { "value": "0x34080000", "magnitude": "BYTES" }, "size": { "value": "0", "magnitude": "KBYTES" } },
      { "fname": "AXISRAM2", "name": "cpuRAM2", "fformat": "FORMAT_RAW", "prop": { "rights": "ACC_WRITE", "throughput": "MID", "latency": "MID", "byteWidth": 8, "freqRatio": 2.50, "read_power": 17.324, "write_power": 15.321 }, "offset": { "value": "0x34100000", "magnitude": "BYTES" }, "size": { "value": "1024", "magnitude": "KBYTES" } },
      { "fname": "AXISRAM3", "name": "npuRAM3", "fformat": "FORMAT_RAW", "prop": { "rights": "ACC_WRITE", "throughput": "HIGH", "latency": "LOW", "byteWidth": 8, "freqRatio": 1.25, "read_power": 18.531, "write_power": 16.201 }, "offset": { "value": "0x34200000", "magnitude": "BYTES" }, "size": { "value": "448", "magnitude": "KBYTES" } },
      { "fname": "AXISRAM4", "name": "npuRAM4", "fformat": "FORMAT_RAW", "prop": { "rights": "ACC_WRITE", "throughput": "HIGH", "latency": "LOW", "byteWidth": 8, "freqRatio": 1.25, "read_power": 18.531, "write_power": 16.201 }, "offset": { "value": "0x34270000", "magnitude": "BYTES" }, "size": { "value": "448", "magnitude": "KBYTES" } },
      { "fname": "AXISRAM5", "name": "npuRAM5", "fformat": "FORMAT_RAW", "prop": { "rights": "ACC_WRITE", "throughput": "HIGH", "latency": "LOW", "byteWidth": 8, "freqRatio": 1.25, "read_power": 18.531, "write_power": 16.201 }, "offset": { "value": "0x342e0000", "magnitude": "BYTES" }, "size": { "value": "448", "magnitude": "KBYTES" } },
      { "fname": "AXISRAM6", "name": "npuRAM6", "fformat": "FORMAT_RAW", "prop": { "rights": "ACC_WRITE", "throughput": "HIGH", "latency": "LOW", "byteWidth": 8, "freqRatio": 1.25, "read_power": 19.006, "write_power": 15.790 }, "offset": { "value": "0x34350000", "magnitude": "BYTES" }, "size": { "value": "448", "magnitude": "KBYTES" } },
      { "fname": "xSPI2", "name": "octoFlash", "fformat": "FORMAT_RAW", "prop": { "rights": "ACC_READ", "throughput": "MID", "latency": "HIGH", "byteWidth": 1, "freqRatio": 6.00, "cacheable": "CACHEABLE_ON", "read_power": 110, "write_power": 400.0, "constants_preferred": "true" }, "offset": { "value": "0x71C00000", "magnitude": "BYTES" }, "size": { "value": "4", "magnitude": "MBYTES" } }
    ]
  }
}

解释一下我的配置

内存池NPU可用容量用途
flexMEM0不用
AXISRAM10不用
AXISRAM21024 KBNPU RAM不够的补充
AXISRAM3~64 × 448 KBNPU 本地高速 RAM
xSPI2 Flash4 MB权重

Cache属于照抄,freqRatio数值越大,说明越慢,read_power/write_power说的是访问他的功耗代价,当前我的设置下,模型会这么编译

NPU RAM3~6
    ↓ 不够
AXISRAM2
    ↓ 仍不够
外部内存

按照目录结构存好,如果你说你在Windows?那你还是用Linux执行,毕竟模型训练各种也还是Linux方便.

编译模型,因为是相对路径的,所以要切换到对应目录.

export STEDGEAI=/opt/st/STEdgeAI/4.0/Utilities/linux/stedgeai
mkdir -p build/model-official
cd build/model-official
cp -a [你的配置路径,我是tools/model_n647] .
"$STEDGEAI" generate \
  --model /root/stm32n6-ai/official-models/yolov8n_320_quant_pc_ii_seg_coco-st.tflite \
  --target stm32n6 --input-data-type uint8 \
  --st-neural-art default@user_neuralart_n647.json \
  --workspace st_ai_ws --output st_ai_output --with-report
arm-none-eabi-objcopy -I binary st_ai_output/network_atonbuf.xSPI2.raw \
  --change-addresses 0x71C00000 -O ihex network-data.hex

量化整体过程大概是这样的.

量化 TFLite

ST Edge AI 前端导入 + 图优化 + 算子规范化

中间 ONNX
+
量化参数 JSON

AtoNN Neural-ART 编译

算子切分 调度 内存复用 NPU/CPU 划分

Epoch Controller 后处理

C代码 + EC微码 + 外部Flash权重 + 接口文件

实际通过复用,NPURAM已经足够了,并且NPURAM6都没用完.

====================================================================================                                                                                              
Memory usage information  (input/output buffers are included in activations)                                                                                                      
   ---------------------------------------------------------------------------------                                                                                              
        flexMEM    [0x34000000 - 0x34000000]:          0  B /          0  B  (  0.00 % used) -- weights:          0  B (  0.00 % used)  activations:          0  B (  0.00 % used)
        cpuRAM1    [0x34080000 - 0x34080000]:          0  B /          0  B  (  0.00 % used) -- weights:          0  B (  0.00 % used)  activations:          0  B (  0.00 % used)
        cpuRAM2    [0x34100000 - 0x34200000]:          0  B /      1.000 MB  (  0.00 % used) -- weights:          0  B (  0.00 % used)  activations:          0  B (  0.00 % used)
        npuRAM3    [0x34200000 - 0x34270000]:    435.938 kB /    448.000 kB  ( 97.31 % used) -- weights:          0  B (  0.00 % used)  activations:    435.938 kB ( 97.31 % used)
        npuRAM4    [0x34270000 - 0x342E0000]:    437.891 kB /    448.000 kB  ( 97.74 % used) -- weights:          0  B (  0.00 % used)  activations:    437.891 kB ( 97.74 % used)
        npuRAM5    [0x342E0000 - 0x34350000]:    440.062 kB /    448.000 kB  ( 98.23 % used) -- weights:          0  B (  0.00 % used)  activations:    440.062 kB ( 98.23 % used)
        npuRAM6    [0x34350000 - 0x343C0000]:    100.000 kB /    448.000 kB  ( 22.32 % used) -- weights:          0  B (  0.00 % used)  activations:    100.000 kB ( 22.32 % used)
        octoFlash  [0x71C00000 - 0x72000000]:      3.355 MB /      4.000 MB  ( 83.87 % used) -- weights:      3.355 MB ( 83.87 % used)  activations:          0  B (  0.00 % used)
                                                                                                                                                                                  
Total:                                             4.736 MB                                  -- weights:      3.355 MB                  activations:      1.381 MB                
====================================================================================                                                                                              
Used memory ranges                                                                                                                                                                
   ---------------------------------------------------------------------------------                                                                                              
        npuRAM3    [0x34200000 - 0x34270000]: 0x34200000-0x3426CFC0                                                                                                               
        npuRAM4    [0x34270000 - 0x342E0000]: 0x34270000-0x342DD790                                                                                                               
        npuRAM5    [0x342E0000 - 0x34350000]: 0x342E0000-0x3434E040                                                                                                               
        npuRAM6    [0x34350000 - 0x343C0000]: 0x34350000-0x34369000                                                                                                               
        octoFlash  [0x71C00000 - 0x72000000]: 0x71C00000-0x71F5AD70     

算子实现,EC就是走NPU的,Hybird就是涉及CPU的运算,因为有些运算NPU做代价可能更大或者不合适.

+----------------------+--------+--------------+                                                                                                                                  
| epoch ID             |  Type  |   Details    |                                                                                                                                  
+----------------------+--------+--------------+                                                                                                                                  
| epochs    2 -> 57    |   EC   |              |                                                                                                                                  
| epoch_58             | Hybrid | DepthToSpace |                                                                                                                                  
| epoch_59             | Hybrid |    Concat    |                                                                                                                                  
| epochs   60 -> 68    |   EC   |              |                                                                                                                                  
| epoch_69             | Hybrid | DepthToSpace |                                                                                                                                  
| epoch_70             | Hybrid |    Concat    |                                                                                                                                  
| epochs   71 -> 89    |   EC   |              |                                                                                                                                  
| epoch_90             | Hybrid |    Concat    |                                                                                                                                  
| epoch_91             | Hybrid |    Concat    |                                                                                                                                  
| epoch_92             | Hybrid | DepthToSpace |                                                                                                                                  
| epoch_93             | Hybrid |    Concat    |                                                                                                                                  
| epochs   94 -> 113   |   EC   |              |                                                                                                                                  
| epoch_114            | Hybrid |    Concat    |                                                                                                                                  
| epoch_115            | Hybrid |    Concat    |                                                                                                                                  
| epochs  116 -> 131   |   EC   |              |                                                                                                                                  
| epoch_132            | Hybrid |    Concat    |                                                                                                                                  
| <no executed epochs> |   EC   |              |                                                                                                                                  
| epoch_135            | Hybrid |    Slice     |                                                                                                                                  
| epoch_136            | Hybrid |    Slice     |                                                                                                                                  
| epochs  137 -> 137   |   EC   |              |                                                                                                                                  
| epoch_138            | Hybrid |  Transpose   |                                                                                                                                  
| epochs  139 -> 139   |   EC   |              |                                                                                                                                  
| epoch_140            |   SW   |   Softmax    |                                                                                                                                  
| epochs  141 -> 144   |   EC   |              |                                                                                                                                  
| epoch_145            | Hybrid |    Slice     |                                                                                                                                  
| epoch_146            | Hybrid |    Slice     |                                                                                                                                  
| epochs  147 -> 149   |   EC   |              |                                                                                                                                  
| epoch_150            | Hybrid |    Concat    |                                                                                                                                  
| epochs  152 -> 152   |   EC   |              |   

资源消耗

 Requested memory size by section - "stm32n6npu" target                                                                                                                           
 ------------------------------- -------- ----------- ------ -----------                                                                                                          
 module                              text      rodata   data         bss                                                                                                          
 ------------------------------- -------- ----------- ------ -----------                                                                                                          
 EC Blobs                              20     236,416      0           0                                                                                                          
 network.o                          1,024      41,359      0           0                                                                                                          
 NetworkRuntime1201_CM55_GCC.a     10,952           0      0           0                                                                                                          
 stai_network.o                         0           0      0           0                                                                                                          
 lib (toolchain)*                       0           0      0           0                                                                                                          
 ll atonn runtime                  10,802      18,103    192       1,741                                                                                                          
 ------------------------------- -------- ----------- ------ -----------                                                                                                          
 RT total**                        22,798     295,878    192       1,741                                                                                                          
 ------------------------------- -------- ----------- ------ -----------                                                                                                          
 weights                                0   3,517,793      0           0                                                                                                          
 activations                            0           0      0   1,447,824                                                                                                          
 io                                     0           0      0           0                                                                                                          
 ------------------------------- -------- ----------- ------ -----------                                                                                                          
 TOTAL                             22,798   3,813,671    192   1,449,565                                                                                                          
 ------------------------------- -------- ----------- ------ -----------                                                                                                          
 *  toolchain objects (libm/libgcc*)                                                                                                                                              
 ** RT AI runtime objects (kernels+infrastructure)                                                                                                                                
                                                                                                                                                                                  
  Summary - "stm32n6npu" target                                                                                                                                                   
  --------------------------------------------------                                                                                                                              
               FLASH (ro)     %*    RAM (rw)      %                                                                                                                               
  --------------------------------------------------                                                                                                                              
  RT total        318,868   8.3%       1,933   0.1%                                                                                                                               
  --------------------------------------------------                                                                                                                              
  TOTAL         3,836,661          1,449,757                                                                                                                                      
  --------------------------------------------------                                                                                                                              
  *  rt/total              

实际上NPU RAM是2MB,他没用完,所以哪怕我写多了,他也不是非要用,而是会根据实际情况选择,转换后的network-data.hex (arm-none-eabi-objcopy 转换)放到工程目录.

生成的4个文件复制到Model

./build/model-official/st_ai_output/network.c
./build/model-official/st_ai_output/network.h
./build/model-official/st_ai_output/stai_network.c
./build/model-official/st_ai_output/stai_network.h
./build/model-official/st_ai_output/network_ecblobs.h

复制中间件到工程

./STEdgeAI/4.0/Middlewares/ST/AI

根据网络写app_config.h,你知道他的INPUT SHAPE,摄像头的packed,以及一些你觉得有必要写的内容.

#ifndef __APP_CONFIG_H
#define __APP_CONFIG_H

#include "stm32n6xx_hal.h"
#include "stai_network.h"

/* This project has one 1024x600 RGB framebuffer.  The original 993 demo
 * used two 480/800x480 layers; those values are not valid for this board. */
#define LCD_BG_WIDTH                        1024
#define LCD_BG_HEIGHT                       600
#define LCD_FG_WIDTH                        1024
#define LCD_FG_HEIGHT                       600

#define CAMERA_MIRROR_FLIP                  CMW_MIRRORFLIP_MIRROR

#define NN_WIDTH                            STAI_NETWORK_IN_1_WIDTH
#define NN_HEIGHT                           STAI_NETWORK_IN_1_HEIGHT
/* Model input, independent of the LCD framebuffer size. */
#define NN_FORMAT                           DCMIPP_PIXEL_PACKER_FORMAT_RGB888_YUV444_1
#define NN_BPP                              STAI_NETWORK_IN_1_CHANNEL
#define NN_CLASSES                          80
#define NN_CLASSES_TABLE                    {\
                                                "person",\
                                                "bicycle",\
                                                "car",\
                                                "motorcycle",\
                                                "airplane",\
                                                "bus",\
                                                "train",\
                                                "truck",\
                                                "boat",\
                                                "traffic light",\
                                                "fire hydrant",\
                                                "stop sign",\
                                                "parking meter",\
                                                "bench",\
                                                "bird",\
                                                "cat",\
                                                "dog",\
                                                "horse",\
                                                "sheep",\
                                                "cow",\
                                                "elephant",\
                                                "bear",\
                                                "zebra",\
                                                "giraffe",\
                                                "backpack",\
                                                "umbrella",\
                                                "handbag",\
                                                "tie",\
                                                "suitcase",\
                                                "frisbee",\
                                                "skis",\
                                                "snowboard",\
                                                "sports ball",\
                                                "kite",\
                                                "baseball bat",\
                                                "baseball glove",\
                                                "skateboard",\
                                                "surfboard",\
                                                "tennis racket",\
                                                "bottle",\
                                                "wine glass",\
                                                "cup",\
                                                "fork",\
                                                "knife",\
                                                "spoon",\
                                                "bowl",\
                                                "banana",\
                                                "apple",\
                                                "sandwich",\
                                                "orange",\
                                                "broccoli",\
                                                "carrot",\
                                                "hot dog",\
                                                "pizza",\
                                                "donut",\
                                                "cake",\
                                                "chair",\
                                                "couch",\
                                                "potted plant",\
                                                "bed",\
                                                "dining table",\
                                                "toilet",\
                                                "tv",\
                                                "laptop",\
                                                "mouse",\
                                                "remote",\
                                                "keyboard",\
                                                "cell phone",\
                                                "microwave",\
                                                "oven",\
                                                "toaster",\
                                                "sink",\
                                                "refrigerator",\
                                                "book",\
                                                "clock",\
                                                "vase",\
                                                "scissors",\
                                                "teddy bear",\
                                                "hair drier",\
                                                "toothbrush"\
                                            }
#define NN_OUTPUT_NUMBER                    STAI_NETWORK_OUT_NUM

#define POSTPROCESS_TYPE                    POSTPROCESS_ISEG_YOLO_V8_UI
#define AI_YOLOV8_SEG_PP_TOTAL_BOXES        STAI_NETWORK_OUT_1_CHANNEL
#define AI_YOLOV8_SEG_PP_NB_CLASSES         NN_CLASSES
#define AI_YOLOV8_SEG_PP_MASK_NB            STAI_NETWORK_OUT_2_CHANNEL
#define AI_YOLOV8_SEG_PP_MASK_SIZE          STAI_NETWORK_OUT_2_HEIGHT
#define AI_YOLOV8_SEG_PP_CONF_THRESHOLD     0.5f
#define AI_YOLOV8_SEG_PP_IOU_THRESHOLD      0.4f
#define AI_YOLOV8_SEG_PP_MAX_BOXES_LIMIT    10

#endif

重点讲下最后的参数怎么来的

复制预处理层的文件 (STM32N6-GettingStarted-PoseEstimation/Middlewares/ai-postprocessing-wrapper)

最后唯一自己写的代码就是应用代码了.

#include "ai_instance_segmentation.h"

#include "FreeRTOS.h"
#include "task.h"
#include "serial_console.h"
#include "stm32n6xx_ll_bus.h"
#include "npu_cache.h"
#include "app_postprocess.h"
#include "app_config.h"
#include "stai_network.h"
#include <stdio.h>
#include <string.h>

#define AI_INPUT_BYTES (NN_WIDTH * NN_HEIGHT * NN_BPP)
#define AI_MAX_BOXES AI_YOLOV8_SEG_PP_MAX_BOXES_LIMIT

STAI_NETWORK_CONTEXT_DECLARE(s_network_context, STAI_NETWORK_CONTEXT_SIZE);
static TaskHandle_t s_ai_task;
static volatile uint32_t s_ai_buffer_busy;
static uint8_t *s_input;
static iseg_pp_outBuffer_t s_boxes[AI_MAX_BOXES];
static volatile uint32_t s_box_count;
static const char *const s_classes[NN_CLASSES] = NN_CLASSES_TABLE;

static void ai_log(const char *s) { (void)serial_console_write(s, (unsigned short)strlen(s)); }

uint8_t *AIInstanceSegmentation_GetInputBuffer(void)
{
  uint8_t *buffer = NULL;
  taskENTER_CRITICAL();
  if ((s_ai_task != NULL) && (s_ai_buffer_busy == 0U))
  {
    s_ai_buffer_busy = 1U;
    buffer = s_input;
  }
  taskEXIT_CRITICAL();
  return buffer;
}

void AIInstanceSegmentation_SubmitFrame(void)
{
  if (s_ai_task != NULL) xTaskNotifyGive(s_ai_task);
}

void AIInstanceSegmentation_DrawDetections(uint16_t *fb, uint32_t width, uint32_t height)
{
  iseg_pp_outBuffer_t boxes[AI_MAX_BOXES];
  uint32_t count;
  taskENTER_CRITICAL();
  count = s_box_count;
  memcpy(boxes, s_boxes, count * sizeof(boxes[0]));
  taskEXIT_CRITICAL();
  for (uint32_t i = 0; i < count; ++i)
  {
    int32_t x0 = (int32_t)((boxes[i].x_center - boxes[i].width * .5f) * width);
    int32_t y0 = (int32_t)((1.0f - boxes[i].y_center - boxes[i].height * .5f) * height);
    int32_t x1 = (int32_t)((boxes[i].x_center + boxes[i].width * .5f) * width);
    int32_t y1 = (int32_t)((1.0f - boxes[i].y_center + boxes[i].height * .5f) * height);
    if (x0 < 0) x0 = 0;
    if (y0 < 0) y0 = 0;
    if (x1 >= (int32_t)width) x1 = (int32_t)width - 1;
    if (y1 >= (int32_t)height) y1 = (int32_t)height - 1;
    if ((x1 <= x0) || (y1 <= y0)) continue;
    for (int32_t t = 0; t < 3; ++t)
    {
      for (int32_t x = x0; x <= x1; ++x)
      { fb[(y0 + t) * width + x] = 0x07e0U; fb[(y1 - t) * width + x] = 0x07e0U; }
      for (int32_t y = y0; y <= y1; ++y)
      { fb[y * width + x0 + t] = 0x07e0U; fb[y * width + x1 - t] = 0x07e0U; }
    }
  }
}

static void ai_npu_init(void)
{
  LL_MEM_EnableClock(LL_MEM_AXISRAM3 | LL_MEM_AXISRAM4 | LL_MEM_AXISRAM5 | LL_MEM_AXISRAM6);
  CLEAR_BIT(RAMCFG_SRAM3_AXI->CR, RAMCFG_CR_SRAMSD);
  CLEAR_BIT(RAMCFG_SRAM4_AXI->CR, RAMCFG_CR_SRAMSD);
  CLEAR_BIT(RAMCFG_SRAM5_AXI->CR, RAMCFG_CR_SRAMSD);
  CLEAR_BIT(RAMCFG_SRAM6_AXI->CR, RAMCFG_CR_SRAMSD);
  LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_NPU);
  LL_AHB5_GRP1_ForceReset(LL_AHB5_GRP1_PERIPH_NPU);
  LL_AHB5_GRP1_ReleaseReset(LL_AHB5_GRP1_PERIPH_NPU);
  LL_MEM_EnableClock(LL_MEM_CACHEAXIRAM);
  LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_CACHEAXI);
  LL_AHB5_GRP1_ForceReset(LL_AHB5_GRP1_PERIPH_CACHEAXI);
  LL_AHB5_GRP1_ReleaseReset(LL_AHB5_GRP1_PERIPH_CACHEAXI);
  npu_cache_enable();
}

void AIInstanceSegmentationTask(void *argument)
{
  char line[128];
  stai_network_info info;
  stai_ptr inputs[1] = {0};
  stai_ptr outputs[STAI_NETWORK_OUT_NUM] = {0};
  stai_size n_inputs = 1;
  stai_size n_outputs = STAI_NETWORK_OUT_NUM;
  uint32_t lengths[STAI_NETWORK_OUT_NUM];
  iseg_yolov8_pp_static_param_t pp;
  (void)argument;

  s_ai_task = xTaskGetCurrentTaskHandle();
  ai_npu_init();
  configASSERT(stai_runtime_init() == STAI_SUCCESS);
  configASSERT(stai_network_init(s_network_context) == STAI_SUCCESS);
  configASSERT(stai_network_get_info(s_network_context, &info) == STAI_SUCCESS);
  configASSERT((info.n_inputs == 1U) && (info.n_outputs == STAI_NETWORK_OUT_NUM));
  configASSERT(stai_network_get_inputs(s_network_context, inputs, &n_inputs) == STAI_SUCCESS);
  configASSERT(stai_network_get_outputs(s_network_context, outputs, &n_outputs) == STAI_SUCCESS);
  configASSERT((n_inputs == 1U) && (n_outputs == STAI_NETWORK_OUT_NUM));
  s_input = (uint8_t *)inputs[0];
  configASSERT(info.inputs[0].size_bytes == AI_INPUT_BYTES);
  for (uint32_t i = 0; i < STAI_NETWORK_OUT_NUM; ++i) lengths[i] = info.outputs[i].size_bytes;
  configASSERT(app_postprocess_init(&pp, &info) == AI_ISEG_POSTPROCESS_ERROR_NO);
  ai_log("ai: ready yolov8-seg / STEdgeAI 4\r\n");

  for (;;)
  {
    (void)ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    SCB_InvalidateDCache_by_Addr((uint32_t *)s_input, AI_INPUT_BYTES);
    configASSERT(stai_network_run(s_network_context, STAI_MODE_SYNC) == STAI_SUCCESS);
    for (uint32_t i = 0; i < STAI_NETWORK_OUT_NUM; ++i)
      SCB_InvalidateDCache_by_Addr((uint32_t *)outputs[i], lengths[i]);
    iseg_pp_out_t result = {0};
    int32_t err = app_postprocess_run((void **)outputs, STAI_NETWORK_OUT_NUM, &result, &pp);
    taskENTER_CRITICAL();
    s_box_count = ((err == 0) && (result.nb_detect > 0))
      ? ((result.nb_detect > AI_MAX_BOXES) ? AI_MAX_BOXES : (uint32_t)result.nb_detect) : 0U;
    memcpy(s_boxes, result.pOutBuff, s_box_count * sizeof(s_boxes[0]));
    taskEXIT_CRITICAL();
    if (s_box_count == 0U)
    { (void)snprintf(line, sizeof(line), "ai: objects=0 pp=%ld\r\n", (long)err); ai_log(line); }
    else for (uint32_t i = 0; i < s_box_count; ++i)
    {
      uint32_t cls = (uint32_t)s_boxes[i].class_index;
      uint32_t confidence = (uint32_t)(s_boxes[i].conf * 1000.0f + 0.5f);
      (void)snprintf(line, sizeof(line), "ai: %s %lu.%03lu\r\n",
        (cls < NN_CLASSES) ? s_classes[cls] : "unknown",
        (unsigned long)(confidence / 1000U), (unsigned long)(confidence % 1000U));
      ai_log(line);
    }
    taskENTER_CRITICAL();
    s_ai_buffer_busy = 0U;
    taskEXIT_CRITICAL();
  }
}

用CubeCLT的工具一路下载就行,Loader没必要自己做,直接用原子的就行,他只是个临时媒介.

ROOT="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
CUBECLT_ROOT="${CUBECLT_ROOT:-/opt/st/stm32cubeclt_1.22.0}"
APP_BIN="${APP_BIN:-$ROOT/build/app/app.bin}"
MODEL_HEX="${MODEL_HEX:-$ROOT/app/AI/Binary/network-data.hex}"
PROGRAMMER="$CUBECLT_ROOT/STM32CubeProgrammer/bin/STM32_Programmer_CLI"

"$PROGRAMMER" -c port=SWD mode=UR reset=HWrst freq=4000 \
  -el "$ROOT/loader/ExtMemLoader.stldr" \
  -d "$APP_BIN" 0x70010000 -v
"$PROGRAMMER" -c port=SWD mode=UR reset=HWrst freq=4000 \
  -el "$ROOT/loader/ExtMemLoader.stldr" \
  -d "$MODEL_HEX" -v

我用的是IMX415,对于STM32N6,这个摄像头未超规格.

退出移动版