[STM32N6]

【STM32N6570-DK测评】视频录制与播放

[复制链接]
184|1
手机看帖
扫描二维码
随时随地手机跟帖
xhackerustc|  楼主 | 2025-5-11 21:13 | 显示全部楼层 |阅读模式
本帖最后由 xhackerustc 于 2025-5-11 22:04 编辑

#申请原创#
  @21小跑堂

STM32N6集成有硬件H264 Encoder,也集成了sdmc控制器,板级设计上又加了tf卡槽,所以自然而然想到摄像头拍摄录制视频存入sd卡,所以笔者很想实现下这个功能。会偷懒的程序员才是好的程序员,ST官方有没有实现了这个功能呢?还真给笔者找到了,在STM32Cube_FW_N6的
Projects/STM32N6570-DK/Applications/VENC/VENC_SDCard/
Projects/STM32N6570-DK/Applications/VENC/VENC_SDCard_ThreadX/

前者是裸机环境,后者为ThreadX RTOS环境,笔者现在对ThreadX不是太感冒,兴趣完全迁移到了zephyr RTOS了,所以就VENC_SDCard吧。可能因STM32N6很新的缘故,这个VENC_SDCard还未来得及加上fatfs的支持,编码好的H264流是当字节流直接存在tf卡上的,无任何文件系统,fatfs的支持可以放TODO List中。


因VENC_SDCard已自包含,笔者不想再架起CubeMX这种大杀器了,直接写了个CMakeLists.txt,内容如下:
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
cmake_minimum_required(VERSION 3.20)

set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
set(CMAKE_ASM_COMPILER arm-none-eabi-gcc)
set(CMAKE_AR arm-none-eabi-ar)
set(CMAKE_OBJCOPY arm-none-eabi-objcopy)
set(CMAKE_OBJDUMP arm-none-eabi-objdump)
set(SIZE arm-none-eabi-size)

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

add_compile_options(-mthumb -mcpu=cortex-m55 -mfloat-abi=hard -mfpu=fpv5-d16 -mcmse -fsingle-precision-constant)
add_compile_options(-ffunction-sections -fdata-sections -fno-builtin -fno-common -Wdouble-promotion -Werror -Wno-unused-parameter -Wno-incompatible-pointer-types)

add_compile_options(-O2)

project(stm32n6 C ASM)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_C_STANDARD 99)

include_directories(Drivers/CMSIS/Include
                    Drivers/CMSIS/Device/ST/STM32N6xx/Include
                    Drivers/STM32N6xx_HAL_Driver/Inc
                    Drivers/BSP/STM32N6570-DK
                    Drivers/BSP/Components/Common
                    Utilities/lcd
                    Drivers/BSP/Components/imx335
                    Middlewares/ST/STM32_ISP_Library/isp/Inc
                    Middlewares/ST/STM32_ISP_Library/evision/Inc
                    Middlewares/Third_Party/VideoEncoder/inc
                    Middlewares/Third_Party/VideoEncoder/source/common
                    Middlewares/Third_Party/VideoEncoder/source/h264
                    Projects/STM32N6570-DK/Applications/VENC/VENC_SDCard/Appli/Inc)

add_definitions(-DSTM32N657xx)

