1use embedded_hal::i2c::I2c;
4
5use crate::devices;
6
7const REG_CONTROL_STATUS1: u8 = 0x00;
8const REG_SECONDS: u8 = 0x02;
9const REG_MINUTE_ALARM: u8 = 0x09;
10const REG_TIMER_CONTROL: u8 = 0x0E;
11const REG_TIMER: u8 = 0x0F;
12
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15pub struct Date {
16 pub year: u16,
17 pub month: u8,
18 pub day: u8,
19 pub weekday: u8,
20}
21
22#[cfg_attr(feature = "defmt", derive(defmt::Format))]
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
24pub struct Time {
25 pub hour: u8,
26 pub minute: u8,
27 pub second: u8,
28}
29
30#[cfg_attr(feature = "defmt", derive(defmt::Format))]
31#[derive(Clone, Copy, Debug, Eq, PartialEq)]
32pub struct DateTime {
33 pub date: Date,
34 pub time: Time,
35}
36
37#[cfg_attr(feature = "defmt", derive(defmt::Format))]
38#[derive(Clone, Copy, Debug, Eq, PartialEq)]
39pub struct Alarm {
40 pub minute: Option<u8>,
41 pub hour: Option<u8>,
42 pub day: Option<u8>,
43 pub weekday: Option<u8>,
44}
45
46#[cfg_attr(feature = "defmt", derive(defmt::Format))]
47#[derive(Clone, Copy, Debug, Eq, PartialEq)]
48pub enum TimerClock {
49 Hz4096,
50 Hz64,
51 Hz1,
52 OneOver60Hz,
53}
54
55#[cfg_attr(feature = "defmt", derive(defmt::Format))]
56#[derive(Clone, Copy, Debug, Eq, PartialEq)]
57pub struct TimerConfig {
58 pub clock: TimerClock,
59 pub count: u8,
60}
61
62#[cfg_attr(feature = "defmt", derive(defmt::Format))]
63#[derive(Clone, Copy, Debug, Eq, PartialEq)]
64pub struct TimedWakeMetadata {
65 pub alarm: Option<Alarm>,
66 pub timer: Option<TimerConfig>,
67}
68
69pub struct Bm8563<I2C> {
70 i2c: I2C,
71 address: u8,
72}
73
74impl<I2C> Bm8563<I2C> {
75 pub const fn new(i2c: I2C) -> Self {
76 Self {
77 i2c,
78 address: devices::i2c::BM8563_RTC,
79 }
80 }
81
82 pub fn release(self) -> I2C {
83 self.i2c
84 }
85}
86
87impl<I2C, Error> Bm8563<I2C>
88where
89 I2C: I2c<Error = Error>,
90{
91 pub fn init(&mut self) -> Result<(), Error> {
92 self.write_register(REG_CONTROL_STATUS1, 0x00)
93 }
94
95 pub fn datetime(&mut self) -> Result<DateTime, Error> {
96 let data = self.datetime_registers()?;
97 Ok(DateTime {
98 time: Time {
99 second: bcd_to_bin(data[0] & 0x7F),
100 minute: bcd_to_bin(data[1] & 0x7F),
101 hour: bcd_to_bin(data[2] & 0x3F),
102 },
103 date: Date {
104 day: bcd_to_bin(data[3] & 0x3F),
105 weekday: data[4] & 0x07,
106 month: bcd_to_bin(data[5] & 0x1F),
107 year: 2000 + u16::from(bcd_to_bin(data[6])),
108 },
109 })
110 }
111
112 pub fn clock_integrity_lost(&mut self) -> Result<bool, Error> {
118 Ok(self.datetime_registers()?[0] & 0x80 != 0)
119 }
120
121 pub fn set_datetime(&mut self, datetime: DateTime) -> Result<(), Error> {
122 let data = [
123 REG_SECONDS,
124 bin_to_bcd(datetime.time.second),
125 bin_to_bcd(datetime.time.minute),
126 bin_to_bcd(datetime.time.hour),
127 bin_to_bcd(datetime.date.day),
128 datetime.date.weekday & 0x07,
129 bin_to_bcd(datetime.date.month),
130 bin_to_bcd((datetime.date.year % 100) as u8),
131 ];
132 self.i2c.write(self.address, &data)
133 }
134
135 pub fn set_alarm(&mut self, alarm: Alarm) -> Result<(), Error> {
136 let disabled = 0x80;
137 let data = [
138 REG_MINUTE_ALARM,
139 alarm.minute.map(bin_to_bcd).unwrap_or(disabled),
140 alarm.hour.map(bin_to_bcd).unwrap_or(disabled),
141 alarm.day.map(bin_to_bcd).unwrap_or(disabled),
142 alarm.weekday.map(bin_to_bcd).unwrap_or(disabled),
143 ];
144 self.i2c.write(self.address, &data)
145 }
146
147 pub fn set_timer(&mut self, timer: TimerConfig) -> Result<(), Error> {
148 self.write_register(REG_TIMER, timer.count)?;
149 self.write_register(REG_TIMER_CONTROL, 0x80 | timer_clock_code(timer.clock))
150 }
151
152 fn datetime_registers(&mut self) -> Result<[u8; 7], Error> {
153 let mut data = [0u8; 7];
154 self.i2c
155 .write_read(self.address, &[REG_SECONDS], &mut data)?;
156 Ok(data)
157 }
158
159 fn write_register(&mut self, register: u8, value: u8) -> Result<(), Error> {
160 self.i2c.write(self.address, &[register, value])
161 }
162}
163
164pub const fn bcd_to_bin(value: u8) -> u8 {
165 ((value >> 4) * 10) + (value & 0x0F)
166}
167
168pub const fn bin_to_bcd(value: u8) -> u8 {
169 ((value / 10) << 4) | (value % 10)
170}
171
172const fn timer_clock_code(clock: TimerClock) -> u8 {
173 match clock {
174 TimerClock::Hz4096 => 0,
175 TimerClock::Hz64 => 1,
176 TimerClock::Hz1 => 2,
177 TimerClock::OneOver60Hz => 3,
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn bcd_round_trips() {
187 assert_eq!(bcd_to_bin(0x59), 59);
188 assert_eq!(bin_to_bcd(42), 0x42);
189 }
190}