Rust嵌入式开发必备:panic-rtt-target插件库的使用,实现高效RTT(实时传输)目标设备错误处理
panic-rtt-target
通过RTT记录panic消息。这是rtt-target的配套库。
文档
RTT必须通过使用其中一个rtt_init
宏进行初始化。否则在编译时会出现链接错误。
panic消息总是记录到打印通道。当panic发生时,通道模式会自动设置为BlockIfFull
,以确保完整的消息总能被记录。如果代码在RTT初始化之前就panic了(这种情况不太可能发生),或者打印通道不存在,则不会记录任何内容。
panic处理器运行在一个不返回的critical_section中,其实现应由用户提供。
使用方法
Cargo.toml:
[dependencies]
rtt-target = "x.y.z"
panic-rtt-target = "x.y.z"
#![no_std]
use panic_rtt_target as _;
use rtt_target::rtt_init_default;
fn main() -> ! {
// 你可以使用`rtt_init_print`或者在初始化后调用`set_print_channel`
rtt_init_default!();
panic!("Something has gone terribly wrong");
}
Defmt支持
你可以启用defmt
特性,这样panic消息会打印到defmt通道。如果你这样做并且同时配置了打印通道和defmt通道,panic消息会同时打印到两个通道。defmt
特性不会自动启用rtt-target/defmt
。这允许你在需要时使用不同的defmt后端。
完整示例代码
#![no_std]
#![no_main]
use panic_rtt_target as _; // 启用panic处理并通过RTT输出
use rtt_target::{rtt_init_print, rprintln};
#[cortex_m_rt::entry]
fn main() -> ! {
// 初始化RTT打印通道
rtt_init_print!();
// 正常日志输出
rprintln!("System started");
// 模拟panic情况
panic!("This is a simulated panic message");
// 注意:panic!是一个发散函数,不会返回
#[allow(unreachable_code)]
loop {}
}
这个示例展示了:
- 如何初始化RTT打印系统
- 正常日志输出使用rprintln!宏
- 模拟panic情况的处理
- panic消息将通过RTT通道输出
要使用这个示例,你需要在Cargo.toml中添加以下依赖:
[dependencies]
cortex-m-rt = "0.7.3"
panic-rtt-target = "0.2.0"
rtt-target = "0.3.1"
1 回复
Rust嵌入式开发必备:panic-rtt-target插件库的使用
完整示例代码
以下是基于内容中提供的示例代码扩展的完整demo,展示了在实际嵌入式项目中使用panic-rtt-target
的完整流程:
#![no_std] // 不使用标准库
#![no_main] // 不使用标准main入口
// 引入必要的库
use panic_rtt_target as _; // panic处理器
use cortex_m_rt::entry; // Cortex-M运行时入口
use rtt_target::{rtt_init_print, rprintln}; // RTT打印功能
// 应用主入口
#[entry]
fn main() -> ! {
// 初始化RTT打印系统
rtt_init_print!(
NoBlockSkip, // 非阻塞模式,缓冲区满时跳过新消息
128, // 上行缓冲区大小(设备到主机)
64 // 下行缓冲区大小(主机到设备)
);
rprintln!("系统初始化完成...");
// 模拟传感器初始化
match init_sensors() {
Ok(_) => rprintln!("传感器初始化成功"),
Err(e) => panic!("传感器初始化失败: {:?}", e),
};
// 主应用循环
loop {
match read_sensor_data() {
Ok(data) => {
rprintln!("温度: {}.{}°C", data.temp / 10, data.temp % 10);
rprintln!("湿度: {}%", data.humidity);
}
Err(e) => {
// 使用自定义错误处理
handle_sensor_error(e);
}
}
// 模拟延迟
cortex_m::asm::delay(8_000_000); // ~1秒延迟(假设8MHz时钟)
}
}
// 传感器数据结构
#[derive(Debug)]
struct SensorData {
temp: u16, // 温度(0.1°C精度)
humidity: u8, // 湿度百分比
light: u16, // 光照强度
}
// 自定义错误类型
#[derive(Debug)]
enum SensorError {
I2CFault,
CRCError,
Timeout,
NotCalibrated,
}
// 初始化传感器
fn init_sensors() -> Result<(), SensorError> {
// 模拟初始化过程
if some_hw_check() {
Ok(())
} else {
Err(SensorError::I2CFault)
}
}
// 读取传感器数据
fn read_sensor_data() -> Result<SensorData, SensorError> {
// 模拟读取过程
if some_condition() {
Ok(SensorData {
temp: 235, // 23.5°C
humidity: 45, // 45%
light: 1023, // 最大光照
})
} else {
Err(SensorError::CRCError)
}
}
// 自定义错误处理函数
fn handle_sensor_error(e: SensorError) -> ! {
// 根据不同错误类型采取不同措施
match e {
SensorError::Timeout => {
rprintln!("警告: 传感器超时,尝试复位...");
// 尝试恢复逻辑...
panic!("传感器无法恢复: 超时");
}
_ => panic!("传感器错误: {:?}", e),
}
}
// 模拟硬件检查
fn some_hw_check() -> bool {
false // 模拟硬件故障
}
// 模拟读取条件
fn some_condition() -> bool {
true // 修改为false可触发错误
}
代码说明
-
初始化配置:
- 使用
rtt_init_print!
宏初始化RTT系统,配置了128字节的上行缓冲区和64字节的下行缓冲区 - 设置了非阻塞模式(
NoBlockSkip
),当缓冲区满时跳过新消息而不是阻塞
- 使用
-
错误处理:
- 展示了两种错误处理方式:直接panic和使用自定义处理函数
- 自定义
handle_sensor_error
函数可以根据错误类型采取不同措施
-
传感器模拟:
- 定义了
SensorData
结构体来组织传感器数据 - 使用
SensorError
枚举定义各种可能的传感器错误
- 定义了
-
实际应用:
- 包含完整的初始化、主循环和错误处理流程
- 展示了如何在真实应用场景中使用RTT输出调试信息
使用方法
- 将上述代码保存为
main.rs
- 在
Cargo.toml
中添加依赖:
[dependencies]
cortex-m-rt = "0.7"
panic-rtt-target = "0.3"
rtt-target = "0.3"
- 使用
cargo build --release
编译 - 通过J-Link调试器和RTT Viewer工具查看输出
输出示例
当应用运行时,你将在RTT Viewer中看到类似输出:
系统初始化完成...
传感器初始化失败: I2CFault
当修改some_condition()
返回false时,会触发:
温度: 23.5°C
湿度: 45%
传感器错误: CRCError