1use crate::clock::ClockCtrl;
8use pac::{CLKGEN, RTC};
9use pac::rtc::rtcctl::RPT_A;
10use rtcc::DateTimeAccess;
11
12use chrono::{Datelike, Timelike};
13
14#[allow(unused_imports)]
15use defmt::{debug, error, info, trace, warn};
16
17pub struct Rtc {
18 rtc: RTC,
19}
20
21fn bcd_to_dec(bcd: u8) -> u8 {
22 (((bcd & 0xf0) >> 4) * 10) + (bcd & 0x0f)
23}
24
25fn dec_to_bcd(dec: u8) -> u8 {
26 ((dec / 10) << 4) | (dec % 10)
27}
28
29impl Rtc {
30 pub fn new(rtc: RTC, clkgen: &mut CLKGEN) -> Rtc {
31 let mut clk = ClockCtrl::new(clkgen);
33 rtc.rtcctl.reset();
35 rtc.almup.reset();
36 rtc.almlow.reset();
37 rtc.inten.write(|w| w.alm().clear_bit());
38 rtc.intclr.write(|w| w.alm().set_bit());
39
40 clk.rtc_use_xt();
42
43 rtc.rtcctl.modify(|_, w| w.hr1224()._24hr());
44
45 Rtc { rtc }
46 }
47
48 pub fn enable(&mut self) {
49 self.rtc.rtcctl.modify(|_, w| w.rstop().run());
50 }
51
52 pub fn disable(&mut self) {
53 self.rtc.rtcctl.modify(|_, w| w.rstop().stop());
54 }
55
56 pub fn set(&self, dt: &chrono::NaiveDateTime) {
60 let date = dt.date();
61 let time = dt.time();
62
63 let year = date.year();
64 let yr = year % 100;
65
66 debug!("set RTC to: {}-{}-{} {}:{}:{}.{}",
67 year,
68 date.month(),
69 date.day(),
70 time.hour(),
71 time.minute(),
72 time.second(),
73 time.nanosecond());
74
75 self.rtc.rtcctl.modify(|_, w| w.wrtc().en());
76
77 self.rtc.ctrlow.write(|w| unsafe {
78 w.ctrhr().bits(dec_to_bcd(time.hour() as u8))
79 .ctrmin().bits(dec_to_bcd(time.minute() as u8))
80 .ctrsec().bits(dec_to_bcd(time.second() as u8))
81 .ctr100().bits(dec_to_bcd((time.nanosecond() / 1_000_000 * 100) as u8))
82 });
83
84 self.rtc.ctrup.write(|w| unsafe {
85 w.ceb().dis() .ctryr()
87 .bits(dec_to_bcd(yr as u8))
88 .ctrmo()
89 .bits(dec_to_bcd(date.month() as u8))
90 .ctrdate()
91 .bits(dec_to_bcd(date.day() as u8))
92 .ctrwkdy()
93 .bits(date.weekday() as u8)
94 });
95
96 self.rtc.rtcctl.modify(|_, w| w.wrtc().dis());
97 }
98
99 pub fn now(&self) -> chrono::NaiveDateTime {
102 let (upper, lower) = loop {
103 let lower = self.rtc.ctrlow.read();
104
105 let no_err = self.rtc.ctrup.read().cterr().is_noerr(); let upper = self.rtc.ctrup.read(); if no_err {
112 break (upper, lower);
113 }
114 };
115
116 let yr = bcd_to_dec(upper.ctryr().bits()) as i32;
117 const CE: i32 = 20;
118
119 chrono::NaiveDate::from_ymd_opt(
120 CE * 100 + yr,
121 bcd_to_dec(upper.ctrmo().bits()).into(),
122 bcd_to_dec(upper.ctrdate().bits()).into(),
123 ).and_then(|y| y.and_hms_milli_opt(
124 bcd_to_dec(lower.ctrhr().bits()).into(),
125 bcd_to_dec(lower.ctrmin().bits()).into(),
126 bcd_to_dec(lower.ctrsec().bits()).into(),
127 u32::from(bcd_to_dec(lower.ctr100().bits())) * 10u32,
128 )).unwrap()
129 }
130
131 pub fn set_alarm_repeat(&mut self, interval: AlarmRepeat) {
133 self.rtc.almup.reset();
134 self.rtc.almlow.reset();
135
136 self.rtc.rtcctl.modify(|_, w| w.rpt().variant(interval.into()));
137
138 match interval {
139 AlarmRepeat::DeciSecond => {
140 self.rtc.almlow.write(|w| unsafe { w.alm100().bits(0xf0) });
141 },
142 AlarmRepeat::CentiSecond => {
143 self.rtc.almlow.write(|w| unsafe { w.alm100().bits(0xff) });
144 },
145 _ => {
146 }
147 }
148 }
149
150 pub fn clear_interrupts(&mut self) {
151 self.rtc.intclr.write(|w| w.alm().set_bit());
152 }
153
154 pub fn disable_alarm_repeat(&mut self) {
155 self.rtc.rtcctl.modify(|_, w| w.rpt().dis());
156 }
157
158 pub fn enable_alarm(&mut self) {
159 cortex_m::interrupt::free(|_| {
160 self.clear_interrupts();
161 self.rtc.inten.write(|w| w.alm().set_bit());
162 unsafe {
163 pac::NVIC::unmask(pac::Interrupt::RTC);
164 }
165 });
166 }
167
168 pub fn disable_alarm(&mut self) {
169 pac::NVIC::mask(pac::Interrupt::RTC);
170 self.rtc.inten.write(|w| w.alm().clear_bit());
171 self.clear_interrupts();
172 }
173}
174
175impl DateTimeAccess for Rtc {
176 type Error = !;
177
178 fn datetime(&mut self) -> Result<chrono::NaiveDateTime, !> {
179 Ok(self.now())
180 }
181
182 fn set_datetime(&mut self, datetime: &chrono::NaiveDateTime) -> Result<(), !> {
183 self.set(datetime);
184
185 Ok(())
186 }
187}
188
189#[derive(Clone, Copy, Debug, PartialEq)]
190#[repr(u8)]
191pub enum AlarmRepeat {
192 Disabled,
193 Year,
194 Month,
195 Week,
196 Day,
197 Hour,
198 Minute,
199 Second,
200
201 DeciSecond,
203
204 CentiSecond,
206}
207
208impl Into<RPT_A> for AlarmRepeat {
209 fn into(self) -> RPT_A {
210 use AlarmRepeat::*;
211 use RPT_A::*;
212
213 match self {
214 Disabled => DIS,
215 Year => YEAR,
216 Month => MONTH,
217 Week => WEEK,
218 Day => DAY,
219 Hour => HR,
220 Minute => MIN,
221 Second => SEC,
222 DeciSecond => SEC,
223 CentiSecond => SEC,
224 }
225 }
226}
227