//! Timers in RTIC with low power mode. //! //! After the end of init the CPU transitions into CStop mode, and D1/D2 //! (aka. CD) transition into DStop mode. //! //! However we set the run_d3/run_srd flag, and enable Autonomous mode on the //! LPTIM3 PREC struture. Therefore LPTIM3 continues to run and fires an //! interrupt that wakes the core. Following each interrupt the core returns to //! CStop mode. //! //! On the first rising edge on PC13, the EXTI interrupt fires. We do not clear //! this interrupt, so we loop in the handler forever. //! //! Nb. on dual core parts you will also need to ensure that CPU2 transitions to //! DStop mode. //! #![deny(warnings)] #![deny(unsafe_code)] #![no_std] #![no_main] extern crate rtic; #[macro_use] mod utilities; #[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true)] mod app { use stm32h7xx_hal::gpio::gpioc::{PC2, PC3, PC4}; use stm32h7xx_hal::gpio::{Edge, ExtiPin, Output, PushPull}; use stm32h7xx_hal::prelude::*; use stm32h7xx_hal::rcc::LowPowerMode; use stm32h7xx_hal::stm32::{LPTIM3, TIM1, TIM2}; use stm32h7xx_hal::time::MilliSeconds; use stm32h7xx_hal::timer::{Enabled, Event, LpTimer, Timer}; use super::*; #[shared] struct SharedResources {} #[local] struct LocalResources { led1: PC2>, led2: PC3>, led3: PC4>, timer1: Timer, timer2: Timer, timer3: LpTimer, } #[init] fn init( ctx: init::Context, ) -> (SharedResources, LocalResources, init::Monotonics) { utilities::logger::init(); let mut syscfg = ctx.device.SYSCFG; // Run D3 / SRD domain #[cfg(not(feature = "rm0455"))] ctx.device.PWR.cpucr.modify(|_, w| w.run_d3().set_bit()); #[cfg(feature = "rm0455")] // 7b3/7a3/7b0 parts ctx.device.PWR.cpucr.modify(|_, w| w.run_srd().set_bit()); let pwr = ctx.device.PWR.constrain(); let vos = example_power!(pwr).freeze(); // RCC let rcc = ctx.device.RCC.constrain(); let ccdr = rcc // D3 / SRD domain .hclk(16.MHz()) // rcc_hclk4 .pclk4(4.MHz()) // rcc_pclk4 .freeze(vos, &syscfg); // Timers let mut timer1 = ctx.device.TIM1.timer( MilliSeconds::from_ticks(250).into_rate(), // Run in CSleep, but not CStop ccdr.peripheral.TIM1.low_power(LowPowerMode::Enabled), &ccdr.clocks, ); timer1.listen(Event::TimeOut); let mut timer2 = ctx.device.TIM2.timer( MilliSeconds::from_ticks(500).into_rate(), // Run in CSleep, but not CStop ccdr.peripheral.TIM2.low_power(LowPowerMode::Enabled), &ccdr.clocks, ); timer2.listen(Event::TimeOut); let mut timer3 = ctx .device .LPTIM3 .timer( MilliSeconds::from_ticks(1000).into_rate(), // Run in LPTIM in D3 / SRD autonomous mode ccdr.peripheral.LPTIM3.low_power(LowPowerMode::Autonomous), &ccdr.clocks, ) .pause(); timer3.listen(Event::TimeOut); let timer3 = timer3.resume(); // GPIO let gpioc = ctx.device.GPIOC.split(ccdr.peripheral.GPIOC); // Enter CStop mode let mut scb = ctx.core.SCB; scb.set_sleepdeep(); // Wakeup let mut exti = ctx.device.EXTI; let mut wakeup = gpioc.pc13.into_floating_input(); wakeup.make_interrupt_source(&mut syscfg); wakeup.trigger_on_edge(&mut exti, Edge::Rising); wakeup.enable_interrupt(&mut exti); // LEDs let mut led1 = gpioc.pc2.into_push_pull_output(); let mut led2 = gpioc.pc3.into_push_pull_output(); let mut led3 = gpioc.pc4.into_push_pull_output(); led1.set_high(); led2.set_high(); led3.set_high(); ( SharedResources {}, LocalResources { led1, led2, led3, timer1, timer2, timer3, }, init::Monotonics(), ) } // RTIC inserts a default idle loop that calls asm::wfi() #[task(binds = EXTI15_10, local = [], priority = 1)] fn exti15_10_interrupt(_: exti15_10_interrupt::Context) { // Once the wakeup is triggered, we loop here forever } #[task(binds = TIM1_UP, local = [led1, timer1], priority = 2)] fn timer1_tick(ctx: timer1_tick::Context) { ctx.local.timer1.clear_irq(); ctx.local.led1.toggle(); } #[task(binds = TIM2, local = [led2, timer2], priority = 2)] fn timer2_tick(ctx: timer2_tick::Context) { ctx.local.timer2.clear_irq(); ctx.local.led2.toggle(); } #[task(binds = LPTIM3, local = [led3, timer3], priority = 2)] fn timer3_tick(ctx: timer3_tick::Context) { ctx.local.timer3.clear_irq(); ctx.local.led3.toggle(); } }