Skip to content
Snippets Groups Projects
Select Git revision
  • master default protected
1 result

sleep.rs

Blame
  • sleep.rs 4.50 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() };
        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();
    }