1. 课程导入与体系总览

在嵌入式系统开发中,最令人头疼的问题往往不是“代码写不出来”,而是“程序跑起来之后,我看不见它”。MCU 内部没有显示器,调试器一旦暂停 CPU,很多与时序、并发、中断相关的问题就会瞬间消失。传统的断点调试擅长回答“变量此刻等于多少”,却很难回答“过去 10 毫秒里,这个任务被调度了几次”“中断响应延迟的抖动有多大”“这段代码实际消耗了多少周期”。SEGGER 的实时观测与性能诊断体系,正是为填补这一空白而构建的一整套工具链。

这套体系的核心价值可以概括为一句话:在不打断、不显著干扰目标系统运行的前提下,把 MCU 内部看不见的运行状态,变成主机端可观察、可量化、可回溯的数据。它由几个相互配合的层次组成:最底层是调试探针(J-Link 系列)与目标芯片的调试接口;中间层是运行在目标机上的实时传输固件(RTT、SystemView 的 target 端);上层则是主机端的可视化与分析工具(RTT Viewer、SystemView、Ozone、J-Scope 等)。理解这套分层结构,是后续所有章节的基础。

SEGGER 实时观测与性能诊断体系分层结构 主机端分析工具层(Host / PC) RTT Viewer 终端日志 / 交互 SystemView 任务/中断时序 Ozone 调试/性能分析 J-Scope 实时变量波形 传输通道层 RTT(内存环形缓冲,双向) SWO / 并行跟踪(单向高速) J-Link 高速调试通道 目标机固件层(Target MCU) SEGGER_RTT.c 日志/终端输出 SEGGER_SYSVIEW.c 事件记录/时间戳 RTOS 适配层 FreeRTOS/embOS 等 应用代码 用户埋点/变量 硬件接口层 J-Link 调试探针 SWD / JTAG 接口 目标 MCU 调试单元(DWT/ITM 等) 读取/控制 数据上行 物理链路 可视化 传输 采集 物理

从上到下看这张图,数据自底向上流动:目标 MCU 的调试单元产生原始信号,经 SWD/JTAG 物理链路到达 J-Link,再由 J-Link 通过 USB 送入主机端工具。而 RTT 则走了一条更巧妙的路径——它不依赖调试单元的跟踪硬件,而是直接在目标 RAM 中开辟一块环形缓冲区,由 J-Link 在后台以极低开销“偷看”这块内存。这意味着 RTT 几乎可以在任何带有 J-Link 支持的芯片上工作,且对 CPU 的干扰远小于传统 printf 重定向到串口。

这套体系覆盖的典型场景包括四类。第一类是日志与终端交互:用 RTT 替代 UART 打印,速度可提升数十倍,且不占用串口外设。第二类是RTOS 任务级时序分析:SystemView 记录每次任务切换、中断进入退出、信号量操作,并以时间轴形式回放,能直观看到优先级反转、任务抖动、CPU 占用率。第三类是函数级性能剖析:通过 Ozone 的采样分析或 SEGGER 的 instrumentation 埋点,统计各函数执行时间与调用次数。第四类是实时变量波形观测:J-Scope 以类似示波器的方式绘制全局变量随时间的变化,适合调试控制环路、传感器融合等场景。

学习路径建议按“先通后专”的顺序推进。第一步,打通 RTT 基础链路:在目标工程中集成 SEGGER_RTT.c,调用 SEGGER_RTT_printf 输出一条日志,用 RTT Viewer 确认能收到。这一步的关键是理解 RTT 控制块的结构和缓冲区大小配置——默认上行 1KB、下行 16 字节,若日志量大需要适当增大。第二步,接入 SystemView:在 RTOS 启动前调用 SEGGER_SYSVIEW_Conf 和 SEGGER_SYSVIEW_Start,并在任务切换钩子中插入记录函数。观察时间轴时,重点看任务块的宽度是否稳定、中断是否被异常拉长。第三步,用 Ozone 做性能分析:加载 elf 文件后开启采样,查看热点函数排名。第四步,按需选用 J-Scope 观测关键变量。

一个容易踩的坑:RTT 的缓冲区位于目标 RAM 中,J-Link 通过后台内存访问读取它。如果目标芯片在低功耗模式下关闭了调试单元或 RAM 保持,RTT 会中断。此时需要检查芯片的低功耗策略,或在调试期间暂时禁用深度睡眠。

为了帮助建立整体认知,下面给出一个最小可用的 RTT 集成示例。它不依赖任何 RTOS,直接在裸机 main 中运行,适合作为第一个验证实验:

#include "SEGGER_RTT.h"

int main(void) {
    SEGGER_RTT_Init();
    SEGGER_RTT_WriteString(0, "RTT ready\r\n");

    uint32_t cnt = 0;
    while (1) {
        SEGGER_RTT_printf(0, "tick=%u\r\n", cnt++);
        for (volatile int i = 0; i < 100000; i++);
    }
}

烧录后打开 RTT Viewer,选择对应的 J-Link 和目标器件,即可看到不断刷新的 tick 计数。如果终端没有输出,按以下顺序排查:确认 J-Link 连接正常且目标已上电;确认 SEGGER_RTT_Init 在第一次输出前被调用;检查 RTT 控制块是否被链接器放置到了正确的 RAM 区域;确认 RTT Viewer 中的“Auto Detection”或手动指定的控制块地址与固件一致。