Skip to content

Commit c637cc6

Browse files
authored
Add RTC calibration function. (#307)
* Add RTC calibration function. * Allow running `cargo fmt` when cloned into other workspace. * Add `offset` range to docs. * Improve RTC calibration docs and API. * Rename `RtcCalibrationCyclePeriod` variants. * Remove unused `mut`.
1 parent 5046e88 commit c637cc6

File tree

1 file changed

+82
-0
lines changed

1 file changed

+82
-0
lines changed

src/rtc.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,23 @@ impl RtcConfig {
158158
}
159159
}
160160

161+
#[derive(Copy, Clone, Debug, PartialEq)]
162+
#[repr(u8)]
163+
pub enum RtcCalibrationCyclePeriod {
164+
/// 8-second calibration period
165+
Seconds8,
166+
/// 16-second calibration period
167+
Seconds16,
168+
/// 32-second calibration period
169+
Seconds32,
170+
}
171+
172+
impl Default for RtcCalibrationCyclePeriod {
173+
fn default() -> Self {
174+
RtcCalibrationCyclePeriod::Seconds32
175+
}
176+
}
177+
161178
impl Rtc {
162179
pub fn rtc(
163180
rtc: RTC,
@@ -479,6 +496,71 @@ impl Rtc {
479496
self.rtc_config = rtc_config;
480497
}
481498

499+
const RTC_CALR_MIN_PPM: f32 = -487.1;
500+
const RTC_CALR_MAX_PPM: f32 = 488.5;
501+
const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537;
502+
503+
/// Calibrate the clock drift.
504+
///
505+
/// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range.
506+
///
507+
/// ### Note
508+
///
509+
/// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler`
510+
/// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12).
511+
pub fn calibrate(&mut self, mut clock_drift: f32, period: RtcCalibrationCyclePeriod) {
512+
if clock_drift < Self::RTC_CALR_MIN_PPM {
513+
clock_drift = Self::RTC_CALR_MIN_PPM;
514+
} else if clock_drift > Self::RTC_CALR_MAX_PPM {
515+
clock_drift = Self::RTC_CALR_MAX_PPM;
516+
}
517+
518+
clock_drift = clock_drift / Self::RTC_CALR_RESOLUTION_PPM;
519+
520+
self.write(false, |rtc| {
521+
rtc.calr.modify(|_, w| unsafe {
522+
match period {
523+
RtcCalibrationCyclePeriod::Seconds8 => {
524+
w.calw8().set_bit().calw16().clear_bit();
525+
}
526+
RtcCalibrationCyclePeriod::Seconds16 => {
527+
w.calw8().clear_bit().calw16().set_bit();
528+
}
529+
RtcCalibrationCyclePeriod::Seconds32 => {
530+
w.calw8().clear_bit().calw16().clear_bit();
531+
}
532+
}
533+
534+
// Extra pulses during calibration cycle period: CALP * 512 - CALM
535+
//
536+
// CALP sets whether pulses are added or omitted.
537+
//
538+
// CALM contains how many pulses (out of 512) are masked in a
539+
// given calibration cycle period.
540+
if clock_drift > 0.0 {
541+
// Maximum (about 512.2) rounds to 512.
542+
clock_drift += 0.5;
543+
544+
// When the offset is positive (0 to 512), the opposite of
545+
// the offset (512 - offset) is masked, i.e. for the
546+
// maximum offset (512), 0 pulses are masked.
547+
w.calp().set_bit().calm().bits(512 - clock_drift as u16)
548+
} else {
549+
// Minimum (about -510.7) rounds to -511.
550+
clock_drift -= 0.5;
551+
552+
// When the offset is negative or zero (-511 to 0),
553+
// the absolute offset is masked, i.e. for the minimum
554+
// offset (-511), 511 pulses are masked.
555+
w.calp()
556+
.clear_bit()
557+
.calm()
558+
.bits((clock_drift * -1.0) as u16)
559+
}
560+
});
561+
})
562+
}
563+
482564
/// Access the wakeup timer
483565
pub fn wakeup_timer(&mut self) -> WakeupTimer {
484566
WakeupTimer { rtc: self }

0 commit comments

Comments
 (0)