pcf8563_dd/lib.rs
1#![cfg_attr(not(any(test, feature = "std")), no_std)]
2//! # PCF8563/BM8563 Real-Time Clock Driver
3//!
4//! This crate provides a bisync-based driver for the PCF8563 and BM8563 real-time clock ICs,
5//! built upon the `device-driver` crate for robust, declarative register definitions via a
6//! YAML manifest. It supports both asynchronous (`async`) and blocking operation through a
7//! unified API, using the [`bisync`](https://docs.rs/bisync) crate for seamless compatibility
8//! with both `embedded-hal` and `embedded-hal-async` traits.
9//!
10//! ## Features
11//!
12//! * **Declarative Register Map:** Full device configuration defined in `device.yaml`.
13//! * **Unified Async/Blocking Support:** Write your code once and use it in both async and blocking contexts via bisync.
14//! * **Type-Safe API:** High-level functions for reading/setting date and time
15//! and a generated low-level API (`ll`) for direct register access.
16//! * **Full RTC Functionality:** Date/time, alarms, timer, and clock output control.
17//! * **Optional `rtcc` Traits (blocking):** Enable the `rtcc` feature to implement
18//! [`rtcc::DateTimeAccess`](https://docs.rs/rtcc/latest/rtcc/trait.DateTimeAccess.html)
19//! and [`rtcc::Rtcc`](https://docs.rs/rtcc/latest/rtcc/trait.Rtcc.html) on the blocking driver.
20//! * **`defmt` and `log` Integration:** Optional support for logging and debugging.
21//!
22//! ## Getting Started
23//!
24//! To use the driver, instantiate `Pcf8563` (blocking) or `Pcf8563Async` (async) with your I2C bus implementation:
25//!
26//! ```rust,no_run
27//! # use embedded_hal::i2c::I2c;
28//! # use pcf8563_dd::Pcf8563;
29//! let i2c_bus = todo!();
30//! let mut rtc = Pcf8563::new(i2c_bus);
31//!
32//! let datetime = rtc.get_datetime()?;
33//! # Ok::<(), pcf8563_dd::RtcError<std::io::Error>>(())
34//! ```
35//!
36//! For async environments, use `Pcf8563Async` (re-exported from the `asynchronous` module):
37//!
38//! ```rust,no_run
39//! # use embedded_hal_async::i2c::I2c;
40//! # use pcf8563_dd::Pcf8563Async;
41//! let i2c_bus = todo!();
42//! let mut rtc = Pcf8563Async::new(i2c_bus);
43//!
44//! let datetime = rtc.get_datetime().await?;
45//! # Ok::<(), pcf8563_dd::RtcError<std::io::Error>>(())
46//! ```
47//!
48//! For a detailed register map, please refer to the `device.yaml` file in the
49//! [repository](https://github.com/okhsunrog/pcf8563-dd).
50//!
51//! ## Supported Devices
52//!
53//! - **PCF8563** - NXP real-time clock (original)
54//! - **BM8563** - Compatible clone found in M5Stack devices
55
56#[macro_use]
57pub(crate) mod fmt;
58
59use thiserror::Error;
60
61device_driver::create_device!(device_name: Pcf8563LowLevel, manifest: "device.yaml");
62
63/// PCF8563/BM8563 I2C address (7-bit)
64pub const PCF8563_I2C_ADDR: u8 = 0x51;
65
66#[derive(Debug, Error)]
67#[cfg_attr(feature = "defmt", derive(defmt::Format))]
68pub enum RtcError<I2cErr> {
69 #[error("I2C error")]
70 I2c(I2cErr),
71 #[error("Invalid input data")]
72 InvalidInputData,
73}
74
75/// Date and time structure
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
77#[cfg_attr(feature = "defmt", derive(defmt::Format))]
78pub struct DateTime {
79 /// Year (0-99, represents 2000-2099 or 1900-1999 based on century flag)
80 pub year: u8,
81 /// Month (1-12)
82 pub month: u8,
83 /// Day of month (1-31)
84 pub day: u8,
85 /// Weekday (0-6, typically 0=Sunday)
86 pub weekday: u8,
87 /// Hours (0-23)
88 pub hours: u8,
89 /// Minutes (0-59)
90 pub minutes: u8,
91 /// Seconds (0-59)
92 pub seconds: u8,
93}
94
95/// Time-only structure (for clock applications without calendar)
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
97#[cfg_attr(feature = "defmt", derive(defmt::Format))]
98pub struct Time {
99 /// Hours (0-23)
100 pub hours: u8,
101 /// Minutes (0-59)
102 pub minutes: u8,
103 /// Seconds (0-59)
104 pub seconds: u8,
105}
106
107/// Alarm configuration
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
109#[cfg_attr(feature = "defmt", derive(defmt::Format))]
110pub struct Alarm {
111 /// Minute alarm (0-59), None if disabled
112 pub minute: Option<u8>,
113 /// Hour alarm (0-23), None if disabled
114 pub hour: Option<u8>,
115 /// Day alarm (1-31), None if disabled
116 pub day: Option<u8>,
117 /// Weekday alarm (0-6), None if disabled
118 pub weekday: Option<u8>,
119}
120
121pub struct Pcf8563Interface<I2CBus> {
122 i2c_bus: I2CBus,
123}
124
125impl<I2CBus> Pcf8563Interface<I2CBus> {
126 pub fn new(i2c_bus: I2CBus) -> Self {
127 Self { i2c_bus }
128 }
129}
130
131#[path = "."]
132mod asynchronous {
133 use bisync::asynchronous::*;
134 use device_driver::AsyncRegisterInterface as RegisterInterface;
135 use embedded_hal_async::i2c::I2c;
136 mod driver;
137 pub use driver::*;
138}
139pub use asynchronous::Pcf8563 as Pcf8563Async;
140
141#[path = "."]
142mod blocking {
143 use bisync::synchronous::*;
144 use device_driver::RegisterInterface;
145 use embedded_hal::i2c::I2c;
146 #[allow(clippy::duplicate_mod)]
147 mod driver;
148 pub use driver::*;
149}
150pub use blocking::Pcf8563;
151
152/// Convert BCD to decimal
153#[inline]
154pub(crate) fn bcd_to_dec(bcd: u8) -> u8 {
155 (bcd & 0x0F) + ((bcd >> 4) * 10)
156}
157
158/// Convert decimal to BCD
159#[inline]
160pub(crate) fn dec_to_bcd(dec: u8) -> u8 {
161 ((dec / 10) << 4) | (dec % 10)
162}