stm32l0xx-hal 0.10.0

Peripheral access API for STM32L0 series microcontrollers
Documentation
//! Interface to the Real-time clock (RTC) peripheral.
//!
//! The real-time clock (RTC) is an independent BCD timer/counter. The RTC
//! provides a time- of-day clock/calendar with programmable alarm interrupts.
//!
//! ## Date Types
//!
//! The date/time types (`NaiveDate`, `NaiveTime` and `NaiveDateTime`), are
//! re-exported from [`rtcc`](https://docs.rs/rtcc/), which in turn re-exports
//! them from [`chrono`](https://docs.rs/chrono/).
//!
//! To use methods like `.year()` or `.hour()`, you may need to import the
//! `Datelike` or `Timelike` traits.
//!
//! ## Valid Date Range
//!
//! The RTC only supports two-digit years (00-99). The value 00 corresponds to
//! the year 2000 (not 1970!). Additionally, the year 00 cannot be used because
//! this value is the RTC domain reset default value. This means that dates in
//! the range 2001-01-01 to 2099-12-31 can be represented.
//!
//! ## More Information
//!
//! See STM32L0x2 reference manual, chapter 26 or STM32L0x1 reference manual,
//! chapter 22 for more details.

use core::convert::TryInto;

use embedded_time::rate::Extensions;
use void::Void;

use crate::{
    hal::timer::{self, Cancel as _},
    pac,
    pwr::PWR,
    rcc::Rcc,
};

#[doc(no_inline)]
pub use rtcc::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike};

/// Errors that can occur when dealing with the RTC.
#[derive(Debug, Eq, PartialEq)]
pub enum Error {
    /// Invalid input data was used (e.g. a year outside the 2000-2099 range).
    InvalidInputData,
}

/// Binary coded decimal with 2 bytes.
struct Bcd2 {
    pub tens: u8,
    pub units: u8,
}

/// Two 32-bit registers (RTC_TR and RTC_DR) contain the seconds, minutes, hours
/// (12- or 24-hour format), day (day of week), date (day of month), month, and
/// year, expressed in binary coded decimal format (BCD). The sub-seconds value
/// is also available in binary format.
///
/// The following helper functions encode into BCD format from integer and
/// decode to an integer from a BCD value respectively.
fn bcd2_encode(word: u32) -> Result<Bcd2, Error> {
    let tens = (word / 10)
        .try_into()
        .map_err(|_| Error::InvalidInputData)?;
    let units = (word % 10)
        .try_into()
        .map_err(|_| Error::InvalidInputData)?;
    Ok(Bcd2 { tens, units })
}

fn bcd2_decode(first: u8, second: u8) -> u32 {
    (first * 10 + second).into()
}

/// Entry point to the RTC API.
pub struct Rtc {
    rtc: pac::RTC,
    read_twice: bool,
}

