Rust嵌入式编程---panic处理和异常处理

panic处理

我们之前学习过panic,这个部分对于Rust是非常重要的,例如索引等内置操作在运行时进行内存安全检查,当尝试超出边界索引时,这会导致恐慌。在标准库(Std)中,panic有一个定义的行为:它展开 panic 线程的堆栈,除非用户选择在 panic 时中止程序,否则会进行错误位置的一个堆栈定位。
但是,在没有标准库的程序中,恐慌行为是未定义的。可以通过声明函数来选择行为。此函数必须在程序的依赖关系图中只出现一次,并且必须具有以下签名:,其中 PanicInfo 是包含有关 panic 位置的信息的结构。#[panic_handler] fn(&PanicInfo) -> !
对于panic,有如下的一些常用种类:
1.panic_halt:死机会导致程序或当前线程通过进入无限循环而停止。
2.panic_abort:紧急导致中止指令被执行。
3.panic_itm:使用 ITM(一种特定于 ARM cortex-M 的外设)记录死机消息。
4.panic_semihosting:使用半主机技术将死机消息记录到主机,这个常用在qemu中,可以打印出错误信息。
这些panic的使用方法如下:
use panic_halt as _
use panic_semihosting as _
如果不重命名,编译器会警告说你有一个未使用的导入,当然不是错误,可以编译通过。
下面通过一个例子来理解一下,panic的使用方法,是一个数组访问越界的一个错误。

#![no_main]
#![no_std]

use panic_semihosting as _;
use cortex_m_rt::entry;
#[entry]
fn main() -> ! {
    let xs = [0, 1, 2];
    let i = xs.len() + 1;
    let _y = xs[i]; //访问越界,加下划线变量就是说未使用不警告。
    loop {}
}

出现以下运行结果,用的是semihosting,会打印输出在这里插入图片描述
这里可以看到,对于panic,是程序运行时自动判断的,整个代码也就加了个semihosting的处理,使用起来很方便。你也可以试试其他类型的panic,观察结果的不同,其实也就是不同的panic处理方式。

异常处理

异常和中断是处理器处理异步事件和致命错误(例如执行无效指令)的硬件机制。异常意味着抢占,并涉及异常处理程序,即响应触发事件的信号而执行的子例程,这就是和。是硬件上的,而panic理解成软件上的,比如访问文件不存在啊、访问越界啊、除0操作等等,而且panic一般没有那么多处理方式,没有抢占和触发panic处理程序等等。cortex-m-rt crate 库提供了一个异常属性标志#[exception]用来声明一个异常处理。
SysTick是系统定时器,能产生周期性的中断。每当中断产生时,就执行处理函数SysTick()。系统定时器要能够产生定时中断,就要先初始化,设置时钟源,设置计数值,启动计数器,开启中断。
详细代码如下:

#![deny(unsafe_code)]
#![no_main]
#![no_std]

use panic_halt as _;
use core::fmt::Write;

use cortex_m::peripheral::syst::SystClkSource;
use cortex_m_rt::{entry, exception};
use cortex_m_semihosting::{
    debug,
    hio::{self, HStdout},
};

#[entry]
fn main() -> ! {
    let p = cortex_m::Peripherals::take().unwrap();
    let mut syst = p.SYST;

    //将系统计时器配置为每秒触发一次SysTick异常
    syst.set_clock_source(SystClkSource::Core);
    syst.set_reload(12_000_000);//周期 = 1s
    syst.clear_current();
    syst.enable_counter();//使能systick计数器
    syst.enable_interrupt();//使能中断
    loop {}
}

#[exception]
fn SysTick() {
    static mut COUNT: u32 = 0;
    static mut STDOUT: Option<HStdout> = None;

    *COUNT += 1;

    // Lazy initialization
    if STDOUT.is_none() {
        *STDOUT = hio::hstdout().ok();
    }

    if let Some(hstdout) = STDOUT.as_mut() {
        write!(hstdout, "{}", *COUNT).ok();
    }
    if *COUNT == 9 {
       //qemu中运行到第九秒暂停退出
        debug::exit(debug::EXIT_SUCCESS);
    }
}

终端打印123456789后退出。

默认异常处理程序

该属性的实际作用是覆盖特定异常的默认异常处理程序。如果不重写特定异常的处理程序,则该函数将由函数处理,该函数默认为:exceptionDefaultHandler

fn DefaultHandler() {
    loop {}
}

你可以在“默认处理程序”上放置断点并捕获未处理的异常,当然也可以重写和覆盖。

硬故障处理程序

当程序进入无效状态时,将触发此异常,因此其处理程序无法返回,因为这可能导致未定义的行为。此外,运行时crate在调用用户定义的处理程序之前会执行一些工作,以提高可调试性。处理程序的参数是指向由异常推送到堆栈中的寄存器的指针。这些寄存器是触发异常时处理器状态的快照,可用于诊断硬故障。HardFaultfn(&ExceptionFrame) -> !
下面是一个执行非法操作的示例:读取不存在的内存位置。该程序在QEMU上不起作用,即它不会崩溃,因为不会检查内存负载,并且会在读取无效内存时返回。

#![no_main]
#![no_std]

use panic_halt as _;
use core::fmt::Write;
use core::ptr;
use cortex_m_rt::{entry, exception, ExceptionFrame};
use cortex_m_semihosting::hio;

#[entry]
fn main() -> ! {
    // 读取不存在的内存地址,注意写法
    unsafe {
        ptr::read_volatile(0x3FFF_FFFE as *const u32);
    }
    loop {}
}

#[exception]
fn HardFault(ef: &ExceptionFrame) -> ! {
    if let Ok(mut hstdout) = hio::hstdout() {
        writeln!(hstdout, "{:#?}", ef).ok();
    }
    loop {}
}

处理程序打印值。如果运行此命令,您将在 OpenOCD 控制台上看到类似的内容。
在这里插入图片描述
该值是发生异常时程序计数器的值,它指向触发异常的指令,所以可以看一下pc的值指向的指令就是错误的地方。
在这里插入图片描述
这里可以看到,是因为ldr指令出现错误,也就是加载读取指令。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值