Skip to content
Snippets Groups Projects
Select Git revision
  • 809b11dfaedf43911d91953af258a3e59db5c621
  • master default protected
2 results

sleep_stop.rs

Blame
  • sleep_stop.rs 6.16 KiB
    //! 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();
    }