mirror of https://github.com/eldruin/ds323x-rs
Compare commits
5 Commits
57ecefccc3
...
e7c41a6424
| Author | SHA1 | Date |
|---|---|---|
|
|
e7c41a6424 | |
|
|
29084a7b9d | |
|
|
7070810621 | |
|
|
775f3288b1 | |
|
|
e2b576edf3 |
15
CHANGELOG.md
15
CHANGELOG.md
|
|
@ -8,6 +8,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
<!-- next-header -->
|
||||
## [Unreleased] - ReleaseDate
|
||||
|
||||
## [0.7.0] - 2025-10-11
|
||||
|
||||
### Added
|
||||
|
||||
- Added `defmt` support behind a feature flag.
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated `rtcc` to version 0.4.0, which has `defmt` support.
|
||||
|
||||
## [0.6.0] - 2025-01-02
|
||||
|
||||
### Changed
|
||||
|
|
@ -91,7 +101,8 @@ this CHANGELOG.
|
|||
[`rtcc`]: https://crates.io/crates/rtcc
|
||||
|
||||
<!-- next-url -->
|
||||
[Unreleased]: https://github.com/eldruin/ds323x-rs/compare/v0.6.0...HEAD
|
||||
[Unreleased]: https://github.com/eldruin/ds323x-rs/compare/v0.7.0...HEAD
|
||||
[0.7.0]: https://github.com/eldruin/ds323x-rs/compare/v0.6.0...v0.7.0
|
||||
[0.6.0]: https://github.com/eldruin/ds323x-rs/compare/v0.5.1...v0.6.0
|
||||
[0.5.1]: https://github.com/eldruin/ds323x-rs/compare/v0.5.0...v0.5.1
|
||||
[0.5.0]: https://github.com/eldruin/ds323x-rs/compare/v0.4.0...v0.5.0
|
||||
|
|
@ -99,4 +110,4 @@ this CHANGELOG.
|
|||
[0.3.2]: https://github.com/eldruin/ds323x-rs/compare/v0.3.1...v0.3.2
|
||||
[0.3.1]: https://github.com/eldruin/ds323x-rs/compare/v0.3.0...v0.3.1
|
||||
[0.3.0]: https://github.com/eldruin/ds323x-rs/compare/v0.2.0...v0.3.0
|
||||
[0.2.0]: https://github.com/eldruin/ds323x-rs/compare/v0.1.0...v0.2.0
|
||||
[0.2.0]: https://github.com/eldruin/ds323x-rs/compare/v0.1.0...v0.2.0
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ds323x"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
authors = ["Diego Barrios Romero <eldruin@gmail.com>"]
|
||||
repository = "https://github.com/eldruin/ds323x-rs"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
|
@ -20,9 +20,13 @@ include = [
|
|||
]
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
defmt = ["dep:defmt", "rtcc/defmt"]
|
||||
|
||||
[dependencies]
|
||||
embedded-hal = "1.0.0"
|
||||
rtcc = "0.3"
|
||||
rtcc = "0.4"
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
embedded-hal-mock = { version = "0.11.1", features = ["eh1"] }
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use crate::{
|
|||
/// - Second, minute and hour: 0
|
||||
/// - Day: 1
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct DayAlarm1 {
|
||||
/// Day of the month [1-31]
|
||||
pub day: u8,
|
||||
|
|
@ -34,6 +35,7 @@ pub struct DayAlarm1 {
|
|||
/// - Second, minute and hour: 0
|
||||
/// - Weekday: 1
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct WeekdayAlarm1 {
|
||||
/// Weekday [1-7]
|
||||
pub weekday: u8,
|
||||
|
|
@ -47,6 +49,7 @@ pub struct WeekdayAlarm1 {
|
|||
|
||||
/// Alarm1 trigger rate
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Alarm1Matching {
|
||||
/// Alarm once per second.
|
||||
OncePerSecond,
|
||||
|
|
@ -68,6 +71,7 @@ pub enum Alarm1Matching {
|
|||
/// - Minute and hour: 0
|
||||
/// - Day: 1
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct DayAlarm2 {
|
||||
/// Day of month [1-31]
|
||||
pub day: u8,
|
||||
|
|
@ -85,6 +89,7 @@ pub struct DayAlarm2 {
|
|||
/// - Minute and hour: 0
|
||||
/// - Weekday: 1
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct WeekdayAlarm2 {
|
||||
/// Weekday [1-7]
|
||||
pub weekday: u8,
|
||||
|
|
@ -96,6 +101,7 @@ pub struct WeekdayAlarm2 {
|
|||
|
||||
/// Alarm2 trigger rate
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Alarm2Matching {
|
||||
/// Alarm once per minute. (00 seconds of every minute)
|
||||
OncePerMinute,
|
||||
|
|
|
|||
|
|
@ -19,11 +19,13 @@ where
|
|||
let mut data = [0; 8];
|
||||
self.iface.read_data(&mut data)?;
|
||||
|
||||
let year = year_from_registers(
|
||||
data[Register::MONTH as usize + 1],
|
||||
data[Register::YEAR as usize + 1],
|
||||
);
|
||||
let month = packed_bcd_to_decimal(data[Register::MONTH as usize + 1] & !BitFlags::CENTURY);
|
||||
let century = data[Register::MONTH as usize + 1] & BitFlags::CENTURY;
|
||||
if century != 0 {
|
||||
return Err(Error::InvalidDeviceCentury);
|
||||
}
|
||||
|
||||
let year = 2000 + (packed_bcd_to_decimal(data[Register::YEAR as usize + 1]) as u16);
|
||||
let month = packed_bcd_to_decimal(data[Register::MONTH as usize + 1]);
|
||||
let day = packed_bcd_to_decimal(data[Register::DOM as usize + 1]);
|
||||
let hour = hours_from_register(data[Register::HOURS as usize + 1]);
|
||||
let minute = packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]);
|
||||
|
|
@ -36,10 +38,9 @@ where
|
|||
}
|
||||
|
||||
fn set_datetime(&mut self, datetime: &NaiveDateTime) -> Result<(), Self::Error> {
|
||||
if datetime.year() < 2000 || datetime.year() > 2100 {
|
||||
if !(2000..=2099).contains(&datetime.year()) {
|
||||
return Err(Error::InvalidInputData);
|
||||
}
|
||||
let (month, year) = month_year_to_registers(datetime.month() as u8, datetime.year() as u16);
|
||||
let mut payload = [
|
||||
Register::SECONDS,
|
||||
decimal_to_packed_bcd(datetime.second() as u8),
|
||||
|
|
@ -47,8 +48,8 @@ where
|
|||
hours_to_register(Hours::H24(datetime.hour() as u8))?,
|
||||
datetime.weekday().number_from_sunday() as u8,
|
||||
decimal_to_packed_bcd(datetime.day() as u8),
|
||||
month,
|
||||
year,
|
||||
decimal_to_packed_bcd(datetime.month() as u8),
|
||||
decimal_to_packed_bcd((datetime.year() - 2000) as u8),
|
||||
];
|
||||
self.iface.write_data(&mut payload)
|
||||
}
|
||||
|
|
@ -92,15 +93,28 @@ where
|
|||
|
||||
fn month(&mut self) -> Result<u8, Self::Error> {
|
||||
let data = self.iface.read_register(Register::MONTH)?;
|
||||
let value = data & !BitFlags::CENTURY;
|
||||
Ok(packed_bcd_to_decimal(value))
|
||||
|
||||
let century = data & BitFlags::CENTURY;
|
||||
if century != 0 {
|
||||
return Err(Error::InvalidDeviceCentury);
|
||||
}
|
||||
|
||||
Ok(packed_bcd_to_decimal(data))
|
||||
}
|
||||
|
||||
fn year(&mut self) -> Result<u16, Self::Error> {
|
||||
let mut data = [0; 3];
|
||||
data[0] = Register::MONTH;
|
||||
self.iface.read_data(&mut data)?;
|
||||
Ok(year_from_registers(data[1], data[2]))
|
||||
|
||||
let century = data[1] & BitFlags::CENTURY;
|
||||
if century != 0 {
|
||||
return Err(Error::InvalidDeviceCentury);
|
||||
}
|
||||
|
||||
let year = 2000 + (packed_bcd_to_decimal(data[2]) as u16);
|
||||
|
||||
Ok(year)
|
||||
}
|
||||
|
||||
fn date(&mut self) -> Result<NaiveDate, Self::Error> {
|
||||
|
|
@ -109,12 +123,15 @@ where
|
|||
self.iface.read_data(&mut data)?;
|
||||
|
||||
let offset = Register::DOM as usize;
|
||||
let year = year_from_registers(
|
||||
data[Register::MONTH as usize + 1 - offset],
|
||||
data[Register::YEAR as usize + 1 - offset],
|
||||
);
|
||||
let month =
|
||||
packed_bcd_to_decimal(data[Register::MONTH as usize + 1 - offset] & !BitFlags::CENTURY);
|
||||
|
||||
let century = data[Register::MONTH as usize + 1 - offset] & BitFlags::CENTURY;
|
||||
if century != 0 {
|
||||
return Err(Error::InvalidDeviceCentury);
|
||||
}
|
||||
|
||||
let year =
|
||||
2000 + (packed_bcd_to_decimal(data[Register::YEAR as usize + 1 - offset]) as u16);
|
||||
let month = packed_bcd_to_decimal(data[Register::MONTH as usize + 1 - offset]);
|
||||
let day = packed_bcd_to_decimal(data[Register::DOM as usize + 1 - offset]);
|
||||
let date = NaiveDate::from_ymd_opt(year.into(), month.into(), day.into());
|
||||
some_or_invalid_error(date)
|
||||
|
|
@ -174,39 +191,30 @@ where
|
|||
}
|
||||
|
||||
fn set_year(&mut self, year: u16) -> Result<(), Self::Error> {
|
||||
if !(2000..=2100).contains(&year) {
|
||||
if !(2000..=2099).contains(&year) {
|
||||
return Err(Error::InvalidInputData);
|
||||
}
|
||||
let data = self.iface.read_register(Register::MONTH)?;
|
||||
let month_bcd = data & !BitFlags::CENTURY;
|
||||
if year > 2099 {
|
||||
let mut data = [
|
||||
Register::MONTH,
|
||||
BitFlags::CENTURY | month_bcd,
|
||||
decimal_to_packed_bcd((year - 2100) as u8),
|
||||
];
|
||||
self.iface.write_data(&mut data)
|
||||
} else {
|
||||
let mut data = [
|
||||
Register::MONTH,
|
||||
month_bcd,
|
||||
decimal_to_packed_bcd((year - 2000) as u8),
|
||||
];
|
||||
self.iface.write_data(&mut data)
|
||||
}
|
||||
|
||||
let mut data = [
|
||||
Register::MONTH,
|
||||
month_bcd,
|
||||
decimal_to_packed_bcd((year - 2000) as u8),
|
||||
];
|
||||
self.iface.write_data(&mut data)
|
||||
}
|
||||
|
||||
fn set_date(&mut self, date: &rtcc::NaiveDate) -> Result<(), Self::Error> {
|
||||
if date.year() < 2000 || date.year() > 2100 {
|
||||
if !(2000..=2099).contains(&date.year()) {
|
||||
return Err(Error::InvalidInputData);
|
||||
}
|
||||
let (month, year) = month_year_to_registers(date.month() as u8, date.year() as u16);
|
||||
let mut payload = [
|
||||
Register::DOW,
|
||||
date.weekday().number_from_sunday() as u8,
|
||||
decimal_to_packed_bcd(date.day() as u8),
|
||||
month,
|
||||
year,
|
||||
decimal_to_packed_bcd(date.month() as u8),
|
||||
decimal_to_packed_bcd((date.year() - 2000) as u8),
|
||||
];
|
||||
self.iface.write_data(&mut payload)
|
||||
}
|
||||
|
|
@ -241,28 +249,6 @@ fn hours_from_register(data: u8) -> Hours {
|
|||
}
|
||||
}
|
||||
|
||||
fn year_from_registers(month: u8, year: u8) -> u16 {
|
||||
let century = month & BitFlags::CENTURY;
|
||||
let year = packed_bcd_to_decimal(year);
|
||||
if century != 0 {
|
||||
2100 + u16::from(year)
|
||||
} else {
|
||||
2000 + u16::from(year)
|
||||
}
|
||||
}
|
||||
|
||||
fn month_year_to_registers(month: u8, year: u16) -> (u8, u8) {
|
||||
if year > 2099 {
|
||||
let month = BitFlags::CENTURY | decimal_to_packed_bcd(month);
|
||||
(month, decimal_to_packed_bcd((year - 2100) as u8))
|
||||
} else {
|
||||
(
|
||||
decimal_to_packed_bcd(month),
|
||||
decimal_to_packed_bcd((year - 2000) as u8),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_24h_format(hours_data: u8) -> bool {
|
||||
hours_data & BitFlags::H24_H12 == 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,14 @@ use embedded_hal::{i2c, spi};
|
|||
|
||||
/// I2C interface
|
||||
#[derive(Debug, Default)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct I2cInterface<I2C> {
|
||||
pub(crate) i2c: I2C,
|
||||
}
|
||||
|
||||
/// SPI interface
|
||||
#[derive(Debug, Default)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct SpiInterface<SPI> {
|
||||
pub(crate) spi: SPI,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -377,6 +377,7 @@ pub const SPI_MODE_3: Mode = MODE_3;
|
|||
|
||||
/// All possible errors in this crate
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error<E> {
|
||||
/// I²C/SPI bus error
|
||||
Comm(E),
|
||||
|
|
@ -387,10 +388,16 @@ pub enum Error<E> {
|
|||
/// It was not possible to read a valid date and/or time.
|
||||
/// The device is probably missing initialization.
|
||||
InvalidDeviceState,
|
||||
/// Device century is not the 20th century.
|
||||
///
|
||||
/// The device does not produce valid dates for centuries
|
||||
/// other than the 20th century.
|
||||
InvalidDeviceCentury,
|
||||
}
|
||||
|
||||
/// Square-wave output frequency
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum SqWFreq {
|
||||
/// 1 Hz (default)
|
||||
_1Hz,
|
||||
|
|
@ -406,6 +413,7 @@ pub enum SqWFreq {
|
|||
///
|
||||
/// This is only available on the DS3232 and DS3234 devices.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TempConvRate {
|
||||
/// Once every 64 seconds (default)
|
||||
_64s,
|
||||
|
|
@ -478,6 +486,7 @@ pub mod ic {
|
|||
|
||||
/// DS3231, DS3232 and DS3234 RTC driver
|
||||
#[derive(Debug, Default)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Ds323x<DI, IC> {
|
||||
iface: DI,
|
||||
control: u8,
|
||||
|
|
|
|||
|
|
@ -57,21 +57,6 @@ impl BitFlags {
|
|||
pub const WEEKDAY: u8 = 0b0100_0000;
|
||||
}
|
||||
|
||||
pub struct DummyOutputPin;
|
||||
|
||||
impl embedded_hal::digital::OutputPin for DummyOutputPin {
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::ErrorType for DummyOutputPin {
|
||||
type Error = embedded_hal::digital::ErrorKind;
|
||||
}
|
||||
|
||||
pub fn new_ds3231(
|
||||
transactions: &[I2cTrans],
|
||||
) -> Ds323x<interface::I2cInterface<I2cMock>, ic::DS3231> {
|
||||
|
|
|
|||
|
|
@ -185,12 +185,6 @@ mod month {
|
|||
get_param_test!(get, month, MONTH, 1, 1);
|
||||
read_set_param_test!(set, set_month, MONTH, 12, 0b0000_0010, 0b0001_0010);
|
||||
set_invalid_param_range_test!(invalid, set_month, 0, 13);
|
||||
|
||||
mod keeps_century {
|
||||
use super::*;
|
||||
get_param_test!(get, month, MONTH, 12, 0b1001_0010);
|
||||
read_set_param_test!(set, set_month, MONTH, 12, 0b1000_0010, 0b1001_0010);
|
||||
}
|
||||
}
|
||||
|
||||
mod year {
|
||||
|
|
@ -206,18 +200,7 @@ mod year {
|
|||
0b1001_1001
|
||||
);
|
||||
|
||||
get_param_read_array_test!(century1_get, year, 2100, MONTH, [0b1000_0000, 0], [0, 0]);
|
||||
read_set_param_write_two_test!(
|
||||
century1_set,
|
||||
set_year,
|
||||
2100,
|
||||
MONTH,
|
||||
0b0001_0010,
|
||||
0b1001_0010,
|
||||
0
|
||||
);
|
||||
|
||||
set_invalid_param_range_test!(invalid, set_year, 1999, 2101);
|
||||
set_invalid_param_range_test!(invalid, set_year, 1999, 2100);
|
||||
}
|
||||
|
||||
macro_rules! invalid_dt_test {
|
||||
|
|
@ -233,7 +216,7 @@ macro_rules! invalid_dt_test {
|
|||
}
|
||||
#[test]
|
||||
fn datetime_too_big() {
|
||||
let dt = new_datetime(2101, 1, 2, 3, 4, 5);
|
||||
let dt = new_datetime(2100, 1, 2, 3, 4, 5);
|
||||
let mut dev = $create_method(&[]);
|
||||
assert_invalid_input_data!(dev.set_datetime(&dt));
|
||||
$destroy_method(dev);
|
||||
|
|
@ -247,7 +230,7 @@ macro_rules! invalid_dt_test {
|
|||
}
|
||||
#[test]
|
||||
fn date_too_big() {
|
||||
let d = new_date(2101, 1, 2);
|
||||
let d = new_date(2100, 1, 2);
|
||||
let mut dev = $create_method(&[]);
|
||||
assert_invalid_input_data!(dev.set_date(&d));
|
||||
$destroy_method(dev);
|
||||
|
|
@ -339,17 +322,6 @@ macro_rules! dt_test {
|
|||
$destroy_method(dev);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_date_century() {
|
||||
let d = new_date(2100, 8, 13);
|
||||
let mut dev = $create_method(&$mac_trans_write!(
|
||||
DOW,
|
||||
[0b0000_0110, 0b0001_0011, 0b1000_1000, 0]
|
||||
));
|
||||
dev.set_date(&d).unwrap();
|
||||
$destroy_method(dev);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_time() {
|
||||
let t = NaiveTime::from_hms_opt(23, 59, 58).unwrap();
|
||||
|
|
|
|||
Loading…
Reference in New Issue