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通道,甚至还有与任意通道设置一起使用的log
和defmt
的日志记录器实现。
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"
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 } // 自定义大小
}
}
最佳实践
- 错误处理: 总是检查write操作的返回值
- 缓冲区管理: 根据数据量选择合适的通道模式
- 实时性: 对时间敏感的操作使用SkipIfFull模式
- 资源优化: 在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"
这个完整示例展示了:
- RTT的多通道配置(日志通道和性能监控通道)
- 实时日志输出使用rprintln!宏
- 性能监控使用DWT周期计数器
- 双向命令处理机制
- 错误处理的最佳实践
- 适合实时嵌入式应用的循环结构
使用此示例时,可以通过J-Link RTT Viewer或pyOCD工具实时查看输出并发送命令进行交互测试。