Rust嵌入式开发库rtt-target的使用,实现高效实时数据传输与调试

Rust嵌入式开发库rtt-target的使用,实现高效实时数据传输与调试

rtt-target是RTT(实时传输)I/O协议的目标端实现。RTT通过使用内存中的环形缓冲区和轮询,通过调试探头实现输入和输出。这使得从微控制器进行调试日志记录具有最小的延迟且无阻塞,即使在不能容忍半主机延迟的实时应用中也可使用。

平台支持

需要使用平台特定的critical-section实现来使用此库。

直接使用write!宏或二进制write方法输出到通道对象不需要锁定,因此不需要任何平台特定的关键部分。

使用

在使用平台特定的关键部分时,打印非常简单:

use rtt_target::{rtt_init_print, rprintln};

fn main() {
    rtt_init_print!();
    loop {
        rprintln!("Hello, world!");
    }
}

rtt-target还支持初始化多个RTT通道,甚至还有与任意通道设置一起使用的logdefmt的日志记录器实现。

defmt集成需要设置features = ["defmt"]。此外,在使用defmt之前,您必须调用rtt_init_defmt!或手动设置通道并调用set_defmt_channel

log集成需要设置features = ["log"]。此外,在使用log之前,您必须调用rtt_init_log!或手动设置通道并调用init_logger/init_logger_with_level

注意:对于您的平台,特别是如果您使用多核MCU,外部日志记录器实现可能比通过log/defmt功能由此crate提供的实现更适合。

开发

examples-cortex-m和panic-test crate默认带有古老的STM32F103C8xx的构建文件,但可以轻松适应任何芯片,因为它们仅包含最少的平台特定运行时代码以使fn main运行。

完整示例代码

// 引入rtt-target库
use rtt_target::{rtt_init_print, rprintln};

// 主函数
fn main() {
    // 初始化RTT打印功能
    rtt_init_print!();
    
    // 主循环
    loop {
        // 通过RTT输出"Hello, world!"消息
        rprintln!("Hello, world!");
        
        // 这里可以添加其他实时数据传输和调试代码
        // 例如:读取传感器数据、处理实时事件等
    }
}
// 使用defmt功能的示例(需要在Cargo.toml中启用defmt特性)
#[cfg(feature = "defmt")]
use rtt_target::rtt_init_defmt;

#[cfg(feature = "defmt")]
fn main() {
    // 初始化defmt RTT通道
    rtt_init_defmt!();
    
    // 使用defmt进行格式化输出
    defmt::info!("Hello from defmt!");
}

// 使用log功能的示例(需要在Cargo.toml中启用log特性)
#[cfg(feature = "log")]
use rtt_target::{rtt_init_log, init_logger};
#[cfg(feature = "log")]
use log::{info, LevelFilter};

#[cfg(feature = "log")]
fn main() {
    // 初始化log RTT通道
    rtt_init_log!();
    
    // 初始化日志记录器
    init_logger(LevelFilter::Info).unwrap();
    
    // 使用log crate进行日志记录
    info!("Hello from log!");
}
// 手动配置多个RTT通道的示例
use rtt_target::{rtt_init, ChannelMode};
use core::fmt::Write;

fn main() {
    // 初始化RTT并获取通道句柄
    let mut channels = rtt_init! {
        up: {
            0: {
                size: 1024
                mode: ChannelMode::BlockIfFull
                name: "Data channel"
            }
            1: {
                size: 128
                mode: ChannelMode::NoBlockSkip
                name: "Log channel"
            }
        }
        down: {
            0: {
                size: 64
                mode: ChannelMode::BlockIfFull
                name: "Command channel"
            }
        }
    };
    
    // 获取上行通道0(数据通道)
    let data_channel = channels.up.0;
    // 获取上行通道1(日志通道)
    let log_channel = channels.up.1;
    // 获取下行通道0(命令通道)
    let command_channel = channels.down.0;
    
    loop {
        // 向数据通道写入数据
        write!(data_channel, "Sensor data: {}", 42).unwrap();
        
        // 向日志通道写入日志
        write!(log_channel, "Debug information").unwrap();
        
        // 检查命令通道是否有数据
        if let Some(cmd) = command_channel.read() {
            // 处理接收到的命令
            write!(log_channel, "Received command: {}", cmd).unwrap();
        }
    }
}

