Compare commits

...

31 Commits

Author SHA1 Message Date
Diego Barrios Romero c5fdd9057f chore: Release ds323x version 0.6.0 2025-01-02 09:26:23 +01:00
Diego Barrios Romero 86dd80a8ad Fix clippy warnings 2025-01-02 08:40:04 +01:00
Diego Barrios Romero 3597c7407e Finish updating to embedded-hal 1.0.0 2025-01-02 08:35:42 +01:00
Diego Barrios Romero 21546c07d3 Update changelog 2025-01-02 08:32:26 +01:00
Diego Barrios Romero ddca93fa30 Update dependency 2025-01-02 08:31:23 +01:00
Diego Barrios Romero b086f3086e Fix formatting 2025-01-02 08:31:08 +01:00
Louis Dupré Bertoni 03e0d9aad9 Update dependencies 2025-01-02 08:27:52 +01:00
Diego Barrios Romero 8204a4dfc1 Support cargo-release 2025-01-02 08:18:02 +01:00
Diego Barrios Romero 4e0998897a Bump MSRV 2025-01-02 08:15:40 +01:00
Diego Barrios Romero f546361477 Update CI 2025-01-02 08:14:10 +01:00
Diego Barrios Romero fa43172087 Prepare 0.5.1 release 2023-07-17 21:53:42 +02:00
Diego Barrios Romero 74009022bf Update changelog 2023-07-17 07:51:52 +02:00
kirbylife fd6a145825 fix set_day method
The method receives in decimal the value to be set as the day of the month, but the ds3231 module expects it in bcd format.  
Using the 'write_register_decimal' method, can be written in the correct format.
2023-07-17 13:50:17 +02:00
Diego Barrios Romero de0231b958 Raise MSRV and update CI 2023-07-16 13:44:50 +02:00
Diego Barrios Romero a8b8428570 Update dependency 2023-07-16 13:44:14 +02:00
Diego Barrios Romero f7d48398dc Update copyright 2023-07-16 13:43:59 +02:00
Diego Barrios Romero fe990d7295 Avoid using deprecated methods 2023-07-16 13:43:46 +02:00
Diego Barrios Romero b06eb59cd6 Add changelog link 2022-02-21 07:11:36 +01:00
Diego Barrios Romero b0ab213098 Prepare 0.5.0 release 2022-02-21 07:10:05 +01:00
Diego Barrios Romero 6c6088f890 Raise MSRV to 1.35.0 2022-02-20 10:55:37 +01:00
Diego Barrios Romero b3323f1a15 Enhance support notice 2022-02-20 10:39:28 +01:00
Diego Barrios Romero 944e661053 Update CI 2022-02-20 10:35:59 +01:00
Diego Barrios Romero faf48c2870 Fix small clippy warnings and run it on tests as well on CI 2022-02-20 10:27:12 +01:00
Diego Barrios Romero 92e6de6c47 Update docs 2022-02-20 10:19:50 +01:00
Diego Barrios Romero 774b3cf527 Update to rtcc 0.3 2022-02-20 10:17:55 +01:00
Diego Barrios Romero fd66a531e2 Document MSRV in readme 2022-02-20 10:08:15 +01:00
Rafael Bachmann e4f15715ed Bump linux-embedded-hal to 0.3.2 2021-10-26 11:18:03 +02:00
Rafael Bachmann 7329fe7c50 Upgrade dependencies 2021-10-26 11:18:03 +02:00
Rafael Bachmann 1930de500d Fix clippy warnings 2021-10-26 11:18:03 +02:00
Diego Barrios Romero a657eec8d6 Prepare 0.4.0 release 2021-05-22 23:16:44 +02:00
Diego Barrios Romero 9c6fe164b8 Fix returning error if the device state is invalid and leads to invalid date/time values 2021-05-22 23:09:10 +02:00
24 changed files with 533 additions and 535 deletions

View File

@ -1,10 +1,5 @@
on:
push:
pull_request:
schedule:
- cron: 0 0 * * 0
name: Build
on: [push, pull_request]
env:
RUSTFLAGS: '--deny warnings'
@ -15,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
rust: [stable, beta, nightly, 1.31.0]
rust: [stable, 1.75.0]
TARGET:
- x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl
@ -27,22 +22,15 @@ jobs:
- thumbv7em-none-eabihf
- thumbv7m-none-eabi
include:
# Test nightly but don't fail
- rust: nightly
experimental: true
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
profile: minimal
toolchain: ${{ matrix.rust }}
target: ${{ matrix.TARGET }}
override: true
targets: ${{ matrix.TARGET }}
- name: Checkout CI scripts
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
repository: 'eldruin/rust-driver-ci-scripts'
ref: 'master'
@ -51,115 +39,69 @@ jobs:
- run: ./ci/patch-no-std.sh
if: ${{ ! contains(matrix.TARGET, 'x86_64') }}
- name: Build
uses: actions-rs/cargo@v1
with:
command: build
args: --target=${{ matrix.TARGET }}
- run: cargo build --target=${{ matrix.TARGET }}
checks:
name: Checks
runs-on: ubuntu-latest
strategy:
matrix:
rust: [stable, beta]
TARGET:
- x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
profile: minimal
toolchain: ${{ matrix.rust }}
target: ${{ matrix.TARGET }}
override: true
targets: x86_64-unknown-linux-gnu
components: rustfmt
- name: Doc
uses: actions-rs/cargo@v1
with:
command: doc
- name: Formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- run: cargo doc
- run: cargo fmt --all -- --check
clippy:
name: Clippy
runs-on: ubuntu-latest
strategy:
matrix:
rust: [1.31.0]
TARGET:
- x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
profile: minimal
toolchain: ${{ matrix.rust }}
target: ${{ matrix.TARGET }}
override: true
toolchain: 1.83.0
targets: x86_64-unknown-linux-gnu
components: clippy
- name: Clippy
uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
- run: cargo clippy --all-targets
test:
name: Tests
runs-on: ubuntu-latest
strategy:
matrix:
rust: [stable, beta, nightly]
rust: [stable]
TARGET: [x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl]
include:
- rust: nightly
experimental: true
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
profile: minimal
toolchain: ${{ matrix.rust }}
target: ${{ matrix.TARGET }}
override: true
targets: ${{ matrix.TARGET }}
- name: Test
uses: actions-rs/cargo@v1
with:
command: test
args: --target=${{ matrix.TARGET }}
run: cargo test --target=${{ matrix.TARGET }}
- name: Build examples
uses: actions-rs/cargo@v1
if: contains(matrix.TARGET, 'x86_64')
with:
command: build
args: --target=${{ matrix.TARGET }} --examples
run: cargo build --target=${{ matrix.TARGET }} --examples
coverage:
name: Coverage
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Run cargo-tarpaulin
uses: actions-rs/tarpaulin@v0.1
with:
args: '--out Lcov -- --test-threads 1'
- name: Generate code coverage
run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
- name: upload to Coveralls
uses: coverallsapp/github-action@master

View File

