新闻详情

新闻详情

首页 / 资讯中心 / 详情

脚本化J-Link RTT调试:rttsh命令行工具实现自动化板级验证与CI集成

发布时间:2026/10/1 15:13:48来源:尧图网络
脚本化J-Link RTT调试:rttsh命令行工具实现自动化板级验证与CI集成
玩嵌入式调试的谁还没被日志逼疯过板子上跑着关键算法想实时看状态只能打开 J-Link RTT Viewer 手动操作想跑个批量测试还得人肉盯屏幕记录数据等想把产线验收脚本接入 CI更是无从下手。我前段时间写了个小工具 rttsh专门解决这套场景。它把 J-Link RTT 调试封装成命令行接口支持脚本文件、数据导出和 CI 集成真正让板卡日志的采集和验证变得像跑 shell 脚本一样顺手。这篇文章聊聊它的设计思路、核心功能和实际踩坑记录适合搞嵌入式、做板级验证、以及在自动化测试里折腾 RTT 的朋友。1. 背景与设计思路从RTT调试痛点谈起1.1 为什么需要脚本化的RTT调试先说说 RTT 本身。SEGGER J-Link 的 RTTReal-Time Transfer和串口完全不是一回事。串口必须依赖 MCU 里的 UART 外设波特率、DMA 一堆配置哪怕 921600 波特率在高速打印场景下也会拖慢系统。RTT 则直接走调试接口通过目标内存中的 RTT Control Block 交换数据打印一条日志可能只消耗几个时钟周期几乎不影响实时性。这个特性让 RTT 在高频日志、时间敏感调试里几乎是不可替代的。可问题也出在这里。日常调试我用 RTT Viewer连接、打印、保存都正常但一旦遇到下面这些场景就特别难受验证脚本没法自动化RTT Viewer 能看能存但没法做断言也没法在特定时刻向设备注入命令。想跑二十个用例只能一个个手动开日志、看结果。数据导出不友好Viewer 的保存格式是固定的纯文本时间戳、二进制数据混在一起。我要统计某个传感器数值的平均值还得自己写解析脚本去扣。没法进 CI持续集成里跑板级测试需要命令行能调用能挂到流水线上能根据退出码判断成功失败。RTT Viewer 是个 GUI 程序这一步就卡死了。还有一个隐性痛点就是调试经验的沉淀。老工程师调一个问题往往是“连接板子、启动 RTT、敲个命令、看现象”这一套组合拳。但这套组合拳如果只能靠人手去敲就永远无法复用。如果能把调试动作固化成一个脚本文件存在仓库里不管是同事还是机器都能一键重跑效率完全是两个量级。1.2 命令行工具的核心设计目标想通了上面的问题我给自己提了几个设计目标。首先工具必须是一个纯命令行的交互式 shell像 redis-cli 那样既能单条命令交互也能从脚本文件里批量读命令。其次脚本语言要足够简单。嵌入式工程师大多不是专业脚本玩家搞太复杂的语法反而劝退。第三退出码要有意义。CI 判断成败全看退出码如果工具内部断言失败还返回 0那流水线就等于摆设。最后数据导出能力要强。至少支持 CSV、JSON、原始二进制三种格式方便下游用 Python 或 Excel 处理。技术选型也比较直接。Python 生态里有 pylink 这类封装库底层走 SEGGER 的 DLL跨平台也方便。我用 Python 写 CLI把连接管理、RTT 读写、脚本解析、断言执行拆成几个独立模块。这么说吧rttsh 的骨架就是一个“命令分发器 设备抽象层”上层用cmd模块处理交互底层用 pylink 控制 J-Link中间夹一个简单的脚本解释器。这样结构也方便以后加命令扩展。2. rttsh的核心功能拆解2.1 脚本化支持调试脚本的语法与执行脚本化是 rttsh 的立身之本。我的设计原则是“一行一条命令不带括号不用缩进”。每条命令由动词和参数组成比如connect --device STM32F407 --if swd --speed 4000、expect Ready timeout 5、assert match BP([0-9.]) --group 1 --cmp gt 3.14。这种风格对人和机器都非常友好人一眼能看懂语言模型也很容易生成。脚本文件用.rttsh后缀支持注释行以#开头。脚本的解释执行是顺序的但为了处理循环和条件分支我加了一组控制命令# 连接与启动 connect --device STM32F407 --if swd --speed 4000 rtt start # 等待设备输出 BOOT_OK expect BOOT_OK timeout 10 # 若匹配成功则进入压力测试循环 if match BOOT_OK: repeat 100 rtt write start_pressure sleep 50 expect CYCLE_DONE timeout 5 log append /tmp/pressure.csv endrepeat else fail NO_BOOT endif核心逻辑是expect命令。它不阻塞整条脚本而是读取 RTT 输出直到匹配某个字符串或超时然后把匹配结果存入内部变量。assert则更严格匹配失败立即中止脚本并返回非零退出码。这里我想强调的是脚本语言的核心语义只有“等待、匹配、断言”三件事所以它足够简单几乎不存在学习曲线又完全覆盖板级验证的需求。一个小技巧是支持-c参数直接执行单条命令。比如在 CI 里不想写整个脚本文件就想快速启动 RTT 并确认发射可以用rttsh -c connect --device STM32F407; rtt start; expect BOOT_OK timeout 5。这对临时诊断特别顺手。2.2 对接AI辅助调试让模型也能玩转板卡标题里提到的“AI在板调试”我的落地方式并不是让 AI 直接操作调试器而是让它生成和调试 rttsh 脚本。有一次我把自己板子的源码、串口日志格式和 rttsh 帮助文档一起丢给在线语言模型让它写一个“检查四次传感器采样并计算平均值”的验证脚本。它几秒钟就输出了repeat 4和assert组合。我在板子上跑了一遍第一次断言阈值写错了模型根据报错信息自我修正第二次就过了。这件事让我意识到 rttsh 天然是 AI 友好的工具。原因是命令行接口简单、输出结构化有明确的退出码和匹配变量语言模型容易理解。为了让这个流程更顺滑我在 rttsh 里增加了--json输出模式。脚本执行过程中每次expect和assert的结果都会以 JSON 流写到 stdout包括匹配值、耗时、行号、动作类型。这样 AI 或者后续的解析器只需要读一行 JSON就能判断板子状态。当然让 AI 直接操作板卡要加保险。我加了两个实用机制一是dryrun模式rttsh -f script.rttsh --dryrun只解析脚本、打印执行计划不真正连设备二是全局超时--timeout 60防止脚本卡住生产线。实际项目里我都是先 dryrun 检查脚本结构再真连板子AI 生成脚本后也建议先跑 dryrun。2.3 批量验证与数据导出从杂乱的日志中提炼数据批量脚本验证是我用得最多的功能。以前做固件验收测试项有十几条上电打印、外设初始化、内存读写、电源管理切换。每条都对应一个.rttsh脚本文件我用一个总控脚本把全部用例串起来。for file in $(ls tests/*.rttsh); do echo Running $file... rttsh -f $file --json results.jsonl echo exit code: $? done数据导出这部分log export命令支持多个格式。比如log export /tmp/adc_data.csv --format csv --timestamp会把 RTT 收到的原始 ASCII 按 CSV 写出每行前加毫秒时间戳。--format json则保留二进制数据和通道信息适合后续做频谱分析。--format raw是对二进制 RTT 通道的精确保真适合协议抓包。说一个处理二进制数据的教训。早期 rttsh 把 RTT 上游通道当纯文本处理结果有一个模块会通过 RTT 发送结构体日志直接被decode成乱码。后来我改成按字节流分块并支持--encoding hex参数需要二进制时用 hex 字符串打印这样既能看到原始字节又不会把非文本数据搞坏。如果你的设备也有类似结构体打印的需求建议从一开始就用 hex 模式别等数据坏了一堆才回头改。2.4 打通CI让板卡验证成为流水线的一环板级测试进 CI最大的障碍就是“没有图形界面也能控制 J-Link”。rttsh 在这方面没什么包袱它本来就是命令行工具跑在 Windows/Linux 的 runner 上都不需要桌面。具体集成方式我把完整示例放到 3.4 节这里先说说架构。CI 里最关键的一点是失败传递。rttsh 所有错误都走统一退出码0 表示脚本执行且所有断言通过1 表示断言失败2 表示连接错误3 表示参数错误4 表示超时。这样流水线里只需要检查$?就能准确判断是板子问题还是环境问题。我甚至会在 CI 阶段里用continue-on-error来分别收集“失败但想看的日志”和“环境准备失败”的情况。另外为了不让 J-Link 被多个并发 job 抢用我在 CI 里对跑板卡的 job 只允许一个并发用 GitHub Actions 的concurrency控制。这是很常见的一个坑两个流水线同时抓同一个 J-Link轻则报错重则把缓冲区搞乱。下面实操章节会详细给一套能直接抄的 CI 配置。3. 实操过程从零构建一个可用的调试流程3.1 环境准备驱动、固件与工具安装rttsh 是 Python 包直接pip install rttsh或者从源码装都可以。但最关键的先决条件是 J-Link 驱动和 SEGGER 软件包。这里多啰嗦一句安装顺序很重要。先装 SEGGER J-Link 软件包再安装 pylink最后装 rttsh。因为 pylink 在初始化时要自动找 SEGGER 的 DLL如果软件包不在默认路径连接会失败。如果你用的是老一代 J-Link v9Windows 11 系统请务必升级驱动到最新版。我遇到过 v9 在 Win11 下系统识别不了设备管理器里一直黄色感叹号。解决方案是去 SEGGER 官网下载最新版 J-Link Software Pack安装完成后用 J-Link Updater 刷一遍固件。注意刷固件时别拔 USBv9 在断电半更新状态下变砖的概率不低。J-Link 固件这块还有一个容易卡住的点。连接时会弹 The firmware of the connected J-Link does not support ... 这样的错误本质是 J-Link 固件版本太旧跟不上驱动软件的新命令。处理方式很简单打开 J-Link Updater把固件更新到与软件包匹配的最新版。现场遇到这个问题我通常直接下载官网最新的 Software Pack然后执行 Updater 一键升级不用额外操作。3.2 连接配置RTT控制块与连接参数rttsh 的连接参数和 J-Link Commander 类似。最常用的参数是设备型号、接口类型、目标速率和 RTT 控制块地址。例如rttsh --device STM32F407 --if swd --speed 4000 --rtt-address 0x20000000--device必须写 SEGGER 支持的型号名比如STM32F407、nRF52840、ATSAMD51。如果型号不对连接会报 “Cannot connect to target”。接口默认是 SWD速度建议先保守用 1000 kHz等确认稳定再往上调。4 MHz 在短杜邦线的情况下比较稳排线一长就容易乱。RTT 控制块地址是很多新人最懵的地方。RTT 输出不需要像传统串口一样初始化外设但需要在目标内存里找到控制块。rttsh 提供了两种方式一种是让工具自动搜索--auto-search另一种是手动指定地址。自动搜索适合快速验证但有时会因为搜索范围有限而找不到。手动指定最适合发布固件在链接脚本里固定一个地址比如0x20000000然后在脚本里写死。这里有个经验建议把 RTT 控制块放到一个固定 RAM 地址段并在.map文件里查一下地址这样以后调试不用每次做内存扫描。3.3 脚本编写与运行示例拿一个最简单的启动验证脚本做例子。设备是一只 STM32F407 板固件上电后打印一行APP_VERSION1.2.3随后进入低功耗模式RTT 不再有输出。我们的目标是验证版本号正确并导出日志。# verify_boot.rttsh connect --device STM32F407 --if swd --speed 4000 rtt start expect APP_VERSION([0-9]\.[0-9]\.[0-9]) timeout 5 assert match APP_VERSION1\.2\.3 log export /tmp/boot_log.csv --format csv --timestamp运行命令rttsh -f verify_boot.rttsh --json我叠加--json看到类似这样的输出{event: expect, status: ok, match: 1.2.3, time_ms: 122} {event: assert, status: pass, line: 5}对于循环压力测试脚本适合这样组织connect --device STM32F407 --if swd --speed 4000 rtt start repeat 5 rtt write UT:1 # 触发一次单元测试 sleep 100 expect UT_PASS timeout 3 log append /tmp/results.txt cycle $i ok endrepeat这里的$i是循环计数器rttsh 的脚本解释器会自动展开。实际执行时板子 100 毫秒跑完一个单元测试脚本总共 10 秒跑完 5 轮退出码 0。整个流程没有 GUI没有人工介入纯命令行搞定。3.4 集成到CI流水线现在给一个 GitHub Actions 的实操配置。我假设你的 runner 是windows-latest因为 Windows 环境对 J-Link v9 驱动最友好Linux 上 v9 老固件坑更多。步骤包括安装驱动、安装 rttsh、跑脚本、上传日志。name: board-test on: [push] jobs: rtt-test: runs-on: windows-latest concurrency: board-rtt steps: - uses: actions/checkoutv4 - name: Download J-Link Software Pack run: | curl -L -O https://www.segger.com/downloads/jlink/JLink_Windows_V796_x86_64.exe ./JLink_Windows_V796_x86_64.exe /S - name: Install rttsh run: | pip install rttsh - name: Run RTT script run: | rttsh -f tests/verify_boot.rttsh --json --timeout 60 env: JLINK_SERIAL: 20090928 - name: Upload logs if: always() uses: actions/upload-artifactv4 with: name: rtt-logs path: /tmp/boot_log.csv几个注意点concurrency: board-rtt控制同一时刻只能跑一个板卡任务避免两个 runner 抢 J-Link。JLINK_SERIAL用来指定具体是哪只 J-Link如果机器上插着多只调试器这个参数能避免连错。if: always()确保即使断言失败也能上传日志这个对排查问题特别重要。跑完流水线你会看到脚本执行、断言失败、日志上传一条龙。板子没插好时 rttsh 会报连接错误退出码 2流水线直接红一眼就知道问题在哪。4. 常见问题与排查技巧实录4.1 J-Link v9在Win11下的驱动问题这个我遇到太多次了。Win11 对老的 J-Link v9 驱动兼容性并不好症状是插上 J-Link 后系统提示“设备无法启动”或者设备管理器里出现带感叹号的未知设备。最直接的解决方案是安装新版驱动。有时候旧驱动残留也会冲突建议用卸载工具把旧 SEGGER 驱动清干净重启后再装。另外J-Link v9 的 EEPROM 固件分区特别小新版驱动在固件升级时偶尔会卡在 50%。别慌重新运行 J-Link Updater多试几次。如果还不行把 USB 线换成带屏蔽的短一点干扰少一些成功率高很多。这个坑我至少折腾过两次后面只要见到 v9第一件事就是检查驱动版本和固件版本。4.2 “The firmware of the connected J-Link does not support...”报错这条报错原文一般类似 “The firmware of the connected J-Link (s/n:20090928) does not support the following features...”。我遇到过 s/n 20090928 的 J-Link v9在升级驱动后连接 STM32 时蹦出这行字。大意是连接时执行了某个新的调试特性固件不支持。处理思路很简单用 J-Link Updater 把固件刷到最新。如果已经在最新还不行就要退一步想是不是你在 rttsh 里开了太新的接口参数。我一般按顺序排查检查 J-Link 固件版本rttsh 加--info参数看固件版本。对比 SEGGER 软件包版本固件和驱动版本要匹配。如果固件实在升不动换用较老的 SEGGER DLL 兼容版本比如 v6.80。最后手段是换一台新一点的 J-Link但一般用不到。这里提醒一句升级 J-Link 固件是有风险的跨版本大升级前建议先导出当前固件备份。SEGGER 的 Updater 里有固件保存功能别嫌麻烦。4.3 RTT控制块找不到或输出不显示连接正常RTT 启动也提示成功但读不到任何输出。这是最气人的。大部分原因是程序还没运行到初始化 RTT 的代码或者控制块地址扫错了。我遇到过一个情况固件里 RTT 控制块在.data段但上电后那段 RAM 被 startup 代码初始化前是随机值自动搜索时找到了假的控制块导致后面全读错。解决办法归纳成几点确认目标板程序真的跑起来了最简单的办法是先用 JLink Commander 读一下内存看目标是否响应。手动指定 RTT 控制块地址别依赖自动。用--rtt-address 0x20000000这种明确地址。调大自动搜索范围比如--auto-search --search-start 0x20000000 --search-end 0x20001000。使用最新版 SEGGER DLL早期版本对 Cortex-M7 的 RTT 自动搜索兼容不好。还有个不起眼的细节就是 J-Link RTT 的 Memory Setup。如果你用的是AT91SAM之类芯片RTT 默认扫描区可能覆盖不到实际 RAM。这时候要手动把内存区间填进去。rttsh 提供了--rtt-range参数可以多个区间逗号分隔比如0x20000000-0x20008000,0x30000000-0x30005000。4.4 脚本卡死与超时处理脚本跑着跑着就卡住是最影响 CI 心情的事。我之前写过一个脚本里面expect DONE没有设置 timeout结果板子某个用例进入死循环CI 直接挂半小时。后来我给 rttsh 加了两层保护单条命令的--timeout和全局的--timeout。实用建议每条expect尽量写明确的timeout这个超时值根据板子真实响应时间放大 50% 到 100%。不要用 0 或非常大的 timeout除非你能保证板子绝对不死锁。如果脚本里有什么长任务比如 Flash 擦除可以分段先expect ERASE_START再expect ERASE_FINISH timeout 30。RTT 缓冲区满导致的卡死也要特别注意。默认 RTT 上行缓冲区只有 1024 字节如果板子一口气打日志而你脚本没及时读新日志就会被覆盖。rttsh 的rtt read会持续把数据从 buffer 搬走但如果脚本停在一个sleep命令里buffer 很容易爆。所以在脚本里减少长sleep改用基于expect的等待更稳妥。最后分享一个小技巧调试 RTT 脚本卡住时别只盯输出。我经常用两个命令排查一个是rttsh -c rtt channel看各个通道的当前 buffer 水线另一个是rttsh -c rtt bank看 J-Link 协议层的缓冲状态。很多“卡死”其实只是上层没读底层 buffer 已经满了。这个观察经验几乎能解决一半的脚本超时问题。还有一条脚本化的调试流程强烈建议先放在本地手工跑通再推 CI。别指望 CI 环境能自动解决驱动和固件问题你的流水线红一半都是环境差异带来的。rttsh 的好处是脚本本身可以随便重跑环境问题排查一次以后就再也不会踩这份投入很值得。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Maven私服搭建实战:Nexus 3.x Docker化部署与高可用配置 2026/10/1 16:37:38

