diff --git a/examples/blinky-rtc.rs b/examples/blinky-rtc.rs new file mode 100644 index 0000000000000000000000000000000000000000..c774c3b524b0310859ed278a62af1a6036499cd5 --- /dev/null +++ b/examples/blinky-rtc.rs @@ -0,0 +1,62 @@ +//! Blinks the user LED using RTC +#![deny(unsafe_code)] +#![deny(warnings)] +#![feature(proc_macro)] +#![no_std] + +extern crate blue_pill; +extern crate cortex_m; +extern crate cortex_m_rtfm as rtfm; + +use blue_pill::Rtc; +use blue_pill::rtc::{RtcClkSource, RtcEvent}; +use blue_pill::led::{self, Green}; +use blue_pill::stm32f103xx::Interrupt; +use rtfm::{app, Threshold}; + +app! { + device: blue_pill::stm32f103xx, + + resources: { + static ON: bool = false; + }, + + tasks: { + RTC: { + path: toggle, + resources: [ON, RTC], + }, + }, +} + +fn init(p: init::Peripherals, _r: init::Resources) { + led::init(p.GPIOC, p.RCC); + + let rtc = Rtc(p.RTC); + + // ~40kHz clock + rtc.init(RtcClkSource::LSI, 40000, p.RCC, p.PWR); + rtc.listen(RtcEvent::Second); + p.NVIC.enable(Interrupt::RTC); +} + +fn idle() -> ! { + // Sleep + loop { + rtfm::wfi(); + } +} + +// TASKS +fn toggle(_t: &mut Threshold, r: RTC::Resources) { + let rtc = Rtc(&**r.RTC); + rtc.clear_flag(RtcEvent::Second); + + **r.ON = !**r.ON; + + if **r.ON { + Green.on(); + } else { + Green.off(); + } +} diff --git a/examples/blinky.rs b/examples/blinky.rs index 4020fae4597487c4ec2021ca3f7ee60b91d3a80d..6f87fb573d41f026e88c365d664d2fe4076a5ad5 100644 --- a/examples/blinky.rs +++ b/examples/blinky.rs @@ -1,4 +1,4 @@ -//! Blinks the user LED +//! Blinks the user LED using sysclk #![deny(unsafe_code)] #![deny(warnings)] #![feature(proc_macro)] diff --git a/src/lib.rs b/src/lib.rs index 515bbcbfd7d8e3558d6f830717895d0c05fca144..a52cca657618b358a43785bebf7644c091171e33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,7 @@ pub mod gpio; pub mod led; pub mod pwm; pub mod qei; +pub mod rtc; pub mod serial; pub mod spi; pub mod time; @@ -41,6 +42,7 @@ pub use hal::prelude; pub use capture::Capture; pub use pwm::Pwm; pub use qei::Qei; +pub use rtc::Rtc; pub use serial::Serial; pub use spi::Spi; pub use timer::{Channel, Timer}; diff --git a/src/rtc.rs b/src/rtc.rs new file mode 100644 index 0000000000000000000000000000000000000000..8953017215a6969d8909fe7dde1d9faa7dba07eb --- /dev/null +++ b/src/rtc.rs @@ -0,0 +1,124 @@ +//! Real Time Clock + +use stm32f103xx::{RCC, PWR, RTC}; + +/// RTC clock source +#[derive(Clone, Copy, Debug)] +pub enum RtcClkSource { + /// No clock + NoClock = 0b00, + /// LSE oscillator clock (32.768 kHz), + LSE = 0b01, + /// LSI oscillator clock (~ 40kHz) + LSI = 0b10, + /// HSE oscillator clock (4-16 MHz) divided by 128 + HSE = 0b11, +} + +/// RTC interrupt event +pub enum RtcEvent { + /// "Second" Tick Interrupt + Second, + /// Alarm + Alarm, + /// Overflow + Overflow, +} + +/// RTC +pub struct Rtc<'a>(pub &'a RTC); + +impl<'a> Rtc<'a> { + /// Initializes RTC + pub fn init(&self, clk_source: RtcClkSource, prescalar: u32, rcc: &RCC, pwr: &PWR) { + let rtc = self.0; + + // Enable backup and power interface clocks + rcc.apb1enr.modify(|_, w| w.bkpen().enabled().pwren().enabled()); + + // unprotect backup and RTC registers + pwr.cr.modify(|_, w| w.dbp().set_bit()); + + // Reset backup domain to allow changing clock source + rcc.bdcr.modify(|_, w| w.bdrst().set_bit()); + rcc.bdcr.modify(|_, w| w.bdrst().clear_bit()); + + match clk_source { + // Turn on clock source and wait for it to be ready + RtcClkSource::LSE => { + rcc.bdcr.modify(|_, w| w.lseon().set_bit()); + while rcc.bdcr.read().lserdy().bit_is_clear() { }; + }, + RtcClkSource::LSI => { + rcc.csr.modify(|_, w| w.lsion().set_bit()); + while rcc.csr.read().lsirdy().bit_is_clear() { }; + }, + RtcClkSource::HSE => { + rcc.cr.modify(|_, w| w.hseon().set_bit()); + while rcc.cr.read().hserdy().bit_is_clear() { }; + }, + RtcClkSource::NoClock => { } + } + + // NOTE once set, the clock source cannot be changed until the backup domain is reset + rcc.bdcr.modify(|_, w| unsafe { w.rtcsel().bits(clk_source as u8) }); + + // enable RTC clock + rcc.bdcr.modify(|_, w| w.rtcen().set_bit()); + + // wait for RTC register sync + while rtc.crl.read().rsf().bit_is_clear() { }; + + while rtc.crl.read().rtoff().bit_is_clear() { }; + // enter config mode + rtc.crl.modify(|_, w| w.cnf().set_bit()); + + rtc.prlh.write(|w| unsafe { w.bits(prescalar >> 16) }); + rtc.prll.write(|w| unsafe { w.bits(prescalar) }); + + // reset counter value + rtc.cnth.write(|w| unsafe { w.bits(0 >> 16) }); + rtc.cntl.write(|w| unsafe { w.bits(0) }); + + // leave config mode + rtc.crl.modify(|_, w| w.cnf().clear_bit()); + while rtc.crl.read().rtoff().bit_is_clear() { }; + } + + /// Starts listening for an interrupt `event` + pub fn listen(&self, event: RtcEvent) { + let rtc = self.0; + + // cannot write to RTC_CRH when RTOFF = 0 + while rtc.crl.read().rtoff().bit_is_clear() { }; + match event { + RtcEvent::Second => rtc.crh.modify(|_, w| w.secie().set_bit()), + RtcEvent::Alarm => rtc.crh.modify(|_, w| w.alrie().set_bit()), + RtcEvent::Overflow => rtc.crh.modify(|_, w| w.owie().set_bit()), + } + } + + /// Stops listening for an interrupt `event` + pub fn unlisten(&self, event: RtcEvent) { + let rtc = self.0; + + // cannot write to RTC_CRH when RTOFF = 0 + while rtc.crl.read().rtoff().bit_is_clear() { }; + match event { + RtcEvent::Second => rtc.crh.modify(|_, w| w.secie().clear_bit()), + RtcEvent::Alarm => rtc.crh.modify(|_, w| w.alrie().clear_bit()), + RtcEvent::Overflow => rtc.crh.modify(|_, w| w.owie().clear_bit()), + } + } + + /// Clear interrupt `event` flag + pub fn clear_flag(&self, event: RtcEvent) { + let rtc = self.0; + + match event { + RtcEvent::Second => rtc.crl.modify(|_, w| w.secf().clear_bit()), + RtcEvent::Alarm => rtc.crl.modify(|_, w| w.alrf().clear_bit()), + RtcEvent::Overflow => rtc.crl.modify(|_, w| w.owf().clear_bit()), + } + } +}