1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*!
  Real time clock

  A continuously running clock that counts seconds. It is part of the backup domain which means
  that the counter is not affected by system resets or standby mode. If Vbat is connected, it is
  not reset even if the rest of the device is powered off. This allows it to be used to wake the
  CPU when it is in low power mode.

  Since it is part of the backup domain, write access to it must be enabled before the RTC can be
  used. See `backup_domain` for more details.

  See examples/rtc.rs and examples/blinky_rtc.rs for usage examples.
*/

use crate::pac::{RTC, RCC};

use crate::backup_domain::BackupDomain;

use nb;
use void::Void;

// The LSE runs at at 32 768 hertz unless an external clock is provided
const LSE_HERTZ: u32 = 32_768;


/**
  Interface to the real time clock
*/
pub struct Rtc {
    regs: RTC,
}


impl Rtc {
    /**
      Initialises the RTC. The `BackupDomain` struct is created by
      `Rcc.bkp.constrain()`.
    */
    pub fn rtc(regs: RTC, bkp: &mut BackupDomain) -> Self {
        let mut result = Rtc {
            regs,
        };

        Rtc::enable_rtc(bkp);

        // Set the prescaler to make it count up once every second.
        let prl = LSE_HERTZ - 1;
        assert!(prl < 1 << 20);
        result.perform_write(|s| {
            s.regs.prlh.write(|w| unsafe { w.bits(prl >> 16) });
            s.regs.prll.write(|w| unsafe { w.bits(prl as u16 as u32) });
        });

        result
    }

    /// Enables the RTC device with the lse as the clock
    fn enable_rtc(_bkp: &mut BackupDomain) {
        // NOTE: Safe RCC access because we are only accessing bdcr
        // and we have a &mut on BackupDomain
        let rcc = unsafe { &*RCC::ptr() };
        rcc.bdcr.modify(|_, w| {
            w
                // start the LSE oscillator
                .lseon().set_bit()
                // Enable the RTC
                .rtcen().set_bit()
                // Set the source of the RTC to LSE
                .rtcsel().lse()
        })
    }

    /// Set the current rtc value to the specified amount of seconds
    pub fn set_seconds(&mut self, seconds: u32) {
        self.perform_write(|s| {
            s.regs.cnth.write(|w| unsafe{w.bits(seconds >> 16)});
            s.regs.cntl.write(|w| unsafe{w.bits(seconds as u16 as u32)});
        });
    }

    /**
      Sets the time at which an alarm will be triggered

      This also clears the alarm flag if it is set
    */
    pub fn set_alarm(&mut self, seconds: u32) {
        // Set alarm time
        // See section 18.3.5 for explanation
        let alarm_value = seconds - 1;
        self.perform_write(|s| {
            s.regs.alrh.write(|w| unsafe{w.alrh().bits((alarm_value >> 16) as u16)});
            s.regs.alrl.write(|w| unsafe{w.alrl().bits(alarm_value as u16)});
        });

        self.clear_alarm_flag();
    }

    /// Enables the RTCALARM interrupt
    pub fn listen_alarm(&mut self) {
        // Enable alarm interrupt
        self.perform_write(|s| {
            s.regs.crh.modify(|_, w| w.alrie().set_bit());
        })
    }

    /// Disables the RTCALARM interrupt
    pub fn unlisten_alarm(&mut self) {
        // Disable alarm interrupt
        self.perform_write(|s| {
            s.regs.crh.modify(|_, w| w.alrie().clear_bit());
        })
    }

    /// Reads the current time
    pub fn seconds(&self) -> u32 {
        // Wait for the APB1 interface to be ready
        while self.regs.crl.read().rsf().bit() == false {}

        self.regs.cnth.read().bits() << 16 | self.regs.cntl.read().bits()
    }

    /// Enables the RTC second interrupt
    pub fn listen_seconds(&mut self) {
        self.perform_write(|s| {
            s.regs.crh.modify(|_, w| w.secie().set_bit())
        })
    }

    /// Disables the RTC second interrupt
    pub fn unlisten_seconds(&mut self) {
        self.perform_write(|s| {
            s.regs.crh.modify(|_, w| w.secie().clear_bit())
        })
    }

    /// Clears the RTC second interrupt flag
    pub fn clear_second_flag(&mut self) {
        self.perform_write(|s| {
            s.regs.crl.modify(|_, w| w.secf().clear_bit())
        })
    }

    /// Clears the RTC alarm interrupt flag
    pub fn clear_alarm_flag(&mut self) {
        self.perform_write(|s| {
            s.regs.crl.modify(|_, w| w.alrf().clear_bit())
        })
    }

    /**
      Return `Ok(())` if the alarm flag is set, `Err(nb::WouldBlock)` otherwise.

      ```rust
      use nb::block;

      rtc.set_alarm(rtc.read_counts() + 5);
      // NOTE: Safe unwrap because Void can't be returned
      block!(rtc.wait_alarm()).unwrap();
      ```
    */
    pub fn wait_alarm(&mut self) -> nb::Result<(), Void> {
        if self.regs.crl.read().alrf().bit() == true {
            self.regs.crl.modify(|_, w| w.alrf().clear_bit());
            Ok(())
        }
        else {
            Err(nb::Error::WouldBlock)
        }
    }


    /**
      The RTC registers can not be written to at any time as documented on page
      485 of the manual. Performing writes using this function ensures that
      the writes are done correctly.
    */
    fn perform_write(&mut self, func: impl Fn(&mut Self)) {
        // Wait for the last write operation to be done
        while self.regs.crl.read().rtoff().bit() == false {}
        // Put the clock into config mode
        self.regs.crl.modify(|_, w| w.cnf().set_bit());

        // Perform the write opertaion
        func(self);

        // Take the device out of config mode
        self.regs.crl.modify(|_, w| w.cnf().clear_bit());
        // Wait for the write to be done
        while !self.regs.crl.read().rtoff().bit() {}
    }
}