☰
CLIbyRTT Viewer:命令行下的J-Link RTT嵌入式调试与日志自动化
2026/10/1 10:48:43 网站建设 项目流程

简介:这是一份围绕JLink RTT Viewer与FreeRTOS+CLI整合的嵌入式调试示例代码包,适合有一定FreeRTOS基础、希望在真实硬件上实现高效交互调试的开发者。包内共6个文件,包含4个C源文件与2个头文件,压缩包整体仅19KB,结构紧凑,覆盖RTT控制台、CLI核心实现、命令注册与解析等关键模块。目前已有282人学习浏览,可作为理解RTOS命令行集成的入门参考。借助这套代码,开发者可以清晰看到RTT与CLI的桥接方式:RTT_CmdConsole负责接收来自RTT Viewer的命令,RTT_CLI完成解析与执行,FreeRTOS_CLI提供命令注册机制,用户自定义命令则单独封装,便于扩展。通过实际运行,能够在不打断程序执行的前提下查看任务状态、内存分配,甚至在运行时修改变量或控制系统行为。对于希望掌握RTOS+CLI集成思路、提升嵌入式调试效率的开发者来说,这份小巧的示例具有很好的参考价值。 搞嵌入式调试这些年,我电脑里工具越攒越多,但真正每天开机的就那么几个。有一阵子我为了看 RTT 日志,又不想每次掏鼠标去点 GUI,就一直想找个能纯命令行跑起来、还能灌进脚本里的 Viewer。后来拿到一个叫CLIbyRTT Viewer.zip的小工具包,算是把这条路走通了。这篇文章就聊聊这个工具到底能干什么、怎么配、怎么用,还有我踩过的几个坑,给同样在跟 J-Link RTT 打交道的朋友做个参考。

可能有朋友还不熟悉 RTT,先简单说一句:RTT 是 SEGGER J-Link 调试器附带的一种实时传输方案,全称 Real-Time Transfer,本质是在目标 MCU 里划一块 RAM 作为缓冲区,通过调试器的 SWD/JTAG 接口往 PC 端读写数据。比起串口打印,它速度快、不占额外引脚、还不用改硬件,做日志输出或者小批量的变量监控特别顺手。官方配套的 RTT Viewer 是图形界面,功能不少,但窗口开多了、要跑自动化脚本的时候就显得笨重。CLIbyRTT Viewer 这类命令行封装正好补上这个缺口,它让你能用终端直接连目标板、看日志、存数据,甚至可以接到 CI 流水线里做自动验证。

1. 先说清楚:RTT Viewer 到底是什么,为什么需要命令行版本

1.1 RTT 技术原理一句话版

RTT 的通信模型不复杂,你就把它想成一块“共享记事本”。MCU 端跑了一个叫 SEGGER_RTT 的库,这个库会在 RAM 中分配一个控制块,里面记录着上行通道(up channel)和下行通道(down channel)的读写指针、缓冲区地址和名称。PC 端通过 J-Link 调试器,在目标暂停或运行状态下都可以直接读写这块 RAM,从而拿到 MCU 用SEGGER_RTT_printf这类函数发出来的字符串,也可以把 PC 端的命令写入下行通道,反向控制 MCU 上的交互逻辑。

这个机制最讨喜的地方是它基本不打扰目标程序的运行。调试器只是通过 JTAG/SWD 口去访问内存,不需要像半主机(semihosting)那样在代码里加一堆额外的处理,也没有物理串口那套波特率匹配的麻烦。对于实时性要求高的场景,比如电机控制、传感器采集、协议栈调试,它比串口日志稳太多了。官方 RTT Viewer 做的事,就是把这个“记事本”的内容拉出来显示,并且让你能切换查看不同通道。

1.2 官方 RTT Viewer 的痛点与 CLI 版本的价值

官方的 SEGGER RTT Viewer 很好用,但它有几个让我一直不舒服的地方。第一,它是个 GUI 程序,每次启动都要手动选设备型号、接口类型、速度,虽然能保存配置,但跨机器迁移时那份配置不容易自动化。第二,它输出的日志对象是界面上的滚动区域,想把它实时落到磁盘文件方便后续分析,还得额外操作或依赖脚本辅助。第三,如果你想在自动化测试里同时管理多个 J-Link、多块目标板,GUI 几乎没法优雅地完成。

