diff --git a/.vscode/launch.json b/.vscode/launch.json index e015a23e07b9dd0e06c531e4eeb7f58770f1b6e6..01a0f4b7bacf13c0d0c68badaf928e8da6d945e2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -107,6 +107,31 @@ }, "cwd": "${workspaceRoot}" }, + { + "type": "cortex-debug", + "request": "launch", + "servertype": "openocd", + "name": "sleep_stop", + "executable": "./target/thumbv7em-none-eabihf/debug/examples/sleep_stop", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32f4x.cfg" + ], + "swoConfig": { + "enabled": true, + "cpuFrequency": 8000000, + "swoFrequency": 2000000, // you may try 1000000 if not working + "source": "probe", + "decoders": [ + { + "type": "console", + "label": "Name", + "port": 0 + } + ] + }, + "cwd": "${workspaceRoot}" + }, { "type": "cortex-debug", "request": "launch", diff --git a/examples/sleep_stop.rs b/examples/sleep_stop.rs new file mode 100644 index 0000000000000000000000000000000000000000..b3b5e3fe402c0f22e548177476f2eea5abd8f9a9 --- /dev/null +++ b/examples/sleep_stop.rs @@ -0,0 +1,206 @@ +//! 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() }; + #[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() + }); + // GPIOB.moder.reset(); + // GPIOA.moder.reset(); + // GPIOB.pupdr.reset(); + // GPIOA.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(); + // GPIOB.moder.write(|w| unsafe { w.bits(0) }); + // GPIOA.moder.write(|w| unsafe { w.bits(0) }); + // GPIOB.pupdr.write(|w| unsafe { w.bits(0) }); + // GPIOA.pupdr.write(|w| unsafe { w.bits(0) }); + // 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 (input mode on reset) + r.RCC + .ahb1enr + .modify(|_, w| w.gpioaen().set_bit().gpioben().set_bit()); + + let mut read: u32 = 0; + r.GPIOA.pupdr.modify(|r, w| { + read = r.bits(); + w.pupdr1().pull_up() + }); + #[allow(non_snake_case)] + let GPIOA = unsafe { &*stm32f40x::GPIOA::ptr() }; + + asm::nop(); + + // 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(); + 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); + + 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(); +}