SEGGER RTT:嵌入式调试的高效输出利器
EMTime

参考: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字符耗时
RTT1us
SWO120μ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
2
3
#define BUFFER_SIZE_UP                        1024  // MCU -> PC
#define BUFFER_SIZE_DOWN 16 // PC -> MCU
#define SEGGER_RTT_PRINTF_BUFFER_SIZE 64 // 格式化临时缓冲区

默认 BUFFER_SIZE_DOWN 是 16,不能随便改太大,否则 RTT Viewer 不一定能正常接收;

缓冲区选型建议(根据传输频率和单次数据量):

情况建议缓冲区大小
每秒发送 10 次 × 每次 1 字节6 字节
每次 2 字节11 字节
每次 5 字节31 字节
每次 10 字节61 字节
每次 50 字节401 字节
每秒发送 1 次 × 每次 500 字节501 字节

暴力点来说,大多数项目配置 1024 字节基本是绰绰有余的,除非你是数据刷屏狂魔;

RTT 最小使用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "SEGGER_RTT.h"

uint8_t RTT_Buffer_R[16] = {0};
uint32_t RTT_Count_R = 0;

int main(void)
{
SEGGER_RTT_ConfigUpBuffer(0, "RTTUP", NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
SEGGER_RTT_ConfigDownBuffer(0, "RTTDOWN", NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);

while (1)
{
static int i = 0;

// 设置当前使用的终端为 0
SEGGER_RTT_SetTerminal(0);
SEGGER_RTT_printf(0, RTT_CTRL_TEXT_RED"%d\r\n", i++);

// 设置当前使用的终端为 1
SEGGER_RTT_SetTerminal(1);
SEGGER_RTT_printf(0, "%d\r\n", i++);

// 从 PC 端接收数据,默认最多只能接收 16 字节
RTT_Count_R = SEGGER_RTT_Read(0, RTT_Buffer_R, 16);
if (RTT_Count_R)
{
RTT_Buffer_R[RTT_Count_R] = '\0'; // 添加字符串终止符
SEGGER_RTT_printf(0, RTT_CTRL_TEXT_YELLOW"%s\r\n", RTT_Buffer_R);
RTT_Count_R = 0;
}
}
}

说明:

  • SEGGER_RTT_SetTerminal(n) 设置的是在哪个虚拟终端窗口显示;
  • SEGGER_RTT_printf(0, …) 的第一个参数是 通道编号,默认为 0,固定使用;

注意:虚拟终端编号与通道编号并非一回事,通道编号决定数据流向的通道,而终端编号仅影响在 RTT Viewer 中显示的窗口;

  • 接收缓冲区推荐用 16 字节,因为 RTT Viewer 默认一次只支持发送一个字符,除非启用了“Send on Enter”;

打开 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 开始;

 Comments
Comment plugin failed to load
Loading comment plugin
💡 请正确填写您的邮箱,以确保能接收到来自本博客的评论回复通知~(*^▽^*)