本帖最后由 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,内容如下:
充分利用大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"
另一终端执行
然后在此openocd的telnet命令session下依次执行如下命令
- halt
- load_image /tmp/build/stm32n6.bin 0x34000400
- reg pc 0x34019038
- resume
现在LCD应该亮屏并显示摄像头拍到的东西,如图
错误出现与解决
但是如果观察STLINK的虚拟串口的输出,会发现奇怪的错误
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
再看看ld文件,它的位置在STM32CubeIDE/Appli/STM32N657XX_LRUN.ld
这里栈放在了STM32N6的DTCM中,即0x30000000开始的128KB TCM,而STM32N6的secure world的SRAM都位于0x34000000,所以下面这个判断总是成立:
即总会认为堆已经用尽,快要增长到栈中去了,所以总是返回-1, errno被设成了ENOMEM,从而libc的malloc()会失败,于是下面这段代码EWLcalloc()会失败:
于是代码返回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运行,一切变得那么丝滑
串口截图
录制结束把tf卡插读卡器接入电脑,运行如下命令读出
- dd if=/dev/sdc of=/tmp/dump.bin bs=512 count=25000
这个dump.bin其实已经是正儿八经的H264视频文件,file探测下文件显示:
用mpv等软件也能播放,只是提示无timestamp:
可运行如下命令设置下30帧/s的帧率copy成转成另外一个视频文件:
- ffmpeg -f h264 -framerate 30 -i /tmp/dump.bin -c copy /tmp/out.mp4
这个文件再用mpv软件播放已经不抱怨无timestamp了
|