在Rust cortex-m-rt框架中,Systick异常处理相关实现疑问:调用特权线程模式main()及异常中#[entry]的使用
在Rust cortex-m-rt框架中,Systick异常处理相关实现疑问:调用特权线程模式main()及异常中#[entry]的使用
首先,先帮你梳理下当前的问题,再一步步给出解决方案:
一、先点评下你的SysTick初始化代码
你的systick_init逻辑大体是对的,但有个小细节需要注意:
你用固定值0x3e8(也就是1000)作为syst_calib来计算重载值,这个思路没问题(本质是把毫秒转成时钟周期数),但要确保DefaultCPUClock是你当前STM32H723核心的实际运行频率(比如HCLK如果是400MHz,那这个值要设为400_000_000)。另外,SysTick的COUNTFLAG需要在中断中手动读取来清除,否则可能会重复触发中断,这个后面会提到。
二、如何实现SysTick的#[exception]处理函数
在cortex-m-rt框架中,你只需要用#[exception]宏标记名为SysTick的函数,就能作为SysTick中断的处理入口。不过要注意:异常处理函数本身是在特权级的Handler模式下运行的,如果你的需求是切换到特权级线程模式执行main(),直接在异常里调用会让main()跑在Handler模式下,这可能不是你想要的——更合理的方式是用"中断触发标志+主循环轮询"的方式,让main()在特权线程模式下执行。
完整代码示例
下面是一个可运行的参考实现:
use cortex_m::{asm, peripheral::SYST}; use cortex_m_rt::{entry, exception}; use core::sync::atomic::{AtomicBool, Ordering}; use stm32h7xx_hal::pac; // 假设你用stm32h7xx-hal库,也可以直接用pac // 定义核心时钟频率,根据你的实际配置修改 const DEFAULT_CPU_CLOCK: u32 = 400_000_000; // 400MHz,STM32H723的典型主频 // 原子标志:用于通知主循环SysTick中断已触发 static TICK_TRIGGERED: AtomicBool = AtomicBool::new(false); #[entry] fn entry() -> ! { // 获取外设控制权 let dp = pac::Peripherals::take().unwrap(); let mut syst = dp.SYST; // 初始化SysTick,设置50ms中断 systick_init(&mut syst, 50); // 这里可以添加其他硬件初始化(比如GPIO、UART等) // 主循环:在特权线程模式下等待中断触发,执行main任务 loop { // 进入低功耗模式,等待中断唤醒 asm::wfi(); // 检查中断标志,如果触发则执行main if TICK_TRIGGERED.swap(false, Ordering::Acquire) { main(); } } } #[exception] fn SysTick() { // 清除SysTick中断标志(必须读取COUNTFLAG来清除) let _ = SYST::get_countflag(); // 设置触发标志,通知主循环执行任务 TICK_TRIGGERED.store(true, Ordering::Release); } // 你的main函数:运行在特权线程模式下 fn main() { // 这里写你需要周期性执行的代码,比如LED翻转、数据处理等 // 示例:翻转LED(假设你已经初始化了LED引脚) // led.toggle(); } // 你的SysTick初始化函数 fn systick_init(syst: &mut pac::SYST, time_ms: u32) { // 计算重载值:(时钟频率 * 毫秒数 / 1000) - 1 let reload_val = (DEFAULT_CPU_CLOCK * time_ms / 1000) - 1; // 配置SysTick syst.set_reload(reload_val); syst.set_clock_source(cortex_m::peripheral::syst::SystClkSource::Core); syst.enable_interrupt(); syst.enable_counter(); }
三、关键注意事项
- 特权线程模式的保证:
#[entry]标记的入口函数默认就是在特权级线程模式下运行的,所以主循环里调用的main()自然也是在这个模式下执行,完全符合你的需求。 - 原子变量的使用:用
AtomicBool而不是普通的静态可变变量,是为了避免中断和主循环同时访问变量导致的竞态条件,保证线程安全。 - 中断标志的清除:SysTick的中断标志必须通过读取
COUNTFLAG来清除,否则会导致中断重复触发,这个步骤一定不能忘。 - 异常处理函数的代码长度:尽量不要在
SysTick异常处理函数里写复杂逻辑,保持它的执行时间极短,避免影响其他中断的响应——所以用标志通知主循环执行任务是更优的设计。
内容来源于stack exchange
相关产品推荐
相关产品推荐

