建设外贸网站费用,低价手机网站建设,图片百度搜索,东庄水利建设公司网站【STM32开发笔记】移植AI框架TensorFlow到STM32单片机【下篇】 一、上篇回顾二、项目准备2.1 准备模板项目2.2 支持计时功能2.3 配置UART4引脚2.4 支持printf重定向到UART42.5 支持printf输出浮点数2.6 支持printf不带\r的换行2.7 支持ccache编译缓存 三、TFLM集成3.1 添加tfli… 【STM32开发笔记】移植AI框架TensorFlow到STM32单片机【下篇】 一、上篇回顾二、项目准备2.1 准备模板项目2.2 支持计时功能2.3 配置UART4引脚2.4 支持printf重定向到UART42.5 支持printf输出浮点数2.6 支持printf不带\r的换行2.7 支持ccache编译缓存 三、TFLM集成3.1 添加tflite-micro源码3.2 修正micro_time.cc代码3.3 构建micro_time.cc的规则3.4 添加TFLM构建规则3.5 添加TFLM函数调用3.6 添加TFLM依赖关系 四、TFLM测试4.1 编译TFLM和Appli项目4.2 下载Boot代码4.3 下载Appli代码4.3 运行TFLM基准测试 五、问题解决5.1 benchmark编译失败5.2 Appli链接报错5.3 benchmark无法正常开始5.4 Release版无法正常返回 六、源码分享七、参考链接 本文将会继续介绍——如何为STM32H7S78-DK开发板准备CMake项目、如何将TFLM集成到基于CMake的STM32项目中、如何在STM32H7S78-DK开发板上运行TFLM基准测试具体包括如何支持计时和printf输出、如何集成TFLM到基于CMake的STM32项目以及解决过程中遇到的一些问题。 一、上篇回顾
书接上回上篇文章主要分为TFLM是什么、TFLM初步体验、TFLM源码浅析、TFLM主体移植几个部分。其中TFLM初步体验部分将会介绍如何在PC上运行TFLM基准测试TFLM源码浅析部分主要介绍TFLM源码是如何进行构建的TFLM主体移植主要介绍如何在基于CMake的STM32项目中构建TFLM库和基准测试。
上篇链接 https://blog.csdn.net/xusiwei1236/article/details/142467410
二、项目准备
2.1 准备模板项目
项目模板采用基于CMake的STM32H7S78-DK项目代码仓为
https://gitcode.com/xusiwei1236/STM32H7S78-DK-XIP该项的ioc文件来自官方STM32CubeH7RS软件包Template_XIP项目修改了部分配置项目类型改为了CMake然后使用CubeMX生成的项目代码即为本项目的主要代码。
2.2 支持计时功能
STM32上使用HAL库记录耗时非常简单只需要用
HAL_GetTick() 获取Tick数即可默认的Tick频率是1000Hz需要注意的是 HAL_GetTickFreq() 返回的枚举值并不是实际的频率例如默认的HAL_TICK_FREQ_1KHZ其值为1而不是1000。
因此记录使用HAL_GetTick记录耗时代码类似
uint32_t start HAL_GetTick();// 需要记录耗时的代码uint32_t end HAL_GetTick();
float cost_s (end - start) / 1000.0f; // 实际耗时单位秒这部模板本身已经支持了不需要额外的工作。
2.3 配置UART4引脚
开发板上自带了ST-Link V3调试器该调试器带有虚拟串口功能。通过查阅原理图我们知道主控MCU和ST-Link之间的连接关系如下图 可以看到ST-Link的虚拟串口和主控芯片的连接关系为
VCP_RX连接到主控芯片的 PD0上VCP_TX连接到主控芯片的 PD1上
接下来需要修改这两个引脚的功能 启用UART4功能 完成上述修改后CtrlS保存然后重新生成项目代码。
2.4 支持printf重定向到UART4
CubeMX选择CMake项目后默认已经生成了 syscalls.c文件已经实现了支持gcc工具链的printf输出的一半功能 这个_write支持printf和fprintf调用__io_puchar进行输出。
另外一半功能——实现__io_puchar输出到UART即可实现printf输出到UART。
需要手动修改main.c文件实现__io_puchar函数 2.5 支持printf输出浮点数
默认生成的CMake项目不支持浮点数打印需要修改链接选项修改文件Appli\CMakeLists.txt
在末尾添加如下代码片段
target_link_options(${CMAKE_PROJECT_NAME} PRIVATE-u _printf_float
)之后再次编译就可以输出浮点数了。
2.6 支持printf不带\r的换行
大部分串口终端工具例如MobaXterm换行需要收到\r\n两个字符才能正常换行。通过修改代码可以让测试代码输出\n结尾也能和\r\n一样自动换行具体实现方式为 这样修改之后printf就同时支持了\r\n和\n两种换行符。
2.7 支持ccache编译缓存
修改CMake有时候需要清理build目录才能正常出发重新配置和构建但清理了build目录后重新构建的过程非常耗时。为了解决这个问题我们可以使用ccache进行加速。
ccache下载链接 Ccache — Download
Windows平台的ccache是压缩包解压到合适的目录后将其配置到PATH环境变量即可在任意位置使用ccache命令。
完成ccache配置之后可以咋CMake代码中加入如下片段实现CMake支持ccache加速
find_program(CCACHE_PROGRAM ccache)
if (CCACHE_PROGRAM)set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_PROGRAM})set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PROGRAM})message(STATUS Ccache found: ${CCACHE_PROGRAM}!)
else ()message(STATUS Ccache not found!)
endif ()三、TFLM集成
3.1 添加tflite-micro源码
首先将PC上运行过基准测试的TFLM代码拷贝到CubeMX生成的基于CMake的STM32H7S78-DK项目中并放在如下目录
Middlewares/tensorflow然后将前“TFLM移植”章节编写完成的CMakeLists.txt文件、micro_time.cc文件也放到这个目录中。另外将后下一步需要修改的generate_cc_arrays.py文件也拷贝一份放到该目录中。
完成上述操作后项目文件布局如下 3.2 修正micro_time.cc代码
TFLM默认的micro_time.cc文件在STM32上不能正常工作上篇文章已经给出了一个版本的实现经过测试上篇文章的代码不能实现预期。
实际需要修改为使用调用HAL库的代码 这里的ticks_per_second函数用于返回ticks频率GetCurrentTimeTicks函数用于返回当前的ticks数
3.3 构建micro_time.cc的规则
为了正确的构建micro_time.cc需要将TFLM默认的micro_time.cc文件过滤掉因此需要修改上一篇文章中我们实现的CMakeLists.txt代码具体修改为 这里默认的micro_time.cc由306行代码匹配上314行实现了将其从MICROLITE_CC_BASE_SRCS中过滤掉。
3.4 添加TFLM构建规则
接下来修改顶层的CMakeLists.txt文件在最后追加两行 这样就将TFLM的构建规则添加到了CubeMX生成的CMake项目中了。
3.5 添加TFLM函数调用
上篇文章中我们实现的TFLM的CMakeLists.txt以及完成了对TFLM库和基准测试库的构建并且最终会生成三个静态库
TFLM库包含TFLM所有类和函数实现代码keyword_benchmark 库包含keyword_benchmark函数实现代码person_detection_benchmark 库包含person_detection_benchmark函数实现代码
接着在我们的Appli子项目的代码中添加对TFLM的调用。
由于C函数支持参数重载编译器生成C函数会经过名称修饰。同样的函数声明代码放在C代码文件中和C代码文件中经过编译生成的二进制符号不同。这导致在C代码中我们无法通过声明函数或包含头文件的方式直接调用C函数否则链接时报告符号找不到。
因此我们无法通过在Appli子项目的main.c中直接调用keyword_benchmark函数或者person_detection_benchmark函数的方式实现基准测试的集成。
为了解决C代码中不能直接调用C函数的问题我们需要引入一个中间层。这里需要用到几个C相关的知识
使用extern C修饰C函数可以让编译器将该C函数按照C函数的方式生成符号不进行名称修饰C代码中可以调用由名称修饰的C函数就像调用其他C函数一样C有默认的内置宏__cplusplus用于只是当前C编译器支持的语言标准版本例如201103L表示C11
在Appli\Src目录中创建tflm_benchmark.h文件内容如下 与之对应的tflm_benchmark.cc文件内容为 这里通过KEYWORD_BENCHMARK和PERSON_DETECTION_BENCHMARK两个宏实现两个基准测试的开关。
接下来就可以修改Appli子项目的main.c调用这个tflm_benchmark函数了 3.6 添加TFLM依赖关系
接下来需要为我们的Appli添加对TFLM的依赖关系包括对TFLM库和基准测试的依赖具体修改的代码为 左侧行号标记绿色的即为新增代码行一共六处作用分别为
60~64行定义了几个CMake变量后面会用到68~69行为当前Appli子项目的构建目标添加KEYWORD_BENCHMARK或PERSON_DETECTION_BENCHMARK宏75行为当前Appli子项目的构建目标添加头文件搜索目录81~84行为当前Appli子项目的构建目标添加三个源代码文件90行为当前Appli子项目的构建目标添加库文件搜索目录96~98行为当前Appli子项目的构建目标添加链接keyword_benchmark或person_detection_benchmark库、tflite-micro库
完成以上全部修改后就完成了对TFLM库和基准测试的集成工作。
四、TFLM测试
好了万事俱备只欠东风
完成前面的所有工作后就可以准备在我们的STM32H7S78-DK上进行TFLM基准测试了。
4.1 编译TFLM和Appli项目
编译构建主要使用VSCode的CMake插件工具栏具体方法不再赘述感兴趣的可以参考我之前发的帖子 【STM32H7S78-DK评测】搭建基于ST官方VSCode扩展的STM32开发环境 - STM32团队 ST意法半导体中文论坛 (stmicroelectronics.cn)
编译之前先清理一下项目 接着构建TFLM核心库 继续生成基准测试库keyword_benchmark 以及基准测试库person_detection_benchmark 紧接着构建Appli项目 构建完成后可以看到RAM、Flash占用信息 例如图中的Flash占用为115244 B。
和Appli类似的方式进行Boot子项目的构建不再赘述。
4.2 下载Boot代码
由于Appli代码需要使用Boot代码进行跳转因此下载Appli代码之前需要线下载Boot代码到开发板上。
下载之前先将STM32H7S78-DK开发板和PC通过USB线连接好板子由三个USB口注意连接到标有STLK的。
接着VSCode上上操作 终端子窗口可以看到输出 4.3 下载Appli代码
接下来将我们的STM32H7S78-DK开发板和PC通过USB线连接好板子由三个USB口注意连接到标有STLK的。
然后在VSCode上操作 终端子窗口可以看到输出 4.3 运行TFLM基准测试
打开MobaXterm添加会话选择STLink的虚拟串口设备参数如下 连接设备之后按下开发板上的NRST按钮重启设备可以看到串口输出如下 可以看到keyword模型初始化耗时3毫秒单独运行一次耗时2毫秒连续运行10次耗时10毫秒速度还是可以的。
与之对比的在PC上运行keyword_benchmark的结果数据 可以看到PC上模型初始化和单独运行一次耗时都不到1毫秒连续运行10次耗时3毫秒。
同样稍加修改Appli\CMakeLists.txt我们可以编译person_detection_benchmark并得到在开发板上运行结果数据 可以看到开发板上运行有人图像的人体检测耗时为993毫秒没有人的耗时为994毫秒连续运行10次的耗时分别为9938毫秒和9940毫秒速度有点慢。
与之对应的PC上运行person_detection_benchmark的结果数据为 可以看到PC上运行有人图像的人体检测耗时为36毫秒没有人的耗时为35毫秒连续运行10次的耗时分别为9938毫秒和9940毫秒
五、问题解决
在前面的第三章、第四章的实践过程中我遇到了一些问题为了保持主体部分的简洁清晰没有将问题描述和解决方法写在第三章、第四章内容中。本章将介绍预提遇到的问题以及问题的解决方法如果你在实践过程中遇到类似的问题可以参考本章的方法进行解决。
5.1 benchmark编译失败
【问题现象】 编译失败报错信息 【问题原因】 直接原因是脚本生成代码中数组和变量定义有问题把路径带入进去了 【解决方法】 修改代码生成脚本 通过排查脚本生成代码可以知道**【问题根因】**是Windows系统的路径分隔符不是/.split(/)失败。
根据走读代码可以知道代码中的base_array_name是文件名的基础部分也就是去掉路径和扩展名重新实现一下就好了。
5.2 Appli链接报错
【问题现象】 Appli链接失败报错信息 【问题原因】 Appli子项目使用了VFP寄存器参数VFP register arguments而libtflite-micro.a没有使用
【解决方法】 顶层修改CMakeLists.txt添加一行 5.3 benchmark无法正常开始
【问题现象】 无法正常运行benchmark
【初步调试】 运行到benchmark入口函数之后无法进入tflite::InitializeTarget函数 下一步直接进入HardFault_Handler
【问题分析】 查看寄存器发现栈指针位置异常 已经超出了链接脚本设置的栈范围 根据这段代码可以知道正常的栈指针范围应该在[0x20000000, 0x20010000)的64K范围内。
【代码排查】 反汇编查看benchmark入口代码 可以看到入口处申请的栈内存空间为8192080K超过链接脚本设置的64K栈空间结合代码内容可以知道是MicroProfiler对象占用的空间。
【解决方法】 修改MicroProfiler代码具体修改如下 因为这个常量是几个数组的大小 初步估算5*4*4K正好是80K
因此把这个常量的值改小即可解决该问题。
5.4 Release版无法正常返回
【问题现象】 Debug版本可以正常运行Release版benchmark函数无法正常返回到main函数
【问题原因】 经过排查 发现原因是benchmark函数写了返回值类型int但没有写return语句
【解决方法】 修改代码benchmark函数最后添加一行return 0;语句即可
六、源码分享
本项目的所有代码已经开源到GitCode平台感兴趣的小伙伴可以免费下载体验 https://gitcode.com/xusiwei1236/STM32H7S78-DK-TFLM.git
本代码仓使用了git submodule特性需要用--recursive选项进行克隆
git clone --recursive https://gitcode.com/xusiwei1236/STM32H7S78-DK-TFLM.git另外tflite-micro依赖的一些三方软件已经打包到了如下仓库
https://gitcode.com/tflm/downloads.git下载方法
# 跳转到 tflite-micro 子目录
cd Middlewares/tensorflow/tflite-micro# 下载 downloads 下的三方软件源码
git clone https://gitcode.com/tflm/downloads.git tensorflow/lite/micro/tools/make/downloads/七、参考链接
CCache下载页面 Ccache — DownloadCMake中使用CCache Use Ccache with CMake | Lindevs官方STM32CubeH7RS软件包的XIP项目模板 Template_XIPTensorFlow Lite for Microcontrollers介绍 TensorFlow Lite for Microcontrollers (google.cn)TensorFlow Lite for Microcontrollers入门 微控制器入门 | TensorFlow (google.cn)tflite-micro 源码GitHub仓 https://github.com/tensorflow/tflite-microCMake最新文档 CMake Reference Documentation — CMake 3.30.3 Documentation