stm32f3xx_hal/
rtc.rs

1//! # Real Time Clock
2//!
3//! Interface to the real time clock. See STM32F303 reference manual, section 27.
4//! For more details, see [ST AN4759][].
5//!
6//! [ST AN4759]: https:/www.st.com%2Fresource%2Fen%2Fapplication_note%2Fdm00226326-using-the-hardware-realtime-clock-rtc-and-the-tamper-management-unit-tamp-with-stm32-microcontrollers-stmicroelectronics.pdf&usg=AOvVaw3PzvL2TfYtwS32fw-Uv37h
7
8use crate::pac::{PWR, RTC};
9use crate::rcc::{Enable, APB1, BDCR};
10use core::convert::TryInto;
11use core::fmt;
12use rtcc::{DateTimeAccess, Datelike, Hours, NaiveDate, NaiveDateTime, NaiveTime, Rtcc, Timelike};
13
14/// RTC error type
15#[derive(Copy, Clone, PartialEq, Eq, Debug)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17#[non_exhaustive]
18pub enum Error {
19    /// Invalid input error
20    InvalidInputData,
21    /// Invalid register data, failed to convert to rtcc Type
22    InvalidRtcData,
23}
24
25/// Real Time Clock peripheral
26pub struct Rtc {
27    /// RTC Peripheral register definition
28    rtc: RTC,
29}
30
31#[cfg(feature = "defmt")]
32impl defmt::Format for Rtc {
33    fn format(&self, f: defmt::Formatter) {
34        defmt::write!(f, "Rtc {{ rtc: RTC }}");
35    }
36}
37
38impl fmt::Debug for Rtc {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        f.debug_struct("Rtc").field("rtc", &"RTC").finish()
41    }
42}
43
44impl Rtc {
45    /// Create and enable a new RTC, and configure its clock source and prescalers.
46    /// From AN4759, Table 7, when using the LSE (The only clock source this module
47    /// supports currently), set `prediv_s` to 255, and `prediv_a` to 127 to get a
48    /// calendar clock of 1Hz.
49    /// The `bypass` argument is `true` if you're using an external oscillator that
50    /// doesn't connect to `OSC32_IN`, such as a MEMS resonator.
51    pub fn new(
52        rtc: RTC,
53        prediv_s: u16,
54        prediv_a: u8,
55        bypass: bool,
56        apb1: &mut APB1,
57        bdcr: &mut BDCR,
58        pwr: &mut PWR,
59    ) -> Self {
60        let mut result = Self { rtc };
61
62        enable_lse(bdcr, bypass);
63        unlock(apb1, pwr);
64        enable(bdcr);
65        result.set_24h_fmt();
66
67        result.rtc.prer.modify(|_, w| {
68            w.prediv_s().bits(prediv_s);
69            w.prediv_a().bits(prediv_a)
70        });
71
72        result
73    }
74
75    /// Sets calendar clock to 24 hr format
76    pub fn set_24h_fmt(&mut self) {
77        self.rtc.cr.modify(|_, w| w.fmt().set_bit());
78    }
79    /// Sets calendar clock to 12 hr format
80    pub fn set_12h_fmt(&mut self) {
81        self.rtc.cr.modify(|_, w| w.fmt().clear_bit());
82    }
83
84    /// Reads current hour format selection
85    #[must_use]
86    pub fn is_24h_fmt(&self) -> bool {
87        self.rtc.cr.read().fmt().bit()
88    }
89
90    /// Get access to the underlying register block.
91    ///
92    /// # Safety
93    ///
94    /// This function is not _memory_ unsafe per se, but does not guarantee
95    /// anything about assumptions of invariants made in this implementation.
96    ///
97    /// Changing specific options can lead to un-expected behavior and nothing
98    /// is guaranteed.
99    pub unsafe fn peripheral(&mut self) -> &mut RTC {
100        &mut self.rtc
101    }
102
103    /// Release the RTC peripheral
104    #[must_use]
105    pub fn free(self) -> RTC {
106        // TODO(Sh3Rm4n): Disable peripheral before releasing it.
107        self.rtc
108    }
109
110    /// As described in Section 27.3.7 in RM0316,
111    /// this function is used to disable write protection
112    /// when modifying an RTC register
113    fn modify<F>(&mut self, mut closure: F)
114    where
115        F: FnMut(&mut RTC),
116    {
117        // Disable write protection
118        self.rtc.wpr.write(|w| w.key().bits(0xCA));
119        self.rtc.wpr.write(|w| w.key().bits(0x53));
120        // Enter init mode
121        let isr = self.rtc.isr.read();
122        if isr.initf().bit_is_clear() {
123            self.rtc.isr.modify(|_, w| w.init().set_bit());
124            while self.rtc.isr.read().initf().bit_is_clear() {}
125        }
126        // Invoke closure
127        closure(&mut self.rtc);
128        // Exit init mode
129        self.rtc.isr.modify(|_, w| w.init().clear_bit());
130        // wait for last write to be done
131        while !self.rtc.isr.read().initf().bit_is_clear() {}
132    }
133}
134
135// TODO: check conditional compilation because of Chrono
136impl DateTimeAccess for Rtc {
137    type Error = Error;
138
139    fn set_datetime(&mut self, date: &NaiveDateTime) -> Result<(), Self::Error> {
140        self.set_24h_fmt();
141        let (year_tens, year_units) =
142            bcd2_encode(u32::try_from(date.year() - 1970).map_err(|_| Error::InvalidInputData)?)?;
143        let (month_tens, month_units) = bcd2_encode(date.month())?;
144        let (day_tens, day_units) = bcd2_encode(date.day())?;
145
146        let (hour_tens, hour_units) = bcd2_encode(date.hour())?;
147        let (minutes_tens, minutes_units) = bcd2_encode(date.minute())?;
148        let (second_tens, second_units) = bcd2_encode(date.second())?;
149
150        self.rtc.dr.write(|w| {
151            w.dt().bits(day_tens);
152            w.du().bits(day_units);
153            w.mt().bit(month_tens > 0);
154            w.mu().bits(month_units);
155            w.yt().bits(year_tens);
156            w.yu().bits(year_units)
157        });
158
159        self.rtc.tr.write(|w| {
160            w.ht().bits(hour_tens);
161            w.hu().bits(hour_units);
162            w.mnt().bits(minutes_tens);
163            w.mnu().bits(minutes_units);
164            w.st().bits(second_tens);
165            w.su().bits(second_units);
166            w.pm().clear_bit()
167        });
168
169        Ok(())
170    }
171
172    fn datetime(&mut self) -> Result<NaiveDateTime, Self::Error> {
173        self.set_24h_fmt();
174
175        let day = self.day()?;
176        let month = self.month()?;
177        let year = self.year()?;
178
179        let seconds = self.seconds()?;
180        let minutes = self.minutes()?;
181        let hours = hours_to_u8(self.hours()?)?;
182
183        NaiveDate::from_ymd_opt(year.into(), month.into(), day.into())
184            .ok_or(Error::InvalidRtcData)?
185            .and_hms_opt(hours.into(), minutes.into(), seconds.into())
186            .ok_or(Error::InvalidRtcData)
187    }
188}
189
190impl Rtcc for Rtc {
191    /// Set time using `NaiveTime` (ISO 8601 time without timezone)
192    ///
193    /// Hour format is 24h
194    fn set_time(&mut self, time: &NaiveTime) -> Result<(), Self::Error> {
195        self.set_24h_fmt();
196        let (hour_tens, hour_units) = bcd2_encode(time.hour())?;
197        let (minutes_tens, minutes_units) = bcd2_encode(time.minute())?;
198        let (seconds_tens, seconds_units) = bcd2_encode(time.second())?;
199        self.rtc.tr.write(|w| {
200            w.ht().bits(hour_tens);
201            w.hu().bits(hour_units);
202            w.mnt().bits(minutes_tens);
203            w.mnu().bits(minutes_units);
204            w.st().bits(seconds_tens);
205            w.su().bits(seconds_units);
206            w.pm().clear_bit()
207        });
208
209        Ok(())
210    }
211
212    fn set_seconds(&mut self, seconds: u8) -> Result<(), Self::Error> {
213        if seconds > 59 {
214            return Err(Error::InvalidInputData);
215        }
216        let (seconds_tens, seconds_units) = bcd2_encode(u32::from(seconds))?;
217        self.modify(|rtc| {
218            rtc.tr
219                .modify(|_, w| w.st().bits(seconds_tens).su().bits(seconds_units));
220        });
221
222        Ok(())
223    }
224
225    fn set_minutes(&mut self, minutes: u8) -> Result<(), Self::Error> {
226        if minutes > 59 {
227            return Err(Error::InvalidInputData);
228        }
229        let (minutes_tens, minutes_units) = bcd2_encode(u32::from(minutes))?;
230        self.modify(|rtc| {
231            rtc.tr
232                .modify(|_, w| w.mnt().bits(minutes_tens).mnu().bits(minutes_units));
233        });
234
235        Ok(())
236    }
237
238    fn set_hours(&mut self, hours: Hours) -> Result<(), Self::Error> {
239        let (hour_tens, hour_units) = hours_to_register(hours)?;
240        match hours {
241            Hours::H24(_) => self.set_24h_fmt(),
242            Hours::AM(_) | Hours::PM(_) => self.set_12h_fmt(),
243        }
244
245        self.rtc
246            .tr
247            .modify(|_, w| w.ht().bits(hour_tens).hu().bits(hour_units));
248
249        Ok(())
250    }
251
252    fn set_weekday(&mut self, weekday: u8) -> Result<(), Self::Error> {
253        if !(1..=7).contains(&weekday) {
254            return Err(Error::InvalidInputData);
255        }
256        // SAFETY: check above ensures, that the weekday number is in the valid range (0x01 - 0x07)
257        self.modify(|rtc| rtc.dr.modify(|_, w| unsafe { w.wdu().bits(weekday) }));
258
259        Ok(())
260    }
261
262    fn set_day(&mut self, day: u8) -> Result<(), Self::Error> {
263        if !(1..=31).contains(&day) {
264            return Err(Error::InvalidInputData);
265        }
266        let (day_tens, day_units) = bcd2_encode(u32::from(day))?;
267        self.modify(|rtc| {
268            rtc.dr
269                .modify(|_, w| w.dt().bits(day_tens).du().bits(day_units));
270        });
271
272        Ok(())
273    }
274
275    fn set_month(&mut self, month: u8) -> Result<(), Self::Error> {
276        if !(1..=12).contains(&month) {
277            return Err(Error::InvalidInputData);
278        }
279        let (month_tens, month_units) = bcd2_encode(u32::from(month))?;
280        self.modify(|rtc| {
281            rtc.dr
282                .modify(|_, w| w.mt().bit(month_tens > 0).mu().bits(month_units));
283        });
284
285        Ok(())
286    }
287
288    fn set_year(&mut self, year: u16) -> Result<(), Self::Error> {
289        if !(1970..=2038).contains(&year) {
290            return Err(Error::InvalidInputData);
291        }
292        let (year_tens, yu) = bcd2_encode(u32::from(year))?;
293        self.modify(|rtc| rtc.dr.modify(|_, w| w.yt().bits(year_tens).yu().bits(yu)));
294
295        Ok(())
296    }
297
298    /// Set the date using `NaiveDate` (ISO 8601 calendar date without timezone).
299    /// `WeekDay` is set using the `set_weekday` method
300    fn set_date(&mut self, date: &NaiveDate) -> Result<(), Self::Error> {
301        let (year_tens, yu) =
302            bcd2_encode(u32::try_from(date.year() - 1970).map_err(|_| Error::InvalidInputData)?)?;
303        let (month_tens, month_units) = bcd2_encode(date.month())?;
304        let (day_tens, day_units) = bcd2_encode(date.day())?;
305
306        self.rtc.dr.write(|w| {
307            w.dt().bits(day_tens);
308            w.du().bits(day_units);
309            w.mt().bit(month_tens > 0);
310            w.mu().bits(month_units);
311            w.yt().bits(year_tens);
312            w.yu().bits(yu)
313        });
314
315        Ok(())
316    }
317
318    fn seconds(&mut self) -> Result<u8, Self::Error> {
319        let tr = self.rtc.tr.read();
320        let seconds = bcd2_decode(tr.st().bits(), tr.su().bits());
321        u8::try_from(seconds).map_err(|_| Error::InvalidRtcData)
322    }
323
324    fn minutes(&mut self) -> Result<u8, Self::Error> {
325        let tr = self.rtc.tr.read();
326        let minutes = bcd2_decode(tr.mnt().bits(), tr.mnu().bits());
327        u8::try_from(minutes).map_err(|_| Error::InvalidRtcData)
328    }
329
330    fn hours(&mut self) -> Result<Hours, Self::Error> {
331        let tr = self.rtc.tr.read();
332        let hours = bcd2_decode(tr.ht().bits(), tr.hu().bits());
333        let hours = u8::try_from(hours).map_err(|_| Error::InvalidRtcData)?;
334        if self.is_24h_fmt() {
335            return Ok(Hours::H24(hours));
336        }
337        if !tr.pm().bit() {
338            return Ok(Hours::AM(hours));
339        }
340        Ok(Hours::PM(hours))
341    }
342
343    fn time(&mut self) -> Result<NaiveTime, Self::Error> {
344        self.set_24h_fmt();
345        let seconds = self.seconds()?;
346        let minutes = self.minutes()?;
347        let hours = hours_to_u8(self.hours()?)?;
348
349        NaiveTime::from_hms_opt(hours.into(), minutes.into(), seconds.into())
350            .ok_or(Error::InvalidRtcData)
351    }
352
353    fn weekday(&mut self) -> Result<u8, Self::Error> {
354        let dr = self.rtc.dr.read();
355        let weekday = bcd2_decode(dr.wdu().bits(), 0x00);
356        u8::try_from(weekday).map_err(|_| Error::InvalidRtcData)
357    }
358
359    fn day(&mut self) -> Result<u8, Self::Error> {
360        let dr = self.rtc.dr.read();
361        let day = bcd2_decode(dr.dt().bits(), dr.du().bits());
362        u8::try_from(day).map_err(|_| Error::InvalidRtcData)
363    }
364
365    fn month(&mut self) -> Result<u8, Self::Error> {
366        let dr = self.rtc.dr.read();
367        let month_tens = u8::from(dr.mt().bit());
368        let month = bcd2_decode(month_tens, dr.mu().bits());
369        u8::try_from(month).map_err(|_| Error::InvalidRtcData)
370    }
371
372    fn year(&mut self) -> Result<u16, Self::Error> {
373        let dr = self.rtc.dr.read();
374        let year = bcd2_decode(dr.yt().bits(), dr.yu().bits());
375        u16::try_from(year).map_err(|_| Error::InvalidRtcData)
376    }
377
378    fn date(&mut self) -> Result<NaiveDate, Self::Error> {
379        let day = self.day()?;
380        let month = self.month()?;
381        let year = self.year()?;
382
383        NaiveDate::from_ymd_opt(year.into(), month.into(), day.into()).ok_or(Error::InvalidRtcData)
384    }
385}
386
387// Two 32-bit registers (RTC_TR and RTC_DR) contain the seconds, minutes, hours (12- or 24-hour format), day (day
388// of week), date (day of month), month, and year, expressed in binary coded decimal format
389// (BCD). The sub-seconds value is also available in binary format.
390//
391// The following helper functions encode into BCD format from integer and
392// decode to an integer from a BCD value respectively.
393fn bcd2_encode(word: u32) -> Result<(u8, u8), Error> {
394    let l = match (word / 10).try_into() {
395        Ok(v) => v,
396        Err(_) => {
397            return Err(Error::InvalidRtcData);
398        }
399    };
400    let r = match (word % 10).try_into() {
401        Ok(v) => v,
402        Err(_) => {
403            return Err(Error::InvalidRtcData);
404        }
405    };
406
407    Ok((l, r))
408}
409
410fn bcd2_decode(fst: u8, snd: u8) -> u32 {
411    u32::from(fst) * 10 + u32::from(snd)
412}
413
414fn hours_to_register(hours: Hours) -> Result<(u8, u8), Error> {
415    match hours {
416        Hours::H24(h) => Ok(bcd2_encode(u32::from(h)))?,
417        Hours::AM(h) => Ok(bcd2_encode(u32::from(h - 1)))?,
418        Hours::PM(h) => Ok(bcd2_encode(u32::from(h + 11)))?,
419    }
420}
421
422fn hours_to_u8(hours: Hours) -> Result<u8, Error> {
423    if let Hours::H24(h) = hours {
424        Ok(h)
425    } else {
426        Err(Error::InvalidInputData)
427    }
428}
429
430/// Enable the low frequency external oscillator. This is the only mode currently
431/// supported, to avoid exposing the `CR` and `CRS` registers.
432fn enable_lse(bdcr: &mut BDCR, bypass: bool) {
433    bdcr.bdcr()
434        .modify(|_, w| w.lseon().set_bit().lsebyp().bit(bypass));
435    while bdcr.bdcr().read().lserdy().bit_is_clear() {}
436}
437
438fn unlock(apb1: &mut APB1, pwr: &mut PWR) {
439    // Enable the backup interface by setting PWREN
440    PWR::enable(apb1);
441    pwr.cr.modify(|_, w| {
442        w
443            // Enable access to the backup registers
444            .dbp()
445            .set_bit()
446    });
447
448    while pwr.cr.read().dbp().bit_is_clear() {}
449}
450
451fn enable(bdcr: &mut BDCR) {
452    bdcr.bdcr().modify(|_, w| w.bdrst().enabled());
453    bdcr.bdcr().modify(|_, w| {
454        w.rtcsel().lse();
455        w.rtcen().enabled();
456        w.bdrst().disabled()
457    });
458}