CLIbyRTT Viewer 的思路就是把上述能力搬进命令行。你只需要敲一条命令,带上设备和端口的参数,它就能连接 J-Link、解析 RTT 控制块、把日志打到 stdout 或文件里。这样做有一个很大的好处:所有启动参数都可以写进 shell 脚本、Makefile、CI 配置,一次编好到处用。对于我这种经常要批量跑测试、对比多版固件输出的人,这个价值是实实在在的。而且 CLI 工具通常更轻,内存占用少,开机驻留也方便。

2. 拿到 CLIbyRTT Viewer.zip 之后:环境准备与解压说明

2.1 解压结构与文件清单解读

下载下来的CLIbyRTT Viewer.zip一般几百 KB 到几 MB 不等,取决于它是否携带 J-Link DLL 动态库。用unzip或 Windows 资源管理器解开之后,你大概率会看到下面这类文件:

  • cli-rtt-viewer.exe或cli_rtt_viewer(Linux/macOS 下的可执行文件)
  • libjlinkarm.so或JLinkARM.dll(J-Link 通信核心库)
  • config.ini或settings.json(默认配置模板)
  • README.md或usage.txt(说明文档)
  • 可能还会有一个examples/目录,放着调用示例脚本

拿到手第一步,我建议先打开 README,不是因为别的,是因为 CLI 工具的参数设计五花八门,不同作者对“设备型号”“连接速度”的命名习惯不一样。有的工具叫--device,有的叫-d,有的是位置参数,不看一眼文档直接上手容易卡在第一条命令上。另外一个值得注意的细节:如果JLinkARM.dll没有随压缩包一起带,你需要确认系统里装了 J-Link 软件包,并且让工具能找到 JLinkARM.dll 的路径,否则它会报类似“failed to load JLinkARM.dll”的错误。这类“运行库缺失”问题在后文我会专门讲。

2.2 运行需要的前置条件(J-Link 驱动、连通性验证)

CLIbyRTT Viewer 的核心链路是“命令行程序 -> J-Link 动态库 -> J-Link 调试器 -> 目标 MCU”,所以前置条件很明确。你在跑任何命令之前,至少要先保证这四件事是通的:

  • J-Link 调试器实体连接:USB 连到 PC,SWD/JTAG 排线连到目标板供电和地、SWDIO、SWCLK。
  • J-Link 驱动:安装 SEGGER 官方 JLink 软件包,至少保证系统里存在 JLinkARM.dll 或 libjlinkarm.so。我建议直接用较新版本,老库对较新固件的 J-Link 支持不好。
  • 目标 MCU 的 RTT 库:目标工程里已经加入 SEGGER_RTT.c、SEGGER_RTT.h 和 SEGGER_RTT_printf.c 等文件,并在代码里调用过SEGGER_RTT_WriteString、SEGGER_RTT_printf之类的函数,确保控制块在 RAM 中真实存在。
  • 连接参数正确:设备型号、接口(SWD 常用)、连接速度。这一步最常踩坑,很多人把 STM32F103 的型号填成 STM32F407,设备 ID 对不上自然连不上。

如果之前用过官方 J-Link Commander,可以先跑一下JLink.exe手动连一次,确认设备型号和速度可行,再把参数转给 CLIbyRTT Viewer。一种常见的偷懒方式是从 JLink Commander 的日志里复制它识别到的设备名,因为 SEGGER 的设备命名有时候比芯片丝印长得多。

3. 核心实操:CLIbyRTT Viewer 的常用命令与参数

3.1 基础连接参数(设备、接口、速度)

命令行工具的第一道坎就是启动参数。我以一份典型的cli-rtt-viewer使用方式为例,帮你把参数语义捋一遍。假设目标芯片是 STM32F103C8,蓝板子,SWD 接口,连接速度 4000 kHz,典型命令长这样:

cli-rtt-viewer --device STM32F103CB --interface SWD --speed 4000 --rtt-address 0x20000000

这里参数好理解:--device是 J-Link 识别的型号名,--interface是调试口类型,--speed是 SWD 时钟频率,--rtt-address是 RTT 控制块的RAM地址。你可能要问:--rtt-address从哪来?两种办法,一是查你的链接脚本里SEGGER_RTT这个符号的地址,在 map 文件里搜_SEGGER_RTT就能看到;二是让工具自动扫描,很多 CLI 实现会支持--auto-address选项,通过扫描 RAM 区域找 RTT 控制块的标志字符串SEGGER RTT来定位。

