如何输出 RTT 日志?

解读

在国内嵌入式/系统级 Rust 岗位面试中,**“RTT 日志”**通常特指 SEGGER RTT(Real-Time Transfer) 方案:通过 J-Link 调试器在 MCU 内部 RAM 中开辟环形缓冲区,实现 零引脚、微秒级延迟、最高 3 MB/s 吞吐 的日志输出。
面试官问“如何输出”,既考察你对 Rust 嵌入式生态的熟悉度,也验证你是否真正在产线上解决过“调试口被占用、波特率受限、不能加 USB 转串口”等现场痛点。回答必须给出 Cargo 依赖、初始化流程、宏封装、链接脚本适配、IDE/命令行抓取 五步闭环,否则会被认为“只玩过开发板”。

知识点

  1. SEGGER RTT 协议原理:MCU 端写环形 buffer,J-Link 通过 SWD 读取,PC 端使用 J-Link RTT Viewer 或 pylink-rtt 抓取。
  2. ** cortex-m-rt 与 rtt-target crate**:rtt-target 提供 rprintln! 宏,内部使用 cortex_m::interrupt::free 保证中断安全。
  3. 内存布局:链接脚本需保留 _SEGGER_RTT 段,防止被 linker garbage collect。
  4. Cargo features 隔离:debug 镜像开启 rtt,release 镜像可关闭以节省 2–3 kB Flash。
  5. 国内量产常用工具链
    • 调试器:J-Link OB(淘宝 30 元版)DAPLink 刷 J-Link固件
    • 上位机:J-Link RTT Viewer 7.20 以上(支持中文时间戳)、RTT-Logger(可转 Syslog 对接阿里云日志服务)。

答案

步骤如下,可直接用于 STM32/GD32/NRF52 等国内主流 MCU:

  1. Cargo.toml 引入依赖
[dependencies]
cortex-m-rt = "0.7"
rtt-target = { version = "0.4", features = ["cortex-m"] }
panic-rtt-target = { version = "0.1", features = ["cortex-m"] }
  1. main.rs 初始化并替换 panic handler
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use rtt_target::{rtt_init_print, rprintln};
use panic_rtt_target as _;

#[entry]
fn main() -> ! {
    rtt_init_print!();          // 申请 Up buffer 1024 B
    rprintln!("===== 国产Rust固件启动 =====");
    let mut counter = 0u32;
    loop {
        rprintln!("tick: {}", counter);
        counter = counter.wrapping_add(1);
        cortex_m::asm::delay(48_000_000); // 1 s @48 MHz
    }
}
  1. memory.x 保留 RTT 控制块
MEMORY
{
  FLASH : ORIGIN = 0x08000000, LENGTH = 256K
  RAM : ORIGIN = 0x20000000, LENGTH = 64K
}
SECTIONS
{
  .rtt (NOLOAD) :
  {
    KEEP(*(.rtt));
    . = ALIGN(4);
  } > RAM
}
  1. build 并烧录
cargo build --release --bin firmware
JLinkExe -device STM32F103C8 -if swd -speed 4000 -autoconnect 1
JLink> loadbin target/thumbv7m-none-eabi/release/firmware 0x08000000
JLink> r
JLink> g
  1. 实时查看日志
JLinkRTTViewer -device STM32F103C8 -if swd -speed 4000 -a "log.txt"

终端即可看到中文时间戳日志,无需额外飞线,不影响主频,SWD 接口复用

拓展思考

  1. 多通道 RTT:使用 rtt_target::rtt_init! 创建 3 个 Up buffer,分别对应 INFO、WARN、TRACE,上位机通过通道号过滤,解决国内“日志太多把 J-Link 堵死”的老问题。
  2. 零拷贝大块数据rtt_target::write 直接写 slice,配合 DMA 双缓冲,可在 1 ms 内导出 4 kB 波形数据,替代传统串口 YModem。
  3. 安全认证场景:RTT 缓冲区位于 RAM,量产时可把 .rtt 段放到 SRAM3(STM32H7 的 64 kB AXI SRAM),烧录后使用 J-Link Commander 的 w4 0x20000000 0xFFFFFFFF 清零,防止日志残留泄露密钥。
  4. 与 defmt 对比:defmt 使用 LEB128 压缩 + 索引字符串表,同样通过 RTT 传输,比 rtt-target 节省 70 % 带宽,但需引入 probe-run 工具链;国内部分公司因 版权合规 仍倾向 SEGGER 官方方案。