//! Sleep mode example #![no_main] #![no_std] extern crate cortex_m; #[macro_use(interrupt)] extern crate stm32f40x; use stm32f40x::Interrupt; #[macro_use(entry, exception)] extern crate cortex_m_rt as rt; extern crate panic_abort; use cortex_m::asm; use rt::ExceptionFrame; use cortex_m::peripheral::Peripherals; // set the MCU in debug sleepdeep mode on WFI/WFE // debugging is possible even if sleeping fn dbg_enable() { let dbg = unsafe { &*stm32f40x::DBG::ptr() }; dbg.dbgmcu_cr.modify(|_, w| { w.dbg_sleep() .set_bit() .dbg_stop() .set_bit() .dbg_standby() .set_bit() .trace_ioen() .set_bit() }); } // set the MCU in true sleepdeep mode on WFI/WFE // debugging is disabled (until re-enabled) fn dbg_disable() { let dbg = unsafe { &*stm32f40x::DBG::ptr() }; dbg.dbgmcu_cr.modify(|_, w| { w.dbg_sleep() .clear_bit() .dbg_stop() .clear_bit() .dbg_standby() .clear_bit() .trace_ioen() .clear_bit() }); } // the program entry point is ... entry!(main); // ... this never ending function fn main() -> ! { let r = stm32f40x::Peripherals::take().unwrap(); let mut p = Peripherals::take().unwrap(); // enable the EXTI1 interrupt p.NVIC.enable(Interrupt::EXTI1); // enable gpioa (input mode on reset) r.RCC.ahb1enr.modify(|_, w| w.gpioaen().set_bit()); r.GPIOA.pupdr.modify(|_, w| w.pupdr1().pull_up()); // SYSCFG.exti1 is PAx (0000) by reset // enbable masking of EXTI line 1 r.EXTI.imr.modify(|_, w| w.mr1().set_bit()); // trigger on falling edge r.EXTI.ftsr.modify(|_, w| w.tr1().set_bit()); asm::bkpt(); blink(); // p.NVIC.set_pending(Interrupt::EXTI1); loop { asm::nop(); // put gdb breakpoint here, the MCU and gdb will detect breakpoint dbg_disable(); asm::wfi(); dbg_enable(); asm::nop(); // put gdb breakpoint here, in debug mode its caught twice // first occation (on wakeup), gdb does NOT report span correctly, // second occation, gdb DEOS report span correctly asm::bkpt(); // here the MCU halt, and gdb detects the asm breakpoint // gdb continue, will catch the gdb breakpoint at "dbg_disable" // this is good as it shows that disabling and re-enabling gdb debugging works // even if the gdb communictation has been disrputed and re-estabished, nice! } } // the dbg_sleep, and dbg_stop both set deos not prohibit sleep mode debugging (error?) // the bdg_standby set further reduces power, and prohibits debugging // the behavior seems not to comply with the documentation // as expected debugging release code puts the gdb breakpoint at the wrong position // as expected the initial breakpoint after wakeup deos not register in gdb correctly // also the blink loop becomes to fast to observe // bind the EXTI1 handler interrupt!(EXTI1, exti1); // unsafe version where we access GPIOA through the (raw) pointer fn blink() { #[allow(non_snake_case)] let GPIOA = unsafe { &*stm32f40x::GPIOA::ptr() }; #[allow(non_snake_case)] let RCC = unsafe { &*stm32f40x::RCC::ptr() }; RCC.ahb1enr.modify(|_, w| w.gpioaen().set_bit()); GPIOA.moder.modify(|_, w| w.moder5().output_mode()); for _ in 0..10 { GPIOA.bsrr.write(|w| w.br5().set_bit()); for _ in 0..1000 { asm::nop(); } // wait_cycles(p, 1000_000); GPIOA.bsrr.write(|w| w.bs5().set_bit()); for _ in 0..1000 { asm::nop(); } // wait_cycles(p, 1000_000); } GPIOA.moder.modify(|_, w| w.moder5().input_mode()); RCC.ahb1enr.modify(|_, w| w.gpioaen().clear_bit()); } // the exti1 interrupt implementation fn exti1() { blink(); // it will take some time so bounces are likely gone // // let's try to "fake" access to GPIOA // // let g = stm32f40x::GPIOA { // // _marker: core::marker::PhantomData, <- the field is private, so we cannot // // }; // asm::bkpt(); // clear pending state let exti = unsafe { &*stm32f40x::EXTI::ptr() }; exti.pr.write(|w| w.pr1().set_bit()); } // define the hard fault handler exception!(HardFault, hard_fault); fn hard_fault(_ef: &ExceptionFrame) -> ! { asm::bkpt(); loop {} } // define the default exception handler exception!(*, default_handler); fn default_handler(_irqn: i16) { asm::bkpt(); }