2. RTT核心原理
RTT(Real Time Transfer)之所以能做到“几乎不打断 CPU 运行”的实时日志输出,根本原因在于它并不依赖目标芯片的串口外设,而是把目标端 RAM 中的一块普通内存当作数据交换区,由调试探针(J-Link)通过调试接口直接读写这块内存。目标程序只做“往内存里写数据”这一件事,真正的数据搬运由 J-Link 在后台完成。理解这一点,就理解了 RTT 的全部核心。
一、控制块:RTT 的“总目录”
RTT 在目标内存中首先放置一个结构体,称为控制块(Control Block),它是整个 RTT 机制的入口。控制块本身不存放日志数据,而是存放“去哪里找数据”的描述信息。其关键字段包括:
- ID 字符串:固定为
"SEGGER RTT",J-Link 连接后会在目标 RAM 中搜索这个字符串,从而定位控制块。这就是“自动识别”的基础。 - 上行缓冲区描述数组:每个上行通道对应一个
{缓冲区指针, 缓冲区大小, 写指针, 读指针, 标志}的描述项。 - 下行缓冲区描述数组:结构相同,方向相反,供主机向目标发送数据。
- 通道数量:上行、下行各有多少个通道(默认各 1 个,最多可配置到 16 个)。
控制块通常由 SEGGER_RTT_Init() 在启动时初始化,也可以放在固定链接段中,使 J-Link 无需运行目标代码即可找到它。
二、环形缓冲区:无锁通信的关键
每个通道的核心是一个环形缓冲区(Ring Buffer),由一块连续内存、一个写指针 WrOff 和一个读指针 RdOff 组成。两个指针都只在 0 ~ Size-1 范围内循环回绕,因此称为“环形”。
上行通道的工作方式如下:
- 目标程序调用
SEGGER_RTT_Write(),把数据拷贝到WrOff指向的位置,然后前移WrOff。 - J-Link 通过调试接口读取
RdOff和WrOff,把两者之间的数据取走,然后更新RdOff。 - 只要
WrOff != RdOff,就说明缓冲区里有待取数据;当WrOff == RdOff时表示缓冲区为空。
这里有一个非常重要的设计:目标端只写 WrOff,主机端只写 RdOff。两个指针由不同的一方单独修改,因此不需要互斥锁,也不会出现“目标在写、主机在读”的竞争。这正是 RTT 能做到极低开销的原因。
SEGGER_RTT_Write() 默认采用“非阻塞”模式:能写多少写多少,剩余数据被丢弃并返回实际写入长度。若使用阻塞模式,则目标会等待主机取走数据,可能拖慢实时性,需谨慎使用。三、上行与下行:双向通道
RTT 的“双向”体现在两套独立的环形缓冲区上:
- 上行通道(Up Buffer):目标 → 主机,用于日志、printf 输出、变量跟踪数据。目标写、主机读。
- 下行通道(Down Buffer):主机 → 目标,用于向目标发送命令、参数、交互输入。主机写、目标读。
两者共用同一套控制块和指针协议,只是读写角色互换。因此 RTT 不仅能“看日志”,还能实现主机与目标之间的双向交互,例如通过 J-Link 向目标下发控制指令。
四、自动识别过程
当 J-Link 连接目标并启动 RTT 时,会执行以下识别流程:
- 在目标 RAM 的已知搜索范围内扫描
"SEGGER RTT"字符串。 - 找到后,以该位置为起点解析控制块,读取通道数量和各缓冲区描述。
- 根据描述中的指针和大小,建立主机端与目标端缓冲区的映射关系。
- 此后主机周期性轮询
WrOff,一旦发现新数据就取走并更新RdOff。
整个过程不需要目标程序主动“通知”主机,也不需要串口中断,因此对目标实时性的影响极小。
五、关键参数与配置示例
在 SEGGER_RTT_Conf.h 中,最常调整的参数是缓冲区大小和通道数量。以下是一组典型配置示例(属于教学示例值,实际应按项目需求调整):
#define BUFFER_SIZE_UP (1024) // 上行缓冲区 1KB
#define BUFFER_SIZE_DOWN (16) // 下行缓冲区 16B
#define SEGGER_RTT_MAX_NUM_UP_BUFFERS (2) // 2 个上行通道
#define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (1) // 1 个下行通道
#define SEGGER_RTT_MODE_DEFAULT SEGGER_RTT_MODE_NO_BLOCK_SKIP
缓冲区越大,能缓存的日志越多,但占用 RAM 也越多;缓冲区越小,越容易在高速打印时丢数据。判断是否丢数据的方法是:观察 SEGGER_RTT_Write() 的返回值是否小于请求写入长度,或使用 SEGGER_RTT_HasDataUp() 检查是否有未取走的数据。
六、小结与判断依据
RTT 的核心可以概括为三句话:控制块是入口,环形缓冲区是载体,双指针分离是无锁的关键。上行通道负责目标到主机的日志输出,下行通道负责主机到目标的命令下发,两者共用同一套指针协议。自动识别则依赖 "SEGGER RTT" 字符串在 RAM 中的定位。
在实际使用中,判断 RTT 是否正常工作的依据包括:J-Link 能否在目标 RAM 中找到控制块、RTT Viewer 是否显示通道列表、SEGGER_RTT_Write() 返回值是否等于写入长度、以及日志是否连续无丢失。掌握这些,就为后续的 RTT 配置、性能调优和故障排查打下了基础。