diff --git a/CHANGELOG.md b/CHANGELOG.md index 42c5a602..6da84340 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - RCC `Bus` trait + private `Enable` and `Reset` traits +- Added `micros_since` and `reset` methods to timer +- Added `select_frequency` method to RTC ### Breaking changes - Change timer/pwm init API - Remove `set_low` and `set_high` for pins in Alternate output mode +- Renames `set_seconds` and `seconds` methods on RTC to `set_time` and `current_time`, respectively +- Starting the timer does not generate interrupt requests anymore ### Changed @@ -38,7 +42,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Replace gpio traits with digital::v2 - Bump `stm32f1` dependency (`0.8.0`) -- ADC now requires the clock configuration for intialisation +- ADC now requires the clock configuration for initialisation - `disable_jtag` now transforms PA15, PB3 and PB4 to forbid their use without desactivating JTAG ### Changed diff --git a/examples/blinky_rtc.rs b/examples/blinky_rtc.rs index 08ad7ecc..c9f37078 100644 --- a/examples/blinky_rtc.rs +++ b/examples/blinky_rtc.rs @@ -42,7 +42,7 @@ fn main() -> ! { let mut led_on = false; loop { // Set the current time to 0 - rtc.set_seconds(0); + rtc.set_time(0); // Trigger the alarm in 5 seconds rtc.set_alarm(5); block!(rtc.wait_alarm()).unwrap(); diff --git a/examples/pwm_input.rs b/examples/pwm_input.rs index b24231e0..e4c323ed 100644 --- a/examples/pwm_input.rs +++ b/examples/pwm_input.rs @@ -32,7 +32,7 @@ fn main() -> ! { let (_pa15, _pb3, pb4) = afio.mapr.disable_jtag(gpioa.pa15, gpiob.pb3, gpiob.pb4); let pb5 = gpiob.pb5; - let mut pwm_input = Timer::tim3(p.TIM3, &clocks, &mut rcc.apb1) + let pwm_input = Timer::tim3(p.TIM3, &clocks, &mut rcc.apb1) .pwm_input( (pb4, pb5), &mut afio.mapr, diff --git a/examples/rtc.rs b/examples/rtc.rs index 977b5c5c..538bfb75 100644 --- a/examples/rtc.rs +++ b/examples/rtc.rs @@ -26,6 +26,6 @@ fn main() -> ! { let rtc = Rtc::rtc(p.RTC, &mut backup_domain); loop { - hprintln!("time: {}", rtc.seconds()).unwrap(); + hprintln!("time: {}", rtc.current_time()).unwrap(); } } diff --git a/examples/serial_config.rs b/examples/serial_config.rs index 1d089568..cac7853d 100644 --- a/examples/serial_config.rs +++ b/examples/serial_config.rs @@ -14,7 +14,6 @@ use stm32f1xx_hal::{ prelude::*, pac, serial::{self, Serial}, - timer::Timer, }; use cortex_m_rt::entry; diff --git a/src/rtc.rs b/src/rtc.rs index a12d66d6..fafac68a 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -15,6 +15,7 @@ use crate::pac::{RTC, RCC}; use crate::backup_domain::BackupDomain; +use crate::time::Hertz; use nb; use void::Void; @@ -30,7 +31,6 @@ pub struct Rtc { regs: RTC, } - impl Rtc { /** Initialises the RTC. The `BackupDomain` struct is created by @@ -70,11 +70,27 @@ impl Rtc { }) } - /// Set the current rtc value to the specified amount of seconds - pub fn set_seconds(&mut self, seconds: u32) { + /// Selects the frequency of the RTC Timer + /// NOTE: Maximum frequency of 16384 Hz using the internal LSE + pub fn select_frequency(&mut self, timeout: impl Into) { + let frequency = timeout.into().0; + + // The manual says that the zero value for the prescaler is not recommended, thus the + // minimum division factor is 2 (prescaler + 1) + assert!(frequency <= LSE_HERTZ / 2); + + let prescaler = LSE_HERTZ / frequency - 1; + self.perform_write( |s| { + s.regs.prlh.write(|w| unsafe { w.bits(prescaler >> 16) }); + s.regs.prll.write(|w| unsafe { w.bits(prescaler as u16 as u32) }); + }); + } + + /// Set the current RTC counter value to the specified amount + pub fn set_time(&mut self, counter_value: u32) { self.perform_write(|s| { - s.regs.cnth.write(|w| unsafe{w.bits(seconds >> 16)}); - s.regs.cntl.write(|w| unsafe{w.bits(seconds as u16 as u32)}); + s.regs.cnth.write(|w| unsafe{w.bits(counter_value >> 16)}); + s.regs.cntl.write(|w| unsafe{w.bits(counter_value as u16 as u32)}); }); } @@ -83,10 +99,10 @@ impl Rtc { This also clears the alarm flag if it is set */ - pub fn set_alarm(&mut self, seconds: u32) { + pub fn set_alarm(&mut self, counter_value: u32) { // Set alarm time // See section 18.3.5 for explanation - let alarm_value = seconds - 1; + let alarm_value = counter_value - 1; self.perform_write(|s| { s.regs.alrh.write(|w| unsafe{w.alrh().bits((alarm_value >> 16) as u16)}); s.regs.alrl.write(|w| unsafe{w.alrl().bits(alarm_value as u16)}); @@ -111,8 +127,8 @@ impl Rtc { }) } - /// Reads the current time - pub fn seconds(&self) -> u32 { + /// Reads the current counter + pub fn current_time(&self) -> u32 { // Wait for the APB1 interface to be ready while self.regs.crl.read().rsf().bit() == false {} diff --git a/src/timer.rs b/src/timer.rs index 6711c369..d90f1261 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -2,7 +2,7 @@ use crate::hal::timer::{CountDown, Periodic}; use crate::pac::{DBGMCU as DBG, TIM1, TIM2, TIM3, TIM4}; -use cast::{u16, u32}; +use cast::{u16, u32, u64}; use cortex_m::peripheral::syst::SystClkSource; use cortex_m::peripheral::SYST; use nb; @@ -43,6 +43,10 @@ impl Timer { timer.start(timeout); timer } + + pub fn release(self) -> SYST { + self.tim + } } impl CountDownTimer { @@ -60,15 +64,36 @@ impl CountDownTimer { } } + /// Resets the counter + pub fn reset(&mut self) { + // According to the Cortex-M3 Generic User Guide, the interrupt request is only generated + // when the counter goes from 1 to 0, so writing zero should not trigger an interrupt + self.tim.clear_current(); + } + + /// Returns the number of microseconds since the last update event. + /// *NOTE:* This method is not a very good candidate to keep track of time, because + /// it is very easy to lose an update event. + pub fn micros_since(&self) -> u32 { + let reload_value = SYST::get_reload(); + let timer_clock = u64(self.clk.0); + let ticks = u64(reload_value - SYST::get_current()); + + // It is safe to make this cast since the maximum ticks is (2^24 - 1) and the minimum sysclk + // is 4Mhz, which gives a maximum period of ~4.2 seconds which is < (2^32 - 1) microsenconds + u32(1_000_000 * ticks / timer_clock).unwrap() + } + /// Stops the timer - pub fn stop(&mut self) { + pub fn stop(mut self) -> Timer { self.tim.disable_counter(); + let Self {tim, clk} = self; + Timer {tim, clk} } /// Releases the SYST - pub fn release(mut self) -> SYST { - self.stop(); - self.tim + pub fn release(self) -> SYST { + self.stop().release() } } @@ -157,8 +182,7 @@ macro_rules! hal { } /// Stops the timer - pub fn stop(mut self) -> Timer<$TIMX> { - self.unlisten(Event::Update); + pub fn stop(self) -> Timer<$TIMX> { self.tim.cr1.modify(|_, w| w.cen().clear_bit()); let Self { tim, clk } = self; Timer { tim, clk } @@ -173,6 +197,33 @@ macro_rules! hal { pub fn release(self) -> $TIMX { self.stop().release() } + + /// Returns the number of microseconds since the last update event. + /// *NOTE:* This method is not a very good candidate to keep track of time, because + /// it is very easy to lose an update event. + pub fn micros_since(&self) -> u32 { + let timer_clock = self.clk.0; + let psc = u32(self.tim.psc.read().psc().bits()); + + // freq_divider is always bigger than 0, since (psc + 1) is always less than + // timer_clock + let freq_divider = u64(timer_clock / (psc + 1)); + let cnt = u64(self.tim.cnt.read().cnt().bits()); + + // It is safe to make this cast, because the maximum timer period in this HAL is + // 1s (1Hz), then 1 second < (2^32 - 1) microseconds + u32(1_000_000 * cnt / freq_divider).unwrap() + } + + /// Resets the counter + pub fn reset(&mut self) { + // Sets the URS bit to prevent an interrupt from being triggered by + // the UG bit + self.tim.cr1.modify(|_, w| w.urs().set_bit()); + + self.tim.egr.write(|w| w.ug().set_bit()); + self.tim.cr1.modify(|_, w| w.urs().clear_bit()); + } } impl CountDown for CountDownTimer<$TIMX> { @@ -197,11 +248,7 @@ macro_rules! hal { self.tim.arr.write(|w| w.arr().bits(arr) ); // Trigger an update event to load the prescaler value to the clock - self.tim.egr.write(|w| w.ug().set_bit()); - // The above line raises an update event which will indicate - // that the timer is already finished. Since this is not the case, - // it should be cleared - self.clear_update_interrupt_flag(); + self.reset(); // start counter self.tim.cr1.modify(|_, w| w.cen().set_bit());