impl Rtc {
    /// Initializes the RTC API.
    ///
    /// The `init` argument will only be used, if the real-time clock is not
    /// already configured. If the clock is not yet configured, and init is set
    /// to `None`, then the datetime corresponding to `2000-01-01 00:00:00`
    /// will be used for initialization.
    ///
    /// # Errors
    ///
    /// Returns [`Error::InvalidInputData`] if the `init` datetime is outside
    /// of the valid range (years 2000-2099).
    ///
    /// # Panics
    ///
    /// Panics, if the ABP1 clock frequency is lower than the RTC clock
    /// frequency. The RTC is currently hardcoded to use the LSE as clock source
    /// which runs at 32768 Hz.
    pub fn new(
        rtc: pac::RTC,
        rcc: &mut Rcc,
        pwr: &PWR,
        init: Option<NaiveDateTime>,
    ) -> Result<Self, Error> {
        // Backup write protection must be disabled by setting th DBP bit in
        // PWR_CR, otherwise it's not possible to access the RTC registers. We
        // assume that this was done during PWR initialization. To make sure it
        // already happened, this function requires a reference to PWR.

        // Select the LSE clock as the clock source for the RTC clock, as that's
        // the only source that keeps the RTC clocked and functional over system
        // resets.
        // Other clock sources are currently not supported.
        //
        // ATTENTION:
        // The prescaler settings in `set` assume that the LSE is selected, and
        // that the frequency is 32768 Hz. If you change the clock selection
        // here, you have to adapt the prescaler settings too.

        // Enable LSE clock
        rcc.enable_lse(pwr);
        rcc.rb.csr.modify(|_, w| {
            // Select LSE as RTC clock source.
            // This is safe, as we're writing a valid bit pattern.
            w.rtcsel().bits(0b01);
            // Enable RTC clock
            w.rtcen().set_bit()
        });

        let apb1_clk = rcc.clocks.apb1_clk();
        let rtc_clk = 32_768u32.Hz(); // LSE crystal frequency

        // The APB1 clock must not be slower than the RTC clock.
        if apb1_clk < rtc_clk {
            panic!(
                "APB1 clock ({}) is slower than RTC clock ({})",
                apb1_clk, rtc_clk,
            );
        }

        // If the APB1 clock frequency is less than 7 times the RTC clock
        // frequency, special care must be taken when reading some registers.
        let read_twice = apb1_clk.0 < 7 * rtc_clk.0;

        // Instantiate `Rtc` struct
        let mut rtc = Self { rtc, read_twice };

        // Initialize RTC, if not yet initialized
        if rtc.rtc.isr.read().inits().bit_is_clear() {
            rtc.set(init.unwrap_or_else(|| NaiveDate::from_ymd(2001, 1, 1).and_hms(0, 0, 0)))?;
        }

        // Disable wakeup timer. It's periodic and persists over resets, but for
        // ease of use, let's disable it on intialization, unless the user
        // wishes to start it again.
        //
        // Can't panic, as the error type is `Void`.
        rtc.wakeup_timer().cancel().unwrap();

        // Clear RSF bit, in case we woke up from Stop or Standby mode. This is
        // necessary, according to section 26.4.8.
        rtc.rtc.isr.write(|w| w.rsf().set_bit());

        Ok(rtc)
    }

    /// Sets the date/time.
    ///
    /// Note: Only dates in the range `2001-01-01 00:00:00` to
    /// `2099-12-31 23:59:59` are supported. If a date outside this range is
    /// passed in, [`Error::InvalidInputData`] will be returned.
    pub fn set(&mut self, instant: NaiveDateTime) -> Result<(), Error> {
        // Validate and encode datetime
        let y: i32 = instant.year();
        if !(2001..=2099).contains(&y) {
            return Err(Error::InvalidInputData);
        }
        let year = bcd2_encode((y - 2000) as u32)?;
        let month = bcd2_encode(instant.month())?;
        let day = bcd2_encode(instant.day())?;
        let hours = bcd2_encode(instant.hour())?;
        let minutes = bcd2_encode(instant.minute())?;
        let seconds = bcd2_encode(instant.second())?;

        // Write instant to RTC
        self.write(move |rtc| {
            // Start initialization
            rtc.isr.modify(|_, w| w.init().set_bit());

            // Wait until RTC register access is allowed
            while rtc.isr.read().initf().bit_is_clear() {}

            // Configure RTC. For now, the default values are all fine.
            rtc.cr.reset();

            // Configure the prescaler to generate a 1 Hz clock for the
            // calendar.
            //
            // ATTENTION:
            // This assumes the RTC clock frequency is 32768 Hz. If this
            // assumption holds no longer true, you need to change this code.
            rtc.prer.write(|w|
                // Safe, because we're only writing valid values to the fields.
                unsafe {
                    w.prediv_a().bits(0x7f);
                    w.prediv_s().bits(0xff)
                });

            // Write time
            rtc.tr.write(|w|
                // Safe, as `NaiveTime` verifies that its fields are valid.
                w
                    // 24-hour format
                    .pm().clear_bit()
                    // Hours
                    .ht().bits(hours.tens)
                    .hu().bits(hours.units)
                    // Minutes
                    .mnt().bits(minutes.tens)
                    .mnu().bits(minutes.units)
                    // Seconds
                    .st().bits(seconds.tens)
                    .su().bits(seconds.units));

            // Write date
            rtc.dr.write(|w|
                // Safe, as `NaiveDate` verifies that its fields are valid.
                w
                    // Year
                    .yt().bits(year.tens)
                    .yu().bits(year.units)
                    // Month
                    .mt().bit(month.tens > 0)
                    .mu().bits(month.units)
                    // Day
                    .dt().bits(day.tens)
                    .du().bits(day.units));

            // Exit initialization
            rtc.isr.modify(|_, w| w.init().clear_bit());
        });

        Ok(())
    }

