工程调试利器:RTT(Real-Time Transfer)保姆级使用教程
摘要: RTT(Real-Time Transfer)是SEGGER推出的高速嵌入式调试技术,利用调试通道实现MB/s级数据传输,无需占用串口引脚。本教程详解RTT优势、文件结构及集成方法:1)硬件需J-Link调试器和支持RTT的MCU;2)软件需安装V864版J-Link驱动包;3)工程集成时添加SEGGER_RTT核心文件,配置缓冲区大小。RTT支持多通道双向通信,适用于实时日志、性能分析等
工程调试利器:RTT(Real-Time Transfer)保姆级使用教程
摘要: 告别繁琐的串口线!RTT提供了一种高速、非侵入式的调试信息传输方式,极大提升嵌入式开发调试效率。本教程将手把手教你从零开始使用RTT。
一、 RTT 是什么?为什么是调试利器?
- 基本概念:
- 由 SEGGER 公司推出的嵌入式调试技术。
- 核心思想:利用调试器(如J-Link)与目标芯片之间已有的调试通道(如SWD/JTAG)传输数据。
- 无需占用额外的硬件串口引脚。
- 核心优势 (为什么用它?):
- 极高速率: 远超传统UART串口(可达MB/s级别)。
- 零硬件成本: 复用调试接口,不占宝贵引脚资源。
- 非侵入式: 对目标系统运行影响极小,尤其适合实时系统调试。
- 双向通信: 不仅可输出(目标->主机),也可输入(主机->目标)。
- 多通道: 支持多个独立的逻辑通道(如:LOG、ERROR、USER INPUT、PROFILING)。
- 与调试器无缝集成: 与J-Link、Ozone等工具深度整合。
- 典型应用场景:
- 高速日志输出(替代
printf)。 - 实时系统状态监控。
- 性能分析(Profiling)数据上传。
- 接收调试命令/控制指令。
- 在资源受限或没有空闲串口的设备上调试。
- 高速日志输出(替代
二、 准备工作
- 硬件要求:
- 支持RTT的目标微控制器(绝大多数现代ARM Cortex-M/M+/R/A内核都支持)。
- SEGGER J-Link 调试器(这是使用RTT的核心硬件)。
- 目标开发板。
- PC(Windows/Linux/macOS)。
- 软件要求:
-
J-Link Software Pack: 必须安装(包含驱动和工具)。
下载:千万别选错了!!!
经过测试,V828安装后没有RTT源码!!!
建议下载V864
安装:一路默认下一步

-
RTT 实现源码:
SEGGER_RTT.c和SEGGER_RTT.h(从SEGGER官网下载或J-Link包中获取)。

-
终端软件(选其一):

J-Link RTT Viewer(SEGGER官方,推荐)J-Link RTT Client(命令行工具)Telnet客户端(如PuTTY, SecureCRT)
-
IDE/编译器: 如Keil MDK, IAR EWARM, GCC等。
-
二、SEGGER RTT V864 核心文件结构解析
SEGGER_RTT_V864/
│
├── RTT/ # RTT核心实现文件目录
│ ├── SEGGER_RTT.c # RTT主要实现:初始化、读写函数等
│ ├── SEGGER_RTT.h # RTT主要头文件:API声明、数据结构、宏定义
│ ├── SEGGER_RTT_ASM_ARMv7M.S # ARMv7-M架构的汇编优化实现(如无锁读写)
│ └── SEGGER_RTT_Printf.c # 简单的printf实现,用于通过RTT输出格式化字符串
│
├── Syscalls/ # 系统调用重定向目录(用于不同工具链)
│ ├── SEGGER_RTT_Syscalls_GCC.c # 针对GCC工具链的系统调用,重定向printf到RTT
│ ├── SEGGER_RTT_Syscalls_KEIL.c # 针对Keil工具链的系统调用
│ ├── SEGGER_RTT_Syscalls_IAR.c # 针对IAR工具链的系统调用
│ └── ... # 可能还有其他工具链的系统调用文件
│
├── Config/
│ └── SEGGER_RTT_Conf.h # RTT配置文件:缓冲区大小、模式等可调整参数
│
└── Examples/ # 示例程序目录
├── Main_RTT_InputEchoApp.c # 回显输入示例:在通道0上回显输入
├── Main_RTT_MenuApp.c # 菜单示例:演示RTT双向功能
├── Main_RTT_PrintfTest.c # 测试RTT的printf实现
└── Main_RTT_SpeedTestApp.c # 性能测试示例(需要embOS)
文件详细说明
1. RTT/SEGGER_RTT.h
- 主要头文件:包含RTT的所有API声明、数据结构和宏定义。
- 关键内容:
SEGGER_RTT_CB:RTT控制块结构,管理所有通道。SEGGER_RTT_BUFFER:缓冲区描述结构,用于上行和下行缓冲区。- API函数声明:如
SEGGER_RTT_Write、SEGGER_RTT_Read等。 - 终端控制序列的宏(如颜色、光标控制)。
- 版本信息(V864)。
2. RTT/SEGGER_RTT.c
- 核心实现文件:包含RTT的主要功能实现。
- 关键内容:
- 全局RTT控制块
_SEGGER_RTT。 - API函数实现:初始化、读写、配置缓冲区等。
- 可重入锁机制(默认使用关中断方式,用户可配置)。
- 全局RTT控制块
3. RTT/SEGGER_RTT_ASM_ARMv7M.S
- 汇编优化实现:针对ARMv7-M架构(Cortex-M3/M4/M7等)的高性能无锁读写函数。
- 关键函数:
SEGGER_RTT_WriteSkipNoLock:无锁写入并跳过已满缓冲区。SEGGER_RTT_WriteNoLock:无锁写入。SEGGER_RTT_ReadNoLock:无锁读取。
4. RTT/SEGGER_RTT_Printf.c
- 格式化输出支持:提供简单的
SEGGER_RTT_printf函数,支持格式化字符串输出。 - 注意:默认可能不支持浮点数,需要额外配置。
5. Syscalls/ 目录
- 系统调用重定向:将标准库的输入输出函数(如
printf、getchar)重定向到RTT。 - 针对不同编译器:
- GCC:
SEGGER_RTT_Syscalls_GCC.c - Keil:
SEGGER_RTT_Syscalls_KEIL.c - IAR:
SEGGER_RTT_Syscalls_IAR.c
- GCC:
- 功能:重定义
_write、_read等底层系统调用函数。
6. Config/SEGGER_RTT_Conf.h
- 配置文件:用户可根据需要调整RTT的各项参数。
- 关键配置:
BUFFER_SIZE_UP:上行缓冲区大小(默认1KB)。BUFFER_SIZE_DOWN:下行缓冲区大小(默认16字节)。SEGGER_RTT_MODE_DEFAULT:默认模式(阻塞或非阻塞)。- 终端数量、打印缓冲区大小、锁机制配置等。
7. Examples/ 目录
- 示例程序:演示RTT的各种用法。
Main_RTT_InputEchoApp.c:回显输入,演示双向通信。Main_RTT_MenuApp.c:通过RTT实现简单的菜单交互。Main_RTT_PrintfTest.c:测试RTT的printf功能。Main_RTT_SpeedTestApp.c:测试RTT的性能(需要embOS支持)。
使用注意事项
- 版本兼容性:V864版本可能引入了新特性或修复,建议查看官方ReleaseNotes。
- 浮点数打印:默认的
SEGGER_RTT_printf可能不支持浮点数,如需支持,需修改SEGGER_RTT_Printf.c或使用标准库的printf重定向。 - 系统调用:根据使用的工具链选择正确的系统调用文件,并正确链接到工程中。
- 性能优化:在高速数据传输时,建议使用汇编优化版本(ARMv7M)并合理设置缓冲区大小。
- 多任务环境:如果是在RTOS中使用,确保正确配置锁机制(如使用信号量)以避免竞态条件。
通过以上结构,SEGGER RTT提供了一个高效、灵活的双向通信机制,适用于各种嵌入式调试和输出场景。
三、 在目标工程中集成RTT
-
获取RTT源码:
- 从SEGGER官网下载最新J-Link软件包或独立RTT包。
- 找到
SEGGER_RTT.c和SEGGER_RTT.h文件。
-
添加文件到工程:

-
将
SEGGER_RTT.c、SEGGER_RTT_Printf.c加入工程的源文件列表。
-
将
SEGGER_RTT.h、SEGGER_RTT_Conf.h所在路径添加到头文件搜索路径。
-
-
配置RTT缓冲区 (关键步骤!):
- 打开
SEGGER_RTT_Conf.h(通常与.c/.h一起提供,或需从示例复制)。 - 配置上行缓冲区(目标->主机):
#define BUFFER_SIZE_UP 1024 // 根据需求调整大小,建议至少512字节 - 配置下行缓冲区(主机->目标):
#define BUFFER_SIZE_DOWN 16 // 通常用于命令输入,不需要太大
- SEGGER_RTT_PRINTF_BUFFER_SIZE :SEGGER_RTT_printf 函数使用的内部缓冲区大小。此值必小于 BUFFER_SIZE_UP 。
- SEGGER_RTT_MODE_DEFAULT :默认模式。可选:SEGGER_RTT_MODE_NO_BLOCK_SKIP (非阻塞,缓冲区满时丢弃新数据) SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL (阻塞,缓冲区满时等待直到有空闲)
注意:阻塞模式在不连接J-Link且缓冲区满时可能导致程序卡住。 - 可选配置:通道数量、是否使用
printf重定向等。
- 打开
-
初始化RTT (可选但推荐):
- 在
main()函数早期(初始化外设后)调用:SEGGER_RTT_Init();
- 在
-
重定向printf到RTT (可选但极实用):
- 修改
SEGGER_RTT_Conf.h启用:#define SEGGER_RTT_PRINTF_ENABLE 1 - 实现
_write或fputc(根据编译器):// 例如在GCC/Newlib中: int _write(int file, char *ptr, int len) { (void) file; // 避免未使用参数警告 SEGGER_RTT_Write(0, ptr, len); // 写入通道0 return len; } - 之后即可在代码中直接使用
printf("Hello RTT! Count = %d\n", count);,输出将自动通过RTT发送。
- 修改
-
SEGGER_RTT_printf 函数默认不支持浮点数格式:
要让SEGGER_RTT_printf函数支持浮点数打印,主要有两种方法:一种是通过sprintf进行中转(无需修改源码,推荐使用),另一种是直接修改SEGGER RTT的源码(一劳永逸,但涉及源码改动)。我将为你详细说明这两种方法的操作步骤和注意事项。-
方法一:使用sprintf中转(安全便捷)
这是最安全、兼容性最好的方法,无需修改SEGGER RTT源码。
实现步骤:
-
包含头文件:在你的源文件中包含必要的头文件。
#include <stdio.h> #include "SEGGER_RTT.h" -
转换并打印:使用
sprintf或snprintf先将浮点数格式化为字符串,然后用SEGGER_RTT_WriteString或SEGGER_RTT_printf输出。float sensor_value = 102.1; char buffer[64]; // 确保缓冲区足够大 // 将浮点数格式化为字符串 sprintf(buffer, "Sensor value: %.2f\n", sensor_value); // 通过RTT输出字符串 SEGGER_RTT_WriteString(0, buffer); // 或者使用snprintf防止缓冲区溢出(更安全) snprintf(buffer, sizeof(buffer), "Sensor value: %.2f\n", sensor_value); SEGGER_RTT_printf(0, "%s", buffer);
封装函数(可选):
你可以封装一个自己的RTT打印函数,简化调用:
#include <stdarg.h> #include <stdio.h> void My_RTT_printf(const char *fmt, ...) { char buffer[128]; va_list args; va_start(args, fmt); int len = vsnprintf(buffer, sizeof(buffer), fmt, args); // 使用vsnprintf处理变参 va_end(args); if (len > 0) { SEGGER_RTT_Write(0, buffer, len); // 将格式化后的字符串写入RTT } } // 使用示例 float temp = 26.5; int humidity = 70; My_RTT_printf("Temperature: %.1f°C, Humidity: %d%%\n", temp, humidity);注意事项:
- 字节对齐:在某些嵌入式系统(如使用了RTOS)中,如果
sprintf转换浮点数得到"0.000000",可能需要检查堆栈的字节对齐,例如将任务堆栈设置为8字节对齐可能解决此问题。 - 缓冲区大小:务必确保字符数组缓冲区足够大,以防止溢出,使用
snprintf比sprintf更安全。 - 代码体积:使用
sprintf等标准库函数可能会增加代码体积,如果资源紧张,需要留意。
-
-
方法二:修改RTT源码(直接高效)
此方法直接修改
SEGGER_RTT_printf.c文件,让SEGGER_RTT_printf直接支持%f格式符。操作步骤:
-
定位函数:打开
SEGGER_RTT_printf.c文件,找到SEGGER_RTT_vprintf函数。 -
添加处理分支:在该函数内部的
switch (c)语句中,找到处理格式说明符(如%d,%s)的代码块,添加对%f的处理。下面是一个参考实现,主要步骤是分别处理整数和小数部分:case 'f': case 'F': { float f_val = (float)va_arg(*pParamList, double); // 提取浮点参数 int int_part = (int)f_val; // 提取整数部分 float frac_part = f_val - int_part; // 提取小数部分 if (f_val < 0) { // 处理负数 _StoreChar(&BufferDesc, '-'); int_part = -int_part; frac_part = -frac_part; } // 打印整数部分 _PrintInt(&BufferDesc, int_part, 10, 0, 0, 0); _StoreChar(&BufferDesc, '.'); // 打印小数点 // 打印小数部分(例如转换为整数打印指定位数) int frac_int = (int)(frac_part * 1000000); // 例如转换为6位整数 _PrintUnsigned(&BufferDesc, (unsigned)frac_int, 10, 6, 6, FORMAT_FLAG_PAD_ZERO); // 打印6位小数,不足补零 } break;注意:以上为示例性代码,实际实现可能需要根据你的精度、四舍五入等需求进行调整。网络上也有更完善的开源实现可供参考。

-
辅助函数(如有需要):某些实现可能需要一个简单的幂运算辅助函数(
_pow)来处理不同的小数位数。static unsigned int _pow(unsigned int base, unsigned int exponent) { unsigned int result = 1; for (unsigned int i = 0; i < exponent; ++i) { result *= base; } return result; }
-
-
四、 连接调试器与使用RTT Viewer
-
硬件连接:
- 将J-Link通过USB连接到PC。
- 将J-Link的SWD/JTAG接口连接到目标板。
- 给目标板上电。
-
启动J-Link RTT Viewer:
- 在PC上运行
J-Link RTT Viewer。
- 在PC上运行
-
配置连接:
-
Target Device: 选择你的目标芯片型号(如
STM32F407ZG)。
-
Target Interface & Speed: 选择
SWD和合适的速率(通常默认即可)。 -
Target Device Core: 选择核心(如
Cortex-M4)。 -
Specify Target Device…: 如果列表没有,手动输入芯片型号。

-
点击
OK。
-
-
建立连接并查看输出:
- 连接成功后,Viewer会自动检测并连接到目标板上的RTT控制块。
- 主窗口会显示目标程序通过
SEGGER_RTT_Write()或重定向printf发送到通道0的数据。 - 如果目标程序在运行,你应该能看到日志输出开始滚动。
-
RTT Viewer 核心功能详解:
- 多标签页(Tabs): 每个标签页可连接到一个不同的RTT通道(通道0是默认的LOG通道)。
- 通道选择: 顶部下拉菜单可选择要查看或输入的通道。
- 输入区(Send): 在下方输入框输入文本,点击
Send或按Enter,可将数据发送到目标板的下行缓冲区(通常是通道0)。目标程序需调用SEGGER_RTT_Read()或SEGGER_RTT_HasKey()来读取。 - 控制按钮:
Connect/Disconnect: 手动连接/断开。Settings: 配置字体、颜色、时间戳、自动滚动等。Clear Window: 清空当前视图。
- 时间戳: 可配置显示每条消息的精确主机端时间戳。
- 查找: 支持在输出日志中搜索文本。
五、 在代码中使用RTT API
- 基本输出:
#include "SEGGER_RTT.h" ... SEGGER_RTT_Write(0, "Hello, World!\n", 14); // 向通道0写入字符串 SEGGER_RTT_printf(0, "Temperature: %.2f C, Voltage: %d mV\n", temp, voltage); // 格式化输出到通道0 - 使用不同通道:
#define CHANNEL_ERROR 1 #define CHANNEL_PROFILING 2 ... SEGGER_RTT_WriteString(CHANNEL_ERROR, "FATAL ERROR: Sensor timeout!\n"); SEGGER_RTT_printf(CHANNEL_PROFILING, "TaskA Exec Time: %u us\n", exec_time);- 在RTT Viewer中,切换到对应通道标签页即可查看。
- 输入处理 (接收主机命令):
char cmdBuffer[32]; int numBytesRead; ... // 检查下行缓冲区是否有数据 if (SEGGER_RTT_HasKey(0)) { // 从通道0的下行缓冲区读取数据 numBytesRead = SEGGER_RTT_Read(0, cmdBuffer, sizeof(cmdBuffer) - 1); if (numBytesRead > 0) { cmdBuffer[numBytesRead] = '\0'; // 确保字符串结束 processCommand(cmdBuffer); // 处理接收到的命令 } } - 其他实用API:
SEGGER_RTT_GetKey(): 从下行缓冲区读取一个字符。SEGGER_RTT_WaitKey(): 阻塞等待并读取一个字符。SEGGER_RTT_HasData(): 检查上行缓冲区是否有待发送数据(通常由RTT内部管理)。SEGGER_RTT_AllocDownBuffer()/SEGGER_RTT_AllocUpBuffer(): 运行时动态分配缓冲区(高级用法)。
六、 使用Telnet连接RTT (替代RTT Viewer)
- 启动J-Link RTT Server:
- 运行
J-Link RTT Server。 - 配置目标芯片、接口等(同RTT Viewer)。
- 在
Connection to Client部分选择Telnet,设置端口号(默认为19021)。 - 点击
Start启动服务器。
- 运行
- 使用Telnet客户端连接:
- 打开PuTTY、SecureCRT或其他Telnet客户端。
- 连接类型选择
Telnet。 - 主机名:
localhost或127.0.0.1。 - 端口:
19021(或你在RTT Server中设置的端口)。 - 连接。
- 使用:
- 连接成功后,即可看到目标程序输出到通道0的数据。
- 在Telnet窗口中直接输入并按回车,即可发送数据到目标板下行缓冲区(通道0)。
- 优点: 轻量、可编写脚本自动化测试。缺点: 功能不如RTT Viewer丰富(如多通道切换、格式化稍弱)。
七、 常见问题与解决方案 (保姆级排错)
- RTT Viewer 连接不上 / 无输出:
- 检查硬件: J-Link灯是否正常?USB线是否可靠?SWD线是否连接正确且牢固?目标板是否供电?芯片是否正常运行?
- 检查软件:
- J-Link驱动是否安装且版本较新?
- 目标芯片型号在RTT Viewer中选择是否正确?
- 目标程序是否成功编译并烧录?确认
SEGGER_RTT.c已包含在工程中并参与了编译链接。 - RTT缓冲区是否配置成功? 检查
SEGGER_RTT_Conf.h中的BUFFER_SIZE_UP是否足够大(至少512)。 - 目标程序是否调用了
SEGGER_RTT_Init()或使用了输出函数?尝试在main开头加一句SEGGER_RTT_WriteString(0, "RTT Init OK!\n");。 - 目标代码是否在运行?检查是否卡在启动代码或HardFault。
- 尝试复位目标: 在RTT Viewer中点击
Reset按钮或手动复位开发板。 - 尝试降低SWD速率: 在RTT Viewer连接配置中降低速度。
- 输出乱码:
- 最常见原因:目标系统时钟配置错误! RTT依赖于目标芯片的时钟(通常是系统时钟
SystemCoreClock)。确保SystemCoreClock变量在代码中被正确设置且反映了实际运行频率。检查system_<device>.c初始化代码。 - 检查RTT Viewer或Telnet客户端的字符编码设置(通常设为UTF-8或ANSI)。
- 最常见原因:目标系统时钟配置错误! RTT依赖于目标芯片的时钟(通常是系统时钟
- 输出延迟或丢失数据:
- 增大上行缓冲区大小(
BUFFER_SIZE_UP),尤其是在高频率输出日志时。 - 检查目标程序是否在长时间关闭全局中断(会影响RTT传输)。
- 降低日志输出频率或优化日志内容。
- 增大上行缓冲区大小(
- 无法接收输入:
- 检查RTT Viewer的输入框是否选择了正确的通道(通常是0)。
- 检查目标程序是否在正确调用
SEGGER_RTT_Read()或SEGGER_RTT_HasKey()来读取下行缓冲区。 - 检查
SEGGER_RTT_Conf.h中的BUFFER_SIZE_DOWN是否大于0。 - 确认下行缓冲区配置正确且API使用无误。
八、 高级技巧与最佳实践
- 性能优化:
- 避免在中断服务程序(ISR)中频繁调用
RTT_printf(格式化耗时),改用RTT_WriteString输出简单信息或先缓存数据在主循环输出。 - 根据实际需求调整缓冲区大小,平衡内存占用与性能。
- 避免在中断服务程序(ISR)中频繁调用
- 多通道管理:
- 为不同类型信息定义清晰通道(LOG, DEBUG, ERROR, COMMAND, PROFILE)。
- 在RTT Viewer中用不同标签页分别监控关键通道。
- 与SystemView集成:
- RTT是SEGGER SystemView性能分析工具的数据传输基础。学习使用SystemView进行更深入的RTOS或裸机任务调度分析。
- 自定义终端控制:
- 利用ANSI Escape序列在RTT输出中实现彩色文本、清屏、光标定位等(需终端支持)。
- 版本管理:
- 将
SEGGER_RTT.c和SEGGER_RTT.h(以及你的SEGGER_RTT_Conf.h)纳入代码版本管理。
- 将
九、 总结
- RTT是嵌入式调试效率的革命性提升工具。
- 掌握集成、配置、连接和基本API使用,即可替代传统串口调试。
- 善用多通道、输入输出和工具链集成,能应对复杂调试场景。
- 遇到问题优先检查硬件连接、驱动版本、目标时钟配置和缓冲区设置。
附录:
更多推荐



所有评论(0)