完整示例demo

基于上述内容,这里提供一个综合性的完整示例,展示rtt-target库在嵌入式实时系统中的典型应用:

//! 综合rtt-target使用示例
//! 展示多通道配置、日志记录和实时数据传输

#![no_std]
#![no_main]

use cortex_m_rt::entry;
use rtt_target::{rtt_init, rprintln, ChannelMode};
use core::fmt::Write;
use panic_rtt_target as _;

// 如果启用defmt特性
#[cfg(feature = "defmt")]
use defmt::info;

// 如果启用log特性  
#[cfg(feature = "log")]
use log::{info, LevelFilter};

#[entry]
fn main() -> ! {
    // 初始化多通道RTT
    let mut channels = rtt_init! {
        up: {
            0: {
                size: 2048
                mode: ChannelMode::BlockIfFull
                name: "Main data channel"
            }
            1: {
                size: 512
                mode: ChannelMode::NoBlockSkip
                name: "Debug log channel"
            }
            2: {
                size: 256
                mode: ChannelMode::BlockIfFull
                name: "Error channel"
            }
        }
        down: {
            0: {
                size: 128
                mode: ChannelMode::BlockIfFull
                name: "Control channel"
            }
        }
    };

    // 获取各个通道句柄
    let data_channel = channels.up.0;
    let log_channel = channels.up.1;
    let error_channel = channels.up.2;
    let control_channel = channels.down.0;

    // 简单打印初始化消息
    rprintln!("RTT initialization complete!");
    write!(log_channel, "System started successfully").unwrap();

    // 模拟传感器数据采集和处理循环
    let mut sensor_value: u32 = 0;
    loop {
        // 模拟传感器数据读取
        sensor_value = sensor_value.wrapping_add(1);
        
        // 通过数据通道发送传感器数据
        write!(data_channel, "Sensor reading: {}", sensor_value).unwrap();
        
        // 定期记录日志
        if sensor_value % 100 == 0 {
            write!(log_channel, "Processed {} samples", sensor_value).unwrap();
        }
        
        // 检查控制通道命令
        if let Some(command) = control_channel.read() {
            match command {
                b'r' => {
                    write!(log_channel, "Reset command received").unwrap();
                    sensor_value = 0;
                }
                b's' => {
                    write!(log_channel, "Stop command received").unwrap();
                    // 处理停止逻辑
                }
                _ => {
                    write!(error_channel, "Unknown command: {}", command).unwrap();
                }
            }
        }

        // 简单的延迟模拟
        for _ in 0..1000 {
            cortex_m::asm::nop();
        }
    }
}

// defmt集成示例(需要启用defmt特性)
#[cfg(feature = "defmt")]
#[entry]
fn main_defmt() -> ! {
    rtt_target::rtt_init_defmt!();
    
    defmt::info!("System starting with defmt...");
    
    loop {
        defmt::info!("Hello from defmt!");
        cortex_m::asm::delay(1_000_000);
    }
}

// log集成示例(需要启用log特性)
#[cfg(feature = "log")]
#[entry] 
fn main_log() -> ! {
    rtt_target::rtt_init_log!();
    rtt_target::init_logger(LevelFilter::Debug).unwrap();
    
    info!("System starting with log...");
    
    loop {
        info!("Hello from log crate!");
        cortex_m::asm::delay(1_000_000);
    }
}

对应的Cargo.toml配置示例:

[package]
name = "rtt-target-example"
version = "0.1.0"
edition = "2021"

