//! 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; fn analog_input() { #[allow(non_snake_case)] let GPIOC = unsafe { &*stm32f40x::GPIOC::ptr() }; #[allow(non_snake_case)] let GPIOD = unsafe { &*stm32f40x::GPIOD::ptr() }; #[allow(non_snake_case)] let GPIOE = unsafe { &*stm32f40x::GPIOE::ptr() }; // #[allow(non_snake_case)] // let GPIOF = unsafe { &*stm32f40x::GPIOF::ptr() }; // #[allow(non_snake_case)] // let GPIOG = unsafe { &*stm32f40x::GPIOG::ptr() }; // #[allow(non_snake_case)] // let GPIOH = unsafe { &*stm32f40x::GPIOH::ptr() }; // #[allow(non_snake_case)] // let GPIOI = unsafe { &*stm32f40x::GPIOI::ptr() }; let r1 = GPIOC.moder.read().bits(); GPIOC.moder.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); // analog mode GPIOD.moder.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); // analog mode GPIOE.moder.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); // analog mode // GPIOF.moder.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); // analog mode // GPIOG.moder.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); // analog mode // GPIOH.moder.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); // analog mode // GPIOI.moder.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); // analog mode let r2 = GPIOC.moder.read().bits(); // asm::bkpt(); } // 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() }; #[allow(non_snake_case)] let GPIOA = unsafe { &*stm32f40x::GPIOA::ptr() }; #[allow(non_snake_case)] let GPIOB = unsafe { &*stm32f40x::GPIOB::ptr() }; dbg.dbgmcu_cr.modify(|_, w| { w.dbg_sleep() .set_bit() .dbg_stop() .set_bit() .dbg_standby() .set_bit() .trace_ioen() .set_bit() }); GPIOA.moder.reset(); GPIOA.pupdr.reset(); GPIOB.moder.reset(); GPIOB.pupdr.reset(); } // set the MCU in true sleepdeep mode on WFI/WFE // debugging is disabled (until re-enabled) fn dbg_disable() { #[allow(non_snake_case)] let DBG = unsafe { &*stm32f40x::DBG::ptr() }; #[allow(non_snake_case)] let GPIOA = unsafe { &*stm32f40x::GPIOA::ptr() }; #[allow(non_snake_case)] let GPIOB = unsafe { &*stm32f40x::GPIOB::ptr() }; asm::nop(); DBG.dbgmcu_cr.modify(|_, w| { w.dbg_sleep() .clear_bit() .dbg_stop() .clear_bit() .dbg_standby() .clear_bit() .trace_ioen() .clear_bit() }); // set all gpio to analog input // let _a_moder_ = GPIOA.moder.read().bits(); // let _b_moder_ = GPIOB.moder.read().bits(); // let _a_pupdr_ = GPIOA.pupdr.read().bits(); // let _b_pupdr_ = GPIOB.pupdr.read().bits(); // 0.142 mA without manipulating GPIO/GPIOB GPIOA.moder.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); // PA, analog GPIOA.moder.modify(|_, w| w.moder1().input_mode()); // PA1, input_mode GPIOB.pupdr.write(|w| unsafe { w.bits(0) }); // PA, floating GPIOA.pupdr.modify(|_, w| w.pupdr1().pull_up()); // PA1, pull up GPIOB.moder.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); // PB, analog GPIOB.pupdr.write(|w| unsafe { w.bits(0) }); // PB, floating // let _a_moder = GPIOA.moder.read().bits(); // let _b_moder = GPIOB.moder.read().bits(); // let _a_pupdr = GPIOA.pupdr.read().bits(); // let _b_pupdr = GPIOB.pupdr.read().bits(); asm::nop(); } // 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, gpiob r.RCC.ahb1enr.modify(|_, w| { w.gpioaen() .set_bit() .gpioben() .set_bit() .gpiocen() .set_bit() .gpioden() .set_bit() .gpioeen() .set_bit() .gpiofen() .set_bit() }); // asm::bkpt(); analog_input(); // asm::bkpt(); let mut read: u32 = 0; // PA1 pull up r.GPIOA.pupdr.modify(|r, w| w.pupdr1().pull_up()); #[allow(non_snake_case)] let GPIOA = unsafe { &*stm32f40x::GPIOA::ptr() }; asm::nop(); // SYSCFG.exti1 is PAx (0000) by reset, so we don't change it // 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()); // clear triggering event r.EXTI.pr.modify(|_, w| w.pr1().set_bit()); p.SCB.set_sleepdeep(); // enable pwr r.RCC.apb1enr.modify(|_, w| w.pwren().set_bit()); // set standby mode // r.PWR // .cr // .write(|w| unsafe { w.bits((1 << 10) | 0x0000_8000) }); r.PWR .cr .modify(|_, w| w.pdds().clear_bit().lpds().set_bit().fpds().set_bit()); // r.PWR // .cr // .modify(|_, w| w.pdds().clear_bit().lpds().clear_bit()); // blink(); // p.NVIC.set_pending(Interrupt::EXTI1); asm::bkpt(); 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 // 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(); } GPIOA.bsrr.write(|w| w.bs5().set_bit()); for _ in 0..1000 { asm::nop(); } } GPIOA.moder.modify(|_, w| w.moder5().input_mode()); // RCC.ahb1enr.modify(|_, w| w.gpioaen().clear_bit()); } // bind the EXTI1 handler interrupt!(EXTI1, exti1); // 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(); }