inky_frame/
pcf.rs

1// Permission is hereby granted, free of charge, to any person obtaining a copy
2// of this software and associated documentation files (the "Software"), to deal
3// in the Software without restriction, including without limitation the rights
4// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
5// copies of the Software, and to permit persons to whom the Software is
6// furnished to do so, subject to the following conditions:
7//
8// The above copyright notice and this permission notice shall be included in
9// all copies or substantial portions of the Software.
10//
11// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17// SOFTWARE.
18//
19
20#![no_implicit_prelude]
21
22extern crate core;
23extern crate rpsp;
24
25use core::cmp;
26use core::convert::Into;
27use core::marker::Send;
28use core::option::Option::{self, None, Some};
29use core::result::Result::{self, Err, Ok};
30
31use rpsp::clock::{AlarmConfig, RtcError, TimeSource};
32use rpsp::i2c::mode::Controller;
33use rpsp::i2c::{I2c, I2cAddress, I2cBus};
34use rpsp::int::Acknowledge;
35use rpsp::time::Time;
36
37const CMD_SAVED: u8 = 0x03u8;
38const CMD_CTRL_1: u8 = 0x00u8;
39const CMD_CTRL_2: u8 = 0x01u8;
40const CMD_ALARM2: u8 = 0x0Bu8;
41const CMD_STATUS: u8 = 0x04u8;
42const CMD_TIMER_MODE: u8 = 0x11u8;
43const CMD_TIMER_VALUE: u8 = 0x10u8;
44
45const ADDR: I2cAddress = I2cAddress::new_7bit(0x51u8);
46
47#[repr(u8)]
48pub enum PcfTick {
49    Speed4kHz = 0u8,
50    Speed64Hz = 1u8,
51    Speed1Hz  = 2u8,
52    Slow      = 3u8,
53}
54#[repr(u8)]
55pub enum PcfOutput {
56    Rate32kHz = 0u8,
57    Rate16kHz = 1u8,
58    Rate8kHz  = 2u8,
59    Rate4kHz  = 3u8,
60    Rate2kHz  = 4u8,
61    Rate1kHz  = 5u8,
62    Rate1Hz   = 6u8,
63    Off       = 7u8,
64}
65
66pub struct PcfRtc<'a> {
67    i2c: I2cBus<'a, Controller>,
68}
69
70impl<'a> PcfRtc<'a> {
71    #[inline(always)]
72    pub fn new(i2c: impl Into<I2cBus<'a, Controller>>) -> PcfRtc<'a> {
73        PcfRtc { i2c: i2c.into() }
74    }
75
76    #[inline(always)]
77    pub fn i2c_bus(&self) -> &I2c<Controller> {
78        &self.i2c
79    }
80    #[inline]
81    pub fn reset(&mut self) -> Result<(), RtcError> {
82        self.i2c.write(ADDR, &[CMD_CTRL_1, 0x58u8])?;
83        loop {
84            self.i2c.write(ADDR, &[CMD_STATUS, 0u8])?;
85            if self.is_stable()? {
86                break;
87            }
88        }
89        Ok(())
90    }
91    #[inline(always)]
92    pub fn now(&mut self) -> Result<Time, RtcError> {
93        self.now_inner()
94    }
95    #[inline(always)]
96    pub fn get_byte(&mut self) -> Result<u8, RtcError> {
97        self.read_reg(CMD_SAVED)
98    }
99    #[inline(always)]
100    pub fn is_24hr(&mut self) -> Result<bool, RtcError> {
101        Ok(self.read_reg(CMD_CTRL_1)? & 0x2 == 0)
102    }
103    #[inline(always)]
104    pub fn is_stable(&mut self) -> Result<bool, RtcError> {
105        Ok(self.read_reg(CMD_STATUS)? & 0x80 == 0)
106    }
107    #[inline]
108    pub fn alarm_disable(&mut self) -> Result<(), RtcError> {
109        // NOTE(sf): Set the register values to disable instead of just clearing
110        //           them, as bit 7 (0x80) as zero means it's enabled.
111        self.i2c
112            .write(ADDR, &[CMD_ALARM2, 0x80u8, 0x80u8, 0x80u8, 0x80u8, 0x80u8])?;
113        Ok(())
114    }
115    #[inline]
116    pub fn timer_disable(&mut self) -> Result<(), RtcError> {
117        let v = self.read_reg(CMD_TIMER_MODE)?;
118        // 0x6 - (0b110)
119        // - Clear TE:    Timer Enable
120        // - Clear TIE:   Timer Interrupt Enable
121        // - Clear TI_TP: Timer Interrupt Mode
122        //
123        // Clear the bottom 3 bits of the timer flags so the timer won't do
124        // anything weird.
125        self.i2c.write(ADDR, &[CMD_TIMER_MODE, v & 0xF8])?;
126        Ok(())
127    }
128    #[inline(always)]
129    pub fn alarm_state(&mut self) -> Result<bool, RtcError> {
130        Ok(self.read_reg(CMD_CTRL_2)? & 0x40 != 0)
131    }
132    #[inline(always)]
133    pub fn timer_state(&mut self) -> Result<bool, RtcError> {
134        Ok(self.read_reg(CMD_CTRL_2)? & 0x8 != 0)
135    }
136    #[inline(always)]
137    pub fn set_byte(&mut self, v: u8) -> Result<(), RtcError> {
138        self.i2c.write(ADDR, &[CMD_SAVED, v])?;
139        Ok(())
140    }
141    pub fn set_time(&mut self, v: Time) -> Result<(), RtcError> {
142        if !v.is_valid() {
143            return Err(RtcError::InvalidTime);
144        }
145        let r = self.read_reg(CMD_CTRL_1)?;
146        // NOTE(sf): Reset the 12_24 register to 0, to avoid any 12/24 hours
147        //           confusion.
148        self.i2c.write(ADDR, &[CMD_CTRL_1, r & 0xFD])?;
149        self.i2c.write(ADDR, &[
150            CMD_STATUS,
151            encode(v.secs),
152            encode(v.mins),
153            encode(v.hours),
154            encode(v.day),
155            encode(v.weekday as u8),
156            encode(v.month as u8),
157            encode(v.year.saturating_sub(0x7D0) as u8),
158        ])?;
159        Ok(())
160    }
161    #[inline]
162    pub fn set_24hr(&mut self, en: bool) -> Result<(), RtcError> {
163        let v = self.read_reg(CMD_CTRL_1)?;
164        self.i2c.write(ADDR, &[CMD_CTRL_1, if en { v & 0xFD } else { v | 0x2 }])?;
165        Ok(())
166    }
167    #[inline]
168    pub fn alarm_clear_state(&mut self) -> Result<bool, RtcError> {
169        let v = self.read_reg(CMD_CTRL_2)?;
170        self.i2c.write(ADDR, &[CMD_CTRL_2, v & 0xBF])?;
171        Ok(v & 0x40 != 0)
172    }
173    #[inline]
174    pub fn timer_clear_state(&mut self) -> Result<bool, RtcError> {
175        let v = self.read_reg(CMD_CTRL_2)?;
176        self.i2c.write(ADDR, &[CMD_CTRL_2, v & 0xF7])?;
177        Ok(v & 0x8 != 0)
178    }
179    #[inline(always)]
180    pub fn set_timer_ms(&mut self, ms: u32) -> Result<(), RtcError> {
181        let (t, s) = ms_to_ticks(ms).ok_or(RtcError::ValueTooLarge)?;
182        self.set_timer(t, s)
183    }
184    pub fn set_alarm(&mut self, v: AlarmConfig) -> Result<(), RtcError> {
185        if v.is_empty() {
186            return self.alarm_disable();
187        }
188        if !v.is_valid() {
189            return Err(RtcError::InvalidTime);
190        }
191        let r = self.read_reg(CMD_CTRL_1)?;
192        // NOTE(sf): Reset the 12_24 register to 0, to avoid any 12/24 hours
193        //           confusion.
194        self.i2c.write(ADDR, &[CMD_CTRL_1, r & 0xFD])?;
195        self.i2c.write(ADDR, &[
196            CMD_ALARM2,
197            v.secs.map_or(0x80u8, encode),
198            v.mins.map_or(0x80u8, encode),
199            v.hours.map_or(0x80u8, encode),
200            v.day.map_or(0x80u8, |i| encode(i.get())),
201            v.weekday.map_or(0x80u8, |i| encode(i as u8)),
202        ])?;
203        Ok(())
204    }
205    #[inline]
206    pub fn set_alarm_interrupt(&mut self, en: bool) -> Result<(), RtcError> {
207        let v = self.read_reg(CMD_CTRL_2)?;
208        self.i2c.write(ADDR, &[
209            CMD_CTRL_2,
210            (if en { v | 0x80 } else { v & 0x7F }) & 0xBF,
211            // Clear the Alarm interrupt register
212        ])?;
213        Ok(())
214    }
215    /// See the bottom of page 26 in the documentation for the PCF85063A IC to
216    /// determine what ticks and speed relate to.
217    ///
218    /// Documentation at: <https://www.nxp.com/docs/en/data-sheet/PCF85063A.pdf>
219    #[inline]
220    pub fn set_timer(&mut self, ticks: u8, speed: PcfTick) -> Result<(), RtcError> {
221        let v = self.read_reg(CMD_TIMER_MODE)?;
222        self.i2c.write(ADDR, &[
223            CMD_TIMER_VALUE,
224            ticks,
225            (v & 0xE7) | (((speed as u8) & 0x3) << 3) | 0x4,
226        ])?;
227        Ok(())
228    }
229    #[inline(always)]
230    pub fn set_time_from(&mut self, mut v: impl TimeSource) -> Result<(), RtcError> {
231        self.set_time(v.now().map_err(|e| e.into())?)
232    }
233    #[inline]
234    pub fn set_timer_interrupt(&mut self, en: bool, pulse: bool) -> Result<(), RtcError> {
235        self.timer_clear_state()?; // Clear the Timer Interrupt flag.
236        let v = self.read_reg(CMD_TIMER_MODE)?;
237        self.i2c.write(ADDR, &[
238            CMD_TIMER_MODE,
239            (v & 0xFC) | if en { 0x2 } else { 0 } | if pulse { 0x1 } else { 0 },
240        ])?;
241        Ok(())
242    }
243
244    #[inline]
245    fn now_inner(&mut self) -> Result<Time, RtcError> {
246        let mut b: [u8; 7] = [0u8; 7];
247        self.i2c.write_single_then_read(ADDR, CMD_STATUS, &mut b)?;
248        let mut d = Time::new(
249            decode(b[6]) as u16 + 0x7D0,
250            decode(b[5]).into(),
251            decode(b[3]),
252            decode(b[2]),
253            decode(b[1]),
254            decode(b[0] & 0x7F),
255            decode(b[4]).into(),
256        );
257        if d.hours >= 24 {
258            // Correct 12hr to 24hr skew.
259            // Hours in 12hr
260            // - AM/PM: Bit 5.
261            // - Hours (10's place [1 or 0]): 4
262            // - Hours (1's place [1 - 9]): 3-0 (in BCD)
263            //
264            // To 24hr:
265            // 1. Decode min unit.
266            // 2. If Bit 4 is 1, add 10.
267            // 3. If Bit 5 is 1, add 12 (it's PM).
268            d.hours = cmp::max(decode(b[2] & 0x7), 9) + if b[2] & 0x10 != 0 { 10 } else { 0 } + if b[2] & 0x20 != 0 { 12 } else { 0 }
269        }
270        if !d.is_valid() { Err(RtcError::InvalidTime) } else { Ok(d) }
271    }
272    #[inline]
273    fn read_reg(&mut self, r: u8) -> Result<u8, RtcError> {
274        self.i2c.write_single(ADDR, r)?;
275        Ok(self.i2c.read_single(ADDR)?)
276    }
277}
278
279impl TimeSource for PcfRtc<'_> {
280    type Error = RtcError;
281
282    #[inline(always)]
283    fn now(&mut self) -> Result<Time, RtcError> {
284        self.now_inner()
285    }
286}
287impl Acknowledge for PcfRtc<'_> {
288    #[inline(always)]
289    fn ack_interrupt(&mut self) -> bool {
290        self.alarm_clear_state().unwrap_or(false) | self.timer_clear_state().unwrap_or(false)
291    }
292}
293
294unsafe impl Send for PcfRtc<'_> {}
295
296#[inline]
297fn encode(v: u8) -> u8 {
298    let i = v / 10;
299    (v - (i * 10)) | (i << 4)
300}
301#[inline]
302fn decode(v: u8) -> u8 {
303    (v & 0xF) + ((v >> 4) & 0xF).wrapping_mul(10)
304}
305#[inline]
306fn ms_to_ticks(v: u32) -> Option<(u8, PcfTick)> {
307    match v {
308        0..=62 => Some((cmp::min((v * 1_000) / 244, 0xFF) as u8, PcfTick::Speed4kHz)),
309        0..=3_800 => Some((cmp::min(v / 15, 0xFF) as u8, PcfTick::Speed64Hz)),
310        0..=255_000 => Some((cmp::min(v / 1_000, 0xFF) as u8, PcfTick::Speed1Hz)),
311        0..=15_300_000 => Some((cmp::min((v / 1_000) / 60, 0xFF) as u8, PcfTick::Slow)),
312        _ => None,
313    }
314}