[dependencies]
cortex-m = "0.7.6"
cortex-m-rt = "0.7.0"
rtt-target = { version = "0.3.1", features = [] } # 根据需要启用defmt或log特性
panic-rtt-target = "0.1.0"

# 可选:启用defmt支持
# [dependencies.defmt]
# version = "0.3"

# 可选:启用log支持  
# [dependencies.log]
# version = "0.4"

[profile.release]
codegen-units = 1
debug = true
lto = true
opt-level = "s"

1 回复

Rust嵌入式开发库rtt-target的使用指南

简介

rtt-target是一个专为Rust嵌入式开发设计的实时传输(RTT)库,它通过内存缓冲区实现主机与目标设备之间的高效数据传输和调试,无需额外的硬件调试接口。

主要特性

  • 零开销的日志输出
  • 实时数据传输
  • 支持多个上行/下行通道
  • 与SEGGER J-Link调试器兼容

使用方法

1. 添加依赖

在Cargo.toml中添加:

[dependencies]
rtt-target = { version = "0.3", features = ["cortex-m"] }

2. 基本初始化

use rtt_target::{rtt_init, ChannelMode};

#[entry]
fn main() -> ! {
    // 初始化RTT
    let channels = rtt_init! {
        up: {
            0: ChannelMode::BlockIfFull
        },
        down: {
            0: ChannelMode::BlockIfFull
        }
    };
    
    let mut output = channels.up.0;
    let mut input = channels.down.0;
    
    // 你的应用代码
    loop {}
}

3. 日志输出示例

use rtt_target::rprintln;

fn main() {
    rtt_init_default!();
    
    rprintln!("系统启动完成");
    rprintln!("温度读数: {}°C", read_temperature());
    rprintln!("警告: 电压过低!");
}

4. 双向数据传输

use rtt_target::{rtt_init, ChannelMode};

fn command_processor() {
    let channels = rtt_init! {
        up: { 0: ChannelMode::BlockIfFull },
        down: { 0: ChannelMode::BlockIfFull }
    };
    
    let mut output = channels.up.0;
    let mut input = channels.down.0;
    
    loop {
        // 读取主机发送的命令
        let mut buf = [0u8; 64];
        if let Ok(len) = input.read(&mut buf) {
            let command = core::str::from_utf8(&buf[..len]).unwrap();
            
            // 处理命令并返回响应
            match command.trim() {
                "get_status" => {
                    output.write_str("状态: 正常运行\n").unwrap();
                }
                "reset" => {
                    output.write_str("执行重启...\n").unwrap();
                    // 执行重启逻辑
                }
                _ => {
                    output.write_str("未知命令\n").unwrap();
                }
            }
        }
    }
}

5. 性能监控示例

use rtt_target::{rtt_init, ChannelMode};
use cortex_m::peripheral::DWT;

fn performance_monitor() {
    let channels = rtt_init! {
        up: { 0: ChannelMode::SkipIfFull }
    };
    
    let mut output = channels.up.0;
    
    // 使用DWT周期计数器进行性能测量
    let mut start = DWT::get_cycle_count();
    
    // 执行需要监控的代码
    critical_function();
    
    let cycles = DWT::get_cycle_count() - start;
    output.write_str(&format!("执行时间: {} 周期\n", cycles)).unwrap();
}

配置选项

通道模式

  • BlockIfFull: 缓冲区满时阻塞
  • SkipIfFull: 缓冲区满时跳过新数据
  • NoBlockSkip: 无阻塞模式

缓冲区大小配置

rtt_init! {
    up: {
        0: ChannelMode::BlockIfFull,  // 默认大小1024字节
        1: ChannelMode::BlockIfFull { size: 2048 }  // 自定义大小
    }
}

最佳实践

  1. 错误处理: 总是检查write操作的返回值
  2. 缓冲区管理: 根据数据量选择合适的通道模式
  3. 实时性: 对时间敏感的操作使用SkipIfFull模式
  4. 资源优化: 在release构建中禁用不必要的调试输出

调试工具配合

