-
Notifications
You must be signed in to change notification settings - Fork 182
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
Conversation
I just realized that the counter value isn't very useful without the prescaler and auto-reload value. What should we do about that ? |
Thanks for the PR, at a quick glance, it looks good to me.
That's a good question... The most straight forward approach is probably to make it return a suitable time unit, i.e. Another option might also be to return a struct which contains the relevant values which has method for conversion into real units. struct TimerCount { arr, prescaler }
impl TimerCount {
pub fn seconds() {..}
pub fn milliseconds() {..}
...
} cc: @mjepronk. Since you opened #89, you may be interested in this |
Yes, It's interesting, thanks for the heads-up. I thought however that |
Yea, you have a point about the programmer knowing it, though I would prefer a static check because it avoids accidental pitfalls |
Yes, of course. Personally I prefer the methods on the timer, instead of the struct. I find it a little bit harder to discover from the API documentation on how to use it. |
Yea, that's a very good point |
How about this: /// 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 = $TIMX::get_clk(&self.clocks).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()
} I tried to get rid of the u64 arithmetic but couldn't find a good solution, maybe someone can help.
The counter goes from zero to the auto-reload value every timer period, and after that it restarts from zero, so you wouldn't be able to get more than 1ms (with 1kHz timer) information that way. What people usually do is increment a counter variable inside the interrupt routine. If you wish to create a crate with time sensitive stuff, I recommend that you take a look at https://github.com/jonas-schievink/rubble, they found a good solution for that without the need of a RTOS. |
My imagined use case for this is for measuring time that is much shorter than the timer period, but yes, in general the counter variable is a more general solution.
@mjepronk you originally suggested this, do you have insights here. You're right about it being somewhat error prone, but only if overflow occurs, right? If you use it to check time to a timeout or something like that, it should be fine I think.
I don't like this, at some point I would like to extend the range of allowable time periods beyond one hertz. |
Yes, my use-case was to implement a timeout without the need for an interrupt. (I wanted to use it to implement the timeout for your ESP01 crate). Like you say, it would only be useful if we could use it beyond 1 Hz (up to several minutes), and with millisecond precision (1 kHz). |
Well, you're still safe up to 4294 seconds, which already sounds a bit absurd for a timer period. Extending the timers to subHz range would need some big refactoring, since all the calculations are made in Hz. The larger period for the SYST would be ~4.2 seconds, so we would have some API differences if we would allow arbitrary large timer period for the vendor's timers. It's somehow doable, just not sure how useful that would really be, I would go for the RTC for any larger period. |
Added the method proposed, still can't think of a good name for it, I'm open to suggestions. Also added the methods to SYST timer.
I was thinking about it and I guess you could do that with RTC, since it has a counter that behaves similar to that. People usually do that to count seconds, but you can even get a 16kHz clock with it. Right now the HAL only uses it with 1Hz, maybe I can add a method to change the frequency this weekend and add to this PR. |
By memory, there is access to thé internal rtc counter value, allowing to count sub seconds using rtc. |
ad072b0
to
3f9f35f
Compare
Added a method to change the RTC counter frequency, which gives a better way to count time without interrupts. But now the naming of |
This looks fine to me! Thank you. However, note that I'm not that familiar with either the hardware or this crate... |
That does seem weird. Perhaps we should change it to |
I thought about that, but I wasn't completely sure if it was worth the breaking. Also, what do you think about the |
Since the functionality is changing, I almost think we should break things here.
Sounds good to me, at least until we get a good crate for typed times :) |
475d5ce
to
6b74c2c
Compare
6b74c2c
to
1b6a080
Compare
This looks good to me now! @therealprof Do you have any concerns with this? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only have a few nits: There's a second Changed
section added to the CHANGELOG.md and the changes itself are not tagged as breaking despite changing public API.
Oopsie, it must have slipped during rebasing. Also, some of the changes are non-breaking. Line 160 in 9c9c6e1
Line 64 in 9c9c6e1
Unfortunately, I missed PR #95 and only saw it now. |
I've already answered you in matrix that you can delete string with interrupt disabling. |
Yeah, sorry, riot didn't notify me for some reason... |
I agree, I would expect interrupts to stay enabled/disabled when calling start and stop |
Fixed the Changelog and made some changes on the timer code to match the |
LGTM |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good apart from some documentation nitpicks
src/timer.rs
Outdated
u32(1_000_000 * cnt / freq_divider).unwrap() | ||
} | ||
|
||
/// Resets the counter and generates an update event |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
src/rtc.rs
Outdated
@@ -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 |
There was a problem hiding this comment.
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
52a84d6
to
f91f704
Compare
This looks good now, thanks! |
Closes #89 and closes #41.
Right now it would be ok to leave the URS bit set, since the HAL has ownership over the registers and we don't implement anything which requires URS bit cleared. However, I decided to clear the URS bit after the update event to prevent future side effects, but I don't have strong feelings about that...