-
Notifications
You must be signed in to change notification settings - Fork 67
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
Add rtc module #93
Add rtc module #93
Conversation
Hey! :) Thank you for this PR, I'll have a look in the following days |
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 would love to see an example like others in src/examples
and a bit more documentation on the public functions.
Don't worry about the commits too much. If needed this can be rebased later :)
I always like to take inspirations of other implementations of HAL abstractions. Have you looked at the implementation from the stm32f1xx-hal
Thank's for the PR again. I hope I didn't overdo it with my comments. Feel free to ask. And take the review with a grain of salt, as I did not check, if this is working on hardware or if the configuration / initialization complies to the reference manual.
@Sh3Rm4n , |
Co-Authored-By: Fabian <[email protected]>
Co-Authored-By: Fabian <[email protected]>
BTW I found this crate: https://crates.io/crates/rtcc at rust-embedded/embedded-hal#207. Maybe this is useful for you, I like the idea to implement these traits for a higher level rtc access. Maybe it is possible to change By the way, I submitted a PR which documents the rtc periphery. This could be useful for this PR or a following one for the next |
@Sh3Rm4n , I've made changes and used the rtcc traits you previously mentioned. Is this what you had in mind? the |
I applied an |
Thoughts on merging this now? We can clean up details later if they arise. |
Could you please provide an example? Eg, how would we modify this to make it work? Thank you. let mut rtc_ = rtc::Rtc::rtc(
dp.RTC,
&mut rcc.apb1,
&mut dp.RCC.bdcr,
&mut dp.RCC.cr,
&mut dp.RCC.csr,
rtc::cfgRTC::default(),
); |
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 have looked mostly on high-level design and coding style.
I'll also look at the actual RTC functionality once I've had time to become more familiar with how it works. It would be helpful if you could add an example (see the examples
folder) so users (and me) can see how this feature is supposed to be used.
@@ -24,6 +24,7 @@ cortex-m-rt = "0.6" | |||
embedded-hal = "0.2" | |||
nb = "0.1" | |||
stm32f3 = "0.11" | |||
rtcc = "0.2.0" |
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.
Since specifying versions in Cargo.toml defaults to the caret operator (https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html), this is the same as "^0.2.0"
, which actually allows any patch version (i.e. 0.2.x
). Therefore, it is less confusing to just specify rtcc = "0.2"
here. We use this pattern for the other dependencies too.
match &cfg_rtc.src { | ||
RTCSrc::LSI => Rtc::enable_lsi(csr), | ||
RTCSrc::HSE => Rtc::enable_hse(cr, false), | ||
RTCSrc::LSE => Rtc::enable_lse(bdcr, false), |
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.
So this is the only reason we need to expose the CSR
and CR
registers from RCC in the first place, right? Like I described in my other comment, at least CR
is problematic. Exposing CSR
seems fine, but I'd still like to avoid it if not necessary.
That said, how important is it that we support all the clock sources for the RTC? Do users really care about them all? Or would it be enough to only support one of them? We need BDCR
anyway for other configurations, so I would suggest always using the LSE and not supporting the other clock sources. Then we wouldn't need access to CR
and CSR
at all.
That's also the approach used by the stm32f1xx-hal: https://github.com/stm32-rs/stm32f1xx-hal/blob/master/src/rtc.rs#L54
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.
That is correct, the reason for exposing CSR
and CR
is to allow clock source selection. The LSE is not included on the STM32F303 Discovery board, so I initially wanted to make the rtc module flexible and allow the user to choose. That being said, if I had to choose one for support, it would be the LSE. I agree.
w.prediv_s() | ||
.bits(cfg_rtc.prediv_s) | ||
.prediv_a() | ||
.bits(cfg_rtc.prediv_a) |
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.
This formatting is very unreadable. It looks like you do three modifications on prediv_s
, but actually you do only one and then another one on prediv_a
. I know rustfmt isn't helpful here, but you can restructure your code a bit to fix this:
regs.prer.write(|w| unsafe {
w.prediv_s().bits(cfg_rtc.prediv_s);
w.prediv_a().bits(cfg_rtc.prediv_a)
});
This also applies to the other instances of register access chaining below.
|
||
fn enable_lsi(csr: &mut CSR) { | ||
csr.csr().write(|w| w.lsion().set_bit()); | ||
while csr.csr().read().lsirdy().bit_is_clear() {} |
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.
Most of the PAC registers come with more descriptive methods than the generic ones ({set,clear}_bit
, bit_is_{set,clear}
. In this case those would be:
csr.csr().write(|w| w.lsion().on());
while csr.csr().read().lsirdy().is_not_ready() {}
Apart from improving readability, they also sometimes prevent bugs (e.g. sometimes you clear an interrupt flag by setting a bit). So I would suggest using them everywhere possible.
} | ||
|
||
fn enable_lsi(csr: &mut CSR) { | ||
csr.csr().write(|w| w.lsion().set_bit()); |
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'm pretty sure you should use modify
here instead of write
. write
sets all register contents except the ones you set in the closure to their reset values. Which means if you have already configured part of a register and don't want to lose this configuration, you should always use modify
.
There are a couple other instances of write
use in this file. Please check if they all work as intended.
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.
My limited knowledge points to the same. The calls to write
followed by unsafe blocks are probably correct, but the others should be modify. (?)
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.
Thank you for catching this. I'll look through the code to see if I confused it anywhere else.
use rtcc::{Datelike, Hours, NaiveDate, NaiveDateTime, NaiveTime, Rtcc, Timelike}; | ||
|
||
/// Invalid input error | ||
// #[derive(Clone, Copy)] |
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.
Did you forget to remove this?
|
||
/// RTC clock input source | ||
#[derive(Copy, Clone, Debug, PartialEq)] | ||
pub enum RTCSrc { |
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.
Check out this RFC for naming conventions in Rust: https://github.com/rust-lang/rfcs/blob/master/text/0430-finalizing-naming-conventions.md
Notably acronyms count as one word for the CamelCase notation, so did should be RtcSrc
. But there is no need to obfuscate the name just to save three characters here, so RtcSource
would be what I prefer.
pub enum RTCSrc { | ||
LSE = 0b01, | ||
LSI = 0b10, | ||
HSE = 0b11, |
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 don't think to use these bit patterns anywhere? If so, I would just leave them out.
|
||
/// RTC configuration struct | ||
#[derive(Copy, Clone, Debug, PartialEq)] | ||
pub struct cfgRTC { |
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.
This name also doesn't follow the naming conventions. How about RtcConfig
?
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.
Will not be a problem with only LSE support
cfgRTC { | ||
src: RTCSrc::LSI, | ||
prediv_s: 311, | ||
prediv_a: 127, |
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.
You discussed this with @Sh3Rm4n before. Like him I think there should be a reference to the relevant manual and section you got these magic numbers from documented.
I would rather have a clean initial implementation, since anything else will fall back on us by making this crate harder to maintain. Hoping that someone will clean up later doesn't work out in my experience, because everyone prefers to work on new features instead ;) If something should be merged faster, then I think the way to go is by merging a (good) MVP first and adding more features later. |
I'm working off a variant of this and tweaking as I go. I'll let you know what I come up with. Excited to get this cleaned up and merged. |
Do you think RTC wakeup code like this should go in this RTC module, or in an interrupt module? Note that the It's not working yet, but I think I'm almost there, since I've done everything I can find in the ref man and rtc man. Thoughts on making |
I can't say much to RTC specifics because I've not read up to that yet. What I can say is that usually how it works is that a HAL crate provides abstractions over peripherals. When you create an instance of such an abstraction, you have to give it ownership over the peripheral registers. The abstraction basically wraps these registers and provides a safe API for them. Peripheral abstractions interact with one another, but they don't access the registers of another peripheral directly, only the safe methods. So in this case, the relevant peripheral abstraction would be So no, I don't think we should provide public access to As a final note, it is always possible to get a register using unsafe code, e.g.: |
That's a 2-for-1 response: The interrupt (wakeup, alarm, tamper etc) functionality needs to be baked into this module, if it's going to control that periph. Eg the code I linked above makes heavy use of it. Btw, that's a great pointer, on the |
Btw, I'm glad this uses the Thoughts on adding the |
Off-topic: Regarding |
Hi, nice to see I do not want to hold it back but I do not think it will be merged into Anyway feel free to express your opinion at rust-embedded/embedded-hal#207 as well. |
@David-OConnor , I believe it makes the most sense to apply the RTC wakeup code in a separate EXTI module, as it is done here . |
Note that most people in the related issue on this repo favor interrupt code to be built into the pins as methods. |
Closing in favor of #136. |
Added rtc module, with supporting helper functions in modules
rcc.rs
andtime.rs
.It's my first type doing a PR, so apologies if the commits are a mess.