使用J-Link RTT Viewer或pyOCD等工具可以实时查看和发送数据:

# 使用pyOCD
pyocd rtt --server

这个库特别适合需要实时调试和监控的嵌入式应用场景,如机器人控制、实时数据采集和工业自动化系统。

完整示例demo

以下是一个完整的rtt-target使用示例,结合了日志输出、双向数据传输和性能监控功能:

//! 完整的rtt-target使用示例
#![no_std]
#![no_main]

use cortex_m_rt::entry;
use panic_rtt_target as _;
use rtt_target::{rtt_init, rprintln, ChannelMode};
use cortex_m::peripheral::DWT;

// 模拟的温度读取函数
fn read_temperature() -> f32 {
    25.5 // 返回模拟温度值
}

// 模拟的关键函数用于性能监控
fn critical_function() {
    // 模拟一些计算工作
    for _ in 0..1000 {
        cortex_m::asm::nop();
    }
}

#[entry]
fn main() -> ! {
    // 初始化RTT通道
    let channels = rtt_init! {
        up: {
            0: ChannelMode::BlockIfFull,    // 用于日志输出
            1: ChannelMode::SkipIfFull      // 用于性能数据,跳过满缓冲区
        },
        down: {
            0: ChannelMode::BlockIfFull     // 用于接收命令
        }
    };

    let mut log_output = channels.up.0;
    let mut perf_output = channels.up.1;
    let mut command_input = channels.down.0;

    // 系统启动日志
    rprintln!("系统启动完成");
    log_output.write_str("RTT初始化成功\n").unwrap();

    let mut counter = 0;

    loop {
        counter += 1;

        // 1. 日志输出示例
        let temperature = read_temperature();
        rprintln!("循环次数: {}, 温度: {:.1}°C", counter, temperature);

        // 2. 性能监控示例
        let start_cycles = DWT::get_cycle_count();
        critical_function();
        let elapsed_cycles = DWT::get_cycle_count() - start_cycles;
        
        perf_output.write_str(&format!("循环{}执行时间: {}周期\n", counter, elapsed_cycles)).unwrap();

        // 3. 双向命令处理示例
        let mut command_buffer = [0u8; 32];
        if let Ok(len) = command_input.read(&mut command_buffer) {
            if let Ok(command) = core::str::from_utf8(&command_buffer[..len]) {
                match command.trim() {
                    "get_status" => {
                        log_output.write_str("状态: 正常运行\n").unwrap();
                        log_output.write_str(&format!("当前温度: {:.1}°C\n", temperature)).unwrap();
                    }
                    "get_counter" => {
                        log_output.write_str(&format!("当前计数器: {}\n", counter)).unwrap();
                    }
                    "reset_counter" => {
                        counter = 0;
                        log_output.write_str("计数器已重置\n").unwrap();
                    }
                    _ => {
                        log_output.write_str("未知命令,可用命令: get_status, get_counter, reset_counter\n").unwrap();
                    }
                }
            }
        }

        // 简单的延时
        for _ in 0..100_000 {
            cortex_m::asm::nop();
        }
    }
}

对应的Cargo.toml配置:

[package]
name = "rtt-target-demo"
version = "0.1.0"
edition = "2021"

[dependencies]
cortex-m = "0.7.6"
cortex-m-rt = "0.7.1"
panic-rtt-target = "0.1.0"
rtt-target = { version = "0.3", features = ["cortex-m"] }

[profile.release]
codegen-units = 1
debug = true
lto = true
opt-level = "s"

这个完整示例展示了:

  1. RTT的多通道配置(日志通道和性能监控通道)
  2. 实时日志输出使用rprintln!宏
  3. 性能监控使用DWT周期计数器
  4. 双向命令处理机制
  5. 错误处理的最佳实践
  6. 适合实时嵌入式应用的循环结构

使用此示例时,可以通过J-Link RTT Viewer或pyOCD工具实时查看输出并发送命令进行交互测试。

回到顶部