stm32f7xx_hal/
rtc.rs

1//! Interface to the real time clock.
2//! For more details, see
3//! [ST AN4759](https://www.st.com/resource/en/application_note/an4759-using-the-hardware-realtime-clock-rtc-and-the-tamper-management-unit-tamp-with-stm32-microcontrollers-stmicroelectronics.pdf)
4
5use crate::pac::rtc::{dr, tr};
6use crate::pac::{PWR, RCC, RTC};
7use crate::rcc::{Clocks, APB1};
8use core::convert::TryInto;
9use time::{Date, PrimitiveDateTime, Time};
10
11/// Invalid input error
12#[derive(Debug)]
13pub enum Error {
14    InvalidInputData,
15}
16
17pub const LSE_BITS: u8 = 0b01;
18
19#[derive(Copy, Clone, PartialEq)]
20pub enum RtcClock {
21    /// LSE (Low-Speed External)
22    ///
23    /// This is in the Backup power domain, and so it can
24    /// remain operational as long as VBat is present.
25    Lse,
26    /// LSI (Low-Speed Internal)
27    ///
28    /// This clock remains functional in Stop or Standby mode,
29    /// but requires VDD to remain powered. LSI is an RC
30    /// oscillator and has poor accuracy.
31    Lsi,
32    /// HSE (High-Speed External) divided by 2..=31
33    ///
34    /// The resulting clock must be lower than 1MHz. This clock is
35    /// automatically disabled by hardware when the CPU enters Stop or
36    /// standby mode.
37    Hse { divider: u8 },
38}
39
40pub struct Rtc {
41    pub regs: RTC,
42}
43
44impl Rtc {
45    /// Create and enable a new RTC, and configure its clock source and prescalers.
46    ///     
47    /// ** Assumes 1970-01-01 00:00:00 Epoch **
48    ///
49    /// See AN4759 (Rev 7) Table 7 for configuration of `prediv_s` and `prediv_a`,
50    /// respectively the formula to calculate `ck_spre` on the same page.
51    ///
52    /// For example, when using the LSE,
53    /// set `prediv_s` to 255, and `prediv_a` to 127 to get a calendar clock of 1Hz.
54    ///
55    /// # Panics
56    /// A panic is triggered in case the RTC returns invalid a date or time, for example, hours greater than 23.
57    ///
58    /// # Note
59    /// This implementation assumes that the APB clock is greater than (>=) seven (7) times the RTC clock.
60    /// This ensures a secure behavior of the synchronization mechanism.
61    pub fn new(
62        regs: RTC,
63        prediv_s: u16,
64        prediv_a: u8,
65        clock_source: RtcClock,
66        clocks: Clocks,
67        apb1: &mut APB1,
68        pwr: &mut PWR,
69    ) -> Option<Self> {
70        let mut result = Self { regs };
71        let rcc = unsafe { &(*RCC::ptr()) };
72
73        // Steps:
74        // Enable PWR and DBP
75        // Enable LSE (if needed)
76        // Enable RTC Clock
77        // Disable Write Protect
78        // Enter Init
79        // Configure 24 hour format
80        // Set prescalers
81        // Exit Init
82        // Enable write protect
83
84        // As per the sample code, unlock comes first. (Enable PWR and DBP)
85        unlock(apb1, pwr);
86
87        match clock_source {
88            RtcClock::Lse => {
89                // Check if LSE is enabled.
90                clocks.lse()?;
91                // Force a reset of the backup domain.
92                rcc.bdcr.modify(|_, w| w.bdrst().enabled());
93                rcc.bdcr.modify(|_, w| w.bdrst().disabled());
94                // Set clock source to LSE.
95                rcc.bdcr.modify(|_, w| w.rtcsel().lse());
96            }
97            RtcClock::Lsi => {
98                // Check if LSI is enabled.
99                clocks.lsi()?;
100                // Force a reset of the backup domain.
101                rcc.bdcr.modify(|_, w| w.bdrst().enabled());
102                rcc.bdcr.modify(|_, w| w.bdrst().disabled());
103                // Set clock source to LSI.
104                rcc.bdcr.modify(|_, w| w.rtcsel().lsi());
105            }
106            RtcClock::Hse { divider } => {
107                // Check if HSE is enabled.
108                clocks.hse()?;
109                // Set RTCPRE division factor (HES_RTC).
110                rcc.cfgr.modify(|_, w| w.rtcpre().bits(divider));
111                // Force a reset of the backup domain.
112                rcc.bdcr.modify(|_, w| w.bdrst().enabled());
113                rcc.bdcr.modify(|_, w| w.bdrst().disabled());
114                // Set clock source to LSE.
115                rcc.bdcr.modify(|_, w| w.rtcsel().hse());
116            }
117        }
118        // Start the actual RTC.
119        rcc.bdcr.modify(|_, w| w.rtcen().enabled());
120
121        result.modify(|regs| {
122            // Set 24 Hour
123            regs.cr.modify(|_, w| w.fmt().clear_bit());
124            // Set prescalers
125            regs.prer.modify(|_, w| {
126                w.prediv_s().bits(prediv_s);
127                w.prediv_a().bits(prediv_a)
128            })
129        });
130
131        Some(result)
132    }
133
134    /// As described in Section 27.3.7 in RM0316,
135    /// this function is used to disable write protection
136    /// when modifying an RTC register
137    fn modify<F>(&mut self, mut closure: F)
138    where
139        F: FnMut(&mut RTC),
140    {
141        // Disable write protection
142        self.regs.wpr.write(|w| unsafe { w.bits(0xCA) });
143        self.regs.wpr.write(|w| unsafe { w.bits(0x53) });
144        // Enter init mode
145        let isr = self.regs.isr.read();
146        if isr.initf().bit_is_clear() {
147            self.regs.isr.modify(|_, w| w.init().set_bit());
148            while self.regs.isr.read().initf().bit_is_clear() {}
149        }
150        // Invoke closure
151        closure(&mut self.regs);
152        // Exit init mode
153        self.regs.isr.modify(|_, w| w.init().clear_bit());
154        // wait for last write to be done
155        while !self.regs.isr.read().initf().bit_is_clear() {}
156
157        // Enable write protection
158        self.regs.wpr.write(|w| unsafe { w.bits(0xFF) });
159    }
160
161    /// Set the time using time::Time.
162    pub fn set_time(&mut self, time: &Time) -> Result<(), Error> {
163        let (ht, hu) = bcd2_encode(time.hour().into())?;
164        let (mnt, mnu) = bcd2_encode(time.minute().into())?;
165        let (st, su) = bcd2_encode(time.second().into())?;
166        self.modify(|regs| {
167            regs.tr.write(|w| {
168                w.ht().bits(ht);
169                w.hu().bits(hu);
170                w.mnt().bits(mnt);
171                w.mnu().bits(mnu);
172                w.st().bits(st);
173                w.su().bits(su);
174                w.pm().clear_bit()
175            })
176        });
177
178        Ok(())
179    }
180
181    /// Set the seconds [0-59].
182    pub fn set_seconds(&mut self, seconds: u8) -> Result<(), Error> {
183        if seconds > 59 {
184            return Err(Error::InvalidInputData);
185        }
186        let (st, su) = bcd2_encode(seconds.into())?;
187        self.modify(|regs| regs.tr.modify(|_, w| w.st().bits(st).su().bits(su)));
188
189        Ok(())
190    }
191
192    /// Set the minutes [0-59].
193    pub fn set_minutes(&mut self, minutes: u8) -> Result<(), Error> {
194        if minutes > 59 {
195            return Err(Error::InvalidInputData);
196        }
197        let (mnt, mnu) = bcd2_encode(minutes.into())?;
198        self.modify(|regs| regs.tr.modify(|_, w| w.mnt().bits(mnt).mnu().bits(mnu)));
199
200        Ok(())
201    }
202
203    /// Set the hours [0-23].
204    pub fn set_hours(&mut self, hours: u8) -> Result<(), Error> {
205        if hours > 23 {
206            return Err(Error::InvalidInputData);
207        }
208        let (ht, hu) = bcd2_encode(hours.into())?;
209
210        self.modify(|regs| regs.tr.modify(|_, w| w.ht().bits(ht).hu().bits(hu)));
211
212        Ok(())
213    }
214
215    /// Set the day of week [1-7].
216    pub fn set_weekday(&mut self, weekday: u8) -> Result<(), Error> {
217        if !(1..=7).contains(&weekday) {
218            return Err(Error::InvalidInputData);
219        }
220        self.modify(|regs| regs.dr.modify(|_, w| unsafe { w.wdu().bits(weekday) }));
221
222        Ok(())
223    }
224
225    /// Set the day of month [1-31].
226    pub fn set_day(&mut self, day: u8) -> Result<(), Error> {
227        if !(1..=31).contains(&day) {
228            return Err(Error::InvalidInputData);
229        }
230        let (dt, du) = bcd2_encode(day as u32)?;
231        self.modify(|regs| regs.dr.modify(|_, w| w.dt().bits(dt).du().bits(du)));
232
233        Ok(())
234    }
235
236    /// Set the month [1-12].
237    pub fn set_month(&mut self, month: u8) -> Result<(), Error> {
238        if !(1..=12).contains(&month) {
239            return Err(Error::InvalidInputData);
240        }
241        let (mt, mu) = bcd2_encode(month as u32)?;
242        self.modify(|regs| regs.dr.modify(|_, w| w.mt().bit(mt > 0).mu().bits(mu)));
243
244        Ok(())
245    }
246
247    /// Set the year [1970-2069].
248    ///
249    /// The year cannot be less than 1970, since the Unix epoch is assumed (1970-01-01 00:00:00).
250    /// Also, the year cannot be greater than 2069 since the RTC range is 0 - 99.
251    pub fn set_year(&mut self, year: u16) -> Result<(), Error> {
252        if !(1970..=2069).contains(&year) {
253            return Err(Error::InvalidInputData);
254        }
255        let (yt, yu) = bcd2_encode(year as u32 - 1970)?;
256        self.modify(|regs| regs.dr.modify(|_, w| w.yt().bits(yt).yu().bits(yu)));
257
258        Ok(())
259    }
260
261    /// Set the date.
262    ///
263    /// The year cannot be less than 1970, since the Unix epoch is assumed (1970-01-01 00:00:00).
264    /// Also, the year cannot be greater than 2069 since the RTC range is 0 - 99.
265    pub fn set_date(&mut self, date: &Date) -> Result<(), Error> {
266        if !(1970..=2069).contains(&date.year()) {
267            return Err(Error::InvalidInputData);
268        }
269
270        let (yt, yu) = bcd2_encode((date.year() - 1970) as u32)?;
271        let (mt, mu) = bcd2_encode(u8::from(date.month()).into())?;
272        let (dt, du) = bcd2_encode(date.day().into())?;
273
274        self.modify(|regs| {
275            regs.dr.write(|w| {
276                w.dt().bits(dt);
277                w.du().bits(du);
278                w.mt().bit(mt > 0);
279                w.mu().bits(mu);
280                w.yt().bits(yt);
281                w.yu().bits(yu)
282            })
283        });
284
285        Ok(())
286    }
287
288    /// Set the date and time.
289    ///
290    /// The year cannot be less than 1970, since the Unix epoch is assumed (1970-01-01 00:00:00).
291    /// Also, the year cannot be greater than 2069 since the RTC range is 0 - 99.
292    pub fn set_datetime(&mut self, date: &PrimitiveDateTime) -> Result<(), Error> {
293        if !(1970..=2069).contains(&date.year()) {
294            return Err(Error::InvalidInputData);
295        }
296
297        let (yt, yu) = bcd2_encode((date.year() - 1970) as u32)?;
298        let (mt, mu) = bcd2_encode(u8::from(date.month()).into())?;
299        let (dt, du) = bcd2_encode(date.day().into())?;
300
301        let (ht, hu) = bcd2_encode(date.hour().into())?;
302        let (mnt, mnu) = bcd2_encode(date.minute().into())?;
303        let (st, su) = bcd2_encode(date.second().into())?;
304
305        self.modify(|regs| {
306            regs.dr.write(|w| {
307                w.dt().bits(dt);
308                w.du().bits(du);
309                w.mt().bit(mt > 0);
310                w.mu().bits(mu);
311                w.yt().bits(yt);
312                w.yu().bits(yu)
313            });
314            regs.tr.write(|w| {
315                w.ht().bits(ht);
316                w.hu().bits(hu);
317                w.mnt().bits(mnt);
318                w.mnu().bits(mnu);
319                w.st().bits(st);
320                w.su().bits(su);
321                w.pm().clear_bit()
322            })
323        });
324
325        Ok(())
326    }
327
328    pub fn get_datetime(&mut self) -> PrimitiveDateTime {
329        // Wait for Registers synchronization flag,  to ensure consistency between the RTC_SSR, RTC_TR and RTC_DR shadow registers.
330        while self.regs.isr.read().rsf().bit_is_clear() {}
331
332        // Reading either RTC_SSR or RTC_TR locks the values in the higher-order calendar shadow registers until RTC_DR is read.
333        // So it is important to always read SSR, TR and then DR or TR and then DR.
334        let tr = self.regs.tr.read();
335        let dr = self.regs.dr.read();
336        // In case the software makes read accesses to the calendar in a time interval smaller
337        // than 2 RTCCLK periods: RSF must be cleared by software after the first calendar read.
338        self.regs.isr.modify(|_, w| w.rsf().clear_bit());
339
340        let seconds = decode_seconds(&tr);
341        let minutes = decode_minutes(&tr);
342        let hours = decode_hours(&tr);
343        let day = decode_day(&dr);
344        let month = decode_month(&dr);
345        let year = decode_year(&dr);
346
347        PrimitiveDateTime::new(
348            Date::from_calendar_date(year.into(), month.try_into().unwrap(), day).unwrap(),
349            Time::from_hms(hours, minutes, seconds).unwrap(),
350        )
351    }
352}
353
354// Two 32-bit registers (RTC_TR and RTC_DR) contain the seconds, minutes, hours (12- or 24-hour format), day (day
355// of week), date (day of month), month, and year, expressed in binary coded decimal format
356// (BCD). The sub-seconds value is also available in binary format.
357//
358// The following helper functions encode into BCD format from integer and
359// decode to an integer from a BCD value respectively.
360fn bcd2_encode(word: u32) -> Result<(u8, u8), Error> {
361    let l = match (word / 10).try_into() {
362        Ok(v) => v,
363        Err(_) => {
364            return Err(Error::InvalidInputData);
365        }
366    };
367    let r = match (word % 10).try_into() {
368        Ok(v) => v,
369        Err(_) => {
370            return Err(Error::InvalidInputData);
371        }
372    };
373
374    Ok((l, r))
375}
376
377fn bcd2_decode(fst: u8, snd: u8) -> u32 {
378    (fst * 10 + snd).into()
379}
380
381fn unlock(apb1: &mut APB1, pwr: &mut PWR) {
382    apb1.enr().modify(|_, w| {
383        w
384            // Enable the backup interface by setting PWREN
385            .pwren()
386            .set_bit()
387    });
388    pwr.cr1.modify(|_, w| {
389        w
390            // Enable access to the backup registers
391            .dbp()
392            .set_bit()
393    });
394}
395
396#[inline(always)]
397fn decode_seconds(tr: &tr::R) -> u8 {
398    bcd2_decode(tr.st().bits(), tr.su().bits()) as u8
399}
400
401#[inline(always)]
402fn decode_minutes(tr: &tr::R) -> u8 {
403    bcd2_decode(tr.mnt().bits(), tr.mnu().bits()) as u8
404}
405
406#[inline(always)]
407fn decode_hours(tr: &tr::R) -> u8 {
408    bcd2_decode(tr.ht().bits(), tr.hu().bits()) as u8
409}
410
411#[inline(always)]
412fn decode_day(dr: &dr::R) -> u8 {
413    bcd2_decode(dr.dt().bits(), dr.du().bits()) as u8
414}
415
416#[inline(always)]
417fn decode_month(dr: &dr::R) -> u8 {
418    let mt: u8 = if dr.mt().bit() { 1 } else { 0 };
419    bcd2_decode(mt, dr.mu().bits()) as u8
420}
421
422#[inline(always)]
423fn decode_year(dr: &dr::R) -> u16 {
424    let year = bcd2_decode(dr.yt().bits(), dr.yu().bits()) + 1970; // 1970-01-01 is the epoch begin.
425    year as u16
426}