Select Git revision
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();
}