CLIbyRTT Viewer:命令行下的J-Link RTT嵌入式调试与日志自动化

CLIbyRTT Viewer:命令行下的J-Link RTT嵌入式调试与日志自动化 简介这是一份围绕JLink RTT Viewer与FreeRTOSCLI整合的嵌入式调试示例代码包适合有一定FreeRTOS基础、希望在真实硬件上实现高效交互调试的开发者。包内共6个文件包含4个C源文件与2个头文件压缩包整体仅19KB结构紧凑覆盖RTT控制台、CLI核心实现、命令注册与解析等关键模块。目前已有282人学习浏览可作为理解RTOS命令行集成的入门参考。借助这套代码开发者可以清晰看到RTT与CLI的桥接方式RTT_CmdConsole负责接收来自RTT Viewer的命令RTT_CLI完成解析与执行FreeRTOS_CLI提供命令注册机制用户自定义命令则单独封装便于扩展。通过实际运行能够在不打断程序执行的前提下查看任务状态、内存分配甚至在运行时修改变量或控制系统行为。对于希望掌握RTOSCLI集成思路、提升嵌入式调试效率的开发者来说这份小巧的示例具有很好的参考价值。 搞嵌入式调试这些年我电脑里工具越攒越多但真正每天开机的就那么几个。有一阵子我为了看 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_viewerLinux/macOS 下的可执行文件libjlinkarm.so或JLinkARM.dllJ-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 连到 PCSWD/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 21这样可以让所有上行通道 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 口或者用lsusbLinux设备管理器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”这类错我建议按三步走确认工具解压后的目录结构没被改动.exe和.dll尽量放在同一目录下。检查环境变量如果工具支持J LINK ARM DLL PATH之类的变量手动指到 JLink 安装目录。用lddLinux或DependenciesWindows检查动态库依赖把缺失项找出来补齐。路径配置这种东西很烦但理解它以后你会发现大多数 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.tsvts命令来自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能省掉你半天琢磨“为什么日志开头总缺一点点”。本文还有配套的精品资源点击获取