    /// Read and return the current date/time from the RTC.
    pub fn now(&mut self) -> NaiveDateTime {
        // We need to wait until the RSF bit is set, for a multitude of reasons:
        // - In case the last read was within two cycles of RTCCLK. Not sure why
        //   that's important, but the documentation says so.
        // - In case the registers are not yet ready after waking up from a
        //   low-power mode.
        // - In case the registers are not yet ready after initialization.
        //
        // All of this is explain in section 26.4.8.
        while self.rtc.isr.read().rsf().bit_is_clear() {}

        // Reading the TR register locks the DR register until we clear the RSF
        // flag, so there's no danger of reading something weird here, as long
        // as this order of access is kept.
        let mut tr = self.rtc.tr.read();
        let mut dr = self.rtc.dr.read();

        // If the APB1 clock frequency is less than 7 times the RTC clock
        // frequency, we need to read the time and date registers twice, until
        // the two reads match. See section 26.4.8.
        if self.read_twice {
            loop {
                let tr2 = self.rtc.tr.read();
                let dr2 = self.rtc.dr.read();

                if tr.bits() == tr2.bits() && dr.bits() == dr2.bits() {
                    break;
                } else {
                    tr = tr2;
                    dr = dr2;
                }
            }
        }

        self.write(|rtc| {
            // Clear the RSF flag, to unlock the TR and DR registers.
            rtc.isr.write(|w| w.rsf().set_bit());
        });

        // Read date and time
        let year = bcd2_decode(dr.yt().bits(), dr.yu().bits()) as i32 + 2000;
        let month = bcd2_decode(dr.mt().bit() as u8, dr.mu().bits());
        let day = bcd2_decode(dr.dt().bits(), dr.du().bits());
        let hour = bcd2_decode(tr.ht().bits(), tr.hu().bits());
        let minute = bcd2_decode(tr.mnt().bits(), tr.mnu().bits());
        let second = bcd2_decode(tr.st().bits(), tr.su().bits());

        NaiveDate::from_ymd(year, month, day).and_hms(hour, minute, second)
    }

    /// Enable interrupts
    ///
    /// The interrupts set to `true` in `interrupts` will be enabled. Those set
    /// to false will not be modified.
    pub fn enable_interrupts(&mut self, interrupts: Interrupts) {
        self.write(|rtc| {
            rtc.cr.modify(|_, w| {
                if interrupts.timestamp {
                    w.tsie().set_bit();
                }
                if interrupts.wakeup_timer {
                    w.wutie().set_bit();
                }
                if interrupts.alarm_b {
                    w.alrbie().set_bit();
                }
                if interrupts.alarm_a {
                    w.alraie().set_bit();
                }
                w
            });
        })
    }

    /// Disable interrupts
    ///
    /// The interrupts set to `true` in `interrupts` will be disabled. Those set
    /// to false will not be modified.
    pub fn disable_interrupts(&mut self, interrupts: Interrupts) {
        self.write(|rtc| {
            rtc.cr.modify(|_, w| {
                if interrupts.timestamp {
                    w.tsie().clear_bit();
                }
                if interrupts.wakeup_timer {
                    w.wutie().clear_bit();
                }
                if interrupts.alarm_b {
                    w.alrbie().clear_bit();
                }
                if interrupts.alarm_a {
                    w.alraie().clear_bit();
                }
                w
            });
        })
    }

    /// Access the wakeup timer
    pub fn wakeup_timer(&mut self) -> WakeupTimer {
        WakeupTimer { rtc: self }
    }

    /// Disable write protection, run the passed in function, then re-enable
    /// write protection.
    fn write<F, R>(&mut self, f: F) -> R
    where
        F: FnOnce(&pac::RTC) -> R,
    {
        // Disable write protection.
        // This is safe, as we're only writing the correct and expected values.
        self.rtc.wpr.write(|w| w.key().bits(0xca));
        self.rtc.wpr.write(|w| w.key().bits(0x53));

        let result = f(&self.rtc);

        // Re-enable write protection.
        // This is safe, as the field accepts the full range of 8-bit values.
        self.rtc.wpr.write(|w| w.key().bits(0xff));

        result
    }
}

