ds3231_rtc/
control.rs

1//! Power control implementation for the DS3231
2//!
3//! This module provides power management functionality for the DS3231 RTC chip,
4//! implementing the `RtcPowerControl` trait to allow starting and stopping the
5//! internal oscillator that drives timekeeping operations.
6//!
7//! ## Hardware Behavior
8//!
9//! The DS3231 uses the Enable Oscillator (EOSC) bit in the Control Register (0Eh)
10//! to control oscillator operation. This bit has specific power-dependent behavior:
11//!
12//! - **When set to logic 0**: The oscillator is enabled (running)
13//! - **When set to logic 1**: The oscillator is disabled, but **only when the DS3231
14//!   switches to battery backup power (VBAT)**
15//!
16//! ### Important
17//!
18//! - **Main Power (VCC)**: When powered by VCC, the oscillator is **always running**
19//!   regardless of the EOSC bit status
20//! - **Battery Power (VBAT)**: The EOSC bit only takes effect during battery backup
21//!   operation to conserve power
22//! - **Default State**: The EOSC bit is cleared (logic 0) when power is first applied
23//!
24//! This means that `halt_clock()` will only stop timekeeping when running on battery
25//! power, making it primarily useful for extending battery life rather than general
26//! clock control.
27
28pub use rtc_hal::control::RtcPowerControl;
29
30use crate::{
31    Ds3231,
32    registers::{EOSC_BIT, Register},
33};
34
35impl<I2C> RtcPowerControl for Ds3231<I2C>
36where
37    I2C: embedded_hal::i2c::I2c,
38{
39    /// Start or resume the RTC oscillator so that timekeeping can continue.
40    ///
41    /// This clears the EOSC bit (sets to logic 0) to enable the oscillator.
42    /// The operation is idempotent - calling it when already running has no effect.
43    ///
44    /// **Note**: When powered by VCC, the oscillator runs regardless of this setting.
45    fn start_clock(&mut self) -> Result<(), Self::Error> {
46        self.clear_register_bits(Register::Control, EOSC_BIT)
47    }
48
49    /// Halt the RTC oscillator to conserve power during battery backup operation.
50    ///
51    /// This sets the EOSC bit (sets to logic 1) to disable the oscillator.
52    ///
53    /// **Important**: This only takes effect when the DS3231 switches to battery
54    /// backup power (VBAT). When powered by VCC, the oscillator continues running
55    /// regardless of this setting.
56    fn halt_clock(&mut self) -> Result<(), Self::Error> {
57        self.set_register_bits(Register::Control, EOSC_BIT)
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use crate::registers::{EOSC_BIT, Register};
65    use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction as I2cTransaction};
66    use rtc_hal::control::RtcPowerControl;
67
68    const DS3231_ADDR: u8 = 0x68;
69
70    #[test]
71    fn test_start_clock_sets_eosc_bit_to_zero() {
72        let expectations = vec![
73            // Read current control register value with EOSC bit set (oscillator disabled)
74            I2cTransaction::write_read(
75                DS3231_ADDR,
76                vec![Register::Control.addr()],
77                vec![EOSC_BIT], // EOSC bit is set (oscillator disabled)
78            ),
79            // Write back with EOSC bit cleared (oscillator enabled)
80            I2cTransaction::write(DS3231_ADDR, vec![Register::Control.addr(), 0b0000_0000]),
81        ];
82
83        let mut i2c_mock = I2cMock::new(&expectations);
84        let mut ds3231 = Ds3231::new(&mut i2c_mock);
85
86        let result = ds3231.start_clock();
87        assert!(result.is_ok());
88
89        i2c_mock.done();
90    }
91
92    #[test]
93    fn test_start_clock_already_running() {
94        let expectations = vec![
95            // Read current control register value with EOSC bit already cleared
96            I2cTransaction::write_read(
97                DS3231_ADDR,
98                vec![Register::Control.addr()],
99                vec![0b0000_0000], // EOSC bit already cleared
100            ),
101            // No write transaction needed since bit is already in correct state
102        ];
103
104        let mut i2c_mock = I2cMock::new(&expectations);
105        let mut ds3231 = Ds3231::new(&mut i2c_mock);
106
107        let result = ds3231.start_clock();
108        assert!(result.is_ok());
109
110        i2c_mock.done();
111    }
112
113    #[test]
114    fn test_start_clock_preserves_other_bits() {
115        let expectations = vec![
116            // Read control register with other bits set and EOSC bit set
117            I2cTransaction::write_read(
118                DS3231_ADDR,
119                vec![Register::Control.addr()],
120                vec![0b0100_0101 | EOSC_BIT], // Other bits set + EOSC bit
121            ),
122            // Write back preserving other bits but clearing EOSC bit
123            I2cTransaction::write(
124                DS3231_ADDR,
125                vec![Register::Control.addr(), 0b0100_0101], // Other bits preserved, EOSC cleared
126            ),
127        ];
128
129        let mut i2c_mock = I2cMock::new(&expectations);
130        let mut ds3231 = Ds3231::new(&mut i2c_mock);
131
132        let result = ds3231.start_clock();
133        assert!(result.is_ok());
134
135        i2c_mock.done();
136    }
137
138    #[test]
139    fn test_halt_clock_sets_eosc_bit_to_one() {
140        let expectations = vec![
141            // Read current control register value with EOSC bit cleared (oscillator enabled)
142            I2cTransaction::write_read(
143                DS3231_ADDR,
144                vec![Register::Control.addr()],
145                vec![0b0000_0000], // EOSC bit is cleared
146            ),
147            // Write back with EOSC bit set (oscillator disabled)
148            I2cTransaction::write(DS3231_ADDR, vec![Register::Control.addr(), EOSC_BIT]),
149        ];
150
151        let mut i2c_mock = I2cMock::new(&expectations);
152        let mut ds3231 = Ds3231::new(&mut i2c_mock);
153
154        let result = ds3231.halt_clock();
155        assert!(result.is_ok());
156
157        i2c_mock.done();
158    }
159
160    #[test]
161    fn test_halt_clock_already_halted() {
162        let expectations = vec![
163            // Read current control register value with EOSC bit already set
164            I2cTransaction::write_read(
165                DS3231_ADDR,
166                vec![Register::Control.addr()],
167                vec![EOSC_BIT], // EOSC bit already set
168            ),
169            // No write transaction needed since bit is already in correct state
170        ];
171
172        let mut i2c_mock = I2cMock::new(&expectations);
173        let mut ds3231 = Ds3231::new(&mut i2c_mock);
174
175        let result = ds3231.halt_clock();
176        assert!(result.is_ok());
177
178        i2c_mock.done();
179    }
180
181    #[test]
182    fn test_halt_clock_preserves_other_bits() {
183        let expectations = vec![
184            // Read control register with other bits set and EOSC bit cleared
185            I2cTransaction::write_read(
186                DS3231_ADDR,
187                vec![Register::Control.addr()],
188                vec![0b0010_1010], // Other bits set, EOSC bit cleared
189            ),
190            // Write back preserving other bits but setting EOSC bit
191            I2cTransaction::write(
192                DS3231_ADDR,
193                vec![Register::Control.addr(), 0b1010_1010 | EOSC_BIT], // Other bits preserved, EOSC set
194            ),
195        ];
196
197        let mut i2c_mock = I2cMock::new(&expectations);
198        let mut ds3231 = Ds3231::new(&mut i2c_mock);
199
200        let result = ds3231.halt_clock();
201        assert!(result.is_ok());
202
203        i2c_mock.done();
204    }
205
206    #[test]
207    fn test_start_clock_i2c_read_error() {
208        let expectations = vec![
209            I2cTransaction::write_read(DS3231_ADDR, vec![Register::Control.addr()], vec![0x00])
210                .with_error(embedded_hal::i2c::ErrorKind::Other),
211        ];
212
213        let mut i2c_mock = I2cMock::new(&expectations);
214        let mut ds3231 = Ds3231::new(&mut i2c_mock);
215
216        let result = ds3231.start_clock();
217        assert!(result.is_err());
218
219        i2c_mock.done();
220    }
221
222    #[test]
223    fn test_start_clock_i2c_write_error() {
224        let expectations = vec![
225            I2cTransaction::write_read(
226                DS3231_ADDR,
227                vec![Register::Control.addr()],
228                vec![EOSC_BIT], // EOSC bit set, needs clearing
229            ),
230            I2cTransaction::write(DS3231_ADDR, vec![Register::Control.addr(), 0b0000_0000])
231                .with_error(embedded_hal::i2c::ErrorKind::Other),
232        ];
233
234        let mut i2c_mock = I2cMock::new(&expectations);
235        let mut ds3231 = Ds3231::new(&mut i2c_mock);
236
237        let result = ds3231.start_clock();
238        assert!(result.is_err());
239
240        i2c_mock.done();
241    }
242
243    #[test]
244    fn test_halt_clock_i2c_read_error() {
245        let expectations = vec![
246            I2cTransaction::write_read(DS3231_ADDR, vec![Register::Control.addr()], vec![0x00])
247                .with_error(embedded_hal::i2c::ErrorKind::Other),
248        ];
249
250        let mut i2c_mock = I2cMock::new(&expectations);
251        let mut ds3231 = Ds3231::new(&mut i2c_mock);
252
253        let result = ds3231.halt_clock();
254        assert!(result.is_err());
255
256        i2c_mock.done();
257    }
258
259    #[test]
260    fn test_halt_clock_i2c_write_error() {
261        let expectations = vec![
262            I2cTransaction::write_read(
263                DS3231_ADDR,
264                vec![Register::Control.addr()],
265                vec![0b0000_0000], // EOSC bit cleared, needs setting
266            ),
267            I2cTransaction::write(DS3231_ADDR, vec![Register::Control.addr(), EOSC_BIT])
268                .with_error(embedded_hal::i2c::ErrorKind::Other),
269        ];
270
271        let mut i2c_mock = I2cMock::new(&expectations);
272        let mut ds3231 = Ds3231::new(&mut i2c_mock);
273
274        let result = ds3231.halt_clock();
275        assert!(result.is_err());
276
277        i2c_mock.done();
278    }
279
280    #[test]
281    fn test_power_control_sequence_start_halt_start() {
282        let expectations = vec![
283            // First start_clock() call
284            I2cTransaction::write_read(DS3231_ADDR, vec![Register::Control.addr()], vec![EOSC_BIT]),
285            I2cTransaction::write(DS3231_ADDR, vec![Register::Control.addr(), 0b0000_0000]),
286            // halt_clock() call
287            I2cTransaction::write_read(
288                DS3231_ADDR,
289                vec![Register::Control.addr()],
290                vec![0b0000_0000],
291            ),
292            I2cTransaction::write(DS3231_ADDR, vec![Register::Control.addr(), EOSC_BIT]),
293            // Second start_clock() call
294            I2cTransaction::write_read(DS3231_ADDR, vec![Register::Control.addr()], vec![EOSC_BIT]),
295            I2cTransaction::write(DS3231_ADDR, vec![Register::Control.addr(), 0b0000_0000]),
296        ];
297
298        let mut i2c_mock = I2cMock::new(&expectations);
299        let mut ds3231 = Ds3231::new(&mut i2c_mock);
300
301        // Test sequence of operations
302        assert!(ds3231.start_clock().is_ok());
303        assert!(ds3231.halt_clock().is_ok());
304        assert!(ds3231.start_clock().is_ok());
305
306        i2c_mock.done();
307    }
308
309    #[test]
310    fn test_start_clock_clears_only_eosc_bit() {
311        // Test that EOSC_BIT has the correct value
312        let expectations = vec![
313            I2cTransaction::write_read(
314                DS3231_ADDR,
315                vec![Register::Control.addr()],
316                vec![0b1111_1111], // All bits set
317            ),
318            I2cTransaction::write(
319                DS3231_ADDR,
320                vec![Register::Control.addr(), !EOSC_BIT], // All bits except EOSC
321            ),
322        ];
323
324        let mut i2c_mock = I2cMock::new(&expectations);
325        let mut ds3231 = Ds3231::new(&mut i2c_mock);
326
327        let result = ds3231.start_clock();
328        assert!(result.is_ok());
329
330        i2c_mock.done();
331    }
332}