file(GLOB_RECURSE SOURCES
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_cortex.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_xspi.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_dma.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_dma_ex.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_rcc.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_rcc_ex.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_gpio.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_pwr.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_pwr_ex.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_exti.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_uart.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_ltdc.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_dma2d.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_rif.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_jpeg.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_dcmipp.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_tim.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_tim_ex.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_sd.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_ll_venc.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_ll_sdmmc.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_i2c.c"
                "Drivers/STM32N6xx_HAL_Driver/Src/stm32n6xx_hal_i2c_ex.c"
                "Drivers/BSP/STM32N6570-DK/stm32n6570_discovery.c"
                "Drivers/BSP/STM32N6570-DK/stm32n6570_discovery_lcd.c"
                "Drivers/BSP/STM32N6570-DK/stm32n6570_discovery_sd.c"
                "Drivers/BSP/STM32N6570-DK/stm32n6570_discovery_camera.c"
                "Drivers/BSP/STM32N6570-DK/stm32n6570_discovery_bus.c"
                "Drivers/BSP/Components/imx335/*.c"
                "Utilities/lcd/stm32_lcd.c"
                "Middlewares/ST/VideoEncoder_EWL/*.c"
                "Middlewares/ST/STM32_ISP_Library/isp/Src/isp_algo.c"
                "Middlewares/ST/STM32_ISP_Library/isp/Src/isp_core.c"
                "Middlewares/ST/STM32_ISP_Library/isp/Src/isp_services.c"
                "Middlewares/ST/STM32_ISP_Library/isp/Src/isp_tool_com.c"
                "Middlewares/ST/STM32_ISP_Library/isp/Src/isp_cmd_parser.c"
                "Middlewares/Third_Party/VideoEncoder/source/h264/*.c"
                "Middlewares/Third_Party/VideoEncoder/source/common/*.c"
                "Projects/STM32N6570-DK/Applications/VENC/VENC_SDCard/Appli/Src/*.c"
                "Projects/STM32N6570-DK/Applications/VENC/VENC_SDCard/STM32CubeIDE/Appli/Src/*.c"
                "Projects/STM32N6570-DK/Applications/VENC/VENC_SDCard/STM32CubeIDE/Appli/Startup/startup_stm32n657xx.s"
                )

set(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/Projects/STM32N6570-DK/Applications/VENC/VENC_SDCard/STM32CubeIDE/Appli/STM32N657XX_LRUN.ld)
add_link_options(
                -mthumb -mcpu=cortex-m55 -mfloat-abi=hard -mfpu=fpv5-d16 -mcmse
                -Wl,--gc-sections,--print-memory-usage,-Map,${PROJECT_NAME}.map
                --specs=nano.specs
                --specs=nosys.specs)
add_link_options(-T ${LINKER_SCRIPT})

add_executable(${PROJECT_NAME}.elf ${SOURCES} ${LINKER_SCRIPT})

# target_link_libraries(${PROJECT_NAME}.elf printfloat)
target_link_libraries(${PROJECT_NAME}.elf ${CMAKE_SOURCE_DIR}/Middlewares/ST/STM32_ISP_Library/evision/Lib/libn6-evision-st-ae_gcc.a)
target_link_libraries(${PROJECT_NAME}.elf ${CMAKE_SOURCE_DIR}/Middlewares/ST/STM32_ISP_Library/evision/Lib/libn6-evision-awb_gcc.a)

set(HEX_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.hex)
set(BIN_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.bin)
set(LST_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.lst)
add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD
        COMMAND ${CMAKE_OBJCOPY} -Oihex [        DISCUZ_CODE_467        ]lt;TARGET_FILE:${PROJECT_NAME}.elf> ${HEX_FILE}
        COMMAND ${CMAKE_OBJCOPY} -Obinary [        DISCUZ_CODE_467        ]lt;TARGET_FILE:${PROJECT_NAME}.elf> ${BIN_FILE}
        COMMAND ${CMAKE_OBJDUMP} --all-headers --demangle --disassemble [        DISCUZ_CODE_467        ]lt;TARGET_FILE:${PROJECT_NAME}.elf> > ${LST_FILE}
        COMMAND ${SIZE} --format=berkeley [        DISCUZ_CODE_467        ]lt;TARGET_FILE:${PROJECT_NAME}.elf>
)
充分利用大sram
咱直接把代码加载进sram跑,尽量避免烧录xspi nor flash,这是不是有linux开发那味儿了。不过这么一来有个问题,FSBL有部分初始化code未运行,所以需要把FSBL/Src/system_stm32n6xx_fsbl.c的SystemInit()的代码完全拷贝添加到Appli/Src/system_stm32n6xx_s.c的SystemInit()中。


编译
cmake -B /tmp/build
cmake --build /tmp/build -j8

在/tmp/build/目录下即有stm32n6.bin生成,此文件即我们要加载进sram运行的bin文件。万事具备上酸菜,sorry上openocd


加载到sram运行
一终端执行
openocd/src/openocd -f interface/stlink.cfg -c "transport select dapdirect_swd"  -f target/stm32n6.cfg -c "adapter speed 8000"
另一终端执行
telnet 127.0.0.1 4444

