尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

DAPLink搭配RTT Viewer实现嵌入式实时调试

发布时间:2026/9/25 1:25:01

资讯中心
01
ARTICLE

DAPLink搭配RTT Viewer实现嵌入式实时调试

DAPLink搭配RTT Viewer实现嵌入式实时调试
1. 为什么我最终选择了RTT Viewer配合DAPLink做调试嵌入式开发做久了调试手段的取舍往往能直接决定项目推进的效率。早些年我用得最多的是串口打印后来也试过用调试器单步跟踪但串口打印占用硬件串口资源、影响实时性单步跟踪又没法看连续运行时的数据流。直到我开始用DAPLink配合RTT Viewer才算找到了一个比较舒服的平衡点。RTT全称是Real Time Transfer是SEGGER公司推出的一种调试技术。它的核心思路是在目标芯片的内存里开辟一小块缓冲区调试器通过调试接口直接读写这块内存目标芯片只需要调用几个简单的API就能把数据写进去PC端的RTT Viewer再从缓冲区里把数据读出来显示。整个过程不需要占用任何硬件串口也不需要目标芯片停下来等对实时性的影响极小。DAPLink则是ARM官方维护的一个开源调试器固件项目刷在各种CMSIS-DAP兼容的调试器上。它支持SWD和JTAG接口能对绝大多数ARM Cortex-M系列芯片进行调试和烧录。V0.0.20这个版本是我目前用得比较稳的一个版本固件体积小、兼容性好在Windows和Linux下都能正常工作。这套组合解决的核心问题是在不占用串口、不打断程序运行的前提下实时输出调试信息。适合所有用ARM Cortex-M芯片做开发的工程师尤其是那些串口资源紧张、或者对实时性要求比较高的项目。不管你是刚入门的嵌入式新手还是做了多年的老手这套方案都值得花时间掌握。2. 环境搭建与固件刷写2.1 DAPLink固件版本选择与刷写方法DAPLink V0.0.20这个版本号其实对应的是固件发布的一个特定构建。我选它不是因为最新而是因为它在多个芯片平台上表现最稳定。新版本有时候会引入一些兼容性问题比如在某些国产Cortex-M芯片上识别不稳定或者SWD时钟速率协商有问题。V0.0.20这个版本我用在STM32F1、F4、G0、G4以及GD32、APM32等多个系列上都没遇到过识别问题。刷写DAPLink固件的方法取决于你手上的硬件。如果你用的是现成的DAPLink调试器比如某宝上常见的那种带金属外壳的通常已经刷好了固件直接插上就能用。但如果你想自己刷或者手上的调试器固件版本太老那就需要进入Bootloader模式。具体操作是按住调试器上的复位按键不放然后用USB线连接到电脑保持按住大约两秒后松开。这时候电脑上会出现一个名为MAINTENANCE或者DAPLINK的U盘盘符。把V0.0.20的固件文件通常是.bin或者.hex格式直接拖进这个盘符里等待盘符自动消失再重新出现固件就刷好了。注意刷写过程中千万不要拔掉USB线否则可能导致固件损坏调试器变砖。如果不小心刷坏了通常还能通过再次进入Bootloader模式重新刷写但有些硬件设计不完善的调试器可能就救不回来了。刷完之后你可以在设备管理器里看到一个新的HID设备名字里带有DAPLink或者CMSIS-DAP字样。这时候驱动就自动装好了不需要额外安装驱动。如果你用的是Windows 7或者更老的系统可能需要手动指定驱动路径但Windows 10和11都是免驱的。2.2 RTT Viewer的获取与安装RTT Viewer是SEGGER公司提供的免费工具包含在J-Link软件包里。你不需要购买J-Link硬件只需要去SEGGER官网下载J-Link Software and Documentation Pack安装之后在安装目录下就能找到JLinkRTTViewer.exe这个可执行文件。安装过程没什么特别的一路下一步就行。安装完成后建议把JLinkRTTViewer.exe的路径加到系统环境变量里这样以后在命令行里也能直接调用。我习惯把它固定在任务栏上调试的时候随手就能打开。有一点需要说明RTT Viewer虽然名字里带JLink但它其实支持任何实现了CMSIS-DAP协议的调试器包括DAPLink。这是因为RTT本身是目标芯片和调试器之间的通信协议跟调试器的品牌无关。只要调试器能读写目标芯片的内存就能用RTT。2.3 目标工程中集成RTT代码RTT要在目标芯片上运行需要把SEGGER的RTT源码集成到你的工程里。这些源码文件不多核心就是SEGGER_RTT.c和SEGGER_RTT.h再加上一个配置文件SEGGER_RTT_Conf.h。你可以在J-Link安装目录下的Samples/RTT文件夹里找到这些文件。把这些文件添加到你的工程里然后在需要输出调试信息的地方调用SEGGER_RTT_printf函数就行了。这个函数的用法和标准printf几乎一样支持格式化字符串。比如#include SEGGER_RTT.h void main(void) { SEGGER_RTT_Init(); SEGGER_RTT_printf(0, System started, clock: %d Hz\n, SystemCoreClock); while(1) { SEGGER_RTT_printf(0, Tick: %d\n, tick_count); HAL_Delay(100); } }第一个参数0表示使用第0个上行缓冲区。RTT支持多个缓冲区你可以把不同模块的调试信息输出到不同的缓冲区里在RTT Viewer里分窗口显示。这个功能在调试复杂系统的时候特别有用比如把电机控制相关的信息输出到缓冲区0把通信协议相关的信息输出到缓冲区1互不干扰。提示SEGGER_RTT_printf默认使用阻塞模式如果缓冲区满了会等待。在中断里调用的时候要小心建议改用SEGGER_RTT_Write或者把缓冲区设大一点。我一般会把上行缓冲区设成1024字节对于大多数调试场景够用了。3. RTT Viewer的配置与连接3.1 连接参数详解打开RTT Viewer之后第一件事是配置连接参数。界面上的选项看起来不少但实际需要关心的就那么几个。首先是Target Device这里要选你实际使用的芯片型号。如果你不确定可以选Cortex-M0/M3/M4这种通用选项RTT Viewer会自动识别。不过选具体型号有个好处就是它能自动设置正确的内存地址范围避免读写到非法区域。然后是Connection to J-Link这里要选USB。虽然DAPLink不是J-Link但RTT Viewer通过CMSIS-DAP协议也能识别到它。如果列表里没有你的调试器检查一下驱动是否装好或者换一个USB口试试。Interface选SWD这是DAPLink最常用的调试接口。Speed可以先设成1MHz等连接稳定之后再往上调。我实测在STM32F4上跑到4MHz也没问题但有些芯片或者长排线的情况下高速率会不稳定。最关键的参数是RTT Control Block的地址。RTT Viewer需要知道目标芯片内存里那块缓冲区的起始地址才能去读取数据。这个地址有两种确定方式一种是自动搜索RTT Viewer会在芯片的内存范围内扫描特定的特征字符串找到控制块的位置另一种是手动指定如果你知道控制块的地址直接填进去就行。自动搜索在大多数情况下都能工作但有时候会失败比如内存里恰好有多个匹配的特征字符串或者搜索范围设得不对。手动指定更可靠但需要你在编译后从map文件里找到_SEGGER_RTT这个符号的地址。3.2 自动搜索与手动指定地址的取舍我一般先用自动搜索如果连上了就省事。自动搜索的时候Search Range要设成芯片RAM的实际范围。比如STM32F103C8T6有20KB RAM起始地址0x20000000那范围就设成0x20000000到0x20005000。范围设得太大搜索时间会变长设得太小可能搜不到。如果自动搜索失败RTT Viewer会提示找不到控制块。这时候就需要手动指定地址了。方法是在编译生成的map文件里搜索_SEGGER_RTT找到它的地址。比如_SEGGER_RTT 0x200000ac Data 4那控制块地址就是0x200000ac。把这个地址填到RTT Viewer的RTT Control Block Address栏里再点OK就能连上了。注意每次重新编译之后_SEGGER_RTT的地址可能会变。如果你改了代码或者换了编译器优化等级记得重新查一下map文件。我习惯在工程里加一个编译后脚本自动从map文件里提取这个地址并打印出来省得每次手动找。3.3 多缓冲区与颜色配置RTT Viewer支持同时显示多个缓冲区的内容每个缓冲区可以设置不同的颜色。这个功能在调试多任务系统的时候特别好用。比如你有三个任务可以把每个任务的日志输出到独立的缓冲区然后在RTT Viewer里用不同颜色区分一眼就能看出哪个任务在什么时候输出了什么。配置方法是在RTT Viewer的Terminal设置里给每个通道指定颜色和显示名称。我一般把通道0设成白色用于系统日志通道1设成绿色用于传感器数据通道2设成黄色用于错误信息。这样即使日志量很大也能快速定位到关键信息。还有一个实用的功能是Log to File。你可以把RTT的输出同时保存到文件里方便事后分析。我一般在跑长时间测试的时候会打开这个功能测试跑完之后用脚本分析日志文件比盯着屏幕看效率高多了。4. 常见问题排查与实战经验4.1 连接失败与识别问题最常见的问题就是RTT Viewer连不上目标芯片。现象是点OK之后弹出一个错误框说找不到RTT控制块或者无法连接到调试器。排查思路是这样的先确认DAPLink本身是否被系统识别。打开设备管理器看看有没有CMSIS-DAP或者DAPLink设备。如果没有换USB线、换USB口试试。有些USB线只有充电功能没有数据功能这个坑我踩过好几次。如果DAPLink识别正常但RTT Viewer连不上那就检查目标芯片是否在运行。有时候芯片进入了低功耗模式或者HardFault调试接口虽然还能访问但RTT控制块没有被初始化。这时候可以先复位一下芯片再试。还有一种情况是芯片的调试接口被禁用了。有些代码在启动时会关闭SWD接口以省电或者防止被读取这时候调试器就访问不了芯片了。解决办法是在代码里保留调试接口或者通过复位时的特殊时序来强制使能调试接口。现象可能原因解决方法设备管理器无DAPLinkUSB线或驱动问题换线换口重装驱动RTT Viewer找不到控制块芯片未运行或地址不对复位芯片手动指定地址连接后无输出缓冲区未初始化或printf未调用检查SEGGER_RTT_Init调用输出乱码时钟配置错误检查SystemCoreClock是否正确输出断续缓冲区太小或速率太高增大缓冲区降低SWD速率4.2 输出乱码与数据丢失输出乱码通常是因为RTT Viewer的显示编码和实际输出的编码不一致。RTT默认使用ASCII编码如果你在代码里输出了中文就需要确保RTT Viewer也设置为对应的编码。不过说实话调试信息用英文最省事中文编码问题在嵌入式环境里挺烦人的。数据丢失是另一个常见问题。现象是RTT Viewer里看到的日志不连续中间少了几行。这通常是因为缓冲区满了新的数据覆盖了旧的数据。RTT的缓冲区是环形缓冲区当写入速度大于读取速度时旧数据就会被覆盖。解决办法有两个一是增大缓冲区在SEGGER_RTT_Conf.h里把BUFFER_SIZE_UP改大比如从1024改成4096二是降低输出频率不要在中断里频繁调用printf可以先把数据存到变量里在主循环里统一输出。提示SEGGER_RTT_printf在缓冲区满时的默认行为是阻塞等待这在中断里会导致中断响应变慢。如果你需要在中断里输出调试信息建议使用SEGGER_RTT_WriteNoLock或者自己实现一个非阻塞的日志队列。4.3 与Keil、IAR等IDE的配合使用RTT Viewer可以独立运行也可以和Keil、IAR等IDE配合使用。在Keil里你可以在Debug模式下打开RTT Viewer这样就能一边单步调试一边看RTT输出。不过要注意Keil在Debug模式下会占用调试器RTT Viewer可能就连不上了。解决办法是先用RTT Viewer连接再进入Keil的Debug模式或者反过来先退出Debug模式再连RTT Viewer。我个人的习惯是RTT Viewer独立运行Keil只用来编译和烧录。调试的时候如果需要在特定位置暂停我会在代码里加一个变量通过RTT输出这个变量的值来判断程序是否执行到了那里。这样比单步调试效率高尤其是在调试时序敏感的代码时。在IAR里也类似IAR自带的Terminal I/O功能其实也是基于类似RTT的机制但兼容性和灵活性不如直接用RTT Viewer。我一般会把IAR的Terminal I/O关掉统一用RTT Viewer来看输出。4.4 性能影响与实时性评估很多人关心RTT对程序实时性的影响。我做过一个简单的测试在STM32F407上主频168MHz调用一次SEGGER_RTT_printf输出20个字节耗时大约2微秒。这个时间包括格式化字符串和写入缓冲区。如果缓冲区没满写入操作就是简单的内存拷贝速度很快。相比之下用硬件串口输出同样长度的数据在115200波特率下需要大约1.7毫秒。也就是说RTT比串口快了将近三个数量级。这个差距在调试高频事件的时候非常明显比如调试PWM控制或者电机换相用串口根本来不及输出用RTT就毫无压力。当然RTT也不是完全没有开销。如果缓冲区满了阻塞模式下会等待PC端读取这时候就会影响实时性。所以我的建议是缓冲区尽量设大输出频率尽量控制不要在中断里做复杂的格式化输出。5. 进阶技巧与效率提升5.1 自定义RTT输出宏直接调用SEGGER_RTT_printf有点啰嗦我一般会封装几个宏让代码更简洁。比如#define LOG_INFO(fmt, ...) SEGGER_RTT_printf(0, [INFO] fmt \n, ##__VA_ARGS__) #define LOG_WARN(fmt, ...) SEGGER_RTT_printf(0, [WARN] fmt \n, ##__VA_ARGS__) #define LOG_ERROR(fmt, ...) SEGGER_RTT_printf(0, [ERROR] fmt \n, ##__VA_ARGS__)这样在代码里就可以写LOG_INFO(Sensor value: %d, val)输出会自动带上级别标签和换行符。配合RTT Viewer的颜色配置不同级别的日志用不同颜色显示排查问题的时候一目了然。还可以加一个编译开关在Release版本里把这些宏定义成空操作避免调试代码影响最终产品的性能和代码体积。这个做法在量产项目里很常见调试的时候打开发布的时候关掉。5.2 用RTT做简易数据可视化RTT Viewer本身只显示文本但你可以通过输出特定格式的数据配合PC端的脚本做可视化。比如输出CSV格式的数据然后用Python脚本读取RTT的输出并实时绘图。我做过一个电机调试的项目用RTT输出三相电流的采样值格式是current_a,current_b,current_cPC端用Python的matplotlib实时绘制波形。这样就能直观地看到电流波形是否正常比看数字快多了。虽然比不上专业的示波器但在没有示波器的情况下这个方案能解决大问题。具体实现思路是RTT Viewer支持将输出重定向到文件或者TCP端口。你可以让RTT Viewer把数据输出到TCP端口然后Python脚本连接这个端口读取数据并绘图。或者更简单一点直接用JLinkRTTClient命令行工具把输出通过管道传给Python脚本。5.3 多通道调试复杂系统在调试包含多个模块的复杂系统时我习惯用多个RTT通道来分离不同模块的日志。比如通道0系统主日志包括启动信息、状态机切换、错误信息通道1通信模块日志包括收发数据、协议解析结果通道2控制算法日志包括PID输入输出、目标值、实际值通道3传感器日志包括原始采样值、滤波后数值在RTT Viewer里可以同时打开这四个通道每个通道一个窗口用不同颜色区分。这样在调试的时候哪个模块出了问题直接看对应的窗口就行不用在一大堆混杂的日志里翻找。配置多通道需要在代码里调用SEGGER_RTT_printf时指定不同的通道号同时确保SEGGER_RTT_Conf.h里的缓冲区数量足够。默认配置是3个上行缓冲区和1个下行缓冲区如果你需要更多通道把BUFFER_COUNT_UP改大就行。注意每个通道都会占用独立的RAM空间通道越多RAM消耗越大。在RAM紧张的芯片上要权衡一下比如STM32F030只有4KB RAM开太多通道可能就不够用了。5.4 结合版本管理做日志追溯RTT输出的日志默认只在内存里断电就没了。如果你需要保留调试日志用于事后分析可以结合版本管理工具来做。我的做法是在代码里加一个版本号宏每次RTT启动时输出这个版本号同时把RTT Viewer的Log to File功能打开日志文件按日期和版本号命名。这样当测试人员反馈问题时你可以根据日志文件里的版本号确定对应的代码版本然后用Git切换到那个版本去复现问题。这个流程在团队协作中特别有用避免了你用的是哪个版本这种扯皮。日志文件的管理也要注意不要无限增长。我一般会设置一个脚本每天清理超过一周的日志文件或者把旧日志压缩归档。对于长时间运行的测试日志文件可能会很大建议只记录关键信息不要把所有调试输出都写进去。6. 从串口调试迁移到RTT的实操建议如果你之前一直用串口打印调试想迁移到RTT我建议分几步走不要一下子全换掉。第一步先在现有工程里集成RTT代码保留串口打印不动。在几个关键位置同时用串口和RTT输出同样的信息对比两者的输出是否一致。这一步主要是验证RTT的配置是否正确以及确认RTT不会影响程序的正常运行。第二步把非关键路径的串口打印替换成RTT输出。比如那些只在初始化阶段输出一次的配置信息或者低频的状态上报这些对实时性要求不高但用RTT可以省下串口资源。替换之后跑一遍完整的功能测试确认没有遗漏。第三步把高频输出的调试信息也迁移到RTT。比如控制循环里的实时数据、中断里的状态标记这些用串口输出会影响实时性用RTT就没什么问题。迁移的时候注意缓冲区大小要够输出频率要控制避免缓冲区溢出导致数据丢失。第四步把串口打印代码从工程里移除或者用编译开关隔离。如果串口还要用于其他用途比如和上位机通信那就保留串口驱动只移除调试打印部分。如果串口完全用于调试那就可以把串口驱动也去掉省下代码空间和硬件资源。整个迁移过程我建议在一两天内完成不要拖太久。因为两套调试系统并存的时候代码会显得比较乱时间长了容易忘记哪些地方还在用串口。迁移完成之后你会发现调试效率有明显的提升尤其是那些之前因为串口速度不够而放弃调试的场景现在都可以轻松应对了。我在最近的一个电机控制项目里做了这个迁移之前用串口输出调试信息控制频率只能跑到10kHz再高就会因为串口输出阻塞而影响控制周期。换成RTT之后控制频率跑到20kHzRTT输出依然流畅完全没有影响。这个提升对于高性能电机控制来说是非常关键的。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。