参考:https://www.armbbs.cn/forum.php?mod=viewthread&tid=86177
在嵌入式开发中,如何在不影响系统实时性的前提下,高效输出调试信息,一直是让人头疼的问题;传统的串口 printf 虽然简单易用,但在输出量大、串口速率较低时,就可能严重阻塞系统主流程,甚至引发时序异常;
为了解决这些问题,SEGGER 提供了 RTT(Real-Time Transfer)机制,它是一种利用 J-Link 仿真器在目标设备与主机之间进行高速内存通信的方案;它几乎不占用 CPU 时间、无需占用串口资源,而且速度快到令人惊喜,非常适合用于日志输出、数据查看甚至远程控制;
这篇博客将围绕 SEGGER RTT 展开,介绍简单原理、使用方法、常见配置及注意事项,帮助你在项目中轻松上手 RTT 输出,彻底告别“卡顿的串口 printf”;
RTT简介
什么是RTT
RTT 是 SEGGER 提供的一种 通过调试接口进行双向通信 的机制,所有支持 J-Link 的调试器都可以使用这个功能;它的核心优势在于:
- 高速传输:输出字符速度远超 SWO 和半主机模式;
- 非阻塞:不会影响目标程序的实时执行;
- 无需额外引脚:不依赖 SWO,引脚需求低;
RTT 的传输是通过 SWD 或 JTAG 接口完成的,不需要像传统串口那样占用 UART 资源,也不需要额外的 IO;只要接好 SWDIO、SWCLK(必要时加 VCC 和 NRST),甚至用三线 JLINK-OB 也能正常使用;
多通道+双向通信
RTT 不只是一个 printf 替代品,它支持 多通道双向通信,这意味着你可以像使用多个串口终端一样,将不同类别的日志输出到不同的“虚拟终端”中;
SEGGER 官方提供的工具 RTT Viewer 支持多个终端窗口,可以将标准输出、错误信息、调试日志分流显示,条理清晰,非常适合调试复杂系统;
RTT vs SWO vs 半主机模式
官方做过一组速度测试(以 STM32F407、168MHz 为例):
| 方式 | 输出82字符耗时 |
|---|---|
| RTT | 1us |
| SWO | 120μs |
| 半主机模式 | 10700μs |
可以看出,RTT 不只是快一点,而是快了几个数量级!如果你以前用的是 SWO 或半主机,这种性能差距足以让你考虑迁移到 RTT;
RTT 与硬件兼容性
RTT 不依赖 SWO 引脚,这对大多数小型开发板非常友好;基本上只要你能用 J-Link 下载程序,就能用 RTT 输出调试信息;
而且 RTT 支持多种接口速度的 J-Link 设备;在默认的 512 字节缓冲区配置下,普通速度的 J-Link 也可以跑到 0.5MB/s,高端版甚至可以达到 1MB/s,轻松应对大体量日志输出的需求;
RTT 工作原理简单解析
核心结构
RTT 的核心在于它在目标芯片的 RAM 中创建了一个称为 RTT 控制块(RTT Control Block) 的结构;这个控制块包含:
- 一个 标识符(ID):用于让 J-Link 调试器能快速在芯片内存中定位这个控制块;
- 多个 通道结构体:每个通道对应一个缓冲区,用来描述缓冲区地址、大小、读写指针等状态信息;
RTT 支持 多个上行通道(Up Buffers) 和 多个下行通道(Down Buffers),即从芯片上传到 PC 端,或者从 PC 发送数据到芯片;这使得 RTT 可以支持例如“标准输出”“错误日志”“调试日志”等多路并行通信;
通道数量和每个缓冲区的大小可以通过编译配置定义,也支持在运行时动态注册新的缓冲区;
数据写入策略
每个 RTT 通道可以配置为阻塞模式或非阻塞模式:
- 阻塞模式:当缓冲区满了,应用程序会等待,直到 J-Link 读出数据腾出空间;这保证了日志不会丢失,但有可能会阻塞当前任务执行;
- 非阻塞模式:当缓冲区满时,多余的数据会直接丢弃,程序继续正常执行;这种模式下,即使调试器没有连接,也不会影响系统实时行为;
缓冲区工作
每个缓冲区本质上是一个环形队列,用一对指针进行数据管理:
对于上行通道(芯片 → 主机):
- 写入指针由应用程序维护(写入数据);
- 读取指针由 J-Link 维护(从芯片读数据);
对于下行通道(主机 → 芯片):
- 写入指针由 J-Link 写;
- 读取指针由芯片读取;
当读取指针和写入指针相等时,说明缓冲区为空;
这种设计方式确保了调试器和芯片之间的同步传输,不需要中断,也不依赖外设,从而最大限度地减少对系统性能的干扰;
RTT移植
准备 RTT 组件
RTT 随 J-Link 工具链一同提供,无需单独下载安装;你可以在默认安装路径下找到 RTT 示例代码,路径如下:
1 | C:\Program Files\SEGGER\JLink\Samples\RTT |
你可以直接把 RTT 目录下的所有文件打包带入你的工程中;
关于 RTT 汇编加速文件(可选)
新版本 RTT 文件夹中包含一个用于优化的汇编文件(如 SEGGER_RTT_ASM_ARM.S),作用是加速字符输出(通过优化内存访问方式),在高频率大数据量输出时能提高吞吐率;
不过由于不同编译器对汇编支持略有差异,如果你暂时用不上或不清楚用法,也可以先忽略这个文件,不会影响基本使用;
缓冲区大小配置说明
RTT 的配置文件为 SEGGER_RTT_Conf.h,你需要关注三个重要参数:
1 |
默认 BUFFER_SIZE_DOWN 是 16,不能随便改太大,否则 RTT Viewer 不一定能正常接收;
缓冲区选型建议(根据传输频率和单次数据量):
| 情况 | 建议缓冲区大小 |
|---|---|
| 每秒发送 10 次 × 每次 1 字节 | 6 字节 |
| 每次 2 字节 | 11 字节 |
| 每次 5 字节 | 31 字节 |
| 每次 10 字节 | 61 字节 |
| 每次 50 字节 | 401 字节 |
| 每秒发送 1 次 × 每次 500 字节 | 501 字节 |
暴力点来说,大多数项目配置 1024 字节基本是绰绰有余的,除非你是数据刷屏狂魔;
RTT 最小使用代码
1 |
|
说明:
- SEGGER_RTT_SetTerminal(n) 设置的是在哪个虚拟终端窗口显示;
- SEGGER_RTT_printf(0, …) 的第一个参数是 通道编号,默认为 0,固定使用;
注意:虚拟终端编号与通道编号并非一回事,通道编号决定数据流向的通道,而终端编号仅影响在 RTT Viewer 中显示的窗口;
- 接收缓冲区推荐用 16 字节,因为 RTT Viewer 默认一次只支持发送一个字符,除非启用了“Send on Enter”;
使用 J-Link RTT Viewer 工具
打开 J-Link RTT Viewer,选择如下参数:
- Interface:JTAG 或 SWD(视你的硬件连接而定);
- Speed:建议设置为中等速度,比如 1000kHz,太高可能不稳定;
- 连接不上时:点击菜单 File -> Connect 尝试重连;
- 多终端支持:在工具右下角可以勾选多个终端进行输出区分;
- 输入设置:建议勾选 Send on Enter,这样输入多个字符后按下回车才会发送,方便处理字符串指令;
总结
SEGGER RTT 凭借其高速、非阻塞、零占用外设资源等优势,成为现代嵌入式开发中调试输出的理想方案;相比传统串口、SWO 或半主机模式,RTT 不仅性能更优,还具有更强的灵活性,支持多通道、双向通信,使日志输出更加有序高效;
通过本篇文章的介绍,我们了解了 RTT 的工作原理、配置方式、缓冲策略以及实际使用示例,并结合实际开发场景给出了配置建议;如果你在项目中苦于串口调试带来的性能瓶颈,不妨试试 RTT——它不仅能大幅提升调试效率,还能让你更专注于系统逻辑本身,而不是被调试工具“牵着鼻子走”;
此外,RTT 的下行通道支持从 PC 向目标设备发送数据,这为构建交互式调试终端提供了可能;例如,可以将 Letter Shell 的输入输出绑定到 RTT 通道上,实现无需串口、无需 IO 引脚的 Shell 控制台;配合 RTT Viewer 或自定义串口工具,你可以像操作普通终端一样通过 J-Link 实时操控设备,极大地拓展了嵌入式调试的边界;
未来在配合如 RTT Viewer、JScope、SystemView 等 SEGGER 工具时,RTT 还能拓展出更多实用功能,助力系统分析与性能评估;写代码的时候不卡壳,调试输出更清爽,从一步启用 RTT 开始;