然后在此openocd的telnet命令session下依次执行如下命令


halt
load_image /tmp/build/stm32n6.bin 0x34000400
reg pc 0x34019038
resume
现在LCD应该亮屏并显示摄像头拍到的东西,如图
293136388.jpg



错误出现与解决
但是如果观察STLINK的虚拟串口的输出,会发现奇怪的错误
11.jpg

What, "error initializing encoder -4"什么意思,难道VENC_SDCard代码有bug?但是VENC_SDCard的README文件明明提到可以运行,甚至怎么从tf卡dump h264数据流步骤都写清楚了。这里又是一个大坑,笔者花了很长时间调试跟踪,最后发现这确实是VENC_SDCard的代码中一个bug,至少对于arm-none-eabi-gcc toolchain来说确实有bug。VENC_SDCard依赖很多中间件,其中之一是VideoEncoder,这个库会用到malloc()函数从堆上分配内存,熟悉arm-none-eabi-gcc toolchain的工程师都知道,其libc是newlibc,它的malloc()实现需要用户提供sbrk()函数,但是我们看下VENC_SDCard给arm-none-eabi-gcc提供的sbrk()函数,它的位置在STM32CubeIDE/Appli/Src/sysmem.c
21.jpg

再看看ld文件,它的位置在STM32CubeIDE/Appli/STM32N657XX_LRUN.ld
2.jpg

这里栈放在了STM32N6的DTCM中,即0x30000000开始的128KB TCM,而STM32N6的secure world的SRAM都位于0x34000000,所以下面这个判断总是成立: 22.jpg

即总会认为堆已经用尽,快要增长到栈中去了,所以总是返回-1, errno被设成了ENOMEM,从而libc的malloc()会失败,于是下面这段代码EWLcalloc()会失败:
3.jpg

于是代码返回H264ENC_MEMORY_ERROR,这个H264ENC_MEMORY_ERROR的值正是-4, 这也就是上面"error initializing encoder -4"的来历。问题出在什么地方,linkscript把栈放在DTCM中,但是sysmem.c为做对应调整,仍然还是用的原来栈在SRAM中的版本。知道了原因修改就很简单了,笔者把STM32CubeIDE/Appli/Src/sysmem.c sbrk()函数修改如下
void *_sbrk(ptrdiff_t incr)
{
  extern uint8_t _end; /* Symbol defined in the linker script */
  extern uint8_t _estack; /* Symbol defined in the linker script */
  extern uint32_t _Min_Heap_Size; /* Symbol defined in the linker script */
  const uint32_t stack_limit = (uint32_t)&_end + (uint32_t)_Min_Heap_Size;
  const uint8_t *max_heap = (uint8_t *)stack_limit;
  uint8_t *prev_heap_end;

  /* Initialize heap end at first call */
  if (NULL == __sbrk_heap_end)
  {
    __sbrk_heap_end = &_end;
  }

  /* Protect heap from growing into the reserved MSP stack */
  if (__sbrk_heap_end + incr > max_heap)
  {
    errno = ENOMEM;
    return (void *)-1;
  }
  prev_heap_end = __sbrk_heap_end;
  __sbrk_heap_end += incr;

  return (void *)prev_heap_end;
}
再次编译加载到sram运行,一切变得那么丝滑

串口截图
5.jpg

录制结束把tf卡插读卡器接入电脑,运行如下命令读出
dd if=/dev/sdc of=/tmp/dump.bin bs=512 count=25000

这个dump.bin其实已经是正儿八经的H264视频文件,file探测下文件显示:
9.jpg

用mpv等软件也能播放,只是提示无timestamp:
10.jpg

可运行如下命令设置下30帧/s的帧率copy成转成另外一个视频文件:
ffmpeg -f h264 -framerate 30 -i /tmp/dump.bin -c copy /tmp/out.mp4

这个文件再用mpv软件播放已经不抱怨无timestamp了

8.jpg

使用特权

评论回复
xhackerustc|  楼主 | 2025-5-11 21:18 | 显示全部楼层
笔者发现的STM32Cube_FW_N6这个bug应该还没人报过,所以笔者是第一个发现并给出解决方案的?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

42

主题

157

帖子

1

粉丝