1. 课程导入与RTT定位
在嵌入式开发中,日志输出几乎是定位问题的第一手段。传统做法是把 printf 重定向到 UART,再用 USB 转串口线接到 PC 上的串口终端。这个方案简单、通用,但一旦系统进入高频采样、中断密集或低功耗场景,它的短板就会暴露:串口波特率通常只有 115200 或 921600,一条几十字节的日志就要占用几百微秒到几毫秒的 CPU 时间;如果日志量超过带宽,输出会被阻塞,甚至反过来改变被调试系统的时序,让原本偶发的 bug 消失或变形。
SEGGER RTT(Real Time Transfer)换了一条路径。它不占用 UART,而是让目标 MCU 通过调试接口(SWD/JTAG)直接访问一小块位于 RAM 中的环形缓冲区,调试探针(J-Link)在后台以内存读写的方式搬运数据,PC 端工具再从探针取走数据。对目标 CPU 而言,写日志只是往 RAM 里拷贝若干字节并更新写指针,耗时通常在微秒级,且不需要等待任何外设。这就是 RTT 被称为“实时传输”的根本原因。
RTT 与传统串口日志的差异
把两者放在一起对比,差异集中在四个维度。第一是物理通道:UART 占用引脚和外设,RTT 复用 SWD/JTAG 的调试访问,不额外占用 GPIO。第二是 CPU 开销:UART 发送需要等待移位寄存器空或使用 DMA,RTT 只做内存拷贝。第三是带宽:J-Link 的 RTT 上行带宽在常见配置下可达数百 KB/s 到 MB/s 量级,远高于 115200 波特率对应的约 11.5 KB/s。第四是双向性:RTT 天然支持上行(目标到 PC)和下行(PC 到目标)两个方向,下行通道可以直接当作命令输入或参数下发通道使用。
需要特别说明的是,RTT 并非在所有场景都优于串口。它依赖调试探针和调试连接,产品脱机运行时没有 J-Link 就没有 RTT;而 UART 只要引脚还在就能输出。因此工程上常见的做法是:开发调试阶段用 RTT 获取高带宽、低干扰的日志,量产或现场诊断保留一路 UART 作为兜底。
实时日志与数据通道的双重用途
RTT 的缓冲区被组织成多个“上行通道”和“下行通道”,默认通道 0 用于文本日志,但通道 1、2……可以承载任意二进制数据。这意味着同一套机制既能打印 "sensor=%d" 这样的文本,也能把 ADC 采样值、PID 中间量、姿态解算结果以二进制形式高速上传,PC 端用脚本解析后直接绘图。反过来,下行通道可以把目标参数、阈值、控制指令从 PC 发回 MCU,形成一个轻量的双向调试链路。
下面这张图展示了 RTT 的整体数据通路:目标端应用通过 SEGGER_RTT_Write 写入 RAM 中的环形缓冲区,J-Link 通过 SWD 访问该内存,PC 端 RTT Viewer 或自定义工具读取并显示;下行方向则反向流动。
本课程的学习路径
课程会沿着“先跑通、再理解、后优化”的顺序展开。第一阶段聚焦环境搭建:安装 J-Link 软件包、在工程中集成 RTT 源码、确认 SEGGER_RTT_Init() 被正确调用、用 RTT Viewer 看到第一条日志。第二阶段深入机制:理解控制块结构、缓冲区大小与模式(NO_BLOCK_SKIP、NO_BLOCK_TRIM、BLOCK_IF_FIFO_FULL)对丢包行为的影响,以及多通道的分配策略。第三阶段面向实战:在中断和 RTOS 任务中安全使用 RTT、用二进制通道上传高速数据、用下行通道做参数在线调整,并处理缓冲区溢出、探针断开等异常。
实验环境准备
为了让后续章节的示例可以直接复现,建议准备如下环境。硬件方面:一块带 SWD 接口的 Cortex-M 开发板(例如 STM32F407 或 nRF52840),一个 J-Link 或兼容探针(J-Link EDU、J-Link OB 均可)。软件方面:SEGGER J-Link 软件包(含 RTT Viewer、J-Link RTT Client)、目标 IDE(Keil MDK、IAR EWARM 或 STM32CubeIDE 任选其一)、以及一个串口终端作为对照。RTT 源码位于 J-Link 安装目录下的 Samples/RTT/,核心文件是 SEGGER_RTT.c、SEGGER_RTT.h 和 SEGGER_RTT_Conf.h。
集成时把这三个文件加入工程,在 main() 开头调用一次初始化,然后就可以直接使用。下面是一段最小可运行示例,用于验证环境是否就绪:
#include "SEGGER_RTT.h"
int main(void)
{
SEGGER_RTT_Init();
SEGGER_RTT_WriteString(0, "RTT boot OK\r\n");
uint32_t cnt = 0;
while (1) {
SEGGER_RTT_printf(0, "tick=%lu\r\n", (unsigned long)cnt++);
for (volatile int i = 0; i < 1000000; i++) { }
}
}
烧录后打开 J-Link RTT Viewer,选择目标器件和接口速度(例如 SWD、4000 kHz),连接成功后应能看到 RTT boot OK 以及持续递增的 tick= 行。如果看不到输出,按以下顺序排查:确认探针已连接且目标已上电;确认 SEGGER_RTT_Init() 确实被执行(可在其中设断点);确认 RTT Viewer 的“Auto Detection”能找到控制块,或手动指定 _SEGGER_RTT 符号地址;确认缓冲区没有被链接脚本放到不可访问的区域。
SEGGER_RTT_printf 前后翻转一个 GPIO,用示波器测量高电平持续时间。在 168 MHz 的 Cortex-M4 上,一条约 20 字节的日志通常只需几微秒,而同样内容走 115200 波特率 UART 需要约 1.7 毫秒。这个数量级差异就是 RTT 在实时调试中的价值所在。