Skip to main content
读取您的固件通过 SEGGER RTT 打印的内容。启用 rtt feature 之后,您还可以往它的下行通道写入,于是一个 cargo test 就能驱动一个交互式控制台。 有两条路径,它们的行为并不相同。
请优先使用交互式会话。单向流产出的是原始的 HTTP 分块传输帧,而不是干净的负载 —— 见下面的警告。

交互式会话

启用该 feature

rtt feature 隐含启用 blocking。它没有异步版本。

打开一个会话

gdbserver 必须已经在运行。 没有先连接就调用 rtt_interactive() 会以 Error::Stream 失败,信息是 No debugger connection found for net 'debug1'. Start one first

RttOptions

channel 同时决定两个方向上的通道。chunk_size 只对交互式会话有效;单向的 HTTP 流会忽略它。

方法

方法参考

read(timeout: Duration) -> Result<Vec<u8>>

timeout 时间内到达的上行通道字节,有多少给多少。
目标空闲时,read() 会把整个超时时间等满。在轮询循环里请改用 try_read(),否则每一轮迭代都要花掉整个超时时长。

try_read() -> Result<Vec<u8>>

已经收到的字节,不等待。没有缓冲内容时返回空 vector。

wait_for(needle: &[u8], timeout: Duration) -> Result<Vec<u8>>

不断累积输出,直到出现 needle 返回: 直到该标记为止、并且包含该标记的全部内容。标记之后的字节会继续留在缓冲区里,供下一次读取。空标记会立即返回;没找到则是 Error::Timeout

write(data: &[u8]) -> Result<()>write_str(s: &str) -> Result<()>

写入目标的 RTT 下行通道。
写入要求固件在该通道上声明了下行缓冲区。仅有 defmt-rtt 只提供上行缓冲区,而在没有下行缓冲区时,目标会静默丢弃您写入的内容 —— 该调用仍然返回 Ok(())。这是目标侧的事实,而不是传输失败,所以该 crate 没有错误可以报告。

stop() -> Result<()>

干净地停止。它会消耗掉该会话。把会话 drop 掉同样会停止它,但 stop() 会把错误暴露出来,而不是吞掉。

单向流

debug.rtt()debug.rtt_with(&RttOptions) 返回一个 RttStream,它实现了 std::io::Read。它不需要任何 cargo feature,也不需要 Socket.IO。
RttStream 产出的是原始的 HTTP 分块传输帧,而不是干净的负载。 一次读取返回的字节形如 384\r\nblink 46823 period=500ms\n...,其中 384 是十六进制的分块长度。把它包进 BufReader 再按行迭代,得到的会是 "64""384" 和空字符串,与真正的固件输出交替出现。一个十六进制的分块长度和一行由您的固件打印出来的内容,在形式上无法区分。这个问题记录在 lager-rs#5。在它修复之前,需要可解析输出的场合请用 rtt_interactive()。交互式路径是干净的。

示例

驱动固件控制台并对回复做断言

在目标空闲时收集启动输出而不被阻塞

说明

  • Box 的 RTT telnet 端口只接受一个客户端。 在同一个探针和同一个通道上开第二个会话,会被以 Error::StreamRTT port 9090 is already in use by another session 拒绝。同一个探针上的两个不同通道属于不同端口,可以同时运行。
  • 字节是原始的defmt 输出是压缩的二进制,必须经 defmt-print -e <elf> 管道处理;wait_for 只在纯文本控制台上才帮得上忙。
  • 连接确认的超时是 30 秒,比 UART 的 15 秒更宽。多出来的时间用于在 RAM 中搜索 RTT 控制块,以及在 gdbserver 稳定下来的过程中重试 telnet 挂接。
  • 会话会在 Socket.IO 握手时带上网关 bearer 令牌,因此受网关保护的 Box 无需额外配置即可使用。
  • 需要 Box 软件 0.35.0 或更高版本。更旧的 Box 会给出 Error::UnsupportedByBox