尧图精选

DAPLink V0.0.20 固件下 RTT Viewer 实时调试实战指南

🕒 发布时间:2026/9/25 4:39:16 📁 来源:尧图网络
1. 为什么我最终放弃了串口打印转向 RTT Viewer嵌入式调试这件事做过几年的人都有一个共同的痛点板子一旦跑飞或者进 HardFault串口打印就跟着一起没了。你辛辛苦苦在代码里埋了几十个printf结果关键时刻一个都出不来只能靠点灯大法一个个试。更别提串口打印本身还占着 UART 外设波特率一高就丢数据波特率一低又拖慢主循环遇到时序敏感的场景根本不敢用。RTTReal Time Transfer这套机制就是冲着这个痛点来的。它的核心思路很直接既然调试器本来就已经通过 SWD 接口连到了芯片的调试单元那为什么不直接借用调试器这条通道来传数据于是 SEGGER 搞出了 RTT在目标芯片的内存里开一块环形缓冲区目标代码往缓冲区里写调试器从缓冲区里读整个过程不占用任何外设也不需要停机。速度比串口快几个数量级而且即使 CPU 在断点停住的时候RTT 缓冲区里的数据依然能被读出来——这一点在排查死机问题时简直是救命稻草。而 DAPLink 是 ARM 官方维护的开源调试器固件市面上大量国产调试器比如各种 CMSIS-DAP 兼容的板子刷的都是它。DAPLink V0.0.20 这个版本在 RTT 支持上已经比较成熟了配合 J-Link 的 RTT Viewer 工具或者 SEGGER 官方的 RTT Viewer就能实现上面说的这套调试流程。我手头这块板子用的是 DAPLink 固件之前一直用串口调试助手凑合直到有一次调一个电机 FOC 的时序问题串口打印直接把控制周期拖崩了才下决心把 RTT 这套流程彻底跑通。这篇内容适合谁看如果你手上有 DAPLink 调试器或者 CMSIS-DAP 兼容的调试器想在 Keil、IAR 或者 VS Code 环境下用 RTT 做实时调试又不想额外买 J-Link那这篇就是写给你的。我会从固件确认、驱动安装、RTT 代码集成、Viewer 连接、到常见问题排查一步步走完。中间踩过的坑我也会原样写出来省得你再花时间。2. DAPLink 固件与 RTT 支持的底层逻辑2.1 DAPLink 到底是个什么东西很多人把 DAPLink 和 CMSIS-DAP 混为一谈其实两者是不同层面的东西。CMSIS-DAP 是 ARM 定义的一套调试接口协议规定了调试器怎么通过 USB 和上位机通信、怎么把 SWD/JTAG 时序打包成 USB 数据包。而 DAPLink 是这套协议的一个具体实现是一套运行在调试器 MCU 上的固件。你可以理解为CMSIS-DAP 是普通话的标准DAPLink 是一个说普通话的具体人。DAPLink 固件通常跑在调试器板子上的那颗 MCU 里常见的是 STM32F103 或者 NXP 的 LPC11U35它对外提供几个 USB 端点一个用于 CMSIS-DAP 调试通信一个用于拖拽下载的 MSC 大容量存储还有一个虚拟串口 CDC。RTT 功能就是挂在 CMSIS-DAP 这个通道上的——上位机通过 DAP 协议发送特定的命令DAPLink 固件去读写目标芯片内存里的 RTT 控制块。这里有个关键点RTT 能不能用取决于 DAPLink 固件有没有实现 DAP_RTT 相关的命令。早期的一些 DAPLink 固件只实现了基本的调试和下载功能RTT 命令是缺失的这种情况下你在 RTT Viewer 里怎么连都连不上。V0.0.20 这个版本已经包含了 RTT 支持但市面上流通的固件版本很杂有些厂商自己改过的固件可能把这块裁掉了。2.2 RTT 控制块在内存里长什么样RTT 的工作机制说白了就是共享内存 轮询。目标芯片上电后RTT 的初始化代码会在 RAM 里分配一块结构体叫_SEGGER_RTT这个结构体就是控制块。控制块里记录了上行通道目标到主机和下行通道主机到目标的缓冲区地址、大小、读写指针等信息。主机端的 RTT Viewer 通过调试器读取目标芯片内存先找到_SEGGER_RTT这个符号的地址然后按照控制块里的描述去读环形缓冲区。因为是直接读内存所以不需要目标 CPU 参与CPU 停住也不影响。缓冲区默认大小是 1KB 上行、16 字节下行这个大小可以在SEGGER_RTT_Conf.h里改。我实测下来上行缓冲区调到 4KB 之后即使在高频打印场景下也很少出现缓冲区满导致的丢数据。但要注意缓冲区是在 RAM 里分配的如果你的芯片 RAM 很紧张比如只有 20KB 的 STM32F030那就得权衡一下别为了调试把业务代码的 RAM 挤没了。2.3 为什么 DAPLink 的 RTT 比 J-Link 慢一些这里得说句实话DAPLink 的 RTT 速度确实不如 J-Link。原因在于 J-Link 固件对 RTT 通道做了专门的优化读写内存的效率很高而 DAPLink 走的是通用的 CMSIS-DAP 内存读写命令每次读写的开销更大。实测在同样的 1MHz SWD 时钟下J-Link 的 RTT 能跑到几百 KB/sDAPLink 大概只有几十 KB/s。但这个速度对于绝大多数调试场景已经足够了。你想想串口 115200 波特率也就 11KB/s 左右DAPLink 的 RTT 已经比它快好几倍了。除非你要传大量波形数据做实时分析否则完全够用。如果真遇到带宽瓶颈可以把 SWD 时钟调高比如 4MHz 或 8MHzRTT 速度会跟着上去。3. 从零跑通 RTT Viewer 的完整操作链路3.1 确认你的 DAPLink 固件版本和 RTT 支持第一步不是急着装软件而是先确认你手上的调试器到底刷的是什么固件。把调试器插到电脑上它会枚举出一个名为DAPLINK或者MBED的 U 盘打开里面的DETAILS.TXT文件里面会写明固件版本号。如果版本低于 V0.0.20建议先升级固件。升级固件的方法很简单把调试器进入 bootloader 模式通常是按住复位键再插入 USB它会枚举成一个MAINTENANCE或者BOOTLOADER的 U 盘把对应版本的.bin或.hex固件文件拖进去拔插一次就完成了。固件文件可以从 DAPLink 的官方仓库或者你调试器厂商的页面下载注意要选对芯片型号对应的固件刷错了会变砖。提示刷固件前一定要确认固件和你的调试器硬件匹配。我见过有人把 STM32F103 版本的固件刷到了 LPC11U35 的板子上结果调试器直接不认了只能拆开用 SWD 重新刷。刷完之后再打开DETAILS.TXT确认版本号是 V0.0.20 或更高。同时留意文件里有没有CMSIS-DAP和RTT相关的字样有些固件的 DETAILS 文件里会列出支持的功能。3.2 驱动安装WinUSB 还是 HIDDAPLink 调试器在 Windows 下有两种驱动模式HID 和 WinUSB。HID 模式不需要装驱动插上就能用但速度慢WinUSB 模式需要装驱动速度快很多。RTT Viewer 建议用 WinUSB 模式否则 RTT 的吞吐量会很难看。装 WinUSB 驱动最省事的办法是用 Zadig 这个工具。打开 Zadig在设备列表里找到你的 DAPLink 设备通常显示为CMSIS-DAP或者DAPLink然后在驱动选择里选WinUSB点Replace Driver就行。装完之后在设备管理器里应该能看到WinUSB device或者CMSIS-DAP之类的条目。如果你用的是 Keil MDKKeil 自带的驱动安装包里其实也包含了 DAPLink 的 WinUSB 驱动装 Keil 的时候勾选上就行。但有时候 Keil 装的驱动版本比较老和 V0.0.20 固件配合会有问题这时候还是建议用 Zadig 重装一遍。3.3 在工程里集成 RTT 代码RTT 要在目标芯片上跑起来需要把 SEGGER 提供的 RTT 源码编译进你的工程。源码就几个文件SEGGER_RTT.c、SEGGER_RTT.h、SEGGER_RTT_Conf.h还有可选的SEGGER_RTT_printf.c。这些文件可以从 SEGGER 官网下载或者从 J-Link 的安装目录里找。集成步骤以 Keil 为例把SEGGER_RTT.c和SEGGER_RTT_printf.c添加到工程的源文件组里。把 RTT 源码所在的目录加到 Include Paths 里。在SEGGER_RTT_Conf.h里根据你的芯片调整缓冲区大小和内存段配置。在main函数开头调用SEGGER_RTT_Init()虽然不调用也能用第一次打印时会自动初始化但显式调用更稳妥。用SEGGER_RTT_printf(0, value %d\n, val)替代原来的printf。这里有个细节要注意SEGGER_RTT_Conf.h里有个SEGGER_RTT_SECTION宏默认是空的表示控制块放在默认的 RAM 段。如果你的工程用了分散加载文件scatter file或者链接脚本可能需要把 RTT 控制块放到一个固定的段里方便调试器定位。不过对于大多数场景默认配置就够了RTT Viewer 会自动扫描内存找控制块。注意如果你同时用了 FreeRTOS 或者其他 RTOSRTT 的打印函数本身是线程安全的内部有锁但在中断里调用SEGGER_RTT_printf要小心因为锁可能导致优先级反转。中断里建议用SEGGER_RTT_Write这种不带格式化的函数或者干脆用SEGGER_RTT_WriteNoLock。3.4 RTT Viewer 的连接配置代码编译下载之后打开 J-Link RTT ViewerSEGGER 官网免费下载。虽然名字叫 J-Link RTT Viewer但它其实支持 CMSIS-DAP 设备。在连接配置界面里Connection to J-Link选USB然后在Target Device里选你的芯片型号。如果列表里没有选Cortex-M0/M3/M4之类的通用内核也行。关键的一步在Connection下拉框里把J-Link改成CMSIS-DAP或者DAPLink。不同版本的 RTT Viewer 这个选项的位置可能不一样有的在高级设置里。Interface选SWDSpeed先设 1000kHz跑通之后再往上调。RTT Control Block选Auto Detection让软件自动扫描内存找控制块。如果自动扫描失败可以手动填_SEGGER_RTT的地址从 map 文件里查。点OK之后如果一切正常你会看到 RTT Viewer 的终端窗口里开始刷出打印信息。如果没反应先别急着怀疑人生往下看常见问题那节。4. 那些让我折腾了半天的常见问题4.1 RTT Viewer 连不上一直提示 Could not find control block这是最常见的问题没有之一。原因通常有三个第一目标芯片根本没跑起来。RTT 控制块是在目标代码第一次调用 RTT 函数时初始化的。如果你的程序卡在启动阶段比如时钟没配好、HardFault 了控制块就不存在Viewer 自然找不到。解决办法是先确认程序能正常跑点个灯或者用调试器单步一下。第二控制块被优化掉了。有些编译器会把没被引用的全局变量优化掉。虽然_SEGGER_RTT是被 RTT 函数引用的一般不会被优化但如果你开了 LTO链接时优化偶尔会出问题。可以在SEGGER_RTT_Conf.h里把SEGGER_RTT_SECTION设成一个特定的段或者在代码里加一句volatile引用强制保留。第三自动扫描的内存范围不对。RTT Viewer 的自动扫描默认只扫 RAM 的前面一段如果你的控制块地址比较靠后就扫不到。这时候需要手动指定地址。从 Keil 的 map 文件里搜_SEGGER_RTT找到它的地址填到 RTT Viewer 的RTT Control Block Address里。我遇到过一次特别坑的情况控制块地址是 0x2000_1234但 RTT Viewer 自动扫描只扫到 0x2000_1000 就停了怎么都找不到。手动填地址之后秒连。所以自动扫描失败的时候手动填地址是首选排查手段。4.2 能连上但打印乱码或者丢数据打印乱码通常是缓冲区配置的问题。检查SEGGER_RTT_Conf.h里的SEGGER_RTT_MAX_NUM_UP_BUFFERS和BUFFER_SIZE_UP确保上行缓冲区数量和你使用的通道号匹配。如果你用SEGGER_RTT_printf(0, ...)通道 0 必须存在。丢数据一般是缓冲区满了。RTT 的写操作是非阻塞的缓冲区满了就直接丢弃新数据。解决办法有两个一是加大缓冲区二是降低打印频率。我一般会在打印频率高的地方加个计数器每 N 次才打印一次避免刷屏。还有一种丢数据的情况是 SWD 时钟太低。DAPLink 的 RTT 读取速度受 SWD 时钟影响很大100kHz 的时钟下主机读取速度可能跟不上目标写入速度导致缓冲区频繁满。把 SWD 时钟调到 1MHz 以上问题基本就解决了。4.3 Keil 调试模式下 RTT 和断点冲突这个坑我踩得最深。在 Keil 里进入 Debug 模式后如果设置了断点CPU 停住的时候 RTT Viewer 有时候会断开连接。原因是 Keil 在断点停住时会独占调试接口RTT Viewer 抢不到 DAP 通道。解决办法是不要在 Keil 的 Debug 模式下同时开 RTT Viewer。正确的流程是用 Keil 下载完程序后退出 Debug 模式让程序自由运行然后再打开 RTT Viewer 连接。这样两者就不冲突了。如果你确实需要在调试的同时看 RTT 输出可以考虑用 VS Code Cortex-Debug 插件它对 RTT 的支持更好可以在调试会话里直接集成 RTT Viewer 的输出不用切来切去。这也是为什么热词里vs code嵌入式和使用vscode开发嵌入式编程一直很火的原因VS Code 这套工具链在调试体验上确实比 Keil 灵活。4.4 DAPLink 固件刷完后设备不识别刷固件刷出问题的情况我也遇到过。症状是插上调试器后电脑没有任何反应设备管理器里出现一个未知设备。这通常是固件和硬件不匹配或者刷写过程中断电导致固件损坏。补救办法是让调试器进入 bootloader 模式。大多数 DAPLink 调试器都有一个复位按键或者 BOOT 跳线按住复位键插入 USB如果 bootloader 没坏会枚举出一个 U 盘重新拖入正确的固件即可。如果 bootloader 也坏了那就只能拆开外壳用另一个调试器通过 SWD 接口给调试器 MCU 重新刷 bootloader这就比较麻烦了。提示刷固件之前先把DETAILS.TXT里的原始版本信息截图保存。万一刷坏了至少知道原来是什么版本方便找回。5. 把 RTT 用出花来的几个进阶技巧5.1 多通道分离把日志和波形分开RTT 支持多个上行通道默认通道 0 用于文本打印。你可以再开一个通道 1 专门传二进制数据比如把 ADC 采样的原始数据通过通道 1 发出来用 SEGGER 的 RTT Viewer 或者自己写个上位机解析。这样文本日志和波形数据互不干扰调试效率高很多。配置方法是在SEGGER_RTT_Conf.h里把SEGGER_RTT_MAX_NUM_UP_BUFFERS改成 2 或更多然后用SEGGER_RTT_Write(1, buf, len)往通道 1 写数据。RTT Viewer 里可以切换通道查看或者用RTT Viewer的Terminal功能同时显示多个通道。5.2 用 RTT 做简易的性能分析RTT 的写入开销很小可以在关键代码段前后打时间戳算出执行时间。比如uint32_t t0 DWT-CYCCNT; // 被测代码 uint32_t t1 DWT-CYCCNT; SEGGER_RTT_printf(0, cycles %u\n, t1 - t0);DWT 的周期计数器需要先使能在main里加几句配置就行。这样测出来的时间精度是 CPU 周期级的比用示波器测 GPIO 翻转还准。我调 FOC 的时候就是用这招测出了电流环的执行时间发现有一版代码因为浮点运算太多单次执行要 8us超过了 PWM 周期的一半果断优化。5.3 RTT 配合 SystemView 做 RTOS 可视化如果你跑的是 FreeRTOS 或者 embOSSEGGER 的 SystemView 可以通过 RTT 通道采集 RTOS 的事件画出任务切换、中断、信号量的时序图。配置稍微复杂一点需要在 RTOS 的钩子函数里调用 SystemView 的 API但效果非常直观。任务优先级反转、中断延迟这些问题看时序图一眼就能定位。不过 SystemView 对 RTT 带宽要求比较高DAPLink 的 RTT 速度可能有点吃力。实测在 4MHz SWD 时钟下SystemView 能跑起来但事件密度高的时候会丢事件。如果只是偶尔看看任务切换够用了。5.4 在 VS Code 里集成 RTT 输出VS Code 的 Cortex-Debug 插件支持 RTT配置方法是在launch.json里加上rttConfig段rttConfig: { enabled: true, address: auto, decoders: [ { port: 0, type: console } ] }这样调试的时候RTT 的输出会直接显示在 VS Code 的调试控制台里不用再开一个 RTT Viewer 窗口。配合 Cortex-Debug 的寄存器查看、变量监视功能整个调试体验比 Keil 舒服不少。这也是我后来逐渐把主力开发环境从 Keil 迁到 VS Code 的原因之一。6. 关于 RTT 调试的一些个人体会RTT 这套东西刚上手的时候会觉得配置有点繁琐但一旦跑通就再也回不去串口打印了。我现在的习惯是新板子到手第一件事就是把 RTT 集成进去哪怕这个项目最后不用也留着备用。因为调试这件事工具越顺手排查问题的效率越高。DAPLink 的 RTT 支持虽然不如 J-Link 那么丝滑但考虑到 DAPLink 调试器的价格通常只有 J-Link 的几分之一这个性价比已经很可以了。V0.0.20 这个版本在稳定性上比早期版本好了很多我连续跑几个小时也没遇到过掉线。最后分享一个小技巧如果你在 RTT Viewer 里看到的数据偶尔有截断可以在SEGGER_RTT_Conf.h里把SEGGER_RTT_MODE_DEFAULT设成SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL。这样缓冲区满的时候写操作会阻塞等待而不是直接丢弃数据。代价是可能影响实时性所以只建议在调试阶段用量产固件里还是用非阻塞模式。另外RTT 控制块的地址在每次重新编译后可能会变如果你用手动填地址的方式连接记得每次改完代码重新查一下 map 文件。用自动扫描就没这个烦恼但自动扫描偶尔会抽风所以两种方式都得会。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →