-
Notifications
You must be signed in to change notification settings - Fork 69
Add rtc module #93
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
Changes from 12 commits
c113526
5a7723a
b24c4f9
9175483
7cf3829
476a92c
44fe6fc
fb505ec
1422683
98c3f24
c629df9
1533749
144588e
4c6d8d6
91f831b
4777f6d
158632a
ad355d6
348e4a9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,10 +4,11 @@ use core::cmp; | |
|
||
use crate::stm32::{ | ||
rcc::{self, cfgr}, | ||
RCC, | ||
PWR, RCC, | ||
}; | ||
|
||
use crate::flash::ACR; | ||
use crate::rtc::RTCSrc; | ||
use crate::time::Hertz; | ||
|
||
/// Extension trait that constrains the `RCC` peripheral | ||
|
@@ -45,6 +46,87 @@ pub struct Rcc { | |
pub cfgr: CFGR, | ||
} | ||
|
||
impl Rcc { | ||
pub(crate) fn enable_rtc(&mut self, src: &RTCSrc) { | ||
match src { | ||
RTCSrc::LSI => self.enable_lsi(), | ||
teskje marked this conversation as resolved.
Show resolved
Hide resolved
|
||
RTCSrc::HSE => self.enable_hse(false), | ||
RTCSrc::LSE => self.enable_lse(false), | ||
} | ||
self.unlock_rtc(); | ||
self.bdcr().modify(|_, w| { | ||
w | ||
// RTC Backup Domain reset bit set high | ||
.bdrst() | ||
.set_bit() | ||
}); | ||
self.bdcr().modify(|_, w| { | ||
w | ||
// RTC clock source selection | ||
.rtcsel() | ||
.bits(*src as u8) | ||
// Enable RTC | ||
.rtcen() | ||
.set_bit() | ||
// RTC backup Domain reset bit set low | ||
.bdrst() | ||
.clear_bit() | ||
}); | ||
} | ||
|
||
pub(crate) fn unlock_rtc(&mut self) { | ||
let pwr = unsafe { &(*PWR::ptr()) }; | ||
self.apb1.enr().modify(|_, w| { | ||
w | ||
// Enable the backup interface by setting PWREN | ||
.pwren() | ||
.set_bit() | ||
}); | ||
pwr.cr.modify(|_, w| { | ||
w | ||
// Enable access to the backup registers | ||
.dbp() | ||
.set_bit() | ||
}); | ||
|
||
while pwr.cr.read().dbp().bit_is_clear() {} | ||
} | ||
|
||
pub(crate) fn enable_lsi(&mut self) { | ||
self.csr().write(|w| w.lsion().set_bit()); | ||
while self.csr().read().lsirdy().bit_is_clear() {} | ||
} | ||
|
||
pub(crate) fn enable_hsi(&mut self) { | ||
self.cr().write(|w| w.hsion().set_bit()); | ||
while self.cr().read().hsirdy().bit_is_clear() {} | ||
} | ||
|
||
pub(crate) fn enable_hse(&mut self, bypass: bool) { | ||
self.cr() | ||
.write(|w| w.hseon().set_bit().hsebyp().bit(bypass)); | ||
while self.cr().read().hserdy().bit_is_clear() {} | ||
} | ||
|
||
pub(crate) fn enable_lse(&mut self, bypass: bool) { | ||
self.bdcr() | ||
.write(|w| w.lseon().set_bit().lsebyp().bit(bypass)); | ||
while self.bdcr().read().lserdy().bit_is_clear() {} | ||
} | ||
|
||
pub(crate) fn cr(&mut self) -> &rcc::CR { | ||
unsafe { &(*RCC::ptr()).cr } | ||
} | ||
|
||
pub(crate) fn csr(&mut self) -> &rcc::CSR { | ||
unsafe { &(*RCC::ptr()).csr } | ||
} | ||
|
||
pub(crate) fn bdcr(&mut self) -> &rcc::BDCR { | ||
unsafe { &(*RCC::ptr()).bdcr } | ||
} | ||
} | ||
|
||
/// AMBA High-performance Bus (AHB) registers | ||
pub struct AHB { | ||
_0: (), | ||
|
@@ -151,6 +233,7 @@ mod usb_clocking { | |
} | ||
|
||
/// Clock configuration | ||
#[derive(Clone, Copy)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We must not allow cloning of the |
||
pub struct CFGR { | ||
hse: Option<u32>, | ||
hclk: Option<u32>, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
use crate::rcc::Rcc; | ||
use crate::stm32::RTC; | ||
use crate::time::{Date, Time, U32Ext}; | ||
|
||
/** | ||
teskje marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Interface to the real time clock | ||
**/ | ||
|
||
pub struct Rtc { | ||
regs: RTC, | ||
} | ||
|
||
impl Rtc { | ||
/** | ||
teskje marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Initializes the RTC | ||
**/ | ||
// Sets default clock source to LSI, since LSE is not included on STM32f3 discovery boards | ||
pub fn rtc(regs: RTC, src: RTCSrc, rcc: &mut Rcc) -> Self { | ||
let mut result = Rtc { regs }; | ||
|
||
rcc.enable_rtc(&src); | ||
|
||
result.regs.cr.modify(|_, w| { | ||
w | ||
// sets hour format to 24 hours | ||
.fmt() | ||
.clear_bit() | ||
}); | ||
|
||
// Prescalers set to produce a 1 hz signal | ||
let (prediv_s, prediv_a) = match src { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where do these numbers come from? Have you considered that HSE is variable and can differ between boards? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These numbers came from AN4795: I like the idea of a function that takes care of choosing the correct prescalers to produce a 1 hz RTC clock source. This is possible for LSI and LSE, but the fact that HSE frequency can vary (I read that the HSE frequency can be between 4 and 32 Mhz) makes that a challenge. The concept of making some methods "visible" given a parameter (the RTCSrc) sounds like a good solution, if it is possible. For example : if RTCSrc::LSE or RTCSrc::LSI, make the following method visible....
else, there is a more general function I think I am not using the correct words to search to see if this feature of making methods "visible" or "invisible" exists, because I am not able to find anything online. Can something like this be accomplished? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah interesting to know. I would like to see a comment or make these tuples for fixed "input" frequencies (meaning LSI LSE) constants. I have difficulties to find the AN4795 Application note, can you provide a link? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apologies, it was a typo. I meant AN4759 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because HSE clock frequencies can vary, perhaps the user should choose a source and prescaler values to produce the desired rtc input clock speed through a configuration structure. eg.
|
||
RTCSrc::LSI => (311_u32, 127_u32), | ||
RTCSrc::HSE => (62992_u32, 127_u32), | ||
RTCSrc::LSE => (255_u32, 127_u32), | ||
}; | ||
|
||
let raw_bits: u32 = prediv_s | (prediv_a << 16); | ||
result.modify(|regs| { | ||
regs.prer.write(|w| unsafe { w.bits(raw_bits) }); | ||
smedellin90 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}); | ||
result | ||
} | ||
|
||
pub fn set_time(&mut self, time: &Time) { | ||
let (ht, hu) = bcd2_encode(time.hours); | ||
let (mnt, mnu) = bcd2_encode(time.minutes); | ||
let (st, su) = bcd2_encode(time.seconds); | ||
self.modify(|regs| { | ||
regs.tr.write(|w| unsafe { | ||
w.ht() | ||
.bits(ht) | ||
.hu() | ||
.bits(hu) | ||
.mnt() | ||
.bits(mnt) | ||
.mnu() | ||
.bits(mnu) | ||
.st() | ||
.bits(st) | ||
.su() | ||
.bits(su) | ||
.pm() | ||
.clear_bit() | ||
}); | ||
regs.cr.modify(|_, w| w.fmt().bit(time.daylight_savings)); | ||
teskje marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}); | ||
} | ||
|
||
pub fn set_date(&mut self, date: &Date) { | ||
let (yt, yu) = bcd2_encode(date.year - 1970); | ||
let (mt, mu) = bcd2_encode(date.month); | ||
let (dt, du) = bcd2_encode(date.day); | ||
|
||
self.modify(|regs| { | ||
regs.dr.write(|w| unsafe { | ||
w.dt() | ||
.bits(dt) | ||
.du() | ||
.bits(du) | ||
.mt() | ||
.bit(mt > 0) | ||
.mu() | ||
.bits(mu) | ||
.yt() | ||
.bits(yt) | ||
.yu() | ||
.bits(yu) | ||
.wdu() | ||
.bits(date.day as u8) | ||
}); | ||
}); | ||
} | ||
|
||
pub fn get_time(&self) -> Time { | ||
let timer = self.regs.tr.read(); | ||
Time::new( | ||
bcd2_decode(timer.ht().bits(), timer.hu().bits()).hours(), | ||
bcd2_decode(timer.mnt().bits(), timer.mnu().bits()).minutes(), | ||
bcd2_decode(timer.st().bits(), timer.su().bits()).seconds(), | ||
self.regs.cr.read().fmt().bit(), | ||
teskje marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
} | ||
|
||
pub fn get_date(&self) -> Date { | ||
let date = self.regs.dr.read(); | ||
Date::new( | ||
(bcd2_decode(date.yt().bits(), date.yu().bits()) + 1970).year(), | ||
bcd2_decode(date.mt().bit() as u8, date.mu().bits()).month(), | ||
bcd2_decode(date.dt().bits(), date.du().bits()).day(), | ||
) | ||
} | ||
|
||
pub fn get_week_day(&self) -> u8 { | ||
teskje marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.regs.dr.read().wdu().bits() | ||
} | ||
|
||
fn modify<F>(&mut self, mut closure: F) | ||
teskje marked this conversation as resolved.
Show resolved
Hide resolved
|
||
where | ||
F: FnMut(&mut RTC) -> (), | ||
smedellin90 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
// Disable write protection | ||
self.regs.wpr.write(|w| unsafe { w.bits(0xCA) }); | ||
self.regs.wpr.write(|w| unsafe { w.bits(0x53) }); | ||
// Enter init mode | ||
let isr = self.regs.isr.read(); | ||
if isr.initf().bit_is_clear() { | ||
self.regs.isr.write(|w| w.init().set_bit()); | ||
while self.regs.isr.read().initf().bit_is_clear() {} | ||
} | ||
// Invoke closure | ||
closure(&mut self.regs); | ||
// Exit init mode | ||
self.regs.isr.write(|w| w.init().clear_bit()); | ||
// wait for last write to be done | ||
while !self.regs.isr.read().initf().bit_is_clear() {} | ||
} | ||
} | ||
|
||
/// RTC clock input source | ||
#[derive(Clone, Copy)] | ||
pub enum RTCSrc { | ||
teskje marked this conversation as resolved.
Show resolved
Hide resolved
|
||
LSE = 0b01, | ||
LSI = 0b10, | ||
HSE = 0b11, | ||
} | ||
|
||
pub trait RtcExt { | ||
fn constrain(self, rcc: &mut Rcc) -> Rtc; | ||
} | ||
|
||
impl RtcExt for RTC { | ||
fn constrain(self, rcc: &mut Rcc) -> Rtc { | ||
Rtc::rtc(self, RTCSrc::LSI, rcc) | ||
} | ||
} | ||
|
||
fn bcd2_encode(word: u32) -> (u8, u8) { | ||
smedellin90 marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, if I understand correctly, this function converts (word / 10, word % 10) But there also needs to be a (panicking) check that |
||
let mut value = word as u8; | ||
let mut bcd_high: u8 = 0; | ||
while value >= 10 { | ||
bcd_high += 1; | ||
value -= 10; | ||
} | ||
let bcd_low = ((bcd_high << 4) | value) as u8; | ||
(bcd_high, bcd_low) | ||
} | ||
|
||
fn bcd2_decode(fst: u8, snd: u8) -> u32 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly, this can be simplified to: fst * 10 + snd |
||
let value = snd | fst << 4; | ||
let value = (value & 0x0F) + ((value & 0xF0) >> 4) * 10; | ||
value as u32 | ||
} |
Uh oh!
There was an error while loading. Please reload this page.