DAPLINK 之 RTT 输出日志
前言
- 1)RTT(Real Time Transfer,实时传输):SEGGER 的 Real Time Transfer (RTT) 是一种经过验证的技术,用于嵌入式应用中的系统监控和交互式用户 I/O。它结合了 SWO(Single Wire Output)和半主机模式(semihosting)的优点,同时提供了非常高的性能。
大致原理是 RTT 在 MCU 的 RAM 中使用 SEGGER RTT 控制块结构管理数据读写。通俗地说,就是 SEGGER RTT 在 RAM 创建一个区域,MCU 将日志写入到这个区域,外部程序使用调试器,通过 SWD/JTAG 协议,访问 MCU 的 DAP,进而读取 RAM 中该区域的日志。
1 安装 SEGGER RTT
-
2)安装完成后,在 \SEGGER\JLink_V794j\Samples\RTT 目录下可找到 RTT 源码。
废弃原因:
首先是我这里使用的 DAPLINK 而非 JLink,而且 DAPLink 的源码已经完整支持 SEGGER RTT,不再需要我们手动移植 SEGGER RTT 代码,所以可以不用安装 SEGGER RTT。
其次,OpenOCD 源码中也包含了对 RTT 的支持,那么我们也就不需要 J-Link RTT Viewer 了(虽然它的 GUI 看着着实不错)。
2 OpenOCD 下的 rtt
-
1)本来通过 《通过DAPLink和STLink使用RTT输出日志》 这篇文章研究 JLink 的 SEGGER RTT 怎么应用到 DAPLink 上,文章提到在没有 JLink 的情况下,可以使用 PyOCD + ST-Link 实现。
-
2)突然想起在研究 OpenOCD 源码时偶然好像见过 rtt_server_register_commands() 注册了 rtt 相关命令,搜索了一下后,看到了这篇文章《配合OpenOCD的RTT使用Trice日志库》,遂尝试了一下后果然可以
2.1 调试环境
- 1)DAPLink 源码可以到 github 上搜索 “DAPLINK air32”:
-
2)添加宏定义:
-
(1)查看 DAPLink 源码发现,要想使用 SEGGER RTT 输出日志,需要声明两个宏定义:
- 另外,DAPLink 提供了两个用于输出日志的宏定义 debug_msg() 和 debug_data()
-
(2)那么我们在 /records/hic_hal/stm32f103xb.yaml 文件中添加 DAPLINK_DEBUG 和 DAPLINK_DEBUG_RTT 两个宏定义。
-
(3)使用
__FILE__
宏定义输出文件信息时,全路径太长,那么我们可以在该文件中这样修改:
使用
__FILENAME__
宏定义来输出不携带路径信息的文件名。Keil 中可以使用__MODULE__
宏定义,而 CMake 中本来应该使用 cmake 内置函数在编译期处理的,但可能由于 DAPLINK 中的 cmake 模板问题,导致一直无法成功,只能在运行期解决,影响性能,不要使用在生产环境。- (4)还有最后一点,如果使用 CMake,那么需要注释掉 /records/tools/gcc_arm.yaml 中的 -Werror 项,否则编译时警告信息将转换为错误导致无法编译通过。
-
-
3)根据自已常用的 IDE 生成 stm32f103xb_bl 和 stm32f103xb_stm32f103rb_if 项目(我这里使用基于 cmake 的 CLion):
progen generate -t cmake -v -p stm32f103xb_bl progen generate -t cmake -v -p stm32f103xb_stm32f103rb_if
-
4)下载或编译最新版本的 OpenOCD(见其它文章)。
openocd --version Open On-Chip Debugger 0.12.0-00006-ga7204e9bf-dirty (2024-02-12-14:47) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html
-
5)最后还要准备一个调试器,我这里使用的是 PWLINK。
2.2 输出日志
-
1)新建一个 rtt.cfg 文件,添加如下内容:
init # rtt setup <address> <size> <ID>. setup RTT rtt setup 0x20000000 0x1000 "SEGGER RTT" rtt start # rtt server start <port> <channel>. Start a RTT server. rtt server start 8888 0
- (1)该文件可以放置到 OpenOCD 的 scripts/ 目录下,以便 OpenOCD 可以找到。
- (2)上述命令也可以通过 telnet 127.0.0.1 4444 连接到 OpenOCD Server 后逐条输入,只是每次都手动输入比较麻烦罢了。
- (3)rtt setup 命令用来设置 SEGGER RTT 控制块在 RAM 中的位置,如果不知道,那么就设置 RAM 的最大地址范围。
-
2)命令行运行如下命令:
openocd -d2 -f interface/cmsis-dap.cfg -f target/stm32f1x.cfg -f rtt.cfg
- 这里指定日志级别为 2 级 Info;调用接口为 cmsis-dap;目标芯片为 stm32f1x;rtt.cfg 为 RTT 的配置。
- 当日志中有 “Control block found at 0x200003dc” 以及 8888 端口的监听等内容时表示成功
-
3)然后,我们随便添加几行日志,编译并下载到开发板中:
-
4)最后,通过 telnet 127.0.0.1 8888 连接到 OpenOCD RTT Server 后,即可看到日志内容:
-
5)至此,学习 DAPLINK 源码的准备工作已经完成了。
3 关于日志中的文件名
-
1)我们知道在 C 语言中,
__FILE__
宏定义输出的是文件的全路径名,观察输出日志可以确认。大多数情况下,我们只需要知道文件名而不需要路径。 -
2)Keil armclang 中,可以通过
__MODULE__
来替代。 -
3)arm_gcc 中:
- (1)可以重新声明一个
__FILENAME__
宏定义来封装 strrchr() 函数。这种做法是程序运行期处理的,显然不太有利于单片机程序。 - (2)使用一些 Makefile 支持的命令(如 subst、notdir、abspath)来定义
__FILENAME__
。这样一来,这些命令在程序编译期就可以把需要的文件名裁剪好,效率更高一些。
- (1)可以重新声明一个
-
4)然而,可能因为 DAPLINK 采用的 CMake 版本较老,其采用的 target_compile_definitions() 方法添加宏定义。我测试过无数种写法均无法成功,无奈只能选择 strrchr() 函数。
-
5)最终结果: