aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/Cargo.toml1
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/rtc/datetime_chrono.rs85
-rw-r--r--embassy-stm32/src/rtc/datetime_no_deps.rs146
-rw-r--r--embassy-stm32/src/rtc/mod.rs238
-rw-r--r--embassy-stm32/src/rtc/v2/mod.rs171
-rw-r--r--embassy-stm32/src/rtc/v2/v2f0.rs41
-rw-r--r--embassy-stm32/src/rtc/v2/v2f2.rs31
-rw-r--r--embassy-stm32/src/rtc/v2/v2f3.rs31
-rw-r--r--embassy-stm32/src/rtc/v2/v2f4.rs31
-rw-r--r--embassy-stm32/src/rtc/v2/v2f7.rs41
-rw-r--r--embassy-stm32/src/rtc/v2/v2h7.rs33
-rw-r--r--embassy-stm32/src/rtc/v2/v2l0.rs26
-rw-r--r--embassy-stm32/src/rtc/v2/v2l1.rs24
-rw-r--r--embassy-stm32/src/rtc/v2/v2l4.rs41
-rw-r--r--embassy-stm32/src/rtc/v2/v2wb.rs39
-rw-r--r--embassy-stm32/src/rtc/v3.rs226
17 files changed, 1207 insertions, 0 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 18b1d4d0e..f27cf6a51 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -66,6 +66,7 @@ stm32-fmc = "0.2.4"
66seq-macro = "0.3.0" 66seq-macro = "0.3.0"
67cfg-if = "1.0.0" 67cfg-if = "1.0.0"
68embedded-io = { version = "0.4.0", features = ["async"], optional = true } 68embedded-io = { version = "0.4.0", features = ["async"], optional = true }
69chrono = { version = "^0.4", default-features = false, optional = true}
69 70
70[dev-dependencies] 71[dev-dependencies]
71critical-section = { version = "1.1", features = ["std"] } 72critical-section = { version = "1.1", features = ["std"] }
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index d4d7155bd..70e6aa2bf 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -49,6 +49,8 @@ pub mod pwm;
49pub mod qspi; 49pub mod qspi;
50#[cfg(rng)] 50#[cfg(rng)]
51pub mod rng; 51pub mod rng;
52#[cfg(all(rtc, not(rtc_v1)))]
53pub mod rtc;
52#[cfg(sdmmc)] 54#[cfg(sdmmc)]
53pub mod sdmmc; 55pub mod sdmmc;
54#[cfg(spi)] 56#[cfg(spi)]
diff --git a/embassy-stm32/src/rtc/datetime_chrono.rs b/embassy-stm32/src/rtc/datetime_chrono.rs
new file mode 100644
index 000000000..b46316cc9
--- /dev/null
+++ b/embassy-stm32/src/rtc/datetime_chrono.rs
@@ -0,0 +1,85 @@
1use chrono::{Datelike, Timelike};
2
3use super::byte_to_bcd2;
4use crate::pac::rtc::Rtc;
5
6/// Alias for [`chrono::NaiveDateTime`]
7pub type DateTime = chrono::NaiveDateTime;
8/// Alias for [`chrono::Weekday`]
9pub type DayOfWeek = chrono::Weekday;
10
11/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs.
12///
13/// [`DateTimeFilter`]: struct.DateTimeFilter.html
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub enum Error {
16 /// The [DateTime] has an invalid year. The year must be between 0 and 4095.
17 InvalidYear,
18 /// The [DateTime] contains an invalid date.
19 InvalidDate,
20 /// The [DateTime] contains an invalid time.
21 InvalidTime,
22}
23
24pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
25 dotw.num_days_from_monday() as u8
26}
27
28pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
29 if dt.year() < 0 || dt.year() > 4095 {
30 // rp2040 can't hold these years
31 Err(Error::InvalidYear)
32 } else {
33 // The rest of the chrono date is assumed to be valid
34 Ok(())
35 }
36}
37
38pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) {
39 let (ht, hu) = byte_to_bcd2(t.hour() as u8);
40 let (mnt, mnu) = byte_to_bcd2(t.minute() as u8);
41 let (st, su) = byte_to_bcd2(t.second() as u8);
42
43 let (dt, du) = byte_to_bcd2(t.day() as u8);
44 let (mt, mu) = byte_to_bcd2(t.month() as u8);
45 let yr = t.year() as u16;
46 let yr_offset = (yr - 1970_u16) as u8;
47 let (yt, yu) = byte_to_bcd2(yr_offset);
48
49 unsafe {
50 rtc.tr().write(|w| {
51 w.set_ht(ht);
52 w.set_hu(hu);
53 w.set_mnt(mnt);
54 w.set_mnu(mnu);
55 w.set_st(st);
56 w.set_su(su);
57 w.set_pm(stm32_metapac::rtc::vals::Ampm::AM);
58 });
59
60 rtc.dr().write(|w| {
61 w.set_dt(dt);
62 w.set_du(du);
63 w.set_mt(mt > 0);
64 w.set_mu(mu);
65 w.set_yt(yt);
66 w.set_yu(yu);
67 w.set_wdu(day_of_week_to_u8(t.weekday()));
68 });
69 }
70}
71
72pub(super) fn datetime(
73 year: u16,
74 month: u8,
75 day: u8,
76 _day_of_week: u8,
77 hour: u8,
78 minute: u8,
79 second: u8,
80) -> Result<DateTime, Error> {
81 let date = chrono::NaiveDate::from_ymd_opt(year.into(), month.try_into().unwrap(), day.into())
82 .ok_or(Error::InvalidDate)?;
83 let time = chrono::NaiveTime::from_hms_opt(hour.into(), minute.into(), second.into()).ok_or(Error::InvalidTime)?;
84 Ok(DateTime::new(date, time))
85}
diff --git a/embassy-stm32/src/rtc/datetime_no_deps.rs b/embassy-stm32/src/rtc/datetime_no_deps.rs
new file mode 100644
index 000000000..173f38377
--- /dev/null
+++ b/embassy-stm32/src/rtc/datetime_no_deps.rs
@@ -0,0 +1,146 @@
1use super::byte_to_bcd2;
2use crate::pac::rtc::Rtc;
3
4/// Errors regarding the [`DateTime`] struct.
5#[derive(Clone, Debug, PartialEq, Eq)]
6pub enum Error {
7 /// The [DateTime] contains an invalid year value. Must be between `0..=4095`.
8 InvalidYear,
9 /// The [DateTime] contains an invalid month value. Must be between `1..=12`.
10 InvalidMonth,
11 /// The [DateTime] contains an invalid day value. Must be between `1..=31`.
12 InvalidDay,
13 /// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday.
14 InvalidDayOfWeek(
15 /// The value of the DayOfWeek that was given.
16 u8,
17 ),
18 /// The [DateTime] contains an invalid hour value. Must be between `0..=23`.
19 InvalidHour,
20 /// The [DateTime] contains an invalid minute value. Must be between `0..=59`.
21 InvalidMinute,
22 /// The [DateTime] contains an invalid second value. Must be between `0..=59`.
23 InvalidSecond,
24}
25
26/// Structure containing date and time information
27pub struct DateTime {
28 /// 0..4095
29 pub year: u16,
30 /// 1..12, 1 is January
31 pub month: u8,
32 /// 1..28,29,30,31 depending on month
33 pub day: u8,
34 ///
35 pub day_of_week: DayOfWeek,
36 /// 0..23
37 pub hour: u8,
38 /// 0..59
39 pub minute: u8,
40 /// 0..59
41 pub second: u8,
42}
43
44/// A day of the week
45#[repr(u8)]
46#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
47#[allow(missing_docs)]
48pub enum DayOfWeek {
49 Monday = 0,
50 Tuesday = 1,
51 Wednesday = 2,
52 Thursday = 3,
53 Friday = 4,
54 Saturday = 5,
55 Sunday = 6,
56}
57
58fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, Error> {
59 Ok(match v {
60 0 => DayOfWeek::Monday,
61 1 => DayOfWeek::Tuesday,
62 2 => DayOfWeek::Wednesday,
63 3 => DayOfWeek::Thursday,
64 4 => DayOfWeek::Friday,
65 5 => DayOfWeek::Saturday,
66 6 => DayOfWeek::Sunday,
67 x => return Err(Error::InvalidDayOfWeek(x)),
68 })
69}
70
71pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
72 dotw as u8
73}
74
75pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
76 if dt.year > 4095 {
77 Err(Error::InvalidYear)
78 } else if dt.month < 1 || dt.month > 12 {
79 Err(Error::InvalidMonth)
80 } else if dt.day < 1 || dt.day > 31 {
81 Err(Error::InvalidDay)
82 } else if dt.hour > 23 {
83 Err(Error::InvalidHour)
84 } else if dt.minute > 59 {
85 Err(Error::InvalidMinute)
86 } else if dt.second > 59 {
87 Err(Error::InvalidSecond)
88 } else {
89 Ok(())
90 }
91}
92
93pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) {
94 let (ht, hu) = byte_to_bcd2(t.hour as u8);
95 let (mnt, mnu) = byte_to_bcd2(t.minute as u8);
96 let (st, su) = byte_to_bcd2(t.second as u8);
97
98 let (dt, du) = byte_to_bcd2(t.day as u8);
99 let (mt, mu) = byte_to_bcd2(t.month as u8);
100 let yr = t.year as u16;
101 let yr_offset = (yr - 1970_u16) as u8;
102 let (yt, yu) = byte_to_bcd2(yr_offset);
103
104 unsafe {
105 rtc.tr().write(|w| {
106 w.set_ht(ht);
107 w.set_hu(hu);
108 w.set_mnt(mnt);
109 w.set_mnu(mnu);
110 w.set_st(st);
111 w.set_su(su);
112 w.set_pm(stm32_metapac::rtc::vals::Ampm::AM);
113 });
114
115 rtc.dr().write(|w| {
116 w.set_dt(dt);
117 w.set_du(du);
118 w.set_mt(mt > 0);
119 w.set_mu(mu);
120 w.set_yt(yt);
121 w.set_yu(yu);
122 w.set_wdu(day_of_week_to_u8(t.day_of_week));
123 });
124 }
125}
126
127pub(super) fn datetime(
128 year: u16,
129 month: u8,
130 day: u8,
131 day_of_week: u8,
132 hour: u8,
133 minute: u8,
134 second: u8,
135) -> Result<DateTime, Error> {
136 let day_of_week = day_of_week_from_u8(day_of_week)?;
137 Ok(DateTime {
138 year,
139 month,
140 day,
141 day_of_week,
142 hour,
143 minute,
144 second,
145 })
146}
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs
new file mode 100644
index 000000000..ee3349b27
--- /dev/null
+++ b/embassy-stm32/src/rtc/mod.rs
@@ -0,0 +1,238 @@
1//! RTC peripheral abstraction
2use core::marker::PhantomData;
3
4#[cfg_attr(feature = "chrono", path = "datetime_chrono.rs")]
5#[cfg_attr(not(feature = "chrono"), path = "datetime_no_deps.rs")]
6mod datetime;
7
8pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
9
10/// refer to AN4759 to compare features of RTC2 and RTC3
11#[cfg_attr(any(rtc_v1), path = "v1.rs")]
12#[cfg_attr(
13 any(
14 rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb
15 ),
16 path = "v2/mod.rs"
17)]
18#[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")]
19mod versions;
20use embassy_hal_common::Peripheral;
21pub use versions::*;
22
23/// Errors that can occur on methods on [RtcClock]
24#[derive(Clone, Debug, PartialEq, Eq)]
25pub enum RtcError {
26 /// An invalid DateTime was given or stored on the hardware.
27 InvalidDateTime(DateTimeError),
28
29 /// The RTC clock is not running
30 NotRunning,
31}
32
33/// RTC Abstraction
34pub struct Rtc<'d, T: Instance> {
35 phantom: PhantomData<&'d mut T>,
36 rtc_config: RtcConfig,
37}
38
39#[derive(Copy, Clone, Debug, PartialEq)]
40#[repr(u8)]
41pub enum RtcClockSource {
42 /// 00: No clock
43 NoClock = 0b00,
44 /// 01: LSE oscillator clock used as RTC clock
45 LSE = 0b01,
46 /// 10: LSI oscillator clock used as RTC clock
47 LSI = 0b10,
48 /// 11: HSE oscillator clock divided by 32 used as RTC clock
49 HSE = 0b11,
50}
51
52#[derive(Copy, Clone, PartialEq)]
53pub struct RtcConfig {
54 /// RTC clock source
55 clock_config: RtcClockSource,
56 /// Asynchronous prescaler factor
57 /// This is the asynchronous division factor:
58 /// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1)
59 /// ck_apre drives the subsecond register
60 async_prescaler: u8,
61 /// Synchronous prescaler factor
62 /// This is the synchronous division factor:
63 /// ck_spre frequency = ck_apre frequency/(PREDIV_S+1)
64 /// ck_spre must be 1Hz
65 sync_prescaler: u16,
66}
67
68impl Default for RtcConfig {
69 /// LSI with prescalers assuming 32.768 kHz.
70 /// Raw sub-seconds in 1/256.
71 fn default() -> Self {
72 RtcConfig {
73 clock_config: RtcClockSource::LSI,
74 async_prescaler: 127,
75 sync_prescaler: 255,
76 }
77 }
78}
79
80impl RtcConfig {
81 /// Sets the clock source of RTC config
82 pub fn clock_config(mut self, cfg: RtcClockSource) -> Self {
83 self.clock_config = cfg;
84 self
85 }
86
87 /// Set the asynchronous prescaler of RTC config
88 pub fn async_prescaler(mut self, prescaler: u8) -> Self {
89 self.async_prescaler = prescaler;
90 self
91 }
92
93 /// Set the synchronous prescaler of RTC config
94 pub fn sync_prescaler(mut self, prescaler: u16) -> Self {
95 self.sync_prescaler = prescaler;
96 self
97 }
98}
99
100#[derive(Copy, Clone, Debug, PartialEq)]
101#[repr(u8)]
102pub enum RtcCalibrationCyclePeriod {
103 /// 8-second calibration period
104 Seconds8,
105 /// 16-second calibration period
106 Seconds16,
107 /// 32-second calibration period
108 Seconds32,
109}
110
111impl Default for RtcCalibrationCyclePeriod {
112 fn default() -> Self {
113 RtcCalibrationCyclePeriod::Seconds32
114 }
115}
116
117impl<'d, T: Instance> Rtc<'d, T> {
118 pub fn new(_rtc: impl Peripheral<P = T> + 'd, rtc_config: RtcConfig) -> Self {
119 unsafe { enable_peripheral_clk() };
120
121 let mut rtc_struct = Self {
122 phantom: PhantomData,
123 rtc_config,
124 };
125
126 rtc_struct.apply_config(rtc_config);
127
128 rtc_struct
129 }
130
131 /// Set the datetime to a new value.
132 ///
133 /// # Errors
134 ///
135 /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
136 pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {
137 self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?;
138 self.write(true, |rtc| self::datetime::write_date_time(rtc, t));
139
140 Ok(())
141 }
142
143 /// Return the current datetime.
144 ///
145 /// # Errors
146 ///
147 /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`].
148 pub fn now(&self) -> Result<DateTime, RtcError> {
149 let r = T::regs();
150 unsafe {
151 let tr = r.tr().read();
152 let second = bcd2_to_byte((tr.st(), tr.su()));
153 let minute = bcd2_to_byte((tr.mnt(), tr.mnu()));
154 let hour = bcd2_to_byte((tr.ht(), tr.hu()));
155 // Reading either RTC_SSR or RTC_TR locks the values in the higher-order
156 // calendar shadow registers until RTC_DR is read.
157 let dr = r.dr().read();
158
159 let weekday = dr.wdu();
160 let day = bcd2_to_byte((dr.dt(), dr.du()));
161 let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));
162 let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16;
163
164 self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime)
165 }
166 }
167
168 /// Check if daylight savings time is active.
169 pub fn get_daylight_savings(&self) -> bool {
170 let cr = unsafe { T::regs().cr().read() };
171 cr.bkp()
172 }
173
174 /// Enable/disable daylight savings time.
175 pub fn set_daylight_savings(&mut self, daylight_savings: bool) {
176 self.write(true, |rtc| {
177 unsafe { rtc.cr().modify(|w| w.set_bkp(daylight_savings)) };
178 })
179 }
180
181 pub fn get_config(&self) -> RtcConfig {
182 self.rtc_config
183 }
184
185 pub const BACKUP_REGISTER_COUNT: usize = BACKUP_REGISTER_COUNT;
186
187 /// Read content of the backup register.
188 ///
189 /// The registers retain their values during wakes from standby mode or system resets. They also
190 /// retain their value when Vdd is switched off as long as V_BAT is powered.
191 pub fn read_backup_register(&self, register: usize) -> Option<u32> {
192 read_backup_register(&T::regs(), register)
193 }
194
195 /// Set content of the backup register.
196 ///
197 /// The registers retain their values during wakes from standby mode or system resets. They also
198 /// retain their value when Vdd is switched off as long as V_BAT is powered.
199 pub fn write_backup_register(&self, register: usize, value: u32) {
200 write_backup_register(&T::regs(), register, value)
201 }
202}
203
204pub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) {
205 let mut bcd_high: u8 = 0;
206 let mut value = byte;
207
208 while value >= 10 {
209 bcd_high += 1;
210 value -= 10;
211 }
212
213 (bcd_high, ((bcd_high << 4) | value) as u8)
214}
215
216pub(crate) fn bcd2_to_byte(bcd: (u8, u8)) -> u8 {
217 let value = bcd.1 | bcd.0 << 4;
218
219 let tmp = ((value & 0xF0) >> 0x4) * 10;
220
221 tmp + (value & 0x0F)
222}
223
224pub(crate) mod sealed {
225 pub trait Instance {
226 fn regs() -> crate::pac::rtc::Rtc;
227 }
228}
229
230pub trait Instance: sealed::Instance + 'static {}
231
232impl sealed::Instance for crate::peripherals::RTC {
233 fn regs() -> crate::pac::rtc::Rtc {
234 crate::pac::RTC
235 }
236}
237
238impl Instance for crate::peripherals::RTC {}
diff --git a/embassy-stm32/src/rtc/v2/mod.rs b/embassy-stm32/src/rtc/v2/mod.rs
new file mode 100644
index 000000000..296adae89
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/mod.rs
@@ -0,0 +1,171 @@
1use stm32_metapac::rtc::vals::{Init, Osel, Pol};
2
3use super::{Instance, RtcConfig};
4use crate::pac::rtc::Rtc;
5
6#[cfg_attr(rtc_v2f0, path = "v2f0.rs")]
7#[cfg_attr(rtc_v2f2, path = "v2f2.rs")]
8#[cfg_attr(rtc_v2f3, path = "v2f3.rs")]
9#[cfg_attr(rtc_v2f4, path = "v2f4.rs")]
10#[cfg_attr(rtc_v2f7, path = "v2f7.rs")]
11#[cfg_attr(rtc_v2h7, path = "v2h7.rs")]
12#[cfg_attr(rtc_v2l0, path = "v2l0.rs")]
13#[cfg_attr(rtc_v2l1, path = "v2l1.rs")]
14#[cfg_attr(rtc_v2l4, path = "v2l4.rs")]
15#[cfg_attr(rtc_v2wb, path = "v2wb.rs")]
16mod family;
17
18pub use family::*;
19
20impl<'d, T: Instance> super::Rtc<'d, T> {
21 /// Applies the RTC config
22 /// It this changes the RTC clock source the time will be reset
23 pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) {
24 // Unlock the backup domain
25 unsafe {
26 unlock_backup_domain(rtc_config.clock_config as u8);
27 }
28
29 self.write(true, |rtc| unsafe {
30 rtc.cr().modify(|w| {
31 #[cfg(rtc_v2f2)]
32 w.set_fmt(false);
33 #[cfg(not(rtc_v2f2))]
34 w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR);
35 w.set_osel(Osel::DISABLED);
36 w.set_pol(Pol::HIGH);
37 });
38
39 rtc.prer().modify(|w| {
40 w.set_prediv_s(rtc_config.sync_prescaler);
41 w.set_prediv_a(rtc_config.async_prescaler);
42 });
43 });
44
45 self.rtc_config = rtc_config;
46 }
47
48 /// Calibrate the clock drift.
49 ///
50 /// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range.
51 ///
52 /// ### Note
53 ///
54 /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler`
55 /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12).
56 #[cfg(not(rtc_v2f2))]
57 pub fn calibrate(&mut self, mut clock_drift: f32, period: super::RtcCalibrationCyclePeriod) {
58 const RTC_CALR_MIN_PPM: f32 = -487.1;
59 const RTC_CALR_MAX_PPM: f32 = 488.5;
60 const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537;
61
62 if clock_drift < RTC_CALR_MIN_PPM {
63 clock_drift = RTC_CALR_MIN_PPM;
64 } else if clock_drift > RTC_CALR_MAX_PPM {
65 clock_drift = RTC_CALR_MAX_PPM;
66 }
67
68 clock_drift = clock_drift / RTC_CALR_RESOLUTION_PPM;
69
70 self.write(false, |rtc| {
71 unsafe {
72 rtc.calr().write(|w| {
73 match period {
74 super::RtcCalibrationCyclePeriod::Seconds8 => {
75 w.set_calw8(stm32_metapac::rtc::vals::Calw8::EIGHT_SECOND);
76 }
77 super::RtcCalibrationCyclePeriod::Seconds16 => {
78 w.set_calw16(stm32_metapac::rtc::vals::Calw16::SIXTEEN_SECOND);
79 }
80 super::RtcCalibrationCyclePeriod::Seconds32 => {
81 // Set neither `calw8` nor `calw16` to use 32 seconds
82 }
83 }
84
85 // Extra pulses during calibration cycle period: CALP * 512 - CALM
86 //
87 // CALP sets whether pulses are added or omitted.
88 //
89 // CALM contains how many pulses (out of 512) are masked in a
90 // given calibration cycle period.
91 if clock_drift > 0.0 {
92 // Maximum (about 512.2) rounds to 512.
93 clock_drift += 0.5;
94
95 // When the offset is positive (0 to 512), the opposite of
96 // the offset (512 - offset) is masked, i.e. for the
97 // maximum offset (512), 0 pulses are masked.
98 w.set_calp(stm32_metapac::rtc::vals::Calp::INCREASEFREQ);
99 w.set_calm(512 - clock_drift as u16);
100 } else {
101 // Minimum (about -510.7) rounds to -511.
102 clock_drift -= 0.5;
103
104 // When the offset is negative or zero (-511 to 0),
105 // the absolute offset is masked, i.e. for the minimum
106 // offset (-511), 511 pulses are masked.
107 w.set_calp(stm32_metapac::rtc::vals::Calp::NOCHANGE);
108 w.set_calm((clock_drift * -1.0) as u16);
109 }
110 });
111 }
112 })
113 }
114
115 pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
116 where
117 F: FnOnce(&crate::pac::rtc::Rtc) -> R,
118 {
119 let r = T::regs();
120 // Disable write protection.
121 // This is safe, as we're only writin the correct and expected values.
122 unsafe {
123 r.wpr().write(|w| w.set_key(0xca));
124 r.wpr().write(|w| w.set_key(0x53));
125
126 // true if initf bit indicates RTC peripheral is in init mode
127 if init_mode && !r.isr().read().initf() {
128 // to update calendar date/time, time format, and prescaler configuration, RTC must be in init mode
129 r.isr().modify(|w| w.set_init(Init::INITMODE));
130 // wait till init state entered
131 // ~2 RTCCLK cycles
132 while !r.isr().read().initf() {}
133 }
134 }
135
136 let result = f(&r);
137
138 unsafe {
139 if init_mode {
140 r.isr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode
141 }
142
143 // Re-enable write protection.
144 // This is safe, as the field accepts the full range of 8-bit values.
145 r.wpr().write(|w| w.set_key(0xff));
146 }
147 result
148 }
149}
150
151/// Read content of the backup register.
152///
153/// The registers retain their values during wakes from standby mode or system resets. They also
154/// retain their value when Vdd is switched off as long as V_BAT is powered.
155pub fn read_backup_register(rtc: &Rtc, register: usize) -> Option<u32> {
156 if register < BACKUP_REGISTER_COUNT {
157 Some(unsafe { rtc.bkpr(register).read().bkp() })
158 } else {
159 None
160 }
161}
162
163/// Set content of the backup register.
164///
165/// The registers retain their values during wakes from standby mode or system resets. They also
166/// retain their value when Vdd is switched off as long as V_BAT is powered.
167pub fn write_backup_register(rtc: &Rtc, register: usize, value: u32) {
168 if register < BACKUP_REGISTER_COUNT {
169 unsafe { rtc.bkpr(register).write(|w| w.set_bkp(value)) }
170 }
171}
diff --git a/embassy-stm32/src/rtc/v2/v2f0.rs b/embassy-stm32/src/rtc/v2/v2f0.rs
new file mode 100644
index 000000000..d6871d91e
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2f0.rs
@@ -0,0 +1,41 @@
1use stm32_metapac::rcc::vals::Rtcsel;
2
3pub const BACKUP_REGISTER_COUNT: usize = 20;
4
5/// Unlock the backup domain
6pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
7 crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
8 while !crate::pac::PWR.cr1().read().dbp() {}
9
10 let reg = crate::pac::RCC.bdcr().read();
11 assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
12
13 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
14 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
15
16 crate::pac::RCC.bdcr().modify(|w| {
17 // Reset
18 w.set_bdrst(false);
19
20 // Select RTC source
21 w.set_rtcsel(Rtcsel(clock_config));
22 w.set_rtcen(true);
23
24 // Restore bcdr
25 w.set_lscosel(reg.lscosel());
26 w.set_lscoen(reg.lscoen());
27
28 w.set_lseon(reg.lseon());
29 w.set_lsedrv(reg.lsedrv());
30 w.set_lsebyp(reg.lsebyp());
31 });
32 }
33}
34
35pub(crate) unsafe fn enable_peripheral_clk() {
36 // enable peripheral clock for communication
37 crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
38
39 // read to allow the pwr clock to enable
40 crate::pac::PWR.cr1().read();
41}
diff --git a/embassy-stm32/src/rtc/v2/v2f2.rs b/embassy-stm32/src/rtc/v2/v2f2.rs
new file mode 100644
index 000000000..e041f3f4e
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2f2.rs
@@ -0,0 +1,31 @@
1use stm32_metapac::rcc::vals::Rtcsel;
2
3pub const BACKUP_REGISTER_COUNT: usize = 20;
4
5/// Unlock the backup domain
6pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
7 crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
8 while !crate::pac::PWR.cr().read().dbp() {}
9
10 let reg = crate::pac::RCC.bdcr().read();
11
12 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
13 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
14
15 crate::pac::RCC.bdcr().modify(|w| {
16 // Reset
17 w.set_bdrst(false);
18
19 // Select RTC source
20 w.set_rtcsel(Rtcsel(clock_config));
21 w.set_rtcen(true);
22
23 w.set_lseon(reg.lseon());
24 w.set_lsebyp(reg.lsebyp());
25 });
26 }
27}
28
29pub(crate) unsafe fn enable_peripheral_clk() {
30 // Nothing to do
31}
diff --git a/embassy-stm32/src/rtc/v2/v2f3.rs b/embassy-stm32/src/rtc/v2/v2f3.rs
new file mode 100644
index 000000000..e041f3f4e
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2f3.rs
@@ -0,0 +1,31 @@
1use stm32_metapac::rcc::vals::Rtcsel;
2
3pub const BACKUP_REGISTER_COUNT: usize = 20;
4
5/// Unlock the backup domain
6pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
7 crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
8 while !crate::pac::PWR.cr().read().dbp() {}
9
10 let reg = crate::pac::RCC.bdcr().read();
11
12 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
13 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
14
15 crate::pac::RCC.bdcr().modify(|w| {
16 // Reset
17 w.set_bdrst(false);
18
19 // Select RTC source
20 w.set_rtcsel(Rtcsel(clock_config));
21 w.set_rtcen(true);
22
23 w.set_lseon(reg.lseon());
24 w.set_lsebyp(reg.lsebyp());
25 });
26 }
27}
28
29pub(crate) unsafe fn enable_peripheral_clk() {
30 // Nothing to do
31}
diff --git a/embassy-stm32/src/rtc/v2/v2f4.rs b/embassy-stm32/src/rtc/v2/v2f4.rs
new file mode 100644
index 000000000..4dd21cae4
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2f4.rs
@@ -0,0 +1,31 @@
1use stm32_metapac::rcc::vals::Rtcsel;
2
3pub const BACKUP_REGISTER_COUNT: usize = 20;
4
5/// Unlock the backup domain
6pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
7 crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
8 while !crate::pac::PWR.cr1().read().dbp() {}
9
10 let reg = crate::pac::RCC.bdcr().read();
11
12 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
13 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
14
15 crate::pac::RCC.bdcr().modify(|w| {
16 // Reset
17 w.set_bdrst(false);
18
19 // Select RTC source
20 w.set_rtcsel(Rtcsel(clock_config));
21 w.set_rtcen(true);
22
23 w.set_lseon(reg.lseon());
24 w.set_lsebyp(reg.lsebyp());
25 });
26 }
27}
28
29pub(crate) unsafe fn enable_peripheral_clk() {
30 // Nothing to do
31}
diff --git a/embassy-stm32/src/rtc/v2/v2f7.rs b/embassy-stm32/src/rtc/v2/v2f7.rs
new file mode 100644
index 000000000..d6871d91e
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2f7.rs
@@ -0,0 +1,41 @@
1use stm32_metapac::rcc::vals::Rtcsel;
2
3pub const BACKUP_REGISTER_COUNT: usize = 20;
4
5/// Unlock the backup domain
6pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
7 crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
8 while !crate::pac::PWR.cr1().read().dbp() {}
9
10 let reg = crate::pac::RCC.bdcr().read();
11 assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
12
13 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
14 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
15
16 crate::pac::RCC.bdcr().modify(|w| {
17 // Reset
18 w.set_bdrst(false);
19
20 // Select RTC source
21 w.set_rtcsel(Rtcsel(clock_config));
22 w.set_rtcen(true);
23
24 // Restore bcdr
25 w.set_lscosel(reg.lscosel());
26 w.set_lscoen(reg.lscoen());
27
28 w.set_lseon(reg.lseon());
29 w.set_lsedrv(reg.lsedrv());
30 w.set_lsebyp(reg.lsebyp());
31 });
32 }
33}
34
35pub(crate) unsafe fn enable_peripheral_clk() {
36 // enable peripheral clock for communication
37 crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
38
39 // read to allow the pwr clock to enable
40 crate::pac::PWR.cr1().read();
41}
diff --git a/embassy-stm32/src/rtc/v2/v2h7.rs b/embassy-stm32/src/rtc/v2/v2h7.rs
new file mode 100644
index 000000000..f3b180683
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2h7.rs
@@ -0,0 +1,33 @@
1use stm32_metapac::rcc::vals::Rtcsel;
2
3pub const BACKUP_REGISTER_COUNT: usize = 20;
4
5/// Unlock the backup domain
6pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
7 crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
8 while !crate::pac::PWR.cr1().read().dbp() {}
9
10 let reg = crate::pac::RCC.bdcr().read();
11 assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
12
13 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
14 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
15
16 crate::pac::RCC.bdcr().modify(|w| {
17 // Reset
18 w.set_bdrst(false);
19
20 // Select RTC source
21 w.set_rtcsel(Rtcsel(clock_config));
22 w.set_rtcen(true);
23
24 w.set_lseon(reg.lseon());
25 w.set_lsedrv(reg.lsedrv());
26 w.set_lsebyp(reg.lsebyp());
27 });
28 }
29}
30
31pub(crate) unsafe fn enable_peripheral_clk() {
32 // Nothing to do
33}
diff --git a/embassy-stm32/src/rtc/v2/v2l0.rs b/embassy-stm32/src/rtc/v2/v2l0.rs
new file mode 100644
index 000000000..dbd3b0882
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2l0.rs
@@ -0,0 +1,26 @@
1pub const BACKUP_REGISTER_COUNT: usize = 20;
2
3/// Unlock the backup domain
4pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
5 // TODO: Missing from PAC?
6 // crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
7 // while !crate::pac::PWR.cr().read().dbp() {}
8
9 let reg = crate::pac::RCC.csr().read();
10
11 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
12 crate::pac::RCC.csr().modify(|w| {
13 // Select RTC source
14 w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config));
15 w.set_rtcen(true);
16
17 w.set_lseon(reg.lseon());
18 w.set_lsedrv(reg.lsedrv());
19 w.set_lsebyp(reg.lsebyp());
20 });
21 }
22}
23
24pub(crate) unsafe fn enable_peripheral_clk() {
25 // Nothing to do
26}
diff --git a/embassy-stm32/src/rtc/v2/v2l1.rs b/embassy-stm32/src/rtc/v2/v2l1.rs
new file mode 100644
index 000000000..1ac78b31a
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2l1.rs
@@ -0,0 +1,24 @@
1pub const BACKUP_REGISTER_COUNT: usize = 20;
2
3/// Unlock the backup domain
4pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
5 crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
6 while !crate::pac::PWR.cr().read().dbp() {}
7
8 let reg = crate::pac::RCC.csr().read();
9
10 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
11 crate::pac::RCC.csr().modify(|w| {
12 // Select RTC source
13 w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config));
14 w.set_rtcen(true);
15
16 w.set_lseon(reg.lseon());
17 w.set_lsebyp(reg.lsebyp());
18 });
19 }
20}
21
22pub(crate) unsafe fn enable_peripheral_clk() {
23 // Nothing to do
24}
diff --git a/embassy-stm32/src/rtc/v2/v2l4.rs b/embassy-stm32/src/rtc/v2/v2l4.rs
new file mode 100644
index 000000000..d6871d91e
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2l4.rs
@@ -0,0 +1,41 @@
1use stm32_metapac::rcc::vals::Rtcsel;
2
3pub const BACKUP_REGISTER_COUNT: usize = 20;
4
5/// Unlock the backup domain
6pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
7 crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
8 while !crate::pac::PWR.cr1().read().dbp() {}
9
10 let reg = crate::pac::RCC.bdcr().read();
11 assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
12
13 if !reg.rtcen() || reg.rtcsel().0 != clock_config {
14 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
15
16 crate::pac::RCC.bdcr().modify(|w| {
17 // Reset
18 w.set_bdrst(false);
19
20 // Select RTC source
21 w.set_rtcsel(Rtcsel(clock_config));
22 w.set_rtcen(true);
23
24 // Restore bcdr
25 w.set_lscosel(reg.lscosel());
26 w.set_lscoen(reg.lscoen());
27
28 w.set_lseon(reg.lseon());
29 w.set_lsedrv(reg.lsedrv());
30 w.set_lsebyp(reg.lsebyp());
31 });
32 }
33}
34
35pub(crate) unsafe fn enable_peripheral_clk() {
36 // enable peripheral clock for communication
37 crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
38
39 // read to allow the pwr clock to enable
40 crate::pac::PWR.cr1().read();
41}
diff --git a/embassy-stm32/src/rtc/v2/v2wb.rs b/embassy-stm32/src/rtc/v2/v2wb.rs
new file mode 100644
index 000000000..98761fa60
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2wb.rs
@@ -0,0 +1,39 @@
1pub const BACKUP_REGISTER_COUNT: usize = 20;
2
3/// Unlock the backup domain
4pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
5 crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
6 while !crate::pac::PWR.cr1().read().dbp() {}
7
8 let reg = crate::pac::RCC.bdcr().read();
9 assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
10
11 if !reg.rtcen() || reg.rtcsel() != clock_config {
12 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
13
14 crate::pac::RCC.bdcr().modify(|w| {
15 // Reset
16 w.set_bdrst(false);
17
18 // Select RTC source
19 w.set_rtcsel(clock_config);
20 w.set_rtcen(true);
21
22 // Restore bcdr
23 w.set_lscosel(reg.lscosel());
24 w.set_lscoen(reg.lscoen());
25
26 w.set_lseon(reg.lseon());
27 w.set_lsedrv(reg.lsedrv());
28 w.set_lsebyp(reg.lsebyp());
29 });
30 }
31}
32
33pub(crate) unsafe fn enable_peripheral_clk() {
34 // enable peripheral clock for communication
35 crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
36
37 // read to allow the pwr clock to enable
38 crate::pac::PWR.cr1().read();
39}
diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs
new file mode 100644
index 000000000..6998c48c2
--- /dev/null
+++ b/embassy-stm32/src/rtc/v3.rs
@@ -0,0 +1,226 @@
1use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType};
2
3use super::{Instance, RtcCalibrationCyclePeriod, RtcConfig};
4use crate::pac::rtc::Rtc;
5
6impl<'d, T: Instance> super::Rtc<'d, T> {
7 /// Applies the RTC config
8 /// It this changes the RTC clock source the time will be reset
9 pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) {
10 // Unlock the backup domain
11 unsafe {
12 #[cfg(feature = "stm32g0c1ve")]
13 {
14 crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
15 while !crate::pac::PWR.cr1().read().dbp() {}
16 }
17
18 #[cfg(not(any(
19 feature = "stm32g0c1ve",
20 feature = "stm32g491re",
21 feature = "stm32u585zi",
22 feature = "stm32g473cc"
23 )))]
24 {
25 crate::pac::PWR
26 .cr1()
27 .modify(|w| w.set_dbp(stm32_metapac::pwr::vals::Dbp::ENABLED));
28 while crate::pac::PWR.cr1().read().dbp() != stm32_metapac::pwr::vals::Dbp::DISABLED {}
29 }
30
31 let reg = crate::pac::RCC.bdcr().read();
32 assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
33
34 let config_rtcsel = rtc_config.clock_config as u8;
35 #[cfg(not(any(
36 feature = "stm32wl54jc-cm0p",
37 feature = "stm32wle5ub",
38 feature = "stm32g0c1ve",
39 feature = "stm32wl55jc-cm4",
40 feature = "stm32wl55uc-cm4",
41 feature = "stm32g491re",
42 feature = "stm32g473cc",
43 feature = "stm32u585zi",
44 feature = "stm32wle5jb"
45 )))]
46 let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel);
47 #[cfg(feature = "stm32g0c1ve")]
48 let config_rtcsel = stm32_metapac::rcc::vals::Rtcsel(config_rtcsel);
49
50 if !reg.rtcen() || reg.rtcsel() != config_rtcsel {
51 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
52
53 crate::pac::RCC.bdcr().modify(|w| {
54 // Reset
55 w.set_bdrst(false);
56
57 // Select RTC source
58 w.set_rtcsel(config_rtcsel);
59
60 w.set_rtcen(true);
61
62 // Restore bcdr
63 w.set_lscosel(reg.lscosel());
64 w.set_lscoen(reg.lscoen());
65
66 w.set_lseon(reg.lseon());
67 w.set_lsedrv(reg.lsedrv());
68 w.set_lsebyp(reg.lsebyp());
69 });
70 }
71 }
72
73 self.write(true, |rtc| {
74 unsafe {
75 rtc.cr().modify(|w| {
76 w.set_fmt(Fmt::TWENTYFOURHOUR);
77 w.set_osel(Osel::DISABLED);
78 w.set_pol(Pol::HIGH);
79 });
80
81 rtc.prer().modify(|w| {
82 w.set_prediv_s(rtc_config.sync_prescaler);
83 w.set_prediv_a(rtc_config.async_prescaler);
84 });
85
86 // TODO: configuration for output pins
87 rtc.cr().modify(|w| {
88 w.set_out2en(false);
89 w.set_tampalrm_type(TampalrmType::PUSHPULL);
90 w.set_tampalrm_pu(TampalrmPu::NOPULLUP);
91 });
92 }
93 });
94
95 self.rtc_config = rtc_config;
96 }
97
98 const RTC_CALR_MIN_PPM: f32 = -487.1;
99 const RTC_CALR_MAX_PPM: f32 = 488.5;
100 const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537;
101
102 /// Calibrate the clock drift.
103 ///
104 /// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range.
105 ///
106 /// ### Note
107 ///
108 /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler`
109 /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12).
110 pub fn calibrate(&mut self, mut clock_drift: f32, period: RtcCalibrationCyclePeriod) {
111 if clock_drift < Self::RTC_CALR_MIN_PPM {
112 clock_drift = Self::RTC_CALR_MIN_PPM;
113 } else if clock_drift > Self::RTC_CALR_MAX_PPM {
114 clock_drift = Self::RTC_CALR_MAX_PPM;
115 }
116
117 clock_drift = clock_drift / Self::RTC_CALR_RESOLUTION_PPM;
118
119 self.write(false, |rtc| {
120 unsafe {
121 rtc.calr().write(|w| {
122 match period {
123 RtcCalibrationCyclePeriod::Seconds8 => {
124 w.set_calw8(Calw8::EIGHTSECONDS);
125 }
126 RtcCalibrationCyclePeriod::Seconds16 => {
127 w.set_calw16(Calw16::SIXTEENSECONDS);
128 }
129 RtcCalibrationCyclePeriod::Seconds32 => {
130 // Set neither `calw8` nor `calw16` to use 32 seconds
131 }
132 }
133
134 // Extra pulses during calibration cycle period: CALP * 512 - CALM
135 //
136 // CALP sets whether pulses are added or omitted.
137 //
138 // CALM contains how many pulses (out of 512) are masked in a
139 // given calibration cycle period.
140 if clock_drift > 0.0 {
141 // Maximum (about 512.2) rounds to 512.
142 clock_drift += 0.5;
143
144 // When the offset is positive (0 to 512), the opposite of
145 // the offset (512 - offset) is masked, i.e. for the
146 // maximum offset (512), 0 pulses are masked.
147 w.set_calp(Calp::INCREASEFREQ);
148 w.set_calm(512 - clock_drift as u16);
149 } else {
150 // Minimum (about -510.7) rounds to -511.
151 clock_drift -= 0.5;
152
153 // When the offset is negative or zero (-511 to 0),
154 // the absolute offset is masked, i.e. for the minimum
155 // offset (-511), 511 pulses are masked.
156 w.set_calp(Calp::NOCHANGE);
157 w.set_calm((clock_drift * -1.0) as u16);
158 }
159 });
160 }
161 })
162 }
163
164 pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
165 where
166 F: FnOnce(&crate::pac::rtc::Rtc) -> R,
167 {
168 let r = T::regs();
169 // Disable write protection.
170 // This is safe, as we're only writin the correct and expected values.
171 unsafe {
172 r.wpr().write(|w| w.set_key(Key::DEACTIVATE1));
173 r.wpr().write(|w| w.set_key(Key::DEACTIVATE2));
174
175 if init_mode && !r.icsr().read().initf() {
176 r.icsr().modify(|w| w.set_init(Init::INITMODE));
177 // wait till init state entered
178 // ~2 RTCCLK cycles
179 while !r.icsr().read().initf() {}
180 }
181 }
182
183 let result = f(&r);
184
185 unsafe {
186 if init_mode {
187 r.icsr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode
188 }
189
190 // Re-enable write protection.
191 // This is safe, as the field accepts the full range of 8-bit values.
192 r.wpr().write(|w| w.set_key(Key::ACTIVATE));
193 }
194 result
195 }
196}
197
198pub(super) unsafe fn enable_peripheral_clk() {
199 // Nothing to do
200}
201
202pub const BACKUP_REGISTER_COUNT: usize = 32;
203
204/// Read content of the backup register.
205///
206/// The registers retain their values during wakes from standby mode or system resets. They also
207/// retain their value when Vdd is switched off as long as V_BAT is powered.
208pub fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> {
209 if register < BACKUP_REGISTER_COUNT {
210 //Some(rtc.bkpr()[register].read().bits())
211 None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
212 } else {
213 None
214 }
215}
216
217/// Set content of the backup register.
218///
219/// The registers retain their values during wakes from standby mode or system resets. They also
220/// retain their value when Vdd is switched off as long as V_BAT is powered.
221pub fn write_backup_register(_rtc: &Rtc, register: usize, _value: u32) {
222 if register < BACKUP_REGISTER_COUNT {
223 // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
224 //unsafe { self.rtc.bkpr()[register].write(|w| w.bits(value)) }
225 }
226}