Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve time keeping with Timer and RTC #94

Merged
merged 6 commits into from
Aug 31, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

- 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

- DMA traits now require AsSlice instead of AsRef
- Add `micros_since` and `reset` methods to timer
- Add `select_frequency` method to RTC

## [v0.4.0] - 2019-08-09

Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/blinky_rtc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion examples/pwm_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion examples/rtc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
1 change: 0 additions & 1 deletion examples/serial_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use stm32f1xx_hal::{
prelude::*,
pac,
serial::{self, Serial},
timer::Timer,
};
use cortex_m_rt::entry;

Expand Down
26 changes: 21 additions & 5 deletions src/rtc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use crate::pac::{RTC, RCC};

use crate::backup_domain::BackupDomain;
use crate::time::Hertz;

use nb;
use void::Void;
Expand All @@ -30,7 +31,6 @@ pub struct Rtc {
regs: RTC,
}


impl Rtc {
/**
Initialises the RTC. The `BackupDomain` struct is created by
Expand Down Expand Up @@ -70,8 +70,24 @@ 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 counter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, this comment is a bit hard to understand without knowlege of the RTC because it's not clear what counter is. Perhaps change it to Selects the frequency of the timer, though that doesn't feel quite right to me either

/// NOTE: Maximum frequency of 16384 Hz using the internal LSE
pub fn select_frequency(&mut self, timeout: impl Into<Hertz>) {
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, seconds: 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)});
Expand Down Expand Up @@ -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 {}

Expand Down
71 changes: 59 additions & 12 deletions src/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -43,6 +43,10 @@ impl Timer<SYST> {
timer.start(timeout);
timer
}

pub fn release(self) -> SYST {
self.tim
}
}

impl CountDownTimer<SYST> {
Expand All @@ -60,15 +64,36 @@ impl CountDownTimer<SYST> {
}
}

/// Resets the timer
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<SYST> {
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()
}
}

Expand Down Expand Up @@ -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 }
Expand All @@ -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 and generates an update event
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment out of date, if I'm reading the function correctly, it does not generate an update event

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, sorry, probably missed that

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> {
Expand All @@ -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());
Expand Down