Maven私服搭建实战:Nexus 3.x Docker化部署与高可用配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
一文搞懂图片像素、文件大小与存储类型:C#像素数组转图片实战 2026/10/1 16:37:29

一文搞懂图片像素、文件大小与存储类型:C#像素数组转图片实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
RK3568/RK3576/RK3588在AGV与服务机器人中的工程化选型与BOM优化 2026/10/1 16:37:29

RK3568/RK3576/RK3588在AGV与服务机器人中的工程化选型与BOM优化

1. 从“够用”到“必须选”:AGV厂商采购决策背后的成本结构重算我第一次在苏州一家AGV底盘供应商的产线办公室里看到他们把三台RK3568开发板并排焊在测试治具上时,心里还嘀咕:这不就是个中端ARM平台?怎么连激光SLAM定位模块都敢直…

阅读更多 →
从零手写轻量神经网络:普通显卡也能训练的开源实战 2026/10/1 16:37:21

从零手写轻量神经网络:普通显卡也能训练的开源实战

先聊点实在的。不少人看到“自研神经网络”这几个字,第一反应是“这得有多少卡、多少算力才玩得动”,第二反应是“这得是多大的团队、多少篇论文堆出来的”。但这次我想说的是另一条路:我最近把一个从零写的神经网络项目完整开源了&#xff0…

阅读更多 →
ROS2通信延迟深度解析:从论文到工程实践 2026/10/1 16:37:01

ROS2通信延迟深度解析:从论文到工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
实体卡带好价盘点:版本、汇率与价格波动逻辑 2026/10/1 16:37:01

实体卡带好价盘点:版本、汇率与价格波动逻辑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