@ -5,9 +5,38 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [Unreleased]
<!-- next-header -->
## [Unreleased] - ReleaseDate
...
## [0.6.0] - 2025-01-02
### Changed
- [breaking-change] Removed `Error::Pin` variant.
- [breaking-change] Update to `embedded-hal` 1.0.0.
- Raised MSRV to version 1.75.0
## [0.5.1] - 2023-07-17
### Fixed
- Fixed `set_day` method. See: [PR #9](https://github.com/eldruin/ds323x-rs/pull/9)
### Changed
- Raised MSRV to version 1.60.0
## [0.5.0] - 2022-02-21
### Changed
- [breaking-change] Update `rtcc` to version 0.3.
- [breaking-change] Remove `get_` from all public method names to comply with the Rust API guidelines.
- Raise MSRV to version 1.35.0
## [0.4.0] - 2021-05-22
### Changed
- [breaking-change] Return `Error::InvalidDeviceState` if it was not possible to read the
date and/or time from the device because the state of the device corresponds to
an invalid date and/or time.
## [0.3.2] - 2021-02-22
@ -61,7 +90,12 @@ this CHANGELOG.
[`chrono`]: https://crates.io/crates/chrono
[`rtcc`]: https://crates.io/crates/rtcc
[Unreleased]: https://github.com/eldruin/ds323x-rs/compare/v0.3.2...HEAD
<!-- next-url -->
[Unreleased]: https://github.com/eldruin/ds323x-rs/compare/v0.6.0...HEAD
[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
[0.4.0]: https://github.com/eldruin/ds323x-rs/compare/v0.3.2...v0.4.0
[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

View File

@ -1,6 +1,6 @@
[package]
name = "ds323x"
version = "0.3.2" # remember to update html_root_url
version = "0.6.0"
authors = ["Diego Barrios Romero <eldruin@gmail.com>"]
repository = "https://github.com/eldruin/ds323x-rs"
license = "MIT OR Apache-2.0"
@ -21,12 +21,13 @@ include = [
edition = "2018"
[dependencies]
embedded-hal = "0.2.3"
rtcc = "0.2"
embedded-hal = "1.0.0"
rtcc = "0.3"
[dev-dependencies]
linux-embedded-hal = "0.3"
embedded-hal-mock = "0.7"
embedded-hal-mock = { version = "0.11.1", features = ["eh1"] }
embedded-hal-bus = "0.2"
linux-embedded-hal = "0.4.0"
[profile.release]
lto = true

View File

@ -1,4 +1,4 @@
Copyright (C) 2018-2021 Diego Barrios Romero
Copyright (C) 2018-2025 Diego Barrios Romero
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View File

@ -2,6 +2,7 @@
[![crates.io](https://img.shields.io/crates/v/ds323x.svg)](https://crates.io/crates/ds323x)
[![Docs](https://docs.rs/ds323x/badge.svg)](https://docs.rs/ds323x)
![MSRV](https://img.shields.io/badge/rustc-1.75+-blue.svg)
[![Build Status](https://github.com/eldruin/ds323x-rs/workflows/Build/badge.svg)](https://github.com/eldruin/ds323x-rs/actions?query=workflow%3ABuild)
[![Coverage Status](https://coveralls.io/repos/eldruin/ds323x-rs/badge.svg?branch=master)](https://coveralls.io/r/eldruin/ds323x-rs?branch=master)
@ -11,8 +12,8 @@ extremely accurate real-time clocks, based on the [`embedded-hal`] traits.
[`embedded-hal`]: https://github.com/rust-embedded/embedded-hal
This driver allows you to:
- Read and set date and time in 12-hour and 24-hour format. See: `get_datetime`.
- Read and set date and time individual elements. For example, see: `get_year`.
- Read and set date and time in 12-hour and 24-hour format. See: `datetime`.
- Read and set date and time individual elements. For example, see: `year`.
- Enable and disable the real-time clock. See: `enable`.
- Read the busy status. See `busy`.
- Read whether the oscillator is or has been stopped. See `has_been_stopped`.
@ -31,7 +32,7 @@ This driver allows you to:
- Enable and disable the 32kHz output. See `enable_32khz_output`.
- Enable and disable the 32kHz output when battery powered. See `enable_32khz_output_on_battery`.
- Temperature conversion:
- Read the temperature. See `get_temperature`.
- Read the temperature. See `temperature`.
- Force a temperature conversion and time compensation. See `convert_temperature`.
- Set the temperature conversion rate. See `set_temperature_conversion_rate`.
- Enable and disable the temperature conversions when battery-powered. See `enable_temperature_conversions_on_battery`.
@ -110,16 +111,19 @@ Please find additional examples using hardware in this repository: [driver-examp
[driver-examples]: https://github.com/eldruin/driver-examples
```rust
use ds323x::{Ds323x, NaiveDate, Rtcc};
use ds323x::{DateTimeAccess, Ds323x, NaiveDate, Rtcc};
use linux_embedded_hal::I2cdev;
fn main() {
let dev = I2cdev::new("/dev/i2c-1").unwrap();
let mut rtc = Ds323x::new_ds3231(dev);
let datetime = NaiveDate::from_ymd(2020, 5, 1).and_hms(19, 59, 58);
let datetime = NaiveDate::from_ymd_opt(2020, 5, 1)
.unwrap()
.and_hms_opt(19, 59, 58)
.unwrap();
rtc.set_datetime(&datetime).unwrap();
// do something else...
let time = rtc.get_time().unwrap();
let time = rtc.time().unwrap();
println!("Time: {}", time);
let _dev = rtc.destroy_ds3231();
@ -128,9 +132,15 @@ fn main() {
## Support
For questions, issues, feature requests, and other changes, please file an
For questions, issues, feature requests like compatibility with other devices and other
changes, please file an
[issue in the github project](https://github.com/eldruin/ds323x-rs/issues).
## Minimum Supported Rust Version (MSRV)
This crate is guaranteed to compile on stable Rust 1.75 and up. It *might*
compile with older versions but that may change in any new patch release.
## License
Licensed under either of
@ -147,4 +157,3 @@ at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.

View File

@ -1,13 +1,16 @@
use ds323x::{Ds323x, NaiveDate, Rtcc};
use ds323x::{DateTimeAccess, Ds323x, NaiveDate, Rtcc};
use linux_embedded_hal::I2cdev;
fn main() {
let dev = I2cdev::new("/dev/i2c-1").unwrap();
let mut rtc = Ds323x::new_ds3231(dev);
let datetime = NaiveDate::from_ymd(2020, 5, 1).and_hms(19, 59, 58);
let datetime = NaiveDate::from_ymd_opt(2020, 5, 1)
.unwrap()
.and_hms_opt(19, 59, 58)
.unwrap();
rtc.set_datetime(&datetime).unwrap();
// do something else...
let time = rtc.get_time().unwrap();
let time = rtc.time().unwrap();
println!("Time: {}", time);
let _dev = rtc.destroy_ds3231();

7
release.toml 100644
View File

@ -0,0 +1,7 @@
pre-release-replacements = [
{file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
{file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1},
{file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
{file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## [Unreleased] - ReleaseDate\n", exactly=1},
{file="CHANGELOG.md", search="<!-- next-url -->", replace="<!-- next-url -->\n[Unreleased]: https://github.com/eldruin/{{crate_name}}-rs/compare/{{tag_name}}...HEAD", exactly=1},
]

View File

@ -2,11 +2,11 @@
use crate::{ic, interface::I2cInterface, BitFlags, Ds323x, CONTROL_POR_VALUE};
use core::marker::PhantomData;
use embedded_hal::blocking::i2c;
use embedded_hal::i2c;
impl<I2C, E> Ds323x<I2cInterface<I2C>, ic::DS3231>
where
I2C: i2c::Write<Error = E> + i2c::WriteRead<Error = E>,
I2C: i2c::I2c<Error = E>,
{
/// Create a new instance of the DS3231 device.
pub fn new_ds3231(i2c: I2C) -> Self {

View File

@ -4,11 +4,11 @@ use crate::{
ic, interface::I2cInterface, BitFlags, Ds323x, Error, TempConvRate, CONTROL_POR_VALUE,
};
use core::marker::PhantomData;
use embedded_hal::blocking::i2c;
use embedded_hal::i2c;
impl<I2C, E> Ds323x<I2cInterface<I2C>, ic::DS3232>
where
I2C: i2c::Write<Error = E> + i2c::WriteRead<Error = E>,
I2C: i2c::I2c<Error = E>,
{
/// Create a new instance of the DS3232 device.
pub fn new_ds3232(i2c: I2C) -> Self {
@ -32,7 +32,7 @@ where
/// [`enable_32khz_output()`](#method.enable_32khz_output).
///
/// Note: This is only available for DS3232 and DS3234 devices.
pub fn enable_32khz_output_on_battery(&mut self) -> Result<(), Error<E, ()>> {
pub fn enable_32khz_output_on_battery(&mut self) -> Result<(), Error<E>> {
let status = self.status | BitFlags::BB32KHZ;
self.write_status_without_clearing_alarm(status)
}
@ -43,7 +43,7 @@ where
/// it enabled. See [`enable_32khz_output()`](#method.enable_32khz_output).
///
/// Note: This is only available for DS3232 and DS3234 devices.
pub fn disable_32khz_output_on_battery(&mut self) -> Result<(), Error<E, ()>> {
pub fn disable_32khz_output_on_battery(&mut self) -> Result<(), Error<E>> {
let status = self.status & !BitFlags::BB32KHZ;
self.write_status_without_clearing_alarm(status)
}
@ -55,10 +55,7 @@ where
/// temperature changes will not be compensated for.
///
/// Note: This is only available for DS3232 and DS3234 devices.
pub fn set_temperature_conversion_rate(
&mut self,
rate: TempConvRate,
) -> Result<(), Error<E, ()>> {
pub fn set_temperature_conversion_rate(&mut self, rate: TempConvRate) -> Result<(), Error<E>> {
let status = match rate {
TempConvRate::_64s => self.status & !BitFlags::CRATE1 & !BitFlags::CRATE0,
TempConvRate::_128s => self.status & !BitFlags::CRATE1 | BitFlags::CRATE0,

View File

@ -2,21 +2,17 @@
use crate::interface::{SpiInterface, WriteData};
use crate::{ic, BitFlags, Ds323x, Error, Register, TempConvRate, CONTROL_POR_VALUE};
use core::marker::PhantomData;
use embedded_hal::{blocking::spi, digital::v2::OutputPin};
use embedded_hal::spi;
impl<SPI, CS, CommE, PinE> Ds323x<SpiInterface<SPI, CS>, ic::DS3234>
impl<SPI, E> Ds323x<SpiInterface<SPI>, ic::DS3234>
where
SPI: spi::Transfer<u8, Error = CommE> + spi::Write<u8, Error = CommE>,
CS: OutputPin<Error = PinE>,
SPI: spi::SpiDevice<u8, Error = E>,
{
/// Create a new instance.
pub fn new_ds3234(spi: SPI, chip_select: CS) -> Self {
pub fn new_ds3234(spi: SPI) -> Self {
const STATUS_POR_VALUE: u8 = BitFlags::OSC_STOP | BitFlags::BB32KHZ | BitFlags::EN32KHZ;
Ds323x {
iface: SpiInterface {
spi,
cs: chip_select,
},
iface: SpiInterface { spi },
control: CONTROL_POR_VALUE,
status: STATUS_POR_VALUE,
_ic: PhantomData,
@ -24,8 +20,8 @@ where
}
/// Destroy driver instance, return SPI bus instance and CS output pin.
pub fn destroy_ds3234(self) -> (SPI, CS) {
(self.iface.spi, self.iface.cs)
pub fn destroy_ds3234(self) -> SPI {
self.iface.spi
}
/// Enable the 32kHz output when battery-powered. (enabled per default)
@ -34,7 +30,7 @@ where
/// [`enable_32khz_output()`](#method.enable_32khz_output).
///
/// Note: This is only available for DS3232 and DS3234 devices.
pub fn enable_32khz_output_on_battery(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn enable_32khz_output_on_battery(&mut self) -> Result<(), Error<E>> {
let status = self.status | BitFlags::BB32KHZ;
self.write_status_without_clearing_alarm(status)
}
@ -45,7 +41,7 @@ where
/// it enabled. See [`enable_32khz_output()`](#method.enable_32khz_output).
///
/// Note: This is only available for DS3232 and DS3234 devices.
pub fn disable_32khz_output_on_battery(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn disable_32khz_output_on_battery(&mut self) -> Result<(), Error<E>> {
let status = self.status & !BitFlags::BB32KHZ;
self.write_status_without_clearing_alarm(status)
}
@ -57,10 +53,7 @@ where
/// temperature changes will not be compensated for.
///
/// Note: This is only available for DS3232 and DS3234 devices.
pub fn set_temperature_conversion_rate(
&mut self,
rate: TempConvRate,
) -> Result<(), Error<CommE, PinE>> {
pub fn set_temperature_conversion_rate(&mut self, rate: TempConvRate) -> Result<(), Error<E>> {
let status = match rate {
TempConvRate::_64s => self.status & !BitFlags::CRATE1 & !BitFlags::CRATE0,
TempConvRate::_128s => self.status & !BitFlags::CRATE1 | BitFlags::CRATE0,
@ -73,14 +66,14 @@ where
/// Enable the temperature conversions when battery-powered. (enabled per default)
///
/// Note: This is only available for DS3234 devices.
pub fn enable_temperature_conversions_on_battery(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn enable_temperature_conversions_on_battery(&mut self) -> Result<(), Error<E>> {
self.iface.write_register(Register::TEMP_CONV, 0)
}
/// Disable the temperature conversions when battery-powered.
///
/// Note: This is only available for DS3234 devices.
pub fn disable_temperature_conversions_on_battery(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn disable_temperature_conversions_on_battery(&mut self) -> Result<(), Error<E>> {
self.iface
.write_register(Register::TEMP_CONV, BitFlags::TEMP_CONV_BAT)
}

View File

@ -132,8 +132,8 @@ fn get_matching_mask_alarm2(matching: Alarm2Matching) -> [u8; 3] {
fn is_hour_valid(hours: Hours) -> bool {
match hours {
Hours::H24(h) if h > 23 => true,
Hours::AM(h) if h < 1 || h > 12 => true,
Hours::PM(h) if h < 1 || h > 12 => true,
Hours::AM(h) if !(1..=12).contains(&h) => true,
Hours::PM(h) if !(1..=12).contains(&h) => true,
_ => false,
}
}
@ -143,16 +143,16 @@ fn amend_hour(hours: Hours) -> Hours {
match hours {
Hours::H24(h) if h > 23 => Hours::H24(0),
Hours::H24(h) => Hours::H24(h),
Hours::AM(h) if h < 1 || h > 12 => Hours::AM(1),
Hours::AM(h) if !(1..=12).contains(&h) => Hours::AM(1),
Hours::AM(h) => Hours::AM(h),
Hours::PM(h) if h < 1 || h > 12 => Hours::PM(1),
Hours::PM(h) if !(1..=12).contains(&h) => Hours::PM(1),
Hours::PM(h) => Hours::PM(h),
}
}
impl<DI, IC, CommE, PinE> Ds323x<DI, IC>
impl<DI, IC, E> Ds323x<DI, IC>
where
DI: ReadData<Error = Error<CommE, PinE>> + WriteData<Error = Error<CommE, PinE>>,
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
{
/// Set Alarm1 for day of the month.
///
@ -165,7 +165,7 @@ where
&mut self,
when: DayAlarm1,
matching: Alarm1Matching,
) -> Result<(), Error<CommE, PinE>> {
) -> Result<(), Error<E>> {
let day_invalid = when.day < 1 || when.day > 31;
let hour_invalid = is_hour_valid(when.hour);
let minute_invalid = when.minute > 59;
@ -200,7 +200,7 @@ where
///
/// Will return an `Error::InvalidInputData` if any of the parameters is out of range.
/// The day is not used by this matching strategy and is set to 1.
pub fn set_alarm1_hms(&mut self, when: NaiveTime) -> Result<(), Error<CommE, PinE>> {
pub fn set_alarm1_hms(&mut self, when: NaiveTime) -> Result<(), Error<E>> {
let alarm = DayAlarm1 {
day: 1,
hour: Hours::H24(when.hour() as u8),
@ -221,7 +221,7 @@ where
&mut self,
when: WeekdayAlarm1,
matching: Alarm1Matching,
) -> Result<(), Error<CommE, PinE>> {
) -> Result<(), Error<E>> {
let weekday_invalid = when.weekday < 1 || when.weekday > 7;
let hour_invalid = is_hour_valid(when.hour);
let minute_invalid = when.minute > 59;
@ -263,7 +263,7 @@ where
&mut self,
when: DayAlarm2,
matching: Alarm2Matching,
) -> Result<(), Error<CommE, PinE>> {
) -> Result<(), Error<E>> {
let day_invalid = when.day < 1 || when.day > 31;
let hour_invalid = is_hour_valid(when.hour);
let minute_invalid = when.minute > 59;
@ -293,7 +293,7 @@ where
///
/// Will return an `Error::InvalidInputData` if any of the parameters is out of range.
/// The day is not used by this matching strategy and is set to 1.
pub fn set_alarm2_hm(&mut self, when: NaiveTime) -> Result<(), Error<CommE, PinE>> {
pub fn set_alarm2_hm(&mut self, when: NaiveTime) -> Result<(), Error<E>> {
let alarm = DayAlarm2 {
day: 1,
hour: Hours::H24(when.hour() as u8),
@ -313,7 +313,7 @@ where
&mut self,
when: WeekdayAlarm2,
matching: Alarm2Matching,
) -> Result<(), Error<CommE, PinE>> {
) -> Result<(), Error<E>> {
let weekday_invalid = when.weekday < 1 || when.weekday > 7;
let hour_invalid = is_hour_valid(when.hour);
let minute_invalid = when.minute > 59;

View File

@ -5,18 +5,18 @@ use crate::{
BitFlags, Ds323x, Error, Register, SqWFreq,
};
impl<DI, IC, CommE, PinE> Ds323x<DI, IC>
impl<DI, IC, E> Ds323x<DI, IC>
where
DI: ReadData<Error = Error<CommE, PinE>> + WriteData<Error = Error<CommE, PinE>>,
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
{
/// Enable the oscillator (set the clock running) (default).
pub fn enable(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn enable(&mut self) -> Result<(), Error<E>> {
let control = self.control;
self.write_control(control & !BitFlags::EOSC)
}
/// Disable the oscillator (stops the clock).
pub fn disable(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn disable(&mut self) -> Result<(), Error<E>> {
let control = self.control;
self.write_control(control | BitFlags::EOSC)
}
@ -24,7 +24,7 @@ where
/// Force a temperature conversion and time compensation with TXCO algorithm.
///
/// The *busy* status should be checked before doing this. See [`busy()`](#method.busy)
pub fn convert_temperature(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn convert_temperature(&mut self) -> Result<(), Error<E>> {
let control = self.iface.read_register(Register::CONTROL)?;
// do not overwrite if a conversion is in progress
if (control & BitFlags::TEMP_CONV) == 0 {
@ -35,90 +35,89 @@ where
}
/// Enable the 32kHz output. (enabled per default)
pub fn enable_32khz_output(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn enable_32khz_output(&mut self) -> Result<(), Error<E>> {
let status = self.status | BitFlags::EN32KHZ;
self.write_status_without_clearing_alarm(status)
}
/// Disable the 32kHz output.
pub fn disable_32khz_output(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn disable_32khz_output(&mut self) -> Result<(), Error<E>> {
let status = self.status & !BitFlags::EN32KHZ;
self.write_status_without_clearing_alarm(status)
}
/// Set the aging offset.
pub fn set_aging_offset(&mut self, offset: i8) -> Result<(), Error<CommE, PinE>> {
pub fn set_aging_offset(&mut self, offset: i8) -> Result<(), Error<E>> {
self.iface
.write_register(Register::AGING_OFFSET, offset as u8)
}
/// Read the aging offset.
pub fn get_aging_offset(&mut self) -> Result<i8, Error<CommE, PinE>> {
pub fn aging_offset(&mut self) -> Result<i8, Error<E>> {
let offset = self.iface.read_register(Register::AGING_OFFSET)?;
Ok(offset as i8)
}
/// Set the interrupt/square-wave output to be used as interrupt output.
pub fn use_int_sqw_output_as_interrupt(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn use_int_sqw_output_as_interrupt(&mut self) -> Result<(), Error<E>> {
let control = self.control;
self.write_control(control | BitFlags::INTCN)
}
/// Set the interrupt/square-wave output to be used as square-wave output. (default)
pub fn use_int_sqw_output_as_square_wave(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn use_int_sqw_output_as_square_wave(&mut self) -> Result<(), Error<E>> {
let control = self.control;
self.write_control(control & !BitFlags::INTCN)
}
/// Enable battery-backed square wave generation.
pub fn enable_square_wave(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn enable_square_wave(&mut self) -> Result<(), Error<E>> {
let control = self.control;
self.write_control(control | BitFlags::BBSQW)
}
/// Disable battery-backed square wave generation.
pub fn disable_square_wave(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn disable_square_wave(&mut self) -> Result<(), Error<E>> {
let control = self.control;
self.write_control(control & !BitFlags::BBSQW)
}
/// Set the square-wave output frequency.
pub fn set_square_wave_frequency(&mut self, freq: SqWFreq) -> Result<(), Error<CommE, PinE>> {
let new_control;
match freq {
SqWFreq::_1Hz => new_control = self.control & !BitFlags::RS2 & !BitFlags::RS1,
SqWFreq::_1_024Hz => new_control = self.control & !BitFlags::RS2 | BitFlags::RS1,
SqWFreq::_4_096Hz => new_control = self.control | BitFlags::RS2 & !BitFlags::RS1,
SqWFreq::_8_192Hz => new_control = self.control | BitFlags::RS2 | BitFlags::RS1,
}
pub fn set_square_wave_frequency(&mut self, freq: SqWFreq) -> Result<(), Error<E>> {
let new_control = match freq {
SqWFreq::_1Hz => self.control & !BitFlags::RS2 & !BitFlags::RS1,
SqWFreq::_1_024Hz => self.control & !BitFlags::RS2 | BitFlags::RS1,
SqWFreq::_4_096Hz => self.control | BitFlags::RS2 & !BitFlags::RS1,
SqWFreq::_8_192Hz => self.control | BitFlags::RS2 | BitFlags::RS1,
};
self.write_control(new_control)
}
/// Enable Alarm1 interrupts.
pub fn enable_alarm1_interrupts(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn enable_alarm1_interrupts(&mut self) -> Result<(), Error<E>> {
let control = self.control;
self.write_control(control | BitFlags::ALARM1_INT_EN)
}
/// Disable Alarm1 interrupts.
pub fn disable_alarm1_interrupts(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn disable_alarm1_interrupts(&mut self) -> Result<(), Error<E>> {
let control = self.control;
self.write_control(control & !BitFlags::ALARM1_INT_EN)
}
/// Enable Alarm2 interrupts.
pub fn enable_alarm2_interrupts(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn enable_alarm2_interrupts(&mut self) -> Result<(), Error<E>> {
let control = self.control;
self.write_control(control | BitFlags::ALARM2_INT_EN)
}
/// Disable Alarm2 interrupts.
pub fn disable_alarm2_interrupts(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn disable_alarm2_interrupts(&mut self) -> Result<(), Error<E>> {
let control = self.control;
self.write_control(control & !BitFlags::ALARM2_INT_EN)
}
fn write_control(&mut self, control: u8) -> Result<(), Error<CommE, PinE>> {
fn write_control(&mut self, control: u8) -> Result<(), Error<E>> {
self.iface.write_register(Register::CONTROL, control)?;
self.control = control;
Ok(())
@ -127,7 +126,7 @@ where
pub(crate) fn write_status_without_clearing_alarm(
&mut self,
status: u8,
) -> Result<(), Error<CommE, PinE>> {
) -> Result<(), Error<E>> {
// avoid clearing alarm flags
let new_status = status | BitFlags::ALARM2F | BitFlags::ALARM1F;
self.iface.write_register(Register::STATUS, new_status)?;

View File

@ -1,83 +1,21 @@
//! Common implementation
use super::{decimal_to_packed_bcd, hours_to_register, packed_bcd_to_decimal};
use super::{
decimal_to_packed_bcd, hours_to_register, packed_bcd_to_decimal, some_or_invalid_error,
};
use crate::{
interface::{ReadData, WriteData},
BitFlags, Datelike, Ds323x, Error, Hours, NaiveDate, NaiveDateTime, NaiveTime, Register, Rtcc,
Timelike,
BitFlags, DateTimeAccess, Datelike, Ds323x, Error, Hours, NaiveDate, NaiveDateTime, NaiveTime,
Register, Rtcc, Timelike,
};
impl<DI, IC, CommE, PinE> Rtcc for Ds323x<DI, IC>
impl<DI, IC, E> DateTimeAccess for Ds323x<DI, IC>
where
DI: ReadData<Error = Error<CommE, PinE>> + WriteData<Error = Error<CommE, PinE>>,
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
{
type Error = Error<CommE, PinE>;
type Error = Error<E>;
fn get_seconds(&mut self) -> Result<u8, Self::Error> {
self.read_register_decimal(Register::SECONDS)
}
fn get_minutes(&mut self) -> Result<u8, Self::Error> {
self.read_register_decimal(Register::MINUTES)
}
fn get_hours(&mut self) -> Result<Hours, Self::Error> {
let data = self.iface.read_register(Register::HOURS)?;
Ok(hours_from_register(data))
}
fn get_time(&mut self) -> Result<NaiveTime, Self::Error> {
let mut data = [0; 4];
self.iface.read_data(&mut data)?;
let hour = hours_from_register(data[Register::HOURS as usize + 1]);
let minute = packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]);
let second = packed_bcd_to_decimal(data[Register::SECONDS as usize + 1]);
Ok(NaiveTime::from_hms(
get_h24(hour).into(),
minute.into(),
second.into(),
))
}
fn get_weekday(&mut self) -> Result<u8, Self::Error> {
self.read_register_decimal(Register::DOW)
}
fn get_day(&mut self) -> Result<u8, Self::Error> {
self.read_register_decimal(Register::DOM)
}
fn get_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))
}
fn get_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]))
}
fn get_date(&mut self) -> Result<NaiveDate, Self::Error> {
let mut data = [0; 4];
data[0] = Register::DOM;
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 day = packed_bcd_to_decimal(data[Register::DOM as usize + 1 - offset]);
Ok(NaiveDate::from_ymd(year.into(), month.into(), day.into()))
}
fn get_datetime(&mut self) -> Result<NaiveDateTime, Self::Error> {
fn datetime(&mut self) -> Result<NaiveDateTime, Self::Error> {
let mut data = [0; 8];
self.iface.read_data(&mut data)?;
@ -91,13 +29,95 @@ where
let minute = packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]);
let second = packed_bcd_to_decimal(data[Register::SECONDS as usize + 1]);
Ok(
rtcc::NaiveDate::from_ymd(year.into(), month.into(), day.into()).and_hms(
get_h24(hour).into(),
minute.into(),
second.into(),
),
)
let date = NaiveDate::from_ymd_opt(year.into(), month.into(), day.into());
let date = some_or_invalid_error(date)?;
let datetime = date.and_hms_opt(get_h24(hour).into(), minute.into(), second.into());
some_or_invalid_error(datetime)
}
fn set_datetime(&mut self, datetime: &NaiveDateTime) -> Result<(), Self::Error> {
if datetime.year() < 2000 || datetime.year() > 2100 {
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),
decimal_to_packed_bcd(datetime.minute() as u8),
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,
];
self.iface.write_data(&mut payload)
}
}
impl<DI, IC, E> Rtcc for Ds323x<DI, IC>
where
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
{
fn seconds(&mut self) -> Result<u8, Self::Error> {
self.read_register_decimal(Register::SECONDS)
}
fn minutes(&mut self) -> Result<u8, Self::Error> {
self.read_register_decimal(Register::MINUTES)
}
fn hours(&mut self) -> Result<Hours, Self::Error> {
let data = self.iface.read_register(Register::HOURS)?;
Ok(hours_from_register(data))
}
fn time(&mut self) -> Result<NaiveTime, Self::Error> {
let mut data = [0; 4];
self.iface.read_data(&mut data)?;
let hour = hours_from_register(data[Register::HOURS as usize + 1]);
let minute = packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]);
let second = packed_bcd_to_decimal(data[Register::SECONDS as usize + 1]);
let time = NaiveTime::from_hms_opt(get_h24(hour).into(), minute.into(), second.into());
some_or_invalid_error(time)
}
fn weekday(&mut self) -> Result<u8, Self::Error> {
self.read_register_decimal(Register::DOW)
}
fn day(&mut self) -> Result<u8, Self::Error> {
self.read_register_decimal(Register::DOM)
}
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))
}
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]))
}
fn date(&mut self) -> Result<NaiveDate, Self::Error> {
let mut data = [0; 4];
data[0] = Register::DOM;
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 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)
}
fn set_seconds(&mut self, seconds: u8) -> Result<(), Self::Error> {
@ -130,21 +150,21 @@ where
}
fn set_weekday(&mut self, weekday: u8) -> Result<(), Self::Error> {
if weekday < 1 || weekday > 7 {
if !(1..=7).contains(&weekday) {
return Err(Error::InvalidInputData);
}
self.iface.write_register(Register::DOW, weekday)
}
fn set_day(&mut self, day: u8) -> Result<(), Self::Error> {
if day < 1 || day > 31 {
if !(1..=31).contains(&day) {
return Err(Error::InvalidInputData);
}
self.iface.write_register(Register::DOM, day)
self.write_register_decimal(Register::DOM, day)
}
fn set_month(&mut self, month: u8) -> Result<(), Self::Error> {
if month < 1 || month > 12 {
if !(1..=12).contains(&month) {
return Err(Error::InvalidInputData);
}
// keep the century bit
@ -154,7 +174,7 @@ where
}
fn set_year(&mut self, year: u16) -> Result<(), Self::Error> {
if year < 2000 || year > 2100 {
if !(2000..=2100).contains(&year) {
return Err(Error::InvalidInputData);
}
let data = self.iface.read_register(Register::MONTH)?;
@ -190,40 +210,18 @@ where
];
self.iface.write_data(&mut payload)
}
fn set_datetime(&mut self, datetime: &NaiveDateTime) -> Result<(), Self::Error> {
if datetime.year() < 2000 || datetime.year() > 2100 {
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),
decimal_to_packed_bcd(datetime.minute() as u8),
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,
];
self.iface.write_data(&mut payload)
}
}
impl<DI, IC, CommE, PinE> Ds323x<DI, IC>
impl<DI, IC, E> Ds323x<DI, IC>
where
DI: ReadData<Error = Error<CommE, PinE>> + WriteData<Error = Error<CommE, PinE>>,
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
{
fn read_register_decimal(&mut self, register: u8) -> Result<u8, Error<CommE, PinE>> {
fn read_register_decimal(&mut self, register: u8) -> Result<u8, Error<E>> {
let data = self.iface.read_register(register)?;
Ok(packed_bcd_to_decimal(data))
}
fn write_register_decimal(
&mut self,
register: u8,
decimal_number: u8,
) -> Result<(), Error<CommE, PinE>> {
fn write_register_decimal(&mut self, register: u8, decimal_number: u8) -> Result<(), Error<E>> {
self.iface
.write_register(register, decimal_to_packed_bcd(decimal_number))
}

View File

@ -17,21 +17,45 @@ fn packed_bcd_to_decimal(bcd: u8) -> u8 {
(bcd >> 4) * 10 + (bcd & 0xF)
}
fn hours_to_register<CommE, PinE>(hours: Hours) -> Result<u8, Error<CommE, PinE>> {
fn hours_to_register<E>(hours: Hours) -> Result<u8, Error<E>> {
match hours {
Hours::H24(h) if h > 23 => Err(Error::InvalidInputData),
Hours::H24(h) => Ok(decimal_to_packed_bcd(h)),
Hours::AM(h) if h < 1 || h > 12 => Err(Error::InvalidInputData),
Hours::AM(h) if !(1..=12).contains(&h) => Err(Error::InvalidInputData),
Hours::AM(h) => Ok(BitFlags::H24_H12 | decimal_to_packed_bcd(h)),
Hours::PM(h) if h < 1 || h > 12 => Err(Error::InvalidInputData),
Hours::PM(h) if !(1..=12).contains(&h) => Err(Error::InvalidInputData),
Hours::PM(h) => Ok(BitFlags::H24_H12 | BitFlags::AM_PM | decimal_to_packed_bcd(h)),
}
}
fn some_or_invalid_error<T, E>(data: Option<T>) -> Result<T, Error<E>> {
if let Some(data) = data {
Ok(data)
} else {
Err(Error::InvalidDeviceState)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn if_some_then_get_inner() {
match some_or_invalid_error::<u8, ()>(Some(1)) {
Ok(1) => (),
_ => panic!(),
}
}
#[test]
fn if_none_then_error() {
match some_or_invalid_error::<u8, ()>(None) {
Err(Error::InvalidDeviceState) => (),
_ => panic!(),
}
}
#[test]
fn can_convert_packed_bcd_to_decimal() {
assert_eq!(0, packed_bcd_to_decimal(0b0000_0000));

View File

@ -5,18 +5,18 @@ use crate::{
BitFlags, Ds323x, Error, Register,
};
impl<DI, IC, CommE, PinE> Ds323x<DI, IC>
impl<DI, IC, E> Ds323x<DI, IC>
where
DI: ReadData<Error = Error<CommE, PinE>> + WriteData<Error = Error<CommE, PinE>>,
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
{
/// Read whether the oscillator is running
pub fn running(&mut self) -> Result<bool, Error<CommE, PinE>> {
pub fn running(&mut self) -> Result<bool, Error<E>> {
let control = self.iface.read_register(Register::CONTROL)?;
Ok((control & BitFlags::EOSC) == 0)
}
/// Read the busy status
pub fn busy(&mut self) -> Result<bool, Error<CommE, PinE>> {
pub fn busy(&mut self) -> Result<bool, Error<E>> {
let status = self.iface.read_register(Register::STATUS)?;
Ok((status & BitFlags::BUSY) != 0)
}
@ -28,7 +28,7 @@ where
///
/// Once this is true, it will stay as such until cleared with
/// [`clear_has_been_stopped_flag()`](#method.clear_has_been_stopped_flag)
pub fn has_been_stopped(&mut self) -> Result<bool, Error<CommE, PinE>> {
pub fn has_been_stopped(&mut self) -> Result<bool, Error<E>> {
let status = self.iface.read_register(Register::STATUS)?;
Ok((status & BitFlags::OSC_STOP) != 0)
}
@ -37,7 +37,7 @@ where
/// stopped at some point.
///
/// See also: [`has_been_stopped()`](#method.has_been_stopped)
pub fn clear_has_been_stopped_flag(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn clear_has_been_stopped_flag(&mut self) -> Result<(), Error<E>> {
let status = self.status & !BitFlags::OSC_STOP;
self.write_status_without_clearing_alarm(status)
}
@ -46,7 +46,7 @@ where
///
/// Once this is true, it will stay as such until cleared with
/// [`clear_alarm1_matched_flag()`](#method.clear_alarm1_matched_flag)
pub fn has_alarm1_matched(&mut self) -> Result<bool, Error<CommE, PinE>> {
pub fn has_alarm1_matched(&mut self) -> Result<bool, Error<E>> {
let status = self.iface.read_register(Register::STATUS)?;
Ok((status & BitFlags::ALARM1F) != 0)
}
@ -54,7 +54,7 @@ where
/// Clear flag signalling whether the Alarm1 has matched at some point.
///
/// See also: [`has_alarm1_matched()`](#method.has_alarm1_matched)
pub fn clear_alarm1_matched_flag(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn clear_alarm1_matched_flag(&mut self) -> Result<(), Error<E>> {
let status = self.status | BitFlags::ALARM2F;
self.iface.write_register(Register::STATUS, status)
}
@ -63,7 +63,7 @@ where
///
/// Once this is true, it will stay as such until cleared with
/// [`clear_alarm2_matched_flag()`](#method.clear_alarm2_matched_flag)
pub fn has_alarm2_matched(&mut self) -> Result<bool, Error<CommE, PinE>> {
pub fn has_alarm2_matched(&mut self) -> Result<bool, Error<E>> {
let status = self.iface.read_register(Register::STATUS)?;
Ok((status & BitFlags::ALARM2F) != 0)
}
@ -71,7 +71,7 @@ where
/// Clear flag signalling whether the Alarm2 has matched at some point.
///
/// See also: [`has_alarm2_matched()`](#method.has_alarm2_matched)
pub fn clear_alarm2_matched_flag(&mut self) -> Result<(), Error<CommE, PinE>> {
pub fn clear_alarm2_matched_flag(&mut self) -> Result<(), Error<E>> {
let status = self.status | BitFlags::ALARM1F;
self.iface.write_register(Register::STATUS, status)
}
@ -80,7 +80,7 @@ where
///
/// Note: It is possible to manually force a temperature conversion with
/// [`convert_temperature()`](#method.convert_temperature)
pub fn get_temperature(&mut self) -> Result<f32, Error<CommE, PinE>> {
pub fn temperature(&mut self) -> Result<f32, Error<E>> {
let mut data = [Register::TEMP_MSB, 0, 0];
self.iface.read_data(&mut data)?;
let is_negative = (data[1] & 0b1000_0000) != 0;

View File

@ -1,10 +1,7 @@
//! I2C/SPI interfaces
use crate::{private, Error, DEVICE_ADDRESS};
use embedded_hal::{
blocking::{i2c, spi},
digital::v2::OutputPin,
};
use embedded_hal::{i2c, spi};
/// I2C interface
#[derive(Debug, Default)]
@ -14,9 +11,8 @@ pub struct I2cInterface<I2C> {
/// SPI interface
#[derive(Debug, Default)]
pub struct SpiInterface<SPI, CS> {
pub struct SpiInterface<SPI> {
pub(crate) spi: SPI,
pub(crate) cs: CS,
}
/// Write data
@ -31,9 +27,9 @@ pub trait WriteData: private::Sealed {
impl<I2C, E> WriteData for I2cInterface<I2C>
where
I2C: i2c::Write<Error = E>,
I2C: i2c::I2c<Error = E>,
{
type Error = Error<E, ()>;
type Error = Error<E>;
fn write_register(&mut self, register: u8, data: u8) -> Result<(), Self::Error> {
let payload: [u8; 2] = [register, data];
self.i2c
@ -42,35 +38,23 @@ where
}
fn write_data(&mut self, payload: &mut [u8]) -> Result<(), Self::Error> {
self.i2c
.write(DEVICE_ADDRESS, &payload)
.map_err(Error::Comm)
self.i2c.write(DEVICE_ADDRESS, payload).map_err(Error::Comm)
}
}
impl<SPI, CS, CommE, PinE> WriteData for SpiInterface<SPI, CS>
impl<SPI, E> WriteData for SpiInterface<SPI>
where
SPI: spi::Write<u8, Error = CommE>,
CS: OutputPin<Error = PinE>,
SPI: spi::SpiDevice<u8, Error = E>,
{
type Error = Error<CommE, PinE>;
type Error = Error<E>;
fn write_register(&mut self, register: u8, data: u8) -> Result<(), Self::Error> {
self.cs.set_low().map_err(Error::Pin)?;
let payload: [u8; 2] = [register + 0x80, data];
let result = self.spi.write(&payload).map_err(Error::Comm);
self.cs.set_high().map_err(Error::Pin)?;
result
self.spi.write(&payload).map_err(Error::Comm)
}
fn write_data(&mut self, payload: &mut [u8]) -> Result<(), Self::Error> {
self.cs.set_low().map_err(Error::Pin)?;
payload[0] += 0x80;
let result = self.spi.write(&payload).map_err(Error::Comm);
self.cs.set_high().map_err(Error::Pin)?;
result
self.spi.write(payload).map_err(Error::Comm)
}
}
@ -86,9 +70,9 @@ pub trait ReadData: private::Sealed {
impl<I2C, E> ReadData for I2cInterface<I2C>
where
I2C: i2c::WriteRead<Error = E>,
I2C: i2c::I2c<Error = E>,
{
type Error = Error<E, ()>;
type Error = Error<E>;
fn read_register(&mut self, register: u8) -> Result<u8, Self::Error> {
let mut data = [0];
self.i2c
@ -105,25 +89,18 @@ where
}
}
impl<SPI, CS, CommE, PinE> ReadData for SpiInterface<SPI, CS>
impl<SPI, E> ReadData for SpiInterface<SPI>
where
SPI: spi::Transfer<u8, Error = CommE>,
CS: OutputPin<Error = PinE>,
SPI: spi::SpiDevice<u8, Error = E>,
{
type Error = Error<CommE, PinE>;
type Error = Error<E>;
fn read_register(&mut self, register: u8) -> Result<u8, Self::Error> {
self.cs.set_low().map_err(Error::Pin)?;
let mut data = [register, 0];
let result = self.spi.transfer(&mut data).map_err(Error::Comm);
self.cs.set_high().map_err(Error::Pin)?;
Ok(result?[1])
let result = self.spi.transfer_in_place(&mut data).map_err(Error::Comm);
result.and(Ok(data[1]))
}
fn read_data(&mut self, mut payload: &mut [u8]) -> Result<(), Self::Error> {
self.cs.set_low().map_err(Error::Pin)?;
let result = self.spi.transfer(&mut payload).map_err(Error::Comm);
self.cs.set_high().map_err(Error::Pin)?;
result?;
Ok(())
fn read_data(&mut self, payload: &mut [u8]) -> Result<(), Self::Error> {
self.spi.transfer_in_place(payload).map_err(Error::Comm)
}
}

View File

@ -4,8 +4,8 @@
//! [`embedded-hal`]: https://github.com/rust-embedded/embedded-hal
//!
//! This driver allows you to:
//! - Read and set date and time in 12-hour and 24-hour format. See: [`get_datetime`].
//! - Read and set date and time individual elements. For example, see: [`get_year`].
//! - Read and set date and time in 12-hour and 24-hour format. See: [`datetime`].
//! - Read and set date and time individual elements. For example, see: [`year`].
//! - Enable and disable the real-time clock. See: [`enable`].
//! - Read the busy status. See [`busy`].
//! - Read whether the oscillator is or has been stopped. See [`has_been_stopped`].
@ -24,48 +24,50 @@
//! - Enable and disable the 32kHz output. See [`enable_32khz_output`].
//! - Enable and disable the 32kHz output when battery powered. See [`enable_32khz_output_on_battery`].
//! - Temperature conversion:
//! - Read the temperature. See [`get_temperature`].
//! - Read the temperature. See [`temperature`].
//! - Force a temperature conversion and time compensation. See [`convert_temperature`].
//! - Set the temperature conversion rate. See [`set_temperature_conversion_rate`].
//! - Enable and disable the temperature conversions when battery-powered. See [`enable_temperature_conversions_on_battery`].
//!
//! [`get_datetime`]: struct.Ds323x.html#method.get_datetime
//! [`get_year`]: struct.Ds323x.html#method.get_year
//! [`enable`]: struct.Ds323x.html#method.enable
//! [`get_temperature`]: struct.Ds323x.html#method.get_temperature
//! [`convert_temperature`]: struct.Ds323x.html#method.convert_temperature
//! [`busy`]: struct.Ds323x.html#method.busy
//! [`has_been_stopped`]: struct.Ds323x.html#method.has_been_stopped
//! [`clear_has_been_stopped_flag`]: struct.Ds323x.html#method.clear_has_been_stopped_flag
//! [`set_aging_offset`]: struct.Ds323x.html#method.set_aging_offset
//! [`enable_32khz_output`]: struct.Ds323x.html#method.enable_32khz_output
//! [`use_int_sqw_output_as_interrupt`]: struct.Ds323x.html#method.use_int_sqw_output_as_interrupt
//! [`enable_square_wave`]: struct.Ds323x.html#method.enable_square_wave
//! [`set_square_wave_frequency`]: struct.Ds323x.html#method.set_square_wave_frequency
//! [`set_alarm1_day`]: struct.Ds323x.html#method.set_alarm1_day
//! [`set_alarm1_hms`]: struct.Ds323x.html#method.set_alarm1_hms
//! [`has_alarm1_matched`]: struct.Ds323x.html#method.has_alarm1_matched
//! [`clear_alarm1_matched_flag`]: struct.Ds323x.html#method.clear_alarm1_matched_flag
//! [`enable_alarm1_interrupts`]: struct.Ds323x.html#method.enable_alarm1_interrupts
//! [`enable_32khz_output_on_battery`]: struct.Ds323x.html#method.enable_32khz_output_on_battery
//! [`set_temperature_conversion_rate`]: struct.Ds323x.html#method.set_temperature_conversion_rate
//! [`enable_temperature_conversions_on_battery`]: struct.Ds323x.html#method.enable_temperature_conversions_on_battery
//! [`datetime`]: Ds323x::datetime
//! [`year`]: Ds323x::year
//! [`enable`]: Ds323x::enable
//! [`temperature`]: Ds323x::temperature
//! [`convert_temperature`]: Ds323x::convert_temperature
//! [`busy`]: Ds323x::busy
//! [`has_been_stopped`]: Ds323x::has_been_stopped
//! [`clear_has_been_stopped_flag`]: Ds323x::clear_has_been_stopped_flag
//! [`set_aging_offset`]: Ds323x::set_aging_offset
//! [`enable_32khz_output`]: Ds323x::enable_32khz_output
//! [`use_int_sqw_output_as_interrupt`]: Ds323x::use_int_sqw_output_as_interrupt
//! [`enable_square_wave`]: Ds323x::enable_square_wave
//! [`set_square_wave_frequency`]: Ds323x::set_square_wave_frequency
//! [`set_alarm1_day`]: Ds323x::set_alarm1_day
//! [`set_alarm1_hms`]: Ds323x::set_alarm1_hms
//! [`has_alarm1_matched`]: Ds323x::has_alarm1_matched
//! [`clear_alarm1_matched_flag`]: Ds323x::clear_alarm1_matched_flag
//! [`enable_alarm1_interrupts`]: Ds323x::enable_alarm1_interrupts
//! [`enable_32khz_output_on_battery`]: Ds323x::enable_32khz_output_on_battery
//! [`set_temperature_conversion_rate`]: Ds323x::set_temperature_conversion_rate
//! [`enable_temperature_conversions_on_battery`]: Ds323x::enable_temperature_conversions_on_battery
//!
//! ## The devices
//!
//! This driver is compatible with the DS3231 and DS3232 I2C devices and the
//! DS3234 SPI device.
//!
//! ### DS3231
//! The DS3231 is a low-cost, extremely accurate I2C real-time clock (RTC) with
//! an integrated temperature-compensated crystal oscillator (TCXO) and crystal.
//! These devices are low-cost temperature-compensated crystal oscillator (TCXO)
//! with a very accurate, temperature-compensated, integrated real-time clock
//! (RTC) including 236/256 bytes of battery-backed SRAM, depending on the model.
//!
//! The device incorporates a battery input, and maintains accurate timekeeping
//! when main power to the device is interrupted. The integration of the
//! crystal resonator enhances the long-term accuracy of the device as well as
//! ### DS3231 and DS3232 details
//!
//! The devices incorporate a battery input, and maintain accurate timekeeping
//! when main power to the devices is interrupted. The integration of the
//! crystal resonator enhances the long-term accuracy of the devices as well as
//! reduces the piece-part count in a manufacturing line.
//! The DS3231 is available in commercial and industrial temperature ranges,
//! and is offered in a 16-pin, 300-mil SO package.
//! The devices are available in commercial and industrial temperature ranges,
//! and are offered in a 16-pin, 300-mil SO package.
//!
//! The RTC maintains seconds, minutes, hours, day, date, month, and year
//! information. The date at the end of the month is automatically adjusted for
@ -81,36 +83,7 @@
//! necessary. Additionally, the RST pin is monitored as a pushbutton
//! input for generating a μP reset.
//!
//! ### DS3232
//! The DS3232 is a low-cost temperature-compensated crystal oscillator (TCXO)
//! with a very accurate, temperature-compensated, integrated real-time clock
//! (RTC) and 236 bytes of battery-backed SRAM.
//!
//! Additionally, the DS3232 incorporates a battery input and maintains
//! accurate timekeeping when main power to the device is interrupted. The
//! integration of the crystal resonator enhances the long-term accuracy of the
//! device as well as reduces the piece-part count in a manufacturing line.
//! The DS3232 is available in commercial and industrial temperature ranges,
//! and is offered in an industry-standard 20-pin, 300-mil SO package.
//!
//! The RTC maintains seconds, minutes, hours, day, date, month, and year
//! information. The date at the end of the month is automatically adjusted for
//! months with fewer than 31 days, including corrections for leap year. The
//! clock operates in either the 24-hour or 12-hour format with an AM/PM
//! indicator. Two programmable time-of-day alarms and a programmable
//! square-wave output are provided. Address and data are transferred serially
//! through an I2C bidirectional bus.
//!
//! A precision temperature-compensated voltage reference and comparator
//! circuit monitors the status of VCC to detect power failures, to provide a
//! reset output, and to automatically switch to the backup supply when
//! necessary. Additionally, the RST pin is monitored as a pushbutton input for
//! generating a μP reset.
//!
//! ### DS3234
//! The DS3234 is a low-cost, extremely accurate SPI bus real-time clock (RTC)
//! with an integrated temperature-compensated crystal oscillator (TCXO) and
//! crystal.
//! ### DS3234 details
//!
//! The DS3234 incorporates a precision, temperature-compensated voltage
//! reference and comparator circuit to monitor VCC. When VCC drops below the
@ -184,21 +157,23 @@
//!
//! ```no_run
//! use ds323x::Ds323x;
//! use linux_embedded_hal::{Pin, Spidev};
//! use embedded_hal_bus::spi::ExclusiveDevice;
//! use linux_embedded_hal::{Delay, SpidevBus, SysfsPin};
//!
//! let dev = Spidev::open("/dev/spidev0.0").unwrap();
//! let chip_select = Pin::new(24);
//! let rtc = Ds323x::new_ds3234(dev, chip_select);
//! let spi = SpidevBus::open("/dev/spidev0.0").unwrap();
//! let chip_select = SysfsPin::new(25);
//! let dev = ExclusiveDevice::new(spi, chip_select, Delay).unwrap();
//! let rtc = Ds323x::new_ds3234(dev);
//! // do something...
//!
//! // get the SPI device and chip select pin back
//! let (dev, chip_select) = rtc.destroy_ds3234();
//! // get the SPI device back
//! let dev = rtc.destroy_ds3234();
//! ```
//!
//! ### Set the current date and time at once
//!
//! ```no_run
//! use ds323x::{Ds323x, NaiveDate, Rtcc};
//! use ds323x::{Ds323x, NaiveDate, DateTimeAccess};
//! use linux_embedded_hal::I2cdev;
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
@ -210,12 +185,12 @@
//! ### Get the current date and time at once
//!
//! ```no_run
//! use ds323x::{Ds323x, Rtcc};
//! use ds323x::{Ds323x, DateTimeAccess};
//! use linux_embedded_hal::I2cdev;
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! let dt = rtc.get_datetime().unwrap();
//! let dt = rtc.datetime().unwrap();
//! println!("{}", dt);
//! // This will print something like: 2020-05-01 19:59:58
//! ```
@ -230,7 +205,7 @@
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! let year = rtc.get_year().unwrap();
//! let year = rtc.year().unwrap();
//! println!("Year: {}", year);
//! ```
//!
@ -270,7 +245,7 @@
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! let temperature = rtc.get_temperature().unwrap();
//! let temperature = rtc.temperature().unwrap();
//! ```
//!
//! ### Read busy status
@ -386,13 +361,14 @@
//! rtc.set_alarm1_hms(time).unwrap();
//! ```
#![doc(html_root_url = "https://docs.rs/ds323x/0.3.2")]
#![deny(unsafe_code, missing_docs)]
#![no_std]
use core::marker::PhantomData;
use embedded_hal::spi::{Mode, MODE_1, MODE_3};
pub use rtcc::{Datelike, Hours, NaiveDate, NaiveDateTime, NaiveTime, Rtcc, Timelike};
pub use rtcc::{
DateTimeAccess, Datelike, Hours, NaiveDate, NaiveDateTime, NaiveTime, Rtcc, Timelike,
};
/// SPI mode 1 (CPOL = 0, CPHA = 1)
pub const SPI_MODE_1: Mode = MODE_1;
@ -401,13 +377,16 @@ pub const SPI_MODE_3: Mode = MODE_3;
/// All possible errors in this crate
#[derive(Debug)]
pub enum Error<CommE, PinE> {
pub enum Error<E> {
/// I²C/SPI bus error
Comm(CommE),
/// Pin setting error
Pin(PinE),
Comm(E),
/// Invalid input data provided
InvalidInputData,
/// Internal device state is invalid.
///
/// It was not possible to read a valid date and/or time.
/// The device is probably missing initialization.
InvalidDeviceState,
}
/// Square-wave output frequency
@ -519,7 +498,7 @@ mod private {
use super::{ic, interface};
pub trait Sealed {}
impl<SPI, CS> Sealed for interface::SpiInterface<SPI, CS> {}
impl<SPI> Sealed for interface::SpiInterface<SPI> {}
impl<I2C> Sealed for interface::I2cInterface<I2C> {}
impl Sealed for ic::DS3231 {}

View File

@ -1,4 +1,4 @@
use embedded_hal_mock::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
use embedded_hal_mock::eh1::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
mod common;
use self::common::{
destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234,
@ -583,7 +583,7 @@ macro_rules! set_alarm_test {
($name:ident, $method:ident, $register:ident, [ $( $registers:expr ),+ ], $( $value:expr ),+) => {
set_values_test!($name, $method,
[ I2cTrans::write(DEV_ADDR, vec![Register::$register, $( $registers ),*]) ],
[ SpiTrans::write(vec![Register::$register + 0x80, $( $registers ),*]) ],
[ SpiTrans::transaction_start(), SpiTrans::write_vec(vec![Register::$register + 0x80, $( $registers ),*]), SpiTrans::transaction_end() ],
$($value),*
);
};
@ -637,7 +637,7 @@ mod alarm1_day {
set_alarm1_hms,
ALARM1_SECONDS,
[4, 3, 2, AM | 1],
NaiveTime::from_hms(2, 3, 4)
NaiveTime::from_hms_opt(2, 3, 4).unwrap()
);
set_alarm_test!(
match_hms,
@ -669,7 +669,7 @@ mod alarm1_day {
match_ms_ignore_invalid_hour,
set_alarm1_day,
ALARM1_SECONDS,
[4, 3, AM | 0, AM | 1],
[4, 3, AM, AM | 1],
DayAlarm1 {
day: 1,
hour: Hours::H24(24),
@ -721,7 +721,7 @@ mod alarm1_day {
match_s_ignore_incorrect_min,
set_alarm1_day,
ALARM1_SECONDS,
[4, AM | 0, AM | 2, AM | 1],
[4, AM, AM | 2, AM | 1],
DayAlarm1 {
day: 1,
hour: Hours::H24(2),
@ -842,7 +842,7 @@ mod alarm1_weekday {
match_s_ignore_incorrect_min,
set_alarm1_weekday,
ALARM1_SECONDS,
[4, AM | 0, AM | 2, AM | BF::WEEKDAY | 1],
[4, AM, AM | 2, AM | BF::WEEKDAY | 1],
WeekdayAlarm1 {
weekday: 1,
hour: Hours::H24(2),
@ -868,7 +868,7 @@ mod alarm1_weekday {
match_ops_ignore_incorrect_sec,
set_alarm1_weekday,
ALARM1_SECONDS,
[AM | 0, AM | 3, AM | 2, AM | BF::WEEKDAY | 1],
[AM, AM | 3, AM | 2, AM | BF::WEEKDAY | 1],
WeekdayAlarm1 {
weekday: 1,
hour: Hours::H24(2),
@ -922,7 +922,7 @@ mod alarm2_day {
set_alarm2_hm,
ALARM2_MINUTES,
[3, 2, AM | 1],
NaiveTime::from_hms(2, 3, 0)
NaiveTime::from_hms_opt(2, 3, 0).unwrap()
);
set_alarm_test!(
match_hm,
@ -964,7 +964,7 @@ mod alarm2_day {
match_m_ignore_invalid_h,
set_alarm2_day,
ALARM2_MINUTES,
[3, AM | 0, AM | 1],
[3, AM, AM | 1],
DayAlarm2 {
day: 1,
hour: Hours::H24(25),
@ -988,7 +988,7 @@ mod alarm2_day {
match_opm_ignore_incorrect_min,
set_alarm2_day,
ALARM2_MINUTES,
[AM | 0, AM | 2, AM | 1],
[AM, AM | 2, AM | 1],
DayAlarm2 {
day: 1,
hour: Hours::H24(2),
@ -1076,7 +1076,7 @@ mod alarm2_weekday {
match_m_ignore_invalid_hour,
set_alarm2_weekday,
ALARM2_MINUTES,
[3, AM | 0, AM | BF::WEEKDAY | 1],
[3, AM, AM | BF::WEEKDAY | 1],
WeekdayAlarm2 {
weekday: 1,
hour: Hours::H24(24),
@ -1100,7 +1100,7 @@ mod alarm2_weekday {
match_opm_ignore_incorrect_min,
set_alarm2_weekday,
ALARM2_MINUTES,
[AM | 0, AM | 2, AM | BF::WEEKDAY | 1],
[AM, AM | 2, AM | BF::WEEKDAY | 1],
WeekdayAlarm2 {
weekday: 1,
hour: Hours::H24(2),

View File

@ -1,5 +1,5 @@
use ds323x::{ic, interface, Ds323x};
use embedded_hal_mock::{
use embedded_hal_mock::eh1::{
i2c::{Mock as I2cMock, Transaction as I2cTrans},
spi::{Mock as SpiMock, Transaction as SpiTrans},
};
@ -59,8 +59,7 @@ impl BitFlags {
pub struct DummyOutputPin;
impl embedded_hal::digital::v2::OutputPin for DummyOutputPin {
type Error = ();
impl embedded_hal::digital::OutputPin for DummyOutputPin {
fn set_low(&mut self) -> Result<(), Self::Error> {
Ok(())
}
@ -69,6 +68,10 @@ impl embedded_hal::digital::v2::OutputPin for DummyOutputPin {
}
}
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> {
@ -82,9 +85,9 @@ pub fn new_ds3232(
}
pub fn new_ds3234(
transactions: &[SpiTrans],
) -> Ds323x<interface::SpiInterface<SpiMock, DummyOutputPin>, ic::DS3234> {
Ds323x::new_ds3234(SpiMock::new(transactions), DummyOutputPin)
transactions: &[SpiTrans<u8>],
) -> Ds323x<interface::SpiInterface<SpiMock<u8>>, ic::DS3234> {
Ds323x::new_ds3234(SpiMock::new(transactions))
}
pub fn destroy_ds3231(dev: Ds323x<interface::I2cInterface<I2cMock>, ic::DS3231>) {
@ -95,8 +98,8 @@ pub fn destroy_ds3232(dev: Ds323x<interface::I2cInterface<I2cMock>, ic::DS3232>)
dev.destroy_ds3232().done();
}
pub fn destroy_ds3234(dev: Ds323x<interface::SpiInterface<SpiMock, DummyOutputPin>, ic::DS3234>) {
dev.destroy_ds3234().0.done();
pub fn destroy_ds3234(dev: Ds323x<interface::SpiInterface<SpiMock<u8>>, ic::DS3234>) {
dev.destroy_ds3234().done();
}
#[macro_export]
@ -205,10 +208,14 @@ macro_rules! get_param_test {
vec![Register::$register],
vec![$binary_value]
)],
[SpiTrans::transfer(
vec![Register::$register, 0],
vec![Register::$register, $binary_value]
)]
[
SpiTrans::transaction_start(),
SpiTrans::transfer_in_place(
vec![Register::$register, 0],
vec![Register::$register, $binary_value]
),
SpiTrans::transaction_end(),
]
);
};
}
@ -223,7 +230,10 @@ macro_rules! transactions_i2c_read {
#[macro_export]
macro_rules! transactions_spi_read {
($register1:ident, [ $( $read_bin:expr ),+ ], [ $( $read_bin2:expr ),+ ]) => {
[ SpiTrans::transfer(vec![Register::$register1, $( $read_bin2 ),*], vec![Register::$register1, $( $read_bin ),*]) ]
[SpiTrans::transaction_start(),
SpiTrans::transfer_in_place(vec![Register::$register1, $( $read_bin2 ),*], vec![Register::$register1, $( $read_bin ),*]),
SpiTrans::transaction_end()
]
}
}
@ -280,10 +290,11 @@ macro_rules! set_param_test {
DEV_ADDR,
vec![Register::$register, $binary_value]
)],
[SpiTrans::write(vec![
Register::$register + 0x80,
$binary_value
])]
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::$register + 0x80, $binary_value]),
SpiTrans::transaction_end(),
]
);
};
}

View File

@ -1,5 +1,5 @@
use ds323x::SqWFreq;
use embedded_hal_mock::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
use embedded_hal_mock::eh1::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
mod common;
use self::common::{
@ -46,10 +46,11 @@ macro_rules! call_method_test {
DEV_ADDR,
vec![Register::$register, $value_enabled]
)],
[SpiTrans::write(vec![
Register::$register + 0x80,
$value_enabled
])]
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::$register + 0x80, $value_enabled]),
SpiTrans::transaction_end(),
]
);
};
}
@ -83,10 +84,11 @@ macro_rules! call_method_status_test {
$method,
new_ds3234,
destroy_ds3234,
[SpiTrans::write(vec![
Register::STATUS + 0x80,
$value_ds323x
])]
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::STATUS + 0x80, $value_ds323x]),
SpiTrans::transaction_end(),
]
);
}
};
@ -104,10 +106,14 @@ macro_rules! change_if_necessary_test {
vec![Register::$register],
vec![$value_enabled]
)],
[SpiTrans::transfer(
vec![Register::$register, 0],
vec![Register::$register, $value_enabled]
)]
[
SpiTrans::transaction_start(),
SpiTrans::transfer_in_place(
vec![Register::$register, 0],
vec![Register::$register, $value_enabled]
),
SpiTrans::transaction_end(),
]
);
call_triple_test!(
@ -122,11 +128,15 @@ macro_rules! change_if_necessary_test {
I2cTrans::write(DEV_ADDR, vec![Register::$register, $value_enabled])
],
[
SpiTrans::transfer(
SpiTrans::transaction_start(),
SpiTrans::transfer_in_place(
vec![Register::$register, 0],
vec![Register::$register, $value_disabled]
),
SpiTrans::write(vec![Register::$register + 0x80, $value_enabled])
SpiTrans::transaction_end(),
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::$register + 0x80, $value_enabled]),
SpiTrans::transaction_end(),
]
);
}
@ -220,18 +230,12 @@ set_param_test!(
get_param_test!(
get_aging_offset_min,
get_aging_offset,
aging_offset,
AGING_OFFSET,
-128,
0b1000_0000
);
get_param_test!(
get_aging_offset_max,
get_aging_offset,
AGING_OFFSET,
127,
127
);
get_param_test!(get_aging_offset_max, aging_offset, AGING_OFFSET, 127, 127);
call_method_test!(
int_sqw_out_int,

View File

@ -1,4 +1,5 @@
use embedded_hal_mock::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
use embedded_hal_mock::eh1::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
use rtcc::NaiveDateTime;
mod common;
use self::common::{
destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234, Register,
@ -6,7 +7,18 @@ use self::common::{
};
#[allow(unused)] // Rust 1.31.0 is confused due to the macros
use ds323x::Rtcc;
use ds323x::{Error, Hours, NaiveDate, NaiveTime};
use ds323x::{DateTimeAccess, Error, Hours, NaiveDate, NaiveTime};
fn new_datetime(y: i32, mo: u32, d: u32, h: u32, min: u32, s: u32) -> NaiveDateTime {
NaiveDate::from_ymd_opt(y, mo, d)
.unwrap()
.and_hms_opt(h, min, s)
.unwrap()
}
fn new_date(y: i32, mo: u32, d: u32) -> NaiveDate {
NaiveDate::from_ymd_opt(y, mo, d).unwrap()
}
macro_rules! read_set_param_write_two_test {
($name:ident, $method:ident, $value:expr, $register:ident, $binary_value1_read:expr, $bin1:expr, $bin2:expr) => {
@ -23,11 +35,15 @@ macro_rules! read_set_param_write_two_test {
I2cTrans::write(DEV_ADDR, vec![Register::$register, $bin1, $bin2])
],
[
SpiTrans::transfer(
SpiTrans::transaction_start(),
SpiTrans::transfer_in_place(
vec![Register::$register, 0],
vec![Register::$register, $binary_value1_read]
),
SpiTrans::write(vec![Register::$register + 0x80, $bin1, $bin2])
SpiTrans::transaction_end(),
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::$register + 0x80, $bin1, $bin2]),
SpiTrans::transaction_end(),
]
);
};
@ -48,11 +64,15 @@ macro_rules! read_set_param_test {
I2cTrans::write(DEV_ADDR, vec![Register::$register, $binary_value_write])
],
[
SpiTrans::transfer(
SpiTrans::transaction_start(),
SpiTrans::transfer_in_place(
vec![Register::$register, 0],
vec![Register::$register, $binary_value_read]
),
SpiTrans::write(vec![Register::$register + 0x80, $binary_value_write])
SpiTrans::transaction_end(),
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::$register + 0x80, $binary_value_write]),
SpiTrans::transaction_end(),
]
);
};
@ -113,76 +133,69 @@ macro_rules! for_all {
mod seconds {
use super::*;
get_param_test!(get, get_seconds, SECONDS, 1, 1);
get_param_test!(get, seconds, SECONDS, 1, 1);
set_param_test!(set, set_seconds, SECONDS, 1, 1);
set_invalid_param_test!(invalid, set_seconds, 60);
}
mod minutes {
use super::*;
get_param_test!(get, get_minutes, MINUTES, 1, 1);
get_param_test!(get, minutes, MINUTES, 1, 1);
set_param_test!(set, set_minutes, MINUTES, 1, 1);
set_invalid_param_test!(invalid, set_minutes, 60);
}
mod hours_24h {
use super::*;
get_param_test!(get, get_hours, HOURS, Hours::H24(21), 0b0010_0001);
get_param_test!(get, hours, HOURS, Hours::H24(21), 0b0010_0001);
set_param_test!(set, set_hours, HOURS, Hours::H24(21), 0b0010_0001);
set_invalid_param_test!(invalid, set_hours, Hours::H24(24));
}
mod hours_12h_am {
use super::*;
get_param_test!(get, get_hours, HOURS, Hours::AM(12), 0b0101_0010);
get_param_test!(get, hours, HOURS, Hours::AM(12), 0b0101_0010);
set_param_test!(set, set_hours, HOURS, Hours::AM(12), 0b0101_0010);
set_invalid_param_range_test!(invalid, set_hours, Hours::AM(0), Hours::AM(13));
}
mod hours_12h_pm {
use super::*;
get_param_test!(get, get_hours, HOURS, Hours::PM(12), 0b0111_0010);
get_param_test!(get, hours, HOURS, Hours::PM(12), 0b0111_0010);
set_param_test!(set, set_hours, HOURS, Hours::PM(12), 0b0111_0010);
set_invalid_param_range_test!(invalid, set_hours, Hours::PM(0), Hours::PM(13));
}
mod weekday {
use super::*;
get_param_test!(get, get_weekday, DOW, 1, 1);
get_param_test!(get, weekday, DOW, 1, 1);
set_param_test!(set, set_weekday, DOW, 1, 1);
set_invalid_param_range_test!(invalid, set_weekday, 0, 8);
}
mod day {
use super::*;
get_param_test!(get, get_day, DOM, 1, 1);
get_param_test!(get, day, DOM, 1, 1);
set_param_test!(set, set_day, DOM, 1, 1);
set_invalid_param_range_test!(invalid, set_day, 0, 32);
}
mod month {
use super::*;
get_param_test!(get, get_month, MONTH, 1, 1);
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, get_month, MONTH, 12, 0b1001_0010);
get_param_test!(get, month, MONTH, 12, 0b1001_0010);
read_set_param_test!(set, set_month, MONTH, 12, 0b1000_0010, 0b1001_0010);
}
}
mod year {
use super::*;
get_param_read_array_test!(
century0_get,
get_year,
2099,
MONTH,
[0, 0b1001_1001],
[0, 0]
);
get_param_read_array_test!(century0_get, year, 2099, MONTH, [0, 0b1001_1001], [0, 0]);
read_set_param_write_two_test!(
century0_set,
set_year,
@ -193,14 +206,7 @@ mod year {
0b1001_1001
);
get_param_read_array_test!(
century1_get,
get_year,
2100,
MONTH,
[0b1000_0000, 0],
[0, 0]
);
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,
@ -220,28 +226,28 @@ macro_rules! invalid_dt_test {
use super::*;
#[test]
fn datetime_too_small() {
let dt = NaiveDate::from_ymd(1999, 1, 2).and_hms(3, 4, 5);
let dt = new_datetime(1999, 1, 2, 3, 4, 5);
let mut dev = $create_method(&[]);
assert_invalid_input_data!(dev.set_datetime(&dt));
$destroy_method(dev);
}
#[test]
fn datetime_too_big() {
let dt = NaiveDate::from_ymd(2101, 1, 2).and_hms(3, 4, 5);
let dt = new_datetime(2101, 1, 2, 3, 4, 5);
let mut dev = $create_method(&[]);
assert_invalid_input_data!(dev.set_datetime(&dt));
$destroy_method(dev);
}
#[test]
fn date_too_small() {
let d = NaiveDate::from_ymd(1999, 1, 2);
let d = new_date(1999, 1, 2);
let mut dev = $create_method(&[]);
assert_invalid_input_data!(dev.set_date(&d));
$destroy_method(dev);
}
#[test]
fn date_too_big() {
let d = NaiveDate::from_ymd(2101, 1, 2);
let d = new_date(2101, 1, 2);
let mut dev = $create_method(&[]);
assert_invalid_input_data!(dev.set_date(&d));
$destroy_method(dev);
@ -258,7 +264,11 @@ macro_rules! transactions_i2c_write {
macro_rules! transactions_spi_write {
($register:ident, [ $( $exp_bin:expr ),+ ]) => {
[ SpiTrans::write(vec![Register::$register + 0x80, $( $exp_bin ),*]) ]
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::$register + 0x80, $( $exp_bin ),*]),
SpiTrans::transaction_end()
]
};
}
@ -269,7 +279,7 @@ macro_rules! dt_test {
use super::*;
#[test]
fn get_datetime() {
let dt = NaiveDate::from_ymd(2018, 8, 13).and_hms(23, 59, 58);
let dt = new_datetime(2018, 8, 13, 23, 59, 58);
let mut dev = $create_method(&$mac_trans_read!(
SECONDS,
[
@ -283,13 +293,13 @@ macro_rules! dt_test {
],
[0, 0, 0, 0, 0, 0, 0]
));
assert_eq!(dt, dev.get_datetime().unwrap());
assert_eq!(dt, dev.datetime().unwrap());
$destroy_method(dev);
}
#[test]
fn set_datetime() {
let dt = NaiveDate::from_ymd(2018, 8, 13).and_hms(23, 59, 58);
let dt = new_datetime(2018, 8, 13, 23, 59, 58);
let mut dev = $create_method(&$mac_trans_write!(
SECONDS,
[
@ -308,19 +318,19 @@ macro_rules! dt_test {
#[test]
fn get_date() {
let d = NaiveDate::from_ymd(2018, 8, 13);
let d = new_date(2018, 8, 13);
let mut dev = $create_method(&$mac_trans_read!(
DOM,
[0b0001_0011, 0b0000_1000, 0b0001_1000],
[0, 0, 0]
));
assert_eq!(d, dev.get_date().unwrap());
assert_eq!(d, dev.date().unwrap());
$destroy_method(dev);
}
#[test]
fn set_date() {
let d = NaiveDate::from_ymd(2018, 8, 13);
let d = new_date(2018, 8, 13);
let mut dev = $create_method(&$mac_trans_write!(
DOW,
[0b0000_0010, 0b0001_0011, 0b0000_1000, 0b0001_1000]
@ -331,7 +341,7 @@ macro_rules! dt_test {
#[test]
fn set_date_century() {
let d = NaiveDate::from_ymd(2100, 8, 13);
let d = new_date(2100, 8, 13);
let mut dev = $create_method(&$mac_trans_write!(
DOW,
[0b0000_0110, 0b0001_0011, 0b1000_1000, 0]
@ -342,19 +352,19 @@ macro_rules! dt_test {
#[test]
fn get_time() {
let t = NaiveTime::from_hms(23, 59, 58);
let t = NaiveTime::from_hms_opt(23, 59, 58).unwrap();
let mut dev = $create_method(&$mac_trans_read!(
SECONDS,
[0b0101_1000, 0b0101_1001, 0b0010_0011],
[0, 0, 0]
));
assert_eq!(t, dev.get_time().unwrap());
assert_eq!(t, dev.time().unwrap());
$destroy_method(dev);
}
#[test]
fn set_time() {
let t = NaiveTime::from_hms(23, 59, 58);
let t = NaiveTime::from_hms_opt(23, 59, 58).unwrap();
let mut dev = $create_method(&$mac_trans_write!(
SECONDS,
[0b0101_1000, 0b0101_1001, 0b0010_0011]

View File

@ -1,5 +1,5 @@
use ds323x::TempConvRate;
use embedded_hal_mock::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
use embedded_hal_mock::eh1::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
#[allow(unused)]
mod common;
@ -24,7 +24,11 @@ macro_rules! call_method_status_test {
$method,
new_ds3234,
destroy_ds3234,
[SpiTrans::write(vec![Register::STATUS + 0x80, $value])]
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::STATUS + 0x80, $value]),
SpiTrans::transaction_end(),
]
);
}
};
@ -66,10 +70,11 @@ macro_rules! set_param_test_2_4 {
DEV_ADDR,
vec![Register::$register, $binary_value]
)],
[SpiTrans::write(vec![
Register::$register + 0x80,
$binary_value
])]
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::$register + 0x80, $binary_value]),
SpiTrans::transaction_end(),
]
);
};
}

View File

@ -1,4 +1,4 @@
use embedded_hal_mock::spi::Transaction as SpiTrans;
use embedded_hal_mock::eh1::spi::Transaction as SpiTrans;
#[allow(unused)]
mod common;
@ -9,7 +9,11 @@ call_test!(
enable_temperature_conversions_on_battery,
new_ds3234,
destroy_ds3234,
[SpiTrans::write(vec![Register::TEMP_CONV + 0x80, 0])]
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::TEMP_CONV + 0x80, 0]),
SpiTrans::transaction_end()
]
);
call_test!(
@ -17,8 +21,9 @@ call_test!(
disable_temperature_conversions_on_battery,
new_ds3234,
destroy_ds3234,
[SpiTrans::write(vec![
Register::TEMP_CONV + 0x80,
BitFlags::TEMP_CONV_BAT
])]
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::TEMP_CONV + 0x80, BitFlags::TEMP_CONV_BAT]),
SpiTrans::transaction_end(),
]
);

View File

@ -1,4 +1,4 @@
use embedded_hal_mock::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
use embedded_hal_mock::eh1::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
mod common;
use self::common::{
destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234,
@ -32,10 +32,10 @@ get_param_test!(
!BF::ALARM2F
);
get_param_read_array_test!(temp_0, get_temperature, 0.0, TEMP_MSB, [0, 0], [0, 0]);
get_param_read_array_test!(temp_0, temperature, 0.0, TEMP_MSB, [0, 0], [0, 0]);
get_param_read_array_test!(
temp_min,
get_temperature,
temperature,
-128.0,
TEMP_MSB,
[0b1000_0000, 0],
@ -43,7 +43,7 @@ get_param_read_array_test!(
);
get_param_read_array_test!(
temp_max,
get_temperature,
temperature,
127.75,
TEMP_MSB,
[0b0111_1111, 0b1100_0000],