我自己实测下来的感受是:能自动扫描就尽量用自动扫描,省得每次改固件地址变了还得改命令。不过自动扫描偶尔有误判,尤其当 RAM 里恰好有其他相似字符串时。如果遇到日志乱码或者明显连错地址导致的异常数据,就回到手动指定地址这条老路上,通常都能救回来。

3.2 日志输出与文件重定向

CLI 工具和 GUI 最大的不同就是它把输出流裸露给你,这意味着你能用管道、重定向这些系统级能力把它编进更大的流程里。比如:

cli-rtt-viewer --device STM32F103CB --interface SWD --speed 4000 --channel 0 > rtt_log.txt 2>&1

这样可以让所有上行通道 0 的日志实时落盘。如果工具本身支持时间戳参数,那就更好了,比如:

cli-rtt-viewer --device STM32F103CB --interface SWD --speed 4000 --timestamp > rtt_log_with_time.txt

带时间戳的日志在分析问题时价值极大,因为你可以把设备端的某个动作和日志输出精确对应起来。不过要注意,RTT 日志本身不带主机时间信息,时间戳一般是在 CLI 程序里按接收到数据的时刻打的,所以它代表的是“主机收到的时间”,不是“MCU 发出的时间”。如果 MCU 那端有延迟缓冲,时间戳会有偏差。这个细节在精度要求高的调试场景下要想清楚。

文件重定向还有一个进阶用法:日志分文件轮转。普通>重定向只能写一个文件,长时间挂机时日志文件会越来越大。如果你用的 CLIbyRTT Viewer 支持--log-mode split之类的参数,可以按大小或时间自动切文件;如果不支持,就用外部工具去切。Linux 上常用logrotate或者干脆用wheel这类 Python 库轮转 stdin 数据;Windows 上可以写一个简单的 PowerShell 脚本定时重启进程或者复制文件。

3.3 查看变量与终端模式(回应热词里的“jlink rtt 怎么使用查看变量”)

热词里有一个搜索很高频:“jlink rtt 怎么使用查看变量”。很多新手以为 RTT Viewer 像 IDE 的 Watch 窗口一样能直接监控全局变量,其实它的机制不太一样。RTT 本身是一套数据通道,你可以在 MCU 端把变量的值格式化后主动发上来,比如:

int32_t motor_speed = 0; while(1) { SEGGER_RTT_printf(0, "motor_speed=%d\n", motor_speed); delay_ms(100); }

然后在 PC 端看到的就是不断刷新的motor_speed=...日志。CLIbyRTT Viewer 也遵循这个逻辑,它不帮你“读内存变量”,而是把 MCU 主动塞进 RTT 通道的数据展示出来。所以如果你想看变量,思路应该是:在固件里用 RTT 函数把变量值打出来,再在终端里观察。

如果工具支持下行通道写入,你还能实现“交互式查看”。比如在--channel 0查看日志的同时,往--channel 1里发命令字符串,MCU 端解析后返回变量值。这相当于给调试界面做了一个简单的命令控制台。CLI 版本对这种交互有一个天然优势:你可以用脚本自动发送查询命令,比如每隔 5 秒发一次get temp,再把返回值采集下来。这在 GUI 里反而不容易做。热词里还有一个“rtt studio 的字体怎么放大”,这个问题在 CLI 工具里就简单了——终端窗口的字体你能随时调大调小,比 GUI 的固定字体机制灵活不少,这个我放到常见问题里细说。

4. 常见问题与排查技巧实录

4.1 找不到设备或连接失败

这个错误太经典了。报错信息常见的有Cannot connect to target、No J-Link found、Could not connect to J-Link。我一般按下面顺序排查:

  • USB 枚举失败:换一个 USB 口,或者用lsusb(Linux)/设备管理器(Windows)确认 J-Link 是否被识别。有些山寨 J-Link 固件有问题,可能出“unknown device”。
  • 目标板供电不足:如果目标板由 J-Link 供电,检查电压跳线;如果目标板独立供电,确保地和信号线共地。很多诡异连不上其实都是共地问题。
  • 设备型号不匹配:J-Link 连接时会对设备 ID 做校验,型号写错会直接失败。去 JLink Commander 里用show device或直接看它的报错建议,把真实型号名抄回来填进 CLI 参数。
  • 速度和接口错乱:SWD 和 JTAG 引脚共用但接线不同,如果线序接错,速度再低都连不上。可以试试把速度降到 1000 kHz 甚至 100 kHz,排除走线太长、干扰导致的连接不稳。

4.2 终端字体太小、显示混乱与编码问题

热词里“rtt studio 的字体怎么放大”非常真实。GUI 程序里字体大小藏在选项设置里,有时候还找不到入口;CLI 工具直接复用终端,所以你把终端字号调大,RTT 日志自然变大。Windows Terminal 里按Ctrl + =放大字体,Linux 终端里按Ctrl + Shift + +,macOS 的 iTerm 则是Cmd + +。这个天然优势让 CLI 工具在演示给同事看时特别省心。

显示混乱则一般是编码问题。MCU 端如果用了 UTF-8 中文字符串,而你的终端默认是 GBK 或 ASCII,就会显示成乱码。解决办法是在终端里把字符编码切到 UTF-8;如果 CLI 工具支持--encoding utf-8之类的参数,直接加上。另外,如果 MCU 发的数据里包含\r\n之外的控制字符,终端行为会不可预测,此时可以用cat -v查看原始字符(Linux),或者用 hexdump 确认到底是什么内容从目标板发出来。

4.3 数据乱码、丢帧与 RTT 缓冲区设置

RTT 虽然快,但缓冲区大小和主机读取策略也会造成数据异常。乱码常分两种:一种是真的数据错误,可能是连接速度过高导致采样不稳定,你降速就好;另一种是数据丢帧后拼接错位,比如 MCU 写入速度快于 PC 端读取速度,缓冲区满了以后旧数据被覆盖,新的读取从中间开始,看起来就像乱码。

排查丢帧,我推荐先看 RTT 缓冲区的配置。SEGGER_RTT 库的SEGGER_RTT_Conf.h里有个BUFFER_SIZE_UP宏,默认值通常是 1024 字节。如果你日志量大,比如每毫秒打几十个字节,这个缓冲区瞬间就满了。把BUFFER_SIZE_UP调到 4096 或更大,同时把 MCU 端的打印频率降下来,能缓解大多数丢帧。还有个隐藏技巧:如果 CLI 工具支持--rtt-ctrl-block扫描到控制块但数据仍然不完整,可以打开它的调试输出,看看它每隔多久轮询一次 RTT 缓冲区,有些工具把轮询周期写死成 100 ms,这会严重拖慢数据吞吐。

4.4 命令行工具报错与路径配置(关联 codex cli 报错的通用性)

热词里频繁出现一段报错:“unable to locate the codex cli binary. set codex cli path or ensure the electron resources include bin/codex.” 这虽然不是 CLIbyRTT Viewer 本身的报错,但它反映了一个 CLI 工具家族通用的痛点——可执行文件路径配置。很多打包成 GUI 壳的 CLI 工具,内部会去固定路径找配套的 CLI 二进制,找不到就罢工。CLIbyRTT Viewer.zip 这类工具包也有类似问题:它依赖的 JLinkARM.dll 或者自身运行时路径如果被移动,程序就找不到库了。

如果你的 CLI 工具报了“unable to locate / cannot find / missing library”这类错,我建议按三步走:

  1. 确认工具解压后的目录结构没被改动,.exe和.dll尽量放在同一目录下。
  2. 检查环境变量,如果工具支持J LINK ARM DLL PATH之类的变量,手动指到 JLink 安装目录。
  3. 用ldd(Linux)或Dependencies(Windows)检查动态库依赖,把缺失项找出来补齐。

路径配置这种东西很烦,但理解它以后,你会发现大多数 CLI 工具的报错都是同一个套路,无非就是“缺库、找不到设备、参数不对”三选一。

5. 进阶玩法:把 CLIbyRTT Viewer 变成自动化利器

5.1 日志自动轮转与时间戳处理

如果你只是偶尔手动开一下,CLI 工具比 GUI 强得有限。但把它拿来做自动化,价值就完全不一样了。我在实际项目里最常用的一条命令是配合timeout和路径变量实现的限时日志采集:

timeout 60 cli-rtt-viewer --device STM32F103CB --interface SWD --speed 4000 --timestamp --output logs/$(date +%Y%m%d_%H%M%S).log

这段命令的意思是:采集 60 秒 RTT 日志,文件名带当前时间戳,存到 logs 目录。这样无论固件测试跑几轮,日志都能按时间归档,不会互相覆盖。如果你需要更长时间挂机,可以做一个外层循环,采集一段、停一段、再采集,避免单文件无限膨胀。

时间戳处理还有一个细节值得说:如果 CLIbyRTT Viewer 不支持--timestamp,你可以用tee加ts在 Linux 上给它加时间前缀:

cli-rtt-viewer --device STM32F103CB --interface SWD --speed 4000 | ts '[%Y-%m-%d %H:%M:%.S]' | tee rtt_log.tsv

ts命令来自moreutils包,会把每行数据加上收到时的系统时间。这样做的好处是你不依赖 CLI 工具本身,纯系统级操作,兼容性更好。

5.2 与 CI 流水线集成

嵌入式项目上 CI,总绕不开“怎么确认固件跑起来以后行为正确”这个问题。以前很多人靠串口日志加正则匹配,但因为串口速率和物理接线,CI 机器上不一定有串口。CLIbyRTT Viewer 配合 J-Link 反而更容易复用:CI runner 上一插 J-Link,一条命令采集日志,再用 grep 或 Python 脚本去匹配关键输出,即可实现自动化冒烟测试。

我举个例子。假设你的固件在启动时会通过 RTT 打印Boot OK和版本号,CI 脚本可以这么写:

#!/bin/bash JLinkExe -CommanderScript flash.jlink cli-rtt-viewer --device STM32F103CB --interface SWD --speed 4000 --duration 10 > rtt_boot.log if grep -q "Boot OK" rtt_boot.log; then echo "TEST PASS" else echo "TEST FAIL" exit 1 fi

这里的--duration假设工具支持采集时长参数;如果工具不支持,就用timeout包裹。实测下来,这样的方案比“用 GUI 看一眼”可靠得多,因为它是可重复、可记录、可报警的。特别是你做固件批量回归的时候,每块板子插上去跑一条这条命令,几秒钟就能知道这版代码刷上去能不能正常起机,省下大量人工盯日志的时间。

5.3 多通道并行与命令下发

很多 CLI 版 RTT 工具支持监听多个通道,而通道的用途可以自己规划。我有一个习惯:通道 0 固定放系统日志,通道 1 放调试变量,通道 2 做命令下发。这样在自动化脚本里,我可以一边收集日志,一边定期往通道 2 写控制指令,模拟按键或修改参数。

命令下发的具体做法要看工具支持程度。有的 CLI 工具提供--send-channel 2 --send "set speed 100"这种一次性参数,有的则支持交互模式。如果你手里的版本不支持通道下发,还有一个野路子:在 MCU 端把 RTT 下行通道的数据当作串口命令行解析,然后在 PC 端用printf 'cmd\n' > /dev/ttyACM0之类的思路,但这里没有 tty,你得买支持 PTY 的包装器或直接用 Python 调用工具的子进程来发送。这一块没有统一标准,拿到工具后先翻 README 确认支持能力,再补脚本。

写在最后的实操体会

我用 CLIbyRTT Viewer 有一段时间了,最大的感受倒不是它比 GUI 快多少,而是它把“看日志”这个动作彻底变成了一个可编程的环节。以前我调试一块新板子,流程是打开 IDE、打开 RTT Viewer、点连接、看窗口、手动存日志;现在就是一条命令敲下去,日志落盘、关键字过滤、甚至自动判断测试通过与否,一气呵成。尤其在同时调两块板子的时候,开两个终端窗口跑两条命令,互不干扰,比来回切换 GUI 窗口舒服太多。

最后再分享一个小技巧,也算是我踩过坑之后总结出来的。如果你发现 CLI 版本的 RTT Viewer 连上以后日志稳定可看,但偶尔会漏掉最开始的几行启动日志,别急着怀疑工具。这是因为 J-Link 和 RTT 控制块建立连接需要时间,而 MCU 启动后的早段输出可能恰好发生在 PC 端连接成功之前。解决办法是在 MCU 端加一个延时:等主频起稳之后delay_ms(200)再开始打印 RTT 日志,给 PC 端工具留出连接窗口,或者用 J-Link 的复位序列功能让目标在 PC 准备好之后再运行。有时候就是这小小的 200ms,能省掉你半天琢磨“为什么日志开头总缺一点点”。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询