/// Flags to enable/disable RTC interrupts.
#[derive(Default)]
pub struct Interrupts {
    pub timestamp: bool,
    pub wakeup_timer: bool,
    pub alarm_a: bool,
    pub alarm_b: bool,
}

/// The RTC wakeup timer
///
/// This timer can be used in two ways:
/// 1. Continually call `wait` until it returns `Ok(())`.
/// 2. Set up the RTC interrupt.
///
/// If you use an interrupt, you should still call `wait` once, after the
/// interrupt fired. This should return `Ok(())` immediately. Doing this will
/// reset the timer flag. If you don't do this, the interrupt will not fire
/// again, if you go to sleep.
///
/// You don't need to call `wait`, if you call `cancel`, as that also resets the
/// flag. Restarting the timer by calling `start` will also reset the flag.
pub struct WakeupTimer<'r> {
    rtc: &'r mut Rtc,
}

impl timer::Periodic for WakeupTimer<'_> {}

impl timer::CountDown for WakeupTimer<'_> {
    type Time = u32;

    /// Starts the wakeup timer
    ///
    /// The `delay` argument specifies the timer delay in seconds. Up to 17 bits
    /// of delay are supported, giving us a range of over 36 hours.
    ///
    /// # Panics
    ///
    /// The `delay` argument must be in the range `1 <= delay < 2^17`.
    /// Panics, if `delay` is outside of that range.
    fn start<T>(&mut self, delay: T)
    where
        T: Into<Self::Time>,
    {
        let delay = delay.into();
        assert!((1..=0x1_FF_FF).contains(&delay));

        let delay = delay - 1;

        // Can't panic, as the error type is `Void`.
        self.cancel().unwrap();

        self.rtc.write(|rtc| {
            // Set the wakeup delay
            rtc.wutr.write(|w|
                // Write the lower 16 bits of `delay`. The 17th bit is taken
                // care of via WUCKSEL in CR (see below).
                // This is safe, as the field accepts a full 16 bit value.
                w.wut().bits(delay as u16));
            // This is safe, as we're only writing valid bit patterns.
            rtc.cr.modify(|_, w| {
                if delay & 0x1_00_00 != 0 {
                    unsafe {
                        w.wucksel().bits(0b110);
                    }
                } else {
                    unsafe {
                        w.wucksel().bits(0b100);
                    }
                }

                // Enable wakeup timer
                w.wute().set_bit()
            });
        });

        // Let's wait for WUTWF to clear. Otherwise we might run into a race
        // condition, if the user calls this method again really quickly.
        while self.rtc.rtc.isr.read().wutwf().bit_is_set() {}
    }

    fn wait(&mut self) -> nb::Result<(), Void> {
        if self.rtc.rtc.isr.read().wutf().bit_is_set() {
            self.rtc.write(|rtc| {
                // Clear wakeup timer flag
                rtc.isr.modify(|_, w| w.wutf().clear_bit());
            });

            return Ok(());
        }

        Err(nb::Error::WouldBlock)
    }
}

impl timer::Cancel for WakeupTimer<'_> {
    type Error = Void;

    fn cancel(&mut self) -> Result<(), Self::Error> {
        self.rtc.write(|rtc| {
            // Disable the wakeup timer
            rtc.cr.modify(|_, w| w.wute().clear_bit());

            // Wait until we're allowed to update the wakeup timer configuration
            while rtc.isr.read().wutwf().bit_is_clear() {}

            // Clear wakeup timer flag
            rtc.isr.modify(|_, w| w.wutf().clear_bit());

            // According to the reference manual, section 26.7.4, the WUTF flag
            // must be cleared at least 1.5 RTCCLK periods "before WUTF is set
            // to 1 again". If that's true, we're on the safe side, because we
            // use ck_spre as the clock for this timer, which we've scaled to 1
            // Hz.
            //
            // I have the sneaking suspicion though that this is a typo, and the
            // quote in the previous paragraph actually tries to refer to WUTE
            // instead of WUTF. In that case, this might be a bug, so if you're
            // seeing something weird, adding a busy loop of some length here
            // would be a good start of your investigation.
        });

        Ok(())
    }
}