Compare commits

..

No commits in common. "master" and "v0.2.0" have entirely different histories.

28 changed files with 1179 additions and 2907 deletions

View File

@ -1,110 +0,0 @@
name: Build
on: [push, pull_request]
env:
RUSTFLAGS: '--deny warnings'
jobs:
build:
name: Build
runs-on: ubuntu-latest
strategy:
matrix:
rust: [stable, 1.75.0]
TARGET:
- x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl
- arm-unknown-linux-gnueabi # Raspberry Pi 1
- armv7-unknown-linux-gnueabihf # Raspberry Pi 2, 3, etc
# Bare metal
- thumbv6m-none-eabi
- thumbv7em-none-eabi
- thumbv7em-none-eabihf
- thumbv7m-none-eabi
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
targets: ${{ matrix.TARGET }}
- name: Checkout CI scripts
uses: actions/checkout@v4
with:
repository: 'eldruin/rust-driver-ci-scripts'
ref: 'master'
path: 'ci'
- run: ./ci/patch-no-std.sh
if: ${{ ! contains(matrix.TARGET, 'x86_64') }}
- run: cargo build --target=${{ matrix.TARGET }}
checks:
name: Checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-unknown-linux-gnu
components: rustfmt
- run: cargo doc
- run: cargo fmt --all -- --check
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.83.0
targets: x86_64-unknown-linux-gnu
components: clippy
- run: cargo clippy --all-targets
test:
name: Tests
runs-on: ubuntu-latest
strategy:
matrix:
rust: [stable]
TARGET: [x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
targets: ${{ matrix.TARGET }}
- name: Test
run: cargo test --target=${{ matrix.TARGET }}
- name: Build examples
run: cargo build --target=${{ matrix.TARGET }} --examples
coverage:
name: Coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- 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
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: './lcov.info'

84
.travis.yml 100644
View File

@ -0,0 +1,84 @@
language: rust
# sudo is required to enable kcov to use the personality syscall
sudo: required
before_script:
- eval git pull --rebase https://github.com/eldruin/ds323x-rs master
- eval git log --pretty=oneline HEAD~5..HEAD
- export PATH=$HOME/.cargo/bin:$PATH
matrix:
include:
- env: TARGET=x86_64-unknown-linux-gnu
before_script:
- cargo install cargo-update || echo "cargo-update already installed"
- cargo install cargo-travis || echo "cargo-travis already installed"
- cargo install-update -a
after_success:
- cargo coveralls --verbose
addons:
apt:
packages:
- libcurl4-openssl-dev
- libelf-dev
- libdw-dev
- binutils-dev
- cmake
sources:
- kalakris-cmake
- env: TARGET=x86_64-unknown-linux-gnu
rust: nightly
- env: TARGET=x86_64-unknown-linux-musl
rust: nightly
# Raspberry Pi 1
- env: TARGET=arm-unknown-linux-gnueabi DISABLE_EXAMPLES=1 DISABLE_TESTS=1
rust: nightly
# Raspberry Pi 2, 3, etc
- env: TARGET=armv7-unknown-linux-gnueabihf DISABLE_EXAMPLES=1 DISABLE_TESTS=1
rust: nightly
# Bare metal
- env: TARGET=thumbv6m-none-eabi
rust: beta
- env: TARGET=thumbv7em-none-eabi
rust: beta
- env: TARGET=thumbv7em-none-eabihf
rust: beta
- env: TARGET=thumbv7m-none-eabi
rust: beta
before_install:
- set -e
- rustup self update
install:
- bash ci/install.sh
script:
- bash ci/script.sh
after_script: set +e
cache: cargo
cache:
directories:
- /home/travis/.cargo
before_cache:
# Travis can't cache files that are not readable by "others"
- chmod -R a+r $HOME/.cargo
- rm -rf /home/travis/.cargo/registry
branches:
only:
- master
- staging
- trying
notifications:
email:
on_success: never

View File

@ -5,74 +5,9 @@ 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).
<!-- next-header -->
## [Unreleased] - ReleaseDate
## [Unreleased]
## [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
### Fixed
- Day bounds on the `set_day()` method. Thanks to @jamesmunns. See:
[PR #5](https://github.com/eldruin/ds323x-rs/pull/5)
## [0.3.1] - 2020-07-10
### Added
- Added methods to set alarms 1 and 2 with a `chrono::NaiveTime`: `set_alarm1_hms()`
and `set_alarm2_hm()`.
### Changed
- Changed alarm setting methods to automatically correct invalid values to irrelevant
input parameters due to the selected matching strategy.
## [0.3.0] - 2020-05-02
### Changed
- [breaking-change] Renamed `is_busy()` and `is_running()` methods `busy()` and `running()`
due to Rust naming conventions.
- Implement trait from [`rtcc`] crate.
- Changed `get_datetime()` and `set_datetime()` parameter from `DateTime`
to `chrono::NaiveDateTime`.
### Added
- Methods to set and get date and time using `chrono::NaiveDate` and `chrono::NaiveTime`:
- `get_time()`
- `set_time()`
- `get_date()`
- `set_date()`
- [`chrono`] (through [`rtcc`]) dependency.
### Removed
- `DateTime` data structure was replaced by `chrono::NaiveDateTime`.
...
## [0.2.0] - 2018-11-16
@ -87,16 +22,5 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
This is the initial release to crates.io. All changes will be documented in
this CHANGELOG.
[`chrono`]: https://crates.io/crates/chrono
[`rtcc`]: https://crates.io/crates/rtcc
<!-- 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
[Unreleased]: https://github.com/eldruin/ds323x-rs/compare/v0.2.0...HEAD
[0.2.0]: https://github.com/eldruin/ds323x-rs/compare/v0.1.0...v0.2.0

View File

@ -1,6 +1,6 @@
[package]
name = "ds323x"
version = "0.6.0"
version = "0.2.0"
authors = ["Diego Barrios Romero <eldruin@gmail.com>"]
repository = "https://github.com/eldruin/ds323x-rs"
license = "MIT OR Apache-2.0"
@ -11,23 +11,25 @@ categories = ["embedded", "hardware-support", "no-std"]
homepage = "https://github.com/eldruin/ds323x-rs"
documentation = "https://docs.rs/ds323x"
include = [
"/**/*.rs",
"/Cargo.toml",
"/README.md",
"/CHANGELOG.md",
"/LICENSE-MIT",
"/LICENSE-APACHE",
"**/*.rs",
"Cargo.toml",
"README.md",
"CHANGELOG.md",
"LICENSE-MIT",
"LICENSE-APACHE",
]
edition = "2018"
[badges]
travis-ci = { repository = "eldruin/ds323x-rs", branch = "master" }
coveralls = { repository = "eldruin/ds323x-rs", branch = "master", service = "github" }
maintenance = { status = "actively-developed" }
[dependencies]
embedded-hal = "1.0.0"
rtcc = "0.3"
embedded-hal = "0.2"
[dev-dependencies]
embedded-hal-mock = { version = "0.11.1", features = ["eh1"] }
embedded-hal-bus = "0.2"
linux-embedded-hal = "0.4.0"
linux-embedded-hal = "0.2"
embedded-hal-mock = "0.4"
[profile.release]
lto = true

View File

@ -1,4 +1,4 @@
Copyright (C) 2018-2025 Diego Barrios Romero
Copyright (C) 2018 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

108
README.md
View File

@ -2,9 +2,9 @@
[![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)
[![Build Status](https://travis-ci.org/eldruin/ds323x-rs.svg?branch=master)](https://travis-ci.org/eldruin/ds323x-rs)
[![Coverage Status](https://coveralls.io/repos/eldruin/ds323x-rs/badge.svg?branch=master)](https://coveralls.io/r/eldruin/ds323x-rs?branch=master)
![Maintenance Intention](https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg)
This is a platform agnostic Rust driver for the DS3231, DS3232 and DS3234
extremely accurate real-time clocks, based on the [`embedded-hal`] traits.
@ -12,17 +12,16 @@ 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: `datetime`.
- Read and set date and time individual elements. For example, see: `year`.
- 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`.
- Enable and disable the real-time clock. See: `enable`.
- Read the busy status. See `busy`.
- Read the busy status. See `is_busy`.
- Read whether the oscillator is or has been stopped. See `has_been_stopped`.
- Clear the has-been-stopped flag. See `clear_has_been_stopped_flag`.
- Set and read the aging offset. See `set_aging_offset`.
- Select the function of the INT/SQW output pin. See `use_int_sqw_output_as_interrupt`.
- Alarms:
- Set alarms 1 and 2 with several matching policies. See `set_alarm1_day`.
- Set alarms 1 and 2 for a time. See `set_alarm1_hms`.
- Read whether alarms 1 or 2 have matched. See `has_alarm1_matched`.
- Clear flag indicating that alarms 1 or 2 have matched. See `clear_alarm1_matched_flag`.
- Enable and disable alarms 1 and 2 interrupt generation. See `enable_alarm1_interrupts`.
@ -32,7 +31,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 `temperature`.
- Read the temperature. See `get_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`.
@ -42,18 +41,16 @@ This driver allows you to:
This driver is compatible with the DS3231 and DS3232 I2C devices and the
DS3234 SPI device.
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.
### DS3231
The DS3231 is a low-cost, extremely accurate I2C real-time clock (RTC) with
an integrated temperature-compensated crystal oscillator (TCXO) and crystal.
### 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
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
reduces the piece-part count in a manufacturing line.
The devices are available in commercial and industrial temperature ranges,
and are offered in a 16-pin, 300-mil SO package.
The DS3231 is available in commercial and industrial temperature ranges,
and is 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
@ -69,7 +66,36 @@ 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 details
### 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.
The DS3234 incorporates a precision, temperature-compensated voltage
reference and comparator circuit to monitor VCC. When VCC drops below the
@ -106,41 +132,30 @@ In the following example an instance of the device DS3231 will be created.
Other devices can be created with similar methods like:
`Ds323x::new_ds3234(...)`.
Please find additional examples using hardware in this repository: [driver-examples]
[driver-examples]: https://github.com/eldruin/driver-examples
```rust
use ds323x::{DateTimeAccess, Ds323x, NaiveDate, Rtcc};
use linux_embedded_hal::I2cdev;
extern crate linux_embedded_hal as hal;
extern crate ds323x;
use ds323x::{ Ds323x, DateTime, Hours };
fn main() {
let dev = I2cdev::new("/dev/i2c-1").unwrap();
let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
let mut rtc = Ds323x::new_ds3231(dev);
let datetime = NaiveDate::from_ymd_opt(2020, 5, 1)
.unwrap()
.and_hms_opt(19, 59, 58)
.unwrap();
let datetime = DateTime {
year: 2018,
month: 08,
day: 20,
weekday: 4,
hour: Hours::H24(19),
minute: 59,
second: 58
};
rtc.set_datetime(&datetime).unwrap();
// do something else...
let time = rtc.time().unwrap();
println!("Time: {}", time);
let _dev = rtc.destroy_ds3231();
let seconds = rtc.get_seconds().unwrap();
println!("Seconds: {}", seconds);
}
```
## Support
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
@ -148,12 +163,11 @@ Licensed under either of
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
http://opensource.org/licenses/MIT)
at your option.
http://opensource.org/licenses/MIT) at your option.
### Contributing
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.

13
ci/install.sh 100644
View File

@ -0,0 +1,13 @@
set -euxo pipefail
main() {
rustup component add rust-src
SYSROOT=$(rustc --print sysroot)
if [[ ! "$SYSROOT" =~ "$TARGET" ]]; then
rustup target add $TARGET
else
echo "Target $TARGET is already installed"
fi
}
main

26
ci/script.sh 100644
View File

@ -0,0 +1,26 @@
set -exo pipefail
main() {
export CARGO_OPTIONS="--target $TARGET"
if [[ ! $TARGET =~ .*linux.* ]]; then
sed -i "s/linux-embedded-hal/#linux-embedded-hal/g" Cargo.toml
sed -i "s/embedded-hal-mock/#embedded-hal-mock/g" Cargo.toml
fi
if [ ! -z $FEATURES ]; then
export CARGO_OPTIONS="$CARGO_OPTIONS --features $FEATURES"
fi
cargo check $CARGO_OPTIONS
cargo build $CARGO_OPTIONS
if [ -z $DISABLE_EXAMPLES ] && [[ $TARGET =~ .*linux.* ]]; then
cargo build $CARGO_OPTIONS --examples
fi
cargo doc $CARGO_OPTIONS
if [ -z $DISABLE_TESTS ] && [[ $TARGET =~ .*linux.* ]]; then
cargo test $CARGO_OPTIONS
fi
}
main

View File

@ -1,17 +1,26 @@
use ds323x::{DateTimeAccess, Ds323x, NaiveDate, Rtcc};
extern crate embedded_hal;
extern crate linux_embedded_hal;
extern crate ds323x;
use linux_embedded_hal::I2cdev;
use ds323x::{ Ds323x, DateTime, Hours };
fn main() {
let dev = I2cdev::new("/dev/i2c-1").unwrap();
let mut rtc = Ds323x::new_ds3231(dev);
let datetime = NaiveDate::from_ymd_opt(2020, 5, 1)
.unwrap()
.and_hms_opt(19, 59, 58)
.unwrap();
let datetime = DateTime {
year: 2018,
month: 08,
day: 20,
weekday: 4,
hour: Hours::H24(19),
minute: 59,
second: 58
};
rtc.set_datetime(&datetime).unwrap();
// do something else...
let time = rtc.time().unwrap();
println!("Time: {}", time);
let seconds = rtc.get_seconds().unwrap();
println!("Seconds: {}", seconds);
let _dev = rtc.destroy_ds3231();
}

View File

@ -1,7 +0,0 @@
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

@ -1,21 +1,25 @@
//! Functions exclusive of DS3231
use crate::{ic, interface::I2cInterface, BitFlags, Ds323x, CONTROL_POR_VALUE};
extern crate embedded_hal as hal;
use hal::blocking;
use core::marker::PhantomData;
use embedded_hal::i2c;
use super::{ Ds323x, BitFlags, ic, CONTROL_POR_VALUE };
use interface::I2cInterface;
impl<I2C, E> Ds323x<I2cInterface<I2C>, ic::DS3231>
where
I2C: i2c::I2c<Error = E>,
I2C: blocking::i2c::Write<Error = E> + blocking::i2c::WriteRead<Error = E>
{
/// Create a new instance of the DS3231 device.
pub fn new_ds3231(i2c: I2C) -> Self {
const STATUS_POR_VALUE: u8 = BitFlags::OSC_STOP | BitFlags::EN32KHZ;
const STATUS_POR_VALUE : u8 = BitFlags::OSC_STOP | BitFlags::EN32KHZ;
Ds323x {
iface: I2cInterface { i2c },
iface: I2cInterface {
i2c,
},
control: CONTROL_POR_VALUE,
status: STATUS_POR_VALUE,
_ic: PhantomData,
_ic: PhantomData
}
}

View File

@ -1,23 +1,26 @@
//! Functions exclusive of DS3232
use crate::{
ic, interface::I2cInterface, BitFlags, Ds323x, Error, TempConvRate, CONTROL_POR_VALUE,
};
extern crate embedded_hal as hal;
use hal::blocking;
use core::marker::PhantomData;
use embedded_hal::i2c;
use super::{ Ds323x, TempConvRate, BitFlags, Error, ic, CONTROL_POR_VALUE };
use interface::I2cInterface;
impl<I2C, E> Ds323x<I2cInterface<I2C>, ic::DS3232>
where
I2C: i2c::I2c<Error = E>,
I2C: blocking::i2c::Write<Error = E> + blocking::i2c::WriteRead<Error = E>
{
/// Create a new instance of the DS3232 device.
pub fn new_ds3232(i2c: I2C) -> Self {
const STATUS_POR_VALUE: u8 = BitFlags::OSC_STOP | BitFlags::BB32KHZ | BitFlags::EN32KHZ;
const STATUS_POR_VALUE : u8 = BitFlags::OSC_STOP | BitFlags::BB32KHZ | BitFlags::EN32KHZ;
Ds323x {
iface: I2cInterface { i2c },
iface: I2cInterface {
i2c,
},
control: CONTROL_POR_VALUE,
status: STATUS_POR_VALUE,
_ic: PhantomData,
_ic: PhantomData
}
}
@ -56,12 +59,13 @@ where
///
/// Note: This is only available for DS3232 and DS3234 devices.
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,
TempConvRate::_256s => self.status | BitFlags::CRATE1 & !BitFlags::CRATE0,
TempConvRate::_512s => self.status | BitFlags::CRATE1 | BitFlags::CRATE0,
};
let status;
match rate {
TempConvRate::_64s => status = self.status & !BitFlags::CRATE1 & !BitFlags::CRATE0,
TempConvRate::_128s => status = self.status & !BitFlags::CRATE1 | BitFlags::CRATE0,
TempConvRate::_256s => status = self.status | BitFlags::CRATE1 & !BitFlags::CRATE0,
TempConvRate::_512s => status = self.status | BitFlags::CRATE1 | BitFlags::CRATE0,
}
self.write_status_without_clearing_alarm(status)
}
}

View File

@ -1,27 +1,33 @@
//! Functions exclusive of DS3234
use crate::interface::{SpiInterface, WriteData};
use crate::{ic, BitFlags, Ds323x, Error, Register, TempConvRate, CONTROL_POR_VALUE};
use core::marker::PhantomData;
use embedded_hal::spi;
impl<SPI, E> Ds323x<SpiInterface<SPI>, ic::DS3234>
extern crate embedded_hal as hal;
use hal::blocking;
use core::marker::PhantomData;
use super::{ Ds323x, TempConvRate, Register, BitFlags, Error, ic, CONTROL_POR_VALUE };
use interface::{ SpiInterface, WriteData };
impl<SPI, CS, E> Ds323x<SpiInterface<SPI, CS>, ic::DS3234>
where
SPI: spi::SpiDevice<u8, Error = E>,
SPI: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
CS: hal::digital::OutputPin
{
/// Create a new instance.
pub fn new_ds3234(spi: SPI) -> Self {
const STATUS_POR_VALUE: u8 = BitFlags::OSC_STOP | BitFlags::BB32KHZ | BitFlags::EN32KHZ;
pub fn new_ds3234(spi: SPI, chip_select: CS) -> Self {
const STATUS_POR_VALUE : u8 = BitFlags::OSC_STOP | BitFlags::BB32KHZ | BitFlags::EN32KHZ;
Ds323x {
iface: SpiInterface { spi },
iface: SpiInterface {
spi,
cs: chip_select
},
control: CONTROL_POR_VALUE,
status: STATUS_POR_VALUE,
_ic: PhantomData,
_ic: PhantomData
}
}
/// Destroy driver instance, return SPI bus instance and CS output pin.
pub fn destroy_ds3234(self) -> SPI {
self.iface.spi
pub fn destroy_ds3234(self) -> (SPI, CS) {
(self.iface.spi, self.iface.cs)
}
/// Enable the 32kHz output when battery-powered. (enabled per default)
@ -54,12 +60,13 @@ where
///
/// Note: This is only available for DS3232 and DS3234 devices.
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,
TempConvRate::_256s => self.status | BitFlags::CRATE1 & !BitFlags::CRATE0,
TempConvRate::_512s => self.status | BitFlags::CRATE1 | BitFlags::CRATE0,
};
let status;
match rate {
TempConvRate::_64s => status = self.status & !BitFlags::CRATE1 & !BitFlags::CRATE0,
TempConvRate::_128s => status = self.status & !BitFlags::CRATE1 | BitFlags::CRATE0,
TempConvRate::_256s => status = self.status | BitFlags::CRATE1 & !BitFlags::CRATE0,
TempConvRate::_512s => status = self.status | BitFlags::CRATE1 | BitFlags::CRATE0,
}
self.write_status_without_clearing_alarm(status)
}
@ -74,7 +81,6 @@ where
///
/// Note: This is only available for DS3234 devices.
pub fn disable_temperature_conversions_on_battery(&mut self) -> Result<(), Error<E>> {
self.iface
.write_register(Register::TEMP_CONV, BitFlags::TEMP_CONV_BAT)
self.iface.write_register(Register::TEMP_CONV, BitFlags::TEMP_CONV_BAT)
}
}

View File

@ -1,19 +1,11 @@
//! Alarm support
use super::{decimal_to_packed_bcd, hours_to_register};
use crate::{
ds323x::{NaiveTime, Timelike},
interface::{ReadData, WriteData},
BitFlags, Ds323x, Error, Hours, Register,
};
extern crate embedded_hal as hal;
use super::super::{ Ds323x, Hours, Register, BitFlags, Error };
use interface::{ ReadData, WriteData };
use super::{ decimal_to_packed_bcd, hours_to_register };
/// Parameters for setting Alarm1 on a day of the month
///
/// Depending on the matching strategy, some fields may not be relevant. In this
/// case, invalid values are ignored and the minimum valid values are used instead to
/// configure the alarm:
/// - Second, minute and hour: 0
/// - Day: 1
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DayAlarm1 {
/// Day of the month [1-31]
@ -23,16 +15,10 @@ pub struct DayAlarm1 {
/// Minute [0-59]
pub minute: u8,
/// Second [0-59]
pub second: u8,
pub second: u8
}
/// Parameters for setting Alarm1 on a weekday
///
/// Depending on the matching strategy, some fields may not be relevant. In this
/// case, invalid values are ignored and the minimum valid values are used instead to
/// configure the alarm:
/// - Second, minute and hour: 0
/// - Weekday: 1
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct WeekdayAlarm1 {
/// Weekday [1-7]
@ -42,7 +28,7 @@ pub struct WeekdayAlarm1 {
/// Minute [0-59]
pub minute: u8,
/// Second [0-59]
pub second: u8,
pub second: u8
}
/// Alarm1 trigger rate
@ -57,16 +43,11 @@ pub enum Alarm1Matching {
/// Alarm when hours, minutes and seconds match.
HoursMinutesAndSecondsMatch,
/// Alarm when date/weekday, hours, minutes and seconds match.
AllMatch,
AllMatch
}
/// Parameters for setting Alarm2 on a day of the month
///
/// Depending on the matching strategy, some fields may not be relevant. In this
/// case, invalid values are ignored and the minimum valid values are used instead to
/// configure the alarm:
/// - Minute and hour: 0
/// - Day: 1
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DayAlarm2 {
/// Day of month [1-31]
@ -74,16 +55,10 @@ pub struct DayAlarm2 {
/// Hour
pub hour: Hours,
/// Minute [0-59]
pub minute: u8,
pub minute: u8
}
/// Parameters for setting Alarm2 on a weekday
///
/// Depending on the matching strategy, some fields may not be relevant. In this
/// case, invalid values are ignored and the minimum valid values are used instead to
/// configure the alarm:
/// - Minute and hour: 0
/// - Weekday: 1
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct WeekdayAlarm2 {
/// Weekday [1-7]
@ -91,7 +66,7 @@ pub struct WeekdayAlarm2 {
/// Hour
pub hour: Hours,
/// Minute [0-59]
pub minute: u8,
pub minute: u8
}
/// Alarm2 trigger rate
@ -104,237 +79,100 @@ pub enum Alarm2Matching {
/// Alarm when hours and minutes match.
HoursAndMinutesMatch,
/// Alarm when date/weekday, hours and minutes match.
AllMatch,
AllMatch
}
fn get_matching_mask_alarm1(matching: Alarm1Matching) -> [u8; 4] {
const AM: u8 = BitFlags::ALARM_MATCH;
const AM : u8 = BitFlags::ALARM_MATCH;
match matching {
Alarm1Matching::OncePerSecond => [AM, AM, AM, AM],
Alarm1Matching::SecondsMatch => [0, AM, AM, AM],
Alarm1Matching::MinutesAndSecondsMatch => [0, 0, AM, AM],
Alarm1Matching::HoursMinutesAndSecondsMatch => [0, 0, 0, AM],
Alarm1Matching::AllMatch => [0, 0, 0, 0],
Alarm1Matching::OncePerSecond => [AM, AM, AM, AM],
Alarm1Matching::SecondsMatch => [ 0, AM, AM, AM],
Alarm1Matching::MinutesAndSecondsMatch => [ 0, 0, AM, AM],
Alarm1Matching::HoursMinutesAndSecondsMatch => [ 0, 0, 0, AM],
Alarm1Matching::AllMatch => [ 0, 0, 0, 0],
}
}
fn get_matching_mask_alarm2(matching: Alarm2Matching) -> [u8; 3] {
const AM: u8 = BitFlags::ALARM_MATCH;
const AM : u8 = BitFlags::ALARM_MATCH;
match matching {
Alarm2Matching::OncePerMinute => [AM, AM, AM],
Alarm2Matching::MinutesMatch => [0, AM, AM],
Alarm2Matching::HoursAndMinutesMatch => [0, 0, AM],
Alarm2Matching::AllMatch => [0, 0, 0],
Alarm2Matching::OncePerMinute => [AM, AM, AM],
Alarm2Matching::MinutesMatch => [ 0, AM, AM],
Alarm2Matching::HoursAndMinutesMatch => [ 0, 0, AM],
Alarm2Matching::AllMatch => [ 0, 0, 0],
}
}
/// Test if hour value is valid
fn is_hour_valid(hours: Hours) -> bool {
match hours {
Hours::H24(h) if h > 23 => true,
Hours::AM(h) if !(1..=12).contains(&h) => true,
Hours::PM(h) if !(1..=12).contains(&h) => true,
_ => false,
}
}
/// Amend invalid hour values
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 !(1..=12).contains(&h) => Hours::AM(1),
Hours::AM(h) => Hours::AM(h),
Hours::PM(h) if !(1..=12).contains(&h) => Hours::PM(1),
Hours::PM(h) => Hours::PM(h),
}
}
impl<DI, IC, E> Ds323x<DI, IC>
where
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
DI: ReadData<Error = E> + WriteData<Error = E>
{
/// Set Alarm1 for day of the month.
///
/// Will return an `Error::InvalidInputData` if any of the used parameters
/// (depending on the matching startegy) is out of range. Any unused
/// parameter is set to the corresponding minimum valid value:
/// - Second, minute, hour: 0
/// - Day: 1
pub fn set_alarm1_day(
&mut self,
when: DayAlarm1,
matching: Alarm1Matching,
) -> 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;
let second_invalid = when.second > 59;
let day = if day_invalid { 1 } else { when.day };
let hour = amend_hour(when.hour);
let minute = if minute_invalid { 0 } else { when.minute };
if (matching == Alarm1Matching::AllMatch && (day_invalid || hour_invalid))
|| (hour_invalid && matching == Alarm1Matching::HoursMinutesAndSecondsMatch)
|| ((matching != Alarm1Matching::SecondsMatch
&& matching != Alarm1Matching::OncePerSecond)
&& minute_invalid)
|| second_invalid
{
/// Will return an `Error::InvalidInputData` if any of the parameters is out of range.
pub fn set_alarm1_day(&mut self, when: DayAlarm1, matching: Alarm1Matching) -> Result<(), Error<E>> {
if when.day < 1 || when.day > 31 ||
when.minute > 59 ||
when.second > 59 {
return Err(Error::InvalidInputData);
}
let match_mask = get_matching_mask_alarm1(matching);
let mut data = [
Register::ALARM1_SECONDS,
decimal_to_packed_bcd(when.second) | match_mask[0],
decimal_to_packed_bcd(minute) | match_mask[1],
hours_to_register(hour)? | match_mask[2],
decimal_to_packed_bcd(day) | match_mask[3],
];
let mut data = [ Register::ALARM1_SECONDS,
decimal_to_packed_bcd(when.second) | match_mask[0],
decimal_to_packed_bcd(when.minute) | match_mask[1],
hours_to_register(when.hour)? | match_mask[2],
decimal_to_packed_bcd(when.day) | match_mask[3]];
self.iface.write_data(&mut data)
}
/// Set Alarm1 for a time (fires when hours, minutes and seconds match).
///
/// 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<E>> {
let alarm = DayAlarm1 {
day: 1,
hour: Hours::H24(when.hour() as u8),
minute: when.minute() as u8,
second: when.second() as u8,
};
self.set_alarm1_day(alarm, Alarm1Matching::HoursMinutesAndSecondsMatch)
}
/// Set Alarm1 for weekday.
///
/// Will return an `Error::InvalidInputData` if any of the used parameters
/// (depending on the matching startegy) is out of range. Any unused
/// parameter is set to the corresponding minimum valid value:
/// - Second, minute, hour: 0
/// - Weekday: 1
pub fn set_alarm1_weekday(
&mut self,
when: WeekdayAlarm1,
matching: Alarm1Matching,
) -> 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;
let second_invalid = when.second > 59;
let weekday = if weekday_invalid { 1 } else { when.weekday };
let hour = amend_hour(when.hour);
let minute = if minute_invalid { 0 } else { when.minute };
let second = if second_invalid { 0 } else { when.second };
if ((hour_invalid || weekday_invalid) && matching == Alarm1Matching::AllMatch)
|| (hour_invalid && matching == Alarm1Matching::HoursMinutesAndSecondsMatch)
|| (minute_invalid
&& (matching != Alarm1Matching::OncePerSecond
&& matching != Alarm1Matching::SecondsMatch))
|| (second_invalid && matching != Alarm1Matching::OncePerSecond)
{
/// Will return an `Error::InvalidInputData` if any of the parameters is out of range.
pub fn set_alarm1_weekday(&mut self, when: WeekdayAlarm1, matching: Alarm1Matching) -> Result<(), Error<E>> {
if when.weekday < 1 || when.weekday > 7 ||
when.minute > 59 ||
when.second > 59 {
return Err(Error::InvalidInputData);
}
let match_mask = get_matching_mask_alarm1(matching);
let mut data = [
Register::ALARM1_SECONDS,
decimal_to_packed_bcd(second) | match_mask[0],
decimal_to_packed_bcd(minute) | match_mask[1],
hours_to_register(hour)? | match_mask[2],
decimal_to_packed_bcd(weekday) | match_mask[3] | BitFlags::WEEKDAY,
];
let mut data = [ Register::ALARM1_SECONDS,
decimal_to_packed_bcd(when.second) | match_mask[0],
decimal_to_packed_bcd(when.minute) | match_mask[1],
hours_to_register(when.hour)? | match_mask[2],
decimal_to_packed_bcd(when.weekday) | match_mask[3] | BitFlags::WEEKDAY];
self.iface.write_data(&mut data)
}
/// Set Alarm2 for date (day of month).
///
/// Will return an `Error::InvalidInputData` if any of the used parameters
/// (depending on the matching startegy) is out of range. Any unused
/// parameter is set to the corresponding minimum valid value:
/// - Minute, hour: 0
/// - Day: 1
pub fn set_alarm2_day(
&mut self,
when: DayAlarm2,
matching: Alarm2Matching,
) -> 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;
let day = if day_invalid { 1 } else { when.day };
let hour = amend_hour(when.hour);
let minute = if minute_invalid { 0 } else { when.minute };
if ((day_invalid || hour_invalid) && matching == Alarm2Matching::AllMatch)
|| (hour_invalid && matching == Alarm2Matching::HoursAndMinutesMatch)
|| (matching != Alarm2Matching::OncePerMinute && minute_invalid)
{
/// Will return an `Error::InvalidInputData` if any of the parameters is out of range.
pub fn set_alarm2_day(&mut self, when: DayAlarm2, matching: Alarm2Matching) -> Result<(), Error<E>> {
if when.day < 1 || when.day > 31 ||
when.minute > 59 {
return Err(Error::InvalidInputData);
}
let match_mask = get_matching_mask_alarm2(matching);
let mut data = [
Register::ALARM2_MINUTES,
decimal_to_packed_bcd(minute) | match_mask[0],
hours_to_register(hour)? | match_mask[1],
decimal_to_packed_bcd(day) | match_mask[2],
];
let mut data = [ Register::ALARM2_MINUTES,
decimal_to_packed_bcd(when.minute) | match_mask[0],
hours_to_register(when.hour)? | match_mask[1],
decimal_to_packed_bcd(when.day) | match_mask[2]];
self.iface.write_data(&mut data)
}
/// Set Alarm2 for a time (fires when hours and minutes match).
///
/// 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<E>> {
let alarm = DayAlarm2 {
day: 1,
hour: Hours::H24(when.hour() as u8),
minute: when.minute() as u8,
};
self.set_alarm2_day(alarm, Alarm2Matching::HoursAndMinutesMatch)
}
/// Set Alarm2 for weekday.
///
/// Will return an `Error::InvalidInputData` if any of the used parameters
/// (depending on the matching startegy) is out of range. Any unused
/// parameter is set to the corresponding minimum valid value:
/// - Minute, hour: 0
/// - Weekday: 1
pub fn set_alarm2_weekday(
&mut self,
when: WeekdayAlarm2,
matching: Alarm2Matching,
) -> 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;
let weekday = if weekday_invalid { 1 } else { when.weekday };
let hour = amend_hour(when.hour);
let minute = if minute_invalid { 0 } else { when.minute };
if (matching == Alarm2Matching::AllMatch && (weekday_invalid || hour_invalid))
|| (matching == Alarm2Matching::HoursAndMinutesMatch && hour_invalid)
|| (minute_invalid && matching != Alarm2Matching::OncePerMinute)
{
/// Will return an `Error::InvalidInputData` if any of the parameters is out of range.
pub fn set_alarm2_weekday(&mut self, when: WeekdayAlarm2, matching: Alarm2Matching) -> Result<(), Error<E>> {
if when.weekday < 1 || when.weekday > 7 ||
when.minute > 59 {
return Err(Error::InvalidInputData);
}
let match_mask = get_matching_mask_alarm2(matching);
let mut data = [
Register::ALARM2_MINUTES,
decimal_to_packed_bcd(minute) | match_mask[0],
hours_to_register(hour)? | match_mask[1],
decimal_to_packed_bcd(weekday) | match_mask[2] | BitFlags::WEEKDAY,
];
let mut data = [ Register::ALARM2_MINUTES,
decimal_to_packed_bcd(when.minute) | match_mask[0],
hours_to_register(when.hour)? | match_mask[1],
decimal_to_packed_bcd(when.weekday) | match_mask[2] | BitFlags::WEEKDAY];
self.iface.write_data(&mut data)
}
}

View File

@ -1,13 +1,12 @@
//! Device configuration
use crate::{
interface::{ReadData, WriteData},
BitFlags, Ds323x, Error, Register, SqWFreq,
};
extern crate embedded_hal as hal;
use super::super::{ Ds323x, SqWFreq, Register, BitFlags, Error };
use interface::{ ReadData, WriteData };
impl<DI, IC, E> Ds323x<DI, IC>
where
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
DI: ReadData<Error = E> + WriteData<Error = E>
{
/// Enable the oscillator (set the clock running) (default).
pub fn enable(&mut self) -> Result<(), Error<E>> {
@ -23,13 +22,12 @@ where
/// Force a temperature conversion and time compensation with TXCO algorithm.
///
/// The *busy* status should be checked before doing this. See [`busy()`](#method.busy)
/// The *busy* status should be checked before doing this. See [`is_busy()`](#method.is_busy)
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 {
self.iface
.write_register(Register::CONTROL, control | BitFlags::TEMP_CONV)?;
self.iface.write_register(Register::CONTROL, control | BitFlags::TEMP_CONV)?;
}
Ok(())
}
@ -48,12 +46,11 @@ where
/// Set the aging offset.
pub fn set_aging_offset(&mut self, offset: i8) -> Result<(), Error<E>> {
self.iface
.write_register(Register::AGING_OFFSET, offset as u8)
self.iface.write_register(Register::AGING_OFFSET, offset as u8)
}
/// Read the aging offset.
pub fn aging_offset(&mut self) -> Result<i8, Error<E>> {
pub fn get_aging_offset(&mut self) -> Result<i8, Error<E>> {
let offset = self.iface.read_register(Register::AGING_OFFSET)?;
Ok(offset as i8)
}
@ -84,12 +81,13 @@ where
/// Set the square-wave output frequency.
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,
};
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,
}
self.write_control(new_control)
}
@ -123,10 +121,7 @@ where
Ok(())
}
pub(crate) fn write_status_without_clearing_alarm(
&mut self,
status: u8,
) -> Result<(), Error<E>> {
pub(crate) fn write_status_without_clearing_alarm(&mut self, status: u8) -> 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,170 +1,160 @@
//! Common implementation
use super::{
decimal_to_packed_bcd, hours_to_register, packed_bcd_to_decimal, some_or_invalid_error,
};
use crate::{
interface::{ReadData, WriteData},
BitFlags, DateTimeAccess, Datelike, Ds323x, Error, Hours, NaiveDate, NaiveDateTime, NaiveTime,
Register, Rtcc, Timelike,
};
extern crate embedded_hal as hal;
use super::super::{ Ds323x, Register, BitFlags, Error };
use super::{ decimal_to_packed_bcd, packed_bcd_to_decimal, hours_to_register };
use interface::{ ReadData, WriteData };
impl<DI, IC, E> DateTimeAccess for Ds323x<DI, IC>
where
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
{
type Error = Error<E>;
fn datetime(&mut self) -> Result<NaiveDateTime, Self::Error> {
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 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]);
let second = packed_bcd_to_decimal(data[Register::SECONDS as usize + 1]);
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)
}
/// Date and time
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DateTime {
/// Year [2000-2099]
pub year : u16,
/// Month [1-12]
pub month : u8,
/// Day [1-31]
pub day : u8,
/// Weekday [1-7]
pub weekday : u8,
/// Hour in 24h/12h format
pub hour : Hours,
/// Minute [0-59]
pub minute : u8,
/// Second [0-59]
pub second : u8,
}
impl<DI, IC, E> Rtcc for Ds323x<DI, IC>
/// Hours in either 12-hour (AM/PM) or 24-hour format
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Hours {
/// AM [1-12]
AM(u8),
/// PM [1-12]
PM(u8),
/// 24H format [0-23]
H24(u8),
}
impl<DI, IC, E> Ds323x<DI, IC>
where
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
DI: ReadData<Error = E> + WriteData<Error = E>
{
fn seconds(&mut self) -> Result<u8, Self::Error> {
/// Read the seconds.
pub fn get_seconds(&mut self) -> Result<u8, Error<E>> {
self.read_register_decimal(Register::SECONDS)
}
fn minutes(&mut self) -> Result<u8, Self::Error> {
/// Read the minutes.
pub fn get_minutes(&mut self) -> Result<u8, Error<E>> {
self.read_register_decimal(Register::MINUTES)
}
fn hours(&mut self) -> Result<Hours, Self::Error> {
/// Read the hours.
pub fn get_hours(&mut self) -> Result<Hours, Error<E>> {
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> {
/// Read the day of the week [1-7].
pub fn get_weekday(&mut self) -> Result<u8, Error<E>> {
self.read_register_decimal(Register::DOW)
}
fn day(&mut self) -> Result<u8, Self::Error> {
/// Read the day of the month [1-31].
pub fn get_day(&mut self) -> Result<u8, Error<E>> {
self.read_register_decimal(Register::DOM)
}
fn month(&mut self) -> Result<u8, Self::Error> {
/// Read the month [1-12].
pub fn get_month(&mut self) -> Result<u8, Error<E>> {
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> {
/// Read the year [2000-2100].
pub fn get_year(&mut self) -> Result<u16, Error<E>> {
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;
/// Read the date and time.
pub fn get_datetime(&mut self) -> Result<DateTime, Error<E>> {
let mut data = [0; 8];
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)
Ok(DateTime {
year: year_from_registers(data[Register::MONTH as usize + 1], data[Register::YEAR as usize + 1]),
month: packed_bcd_to_decimal(data[Register::MONTH as usize + 1] & !BitFlags::CENTURY),
day: packed_bcd_to_decimal(data[Register::DOM as usize + 1]),
weekday: packed_bcd_to_decimal(data[Register::DOW as usize + 1]),
hour: hours_from_register(data[Register::HOURS as usize + 1]),
minute: packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]),
second: packed_bcd_to_decimal(data[Register::SECONDS as usize + 1])
})
}
fn set_seconds(&mut self, seconds: u8) -> Result<(), Self::Error> {
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))
}
/// Set the seconds [0-59].
///
/// Will return an `Error::InvalidInputData` if the seconds are out of range.
pub fn set_seconds(&mut self, seconds: u8) -> Result<(), Error<E>> {
if seconds > 59 {
return Err(Error::InvalidInputData);
}
self.write_register_decimal(Register::SECONDS, seconds)
}
fn set_minutes(&mut self, minutes: u8) -> Result<(), Self::Error> {
/// Set the minutes [0-59].
///
/// Will return an `Error::InvalidInputData` if the minutes are out of range.
pub fn set_minutes(&mut self, minutes: u8) -> Result<(), Error<E>> {
if minutes > 59 {
return Err(Error::InvalidInputData);
}
self.write_register_decimal(Register::MINUTES, minutes)
}
fn set_hours(&mut self, hours: Hours) -> Result<(), Self::Error> {
/// Set the hours.
///
/// Changes the operating mode to 12h/24h depending on the parameter.
///
/// Will return an `Error::InvalidInputData` if the hours are out of range.
pub fn set_hours(&mut self, hours: Hours) -> Result<(), Error<E>> {
let value = hours_to_register(hours)?;
self.iface.write_register(Register::HOURS, value)
}
fn set_time(&mut self, time: &NaiveTime) -> Result<(), Self::Error> {
let mut payload = [
Register::SECONDS,
decimal_to_packed_bcd(time.second() as u8),
decimal_to_packed_bcd(time.minute() as u8),
hours_to_register(Hours::H24(time.hour() as u8))?,
];
self.iface.write_data(&mut payload)
}
fn set_weekday(&mut self, weekday: u8) -> Result<(), Self::Error> {
if !(1..=7).contains(&weekday) {
/// Set the day of week [1-7].
///
/// Will return an `Error::InvalidInputData` if the day is out of range.
pub fn set_weekday(&mut self, weekday: u8) -> Result<(), Error<E>> {
if weekday < 1 || weekday > 7 {
return Err(Error::InvalidInputData);
}
self.iface.write_register(Register::DOW, weekday)
}
fn set_day(&mut self, day: u8) -> Result<(), Self::Error> {
if !(1..=31).contains(&day) {
/// Set the day of month [1-31].
///
/// Will return an `Error::InvalidInputData` if the day is out of range.
pub fn set_day(&mut self, day: u8) -> Result<(), Error<E>> {
if day < 1 || day > 7 {
return Err(Error::InvalidInputData);
}
self.write_register_decimal(Register::DOM, day)
self.iface.write_register(Register::DOM, day)
}
fn set_month(&mut self, month: u8) -> Result<(), Self::Error> {
if !(1..=12).contains(&month) {
/// Set the month [1-12].
///
/// Will return an `Error::InvalidInputData` if the month is out of range.
pub fn set_month(&mut self, month: u8) -> Result<(), Error<E>> {
if month < 1 || month > 12 {
return Err(Error::InvalidInputData);
}
// keep the century bit
@ -173,71 +163,65 @@ where
self.iface.write_register(Register::MONTH, value)
}
fn set_year(&mut self, year: u16) -> Result<(), Self::Error> {
if !(2000..=2100).contains(&year) {
/// Set the year [2000-2100].
///
/// Will return an `Error::InvalidInputData` if the year is out of range.
pub fn set_year(&mut self, year: u16) -> Result<(), Error<E>> {
if year < 2000 || year > 2100 {
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),
];
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),
];
}
else {
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 {
/// Set the date and time.
///
/// Will return an `Error::InvalidInputData` if any of the parameters is out of range.
pub fn set_datetime(&mut self, datetime: &DateTime) -> Result<(), Error<E>> {
if datetime.year < 2000 || datetime.year > 2100 ||
datetime.month < 1 || datetime.month > 12 ||
datetime.day < 1 || datetime.day > 31 ||
datetime.weekday < 1 || datetime.weekday > 7 ||
datetime.minute > 59 ||
datetime.second > 59 {
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,
];
let (month, year) = month_year_to_registers(datetime.month, datetime.year);
let mut payload = [Register::SECONDS,
decimal_to_packed_bcd(datetime.second),
decimal_to_packed_bcd(datetime.minute),
hours_to_register(datetime.hour)?,
decimal_to_packed_bcd(datetime.weekday),
decimal_to_packed_bcd(datetime.day),
month, year];
self.iface.write_data(&mut payload)
}
}
impl<DI, IC, E> Ds323x<DI, IC>
where
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
{
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<E>> {
self.iface
.write_register(register, decimal_to_packed_bcd(decimal_number))
self.iface.write_register(register, decimal_to_packed_bcd(decimal_number))
}
}
fn hours_from_register(data: u8) -> Hours {
if is_24h_format(data) {
Hours::H24(packed_bcd_to_decimal(data & !BitFlags::H24_H12))
} else if is_am(data) {
Hours::AM(packed_bcd_to_decimal(
data & !(BitFlags::H24_H12 | BitFlags::AM_PM),
))
} else {
Hours::PM(packed_bcd_to_decimal(
data & !(BitFlags::H24_H12 | BitFlags::AM_PM),
))
}
else if is_am(data) {
Hours::AM(packed_bcd_to_decimal(data & !(BitFlags::H24_H12 | BitFlags::AM_PM)))
}
else {
Hours::PM(packed_bcd_to_decimal(data & !(BitFlags::H24_H12 | BitFlags::AM_PM)))
}
}
@ -246,7 +230,8 @@ fn year_from_registers(month: u8, year: u8) -> u16 {
let year = packed_bcd_to_decimal(year);
if century != 0 {
2100 + u16::from(year)
} else {
}
else {
2000 + u16::from(year)
}
}
@ -255,11 +240,9 @@ 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),
)
}
else {
(decimal_to_packed_bcd(month), decimal_to_packed_bcd((year - 2000) as u8))
}
}
@ -271,30 +254,3 @@ fn is_am(hours_data: u8) -> bool {
hours_data & BitFlags::AM_PM == 0
}
fn get_h24(hour: Hours) -> u8 {
match hour {
Hours::H24(h) => h,
Hours::AM(h) => h,
Hours::PM(h) => h + 12,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_convert_to_h24() {
assert_eq!(0, get_h24(Hours::H24(0)));
assert_eq!(0, get_h24(Hours::AM(0)));
assert_eq!(12, get_h24(Hours::PM(0)));
assert_eq!(1, get_h24(Hours::H24(1)));
assert_eq!(1, get_h24(Hours::AM(1)));
assert_eq!(13, get_h24(Hours::PM(1)));
assert_eq!(23, get_h24(Hours::H24(23)));
assert_eq!(12, get_h24(Hours::AM(12)));
assert_eq!(23, get_h24(Hours::PM(11)));
}
}

View File

@ -1,11 +1,11 @@
mod alarms;
mod configuration;
mod status;
pub use self::alarms::{
Alarm1Matching, Alarm2Matching, DayAlarm1, DayAlarm2, WeekdayAlarm1, WeekdayAlarm2,
};
mod alarms;
pub use self::alarms::{ DayAlarm1, WeekdayAlarm1, Alarm1Matching,
DayAlarm2, WeekdayAlarm2, Alarm2Matching };
mod datetime;
use crate::{BitFlags, Error, Hours, NaiveTime, Timelike};
pub use self::datetime::{ Hours, DateTime };
use super::{ BitFlags, Error };
// Transforms a decimal number to packed BCD format
fn decimal_to_packed_bcd(dec: u8) -> u8 {
@ -21,46 +21,23 @@ 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 !(1..=12).contains(&h) => Err(Error::InvalidInputData),
Hours::AM(h) => Ok(BitFlags::H24_H12 | decimal_to_packed_bcd(h)),
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)),
Hours::AM(h) if h < 1 || h > 12 => 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) => 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));
assert_eq!(1, packed_bcd_to_decimal(0b0000_0001));
assert_eq!(9, packed_bcd_to_decimal(0b0000_1001));
assert_eq!(0, packed_bcd_to_decimal(0b0000_0000));
assert_eq!(1, packed_bcd_to_decimal(0b0000_0001));
assert_eq!(9, packed_bcd_to_decimal(0b0000_1001));
assert_eq!(10, packed_bcd_to_decimal(0b0001_0000));
assert_eq!(11, packed_bcd_to_decimal(0b0001_0001));
assert_eq!(19, packed_bcd_to_decimal(0b0001_1001));
@ -71,9 +48,9 @@ mod tests {
#[test]
fn can_convert_decimal_to_packed_bcd() {
assert_eq!(0b0000_0000, decimal_to_packed_bcd(0));
assert_eq!(0b0000_0001, decimal_to_packed_bcd(1));
assert_eq!(0b0000_1001, decimal_to_packed_bcd(9));
assert_eq!(0b0000_0000, decimal_to_packed_bcd( 0));
assert_eq!(0b0000_0001, decimal_to_packed_bcd( 1));
assert_eq!(0b0000_1001, decimal_to_packed_bcd( 9));
assert_eq!(0b0001_0000, decimal_to_packed_bcd(10));
assert_eq!(0b0001_0001, decimal_to_packed_bcd(11));
assert_eq!(0b0001_1001, decimal_to_packed_bcd(19));

View File

@ -1,22 +1,21 @@
//! Device status
use crate::{
interface::{ReadData, WriteData},
BitFlags, Ds323x, Error, Register,
};
extern crate embedded_hal as hal;
use super::super::{ Ds323x, Register, BitFlags, Error };
use interface::{ ReadData, WriteData };
impl<DI, IC, E> Ds323x<DI, IC>
where
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
DI: ReadData<Error = E> + WriteData<Error = E>
{
/// Read whether the oscillator is running
pub fn running(&mut self) -> Result<bool, Error<E>> {
pub fn is_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<E>> {
pub fn is_busy(&mut self) -> Result<bool, Error<E>> {
let status = self.iface.read_register(Register::STATUS)?;
Ok((status & BitFlags::BUSY) != 0)
}
@ -80,7 +79,7 @@ where
///
/// Note: It is possible to manually force a temperature conversion with
/// [`convert_temperature()`](#method.convert_temperature)
pub fn temperature(&mut self) -> Result<f32, Error<E>> {
pub fn get_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;
@ -88,7 +87,8 @@ where
if is_negative {
let temp_sign_extended = temp | 0b1111_1100_0000_0000;
Ok(f32::from(temp_sign_extended as i16) * 0.25)
} else {
}
else {
Ok(f32::from(temp) * 0.25)
}
}

View File

@ -1,7 +1,10 @@
//! I2C/SPI interfaces
use crate::{private, Error, DEVICE_ADDRESS};
use embedded_hal::{i2c, spi};
#![deny(missing_docs)]
extern crate embedded_hal as hal;
use hal::blocking;
use super::{ DEVICE_ADDRESS, Error };
/// I2C interface
#[derive(Debug, Default)]
@ -11,69 +14,87 @@ pub struct I2cInterface<I2C> {
/// SPI interface
#[derive(Debug, Default)]
pub struct SpiInterface<SPI> {
pub struct SpiInterface<SPI, CS> {
pub(crate) spi: SPI,
pub(crate) cs: CS
}
/// Write data
pub trait WriteData: private::Sealed {
pub trait WriteData {
/// Error type
type Error;
/// Write to an u8 register
fn write_register(&mut self, register: u8, data: u8) -> Result<(), Self::Error>;
fn write_register(&mut self, register: u8, data: u8) -> Result<(), Error<Self::Error>>;
/// Write data. The first element corresponds to the starting address.
fn write_data(&mut self, payload: &mut [u8]) -> Result<(), Self::Error>;
fn write_data(&mut self, payload: &mut [u8]) -> Result<(), Error<Self::Error>>;
}
impl<I2C, E> WriteData for I2cInterface<I2C>
where
I2C: i2c::I2c<Error = E>,
I2C: blocking::i2c::Write<Error = E>
{
type Error = Error<E>;
fn write_register(&mut self, register: u8, data: u8) -> Result<(), Self::Error> {
type Error = E;
fn write_register(&mut self, register: u8, data: u8) -> Result<(), Error<E>> {
let payload: [u8; 2] = [register, data];
self.i2c
.write(DEVICE_ADDRESS, &payload)
.map_err(Error::Comm)
}
fn write_data(&mut self, payload: &mut [u8]) -> Result<(), Self::Error> {
self.i2c.write(DEVICE_ADDRESS, payload).map_err(Error::Comm)
fn write_data(&mut self, payload: &mut [u8]) -> Result<(), Error<Self::Error>> {
self.i2c
.write(DEVICE_ADDRESS, &payload)
.map_err(Error::Comm)
}
}
impl<SPI, E> WriteData for SpiInterface<SPI>
impl<SPI, CS, E> WriteData for SpiInterface<SPI, CS>
where
SPI: spi::SpiDevice<u8, Error = E>,
SPI: blocking::spi::Write<u8, Error = E>,
CS: hal::digital::OutputPin
{
type Error = Error<E>;
fn write_register(&mut self, register: u8, data: u8) -> Result<(), Self::Error> {
type Error = E;
fn write_register(&mut self, register: u8, data: u8) -> Result<(), Error<E>> {
self.cs.set_low();
let payload: [u8; 2] = [register + 0x80, data];
self.spi.write(&payload).map_err(Error::Comm)
let result = self.spi
.write(&payload)
.map_err(Error::Comm);
self.cs.set_high();
result
}
fn write_data(&mut self, payload: &mut [u8]) -> Result<(), Self::Error> {
fn write_data(&mut self, payload: &mut [u8]) -> Result<(), Error<Self::Error>> {
self.cs.set_low();
payload[0] += 0x80;
self.spi.write(payload).map_err(Error::Comm)
let result = self.spi
.write(&payload)
.map_err(Error::Comm);
self.cs.set_high();
result
}
}
/// Read data
pub trait ReadData: private::Sealed {
pub trait ReadData {
/// Error type
type Error;
/// Read an u8 register
fn read_register(&mut self, register: u8) -> Result<u8, Self::Error>;
fn read_register(&mut self, register: u8) -> Result<u8, Error<Self::Error>>;
/// Read some data. The first element corresponds to the starting address.
fn read_data(&mut self, payload: &mut [u8]) -> Result<(), Self::Error>;
fn read_data(&mut self, payload: &mut [u8]) -> Result<(), Error<Self::Error>>;
}
impl<I2C, E> ReadData for I2cInterface<I2C>
where
I2C: i2c::I2c<Error = E>,
I2C: blocking::i2c::WriteRead<Error = E>
{
type Error = Error<E>;
fn read_register(&mut self, register: u8) -> Result<u8, Self::Error> {
type Error = E;
fn read_register(&mut self, register: u8) -> Result<u8, Error<E>> {
let mut data = [0];
self.i2c
.write_read(DEVICE_ADDRESS, &[register], &mut data)
@ -81,7 +102,7 @@ where
.and(Ok(data[0]))
}
fn read_data(&mut self, payload: &mut [u8]) -> Result<(), Self::Error> {
fn read_data(&mut self, payload: &mut [u8]) -> Result<(), Error<Self::Error>> {
let len = payload.len();
self.i2c
.write_read(DEVICE_ADDRESS, &[payload[0]], &mut payload[1..len])
@ -89,18 +110,30 @@ where
}
}
impl<SPI, E> ReadData for SpiInterface<SPI>
impl<SPI, CS, E> ReadData for SpiInterface<SPI, CS>
where
SPI: spi::SpiDevice<u8, Error = E>,
SPI: blocking::spi::Transfer<u8, Error = E>,
CS: hal::digital::OutputPin
{
type Error = Error<E>;
fn read_register(&mut self, register: u8) -> Result<u8, Self::Error> {
type Error = E;
fn read_register(&mut self, register: u8) -> Result<u8, Error<E>> {
self.cs.set_low();
let mut data = [register, 0];
let result = self.spi.transfer_in_place(&mut data).map_err(Error::Comm);
result.and(Ok(data[1]))
let result = self.spi
.transfer(&mut data)
.map_err(Error::Comm);
self.cs.set_high();
let result = result?;
Ok(result[1])
}
fn read_data(&mut self, payload: &mut [u8]) -> Result<(), Self::Error> {
self.spi.transfer_in_place(payload).map_err(Error::Comm)
fn read_data(&mut self, mut payload: &mut [u8]) -> Result<(), Error<Self::Error>> {
self.cs.set_low();
let result = self.spi
.transfer(&mut payload)
.map_err(Error::Comm);
self.cs.set_high();
result?;
Ok(())
}
}

View File

@ -4,17 +4,16 @@
//! [`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: [`datetime`].
//! - Read and set date and time individual elements. For example, see: [`year`].
//! - 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`].
//! - Enable and disable the real-time clock. See: [`enable`].
//! - Read the busy status. See [`busy`].
//! - Read the busy status. See [`is_busy`].
//! - Read whether the oscillator is or has been stopped. See [`has_been_stopped`].
//! - Clear the has-been-stopped flag. See [`clear_has_been_stopped_flag`].
//! - Set and read the aging offset. See [`set_aging_offset`].
//! - Select the function of the INT/SQW output pin. See [`use_int_sqw_output_as_interrupt`].
//! - Alarms:
//! - Set alarms 1 and 2 with several matching policies. See [`set_alarm1_day`].
//! - Set alarms 1 and 2 for a time. See [`set_alarm1_hms`].
//! - Read whether alarms 1 or 2 have matched. See [`has_alarm1_matched`].
//! - Clear flag indicating that alarms 1 or 2 have matched. See [`clear_alarm1_matched_flag`].
//! - Enable and disable alarms 1 and 2 interrupt generation. See [`enable_alarm1_interrupts`].
@ -24,50 +23,47 @@
//! - 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 [`temperature`].
//! - Read the temperature. See [`get_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`].
//!
//! [`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
//! [`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
//! [`is_busy`]: struct.Ds323x.html#method.is_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
//! [`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
//!
//! ## The devices
//!
//! This driver is compatible with the DS3231 and DS3232 I2C devices and the
//! DS3234 SPI device.
//!
//! 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.
//! ### DS3231
//! The DS3231 is a low-cost, extremely accurate I2C real-time clock (RTC) with
//! an integrated temperature-compensated crystal oscillator (TCXO) and crystal.
//!
//! ### 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
//! 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
//! reduces the piece-part count in a manufacturing line.
//! The devices are available in commercial and industrial temperature ranges,
//! and are offered in a 16-pin, 300-mil SO package.
//! The DS3231 is available in commercial and industrial temperature ranges,
//! and is 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
@ -83,7 +79,36 @@
//! necessary. Additionally, the RST pin is monitored as a pushbutton
//! input for generating a μP reset.
//!
//! ### DS3234 details
//! ### 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.
//!
//! The DS3234 incorporates a precision, temperature-compensated voltage
//! reference and comparator circuit to monitor VCC. When VCC drops below the
@ -121,156 +146,202 @@
//! DS3231 as an example, except when using features specific to another IC,
//! for example, RAM access which is not available in the DS3231 device.
//!
//! Please find additional examples using hardware in this repository: [driver-examples]
//!
//! [driver-examples]: https://github.com/eldruin/driver-examples
//!
//! ### Create a driver instance for the DS3231
//!
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::Ds323x;
//! use linux_embedded_hal::I2cdev;
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let rtc = Ds323x::new_ds3231(dev);
//! // do something...
//!
//! // get the I2C device back
//! let dev = rtc.destroy_ds3231();
//! # }
//! ```
//!
//! ### Create a driver instance for the DS3232
//!
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::Ds323x;
//! use linux_embedded_hal::I2cdev;
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let rtc = Ds323x::new_ds3232(dev);
//! // do something...
//!
//! // get the I2C device back
//! let dev = rtc.destroy_ds3232();
//! # }
//! ```
//!
//! ### Create a driver instance for the DS3234
//!
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::Ds323x;
//! use embedded_hal_bus::spi::ExclusiveDevice;
//! use linux_embedded_hal::{Delay, SpidevBus, SysfsPin};
//!
//! 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);
//! # fn main() {
//! let dev = hal::Spidev::open("/dev/spidev0.0").unwrap();
//! let chip_select = hal::Pin::new(24);
//! let rtc = Ds323x::new_ds3234(dev, chip_select);
//! // do something...
//!
//! // get the SPI device back
//! let dev = rtc.destroy_ds3234();
//! // get the SPI device and chip select pin back
//! let (dev, chip_select) = rtc.destroy_ds3234();
//! # }
//! ```
//!
//! ### Set the current date and time at once
//!
//! ```no_run
//! use ds323x::{Ds323x, NaiveDate, DateTimeAccess};
//! use linux_embedded_hal::I2cdev;
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::{ Ds323x, DateTime, Hours };
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! # fn main() {
//! let dev = hal::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 = DateTime {
//! year: 2018,
//! month: 08,
//! day: 15,
//! weekday: 4,
//! hour: Hours::H24(19),
//! minute: 59,
//! second: 58
//! };
//! rtc.set_datetime(&datetime).unwrap();
//! # }
//! ```
//!
//! ### Get the current date and time at once
//!
//! ```no_run
//! use ds323x::{Ds323x, DateTimeAccess};
//! use linux_embedded_hal::I2cdev;
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::{ Ds323x, Hours };
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! let dt = rtc.datetime().unwrap();
//! println!("{}", dt);
//! // This will print something like: 2020-05-01 19:59:58
//!
//! let datetime = rtc.get_datetime().unwrap();
//!
//! // The hours depend on the RTC running mode
//! match datetime.hour {
//! Hours::H24(h) => println!("{}-{}-{}, {} {}:{}:{}", datetime.year,
//! datetime.month, datetime.day, datetime.weekday,
//! h, datetime.minute, datetime.second),
//! Hours::AM(h) => println!("{}-{}-{}, {} {}:{}:{} AM", datetime.year,
//! datetime.month, datetime.day, datetime.weekday,
//! h, datetime.minute, datetime.second),
//! Hours::PM(h) => println!("{}-{}-{}, {} {}:{}:{} PM", datetime.year,
//! datetime.month, datetime.day, datetime.weekday,
//! h, datetime.minute, datetime.second),
//! }
//! // This will print something like: 2018-08-15, 4 19:59:58
//! # }
//! ```
//!
//! ### Get the year
//!
//! Similar methods exist for month, day, weekday, hours, minutes and seconds.
//!
//! ```no_run
//! use ds323x::{Ds323x, Rtcc};
//! use linux_embedded_hal::I2cdev;
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::{ Ds323x, Hours };
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! let year = rtc.year().unwrap();
//! let year = rtc.get_year().unwrap();
//! println!("Year: {}", year);
//! # }
//! ```
//! Similar methods exist for month, day, weekday, hours, minutes and seconds.
//!
//! ### Set the year
//!
//! Similar methods exist for month, day, weekday, hours, minutes and seconds.
//!
//! ```no_run
//! use ds323x::{Ds323x, Rtcc};
//! use linux_embedded_hal::I2cdev;
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::{ Ds323x, Hours };
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! rtc.set_year(2018).unwrap();
//! # }
//! ```
//! Similar methods exist for month, day, weekday, hours, minutes and seconds.
//!
//! ### Enable/disable the device
//!
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::Ds323x;
//! use linux_embedded_hal::I2cdev;
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! rtc.disable().unwrap(); // stops the clock
//! let running = rtc.running().unwrap();
//! println!("Is running: {}", running); // will print false
//! let is_running = rtc.is_running().unwrap();
//! println!("Is running: {}", is_running); // will print false
//! rtc.enable().unwrap(); // set clock to run
//! println!("Is running: {}", running); // will print true
//! println!("Is running: {}", is_running); // will print true
//! # }
//! ```
//!
//! ### Read the temperature
//!
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::Ds323x;
//! use linux_embedded_hal::I2cdev;
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! let temperature = rtc.temperature().unwrap();
//! let temperature = rtc.get_temperature().unwrap();
//! # }
//! ```
//!
//! ### Read busy status
//!
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::Ds323x;
//! use linux_embedded_hal::I2cdev;
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! let busy = rtc.busy().unwrap();
//! let is_busy = rtc.is_busy().unwrap();
//! # }
//! ```
//!
//! ### Enable the square-wave output with a frequency of 4.096Hz
//!
//! ```no_run
//! use ds323x::{Ds323x, SqWFreq};
//! use linux_embedded_hal::I2cdev;
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::{ Ds323x, SqWFreq };
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! rtc.set_square_wave_frequency(SqWFreq::_4_096Hz).unwrap();
//! // The same output pin can be used for interrupts or as square-wave output
//! rtc.use_int_sqw_output_as_square_wave().unwrap();
//! rtc.enable_square_wave().unwrap();
//! # }
//! ```
//!
//! ### Enable the 32kHz output except when on battery power
@ -279,24 +350,30 @@
//! available for the devices DS3232 and DS3234.
//!
//! ```no_run
//! use ds323x::{Ds323x, SqWFreq};
//! use linux_embedded_hal::I2cdev;
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::{ Ds323x, SqWFreq };
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3232(dev);
//! rtc.disable_32khz_output_on_battery().unwrap(); // only available for DS3232 and DS3234
//! rtc.enable_32khz_output().unwrap();
//! # }
//! ```
//!
//! ### Set the aging offset
//!
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::Ds323x;
//! use linux_embedded_hal::I2cdev;
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! rtc.set_aging_offset(-15).unwrap();
//! # }
//! ```
//!
//! ### Set the temperature conversion rate to once every 128 seconds
@ -304,21 +381,26 @@
//! This is only available for the devices DS3232 and DS3234.
//!
//! ```no_run
//! use ds323x::{Ds323x, TempConvRate};
//! use linux_embedded_hal::I2cdev;
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::{ Ds323x, TempConvRate };
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3232(dev);
//! rtc.set_temperature_conversion_rate(TempConvRate::_128s).unwrap();
//! # }
//! ```
//!
//! ### Set the Alarm1 to each week on a week day at a specific time
//!
//! ```no_run
//! use ds323x::{Ds323x, Hours, WeekdayAlarm1, Alarm1Matching};
//! use linux_embedded_hal::I2cdev;
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::{ Ds323x, Hours, WeekdayAlarm1, Alarm1Matching };
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! let alarm1 = WeekdayAlarm1 {
//! weekday: 1,
@ -327,6 +409,7 @@
//! second: 15
//! };
//! rtc.set_alarm1_weekday(alarm1, Alarm1Matching::AllMatch).unwrap();
//! # }
//! ```
//!
//! ### Set the Alarm2 to each day at the same time and enable interrupts on output
@ -334,10 +417,12 @@
//! The INT/SQW output pin will be set to 1 when it the alarm matches.
//!
//! ```no_run
//! use ds323x::{Ds323x, Hours, DayAlarm2, Alarm2Matching};
//! use linux_embedded_hal::I2cdev;
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::{ Ds323x, Hours, DayAlarm2, Alarm2Matching };
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! let alarm2 = DayAlarm2 {
//! day: 1, // does not matter given the chosen matching
@ -347,33 +432,15 @@
//! rtc.set_alarm2_day(alarm2, Alarm2Matching::HoursAndMinutesMatch).unwrap();
//! rtc.use_int_sqw_output_as_interrupt().unwrap();
//! rtc.enable_alarm2_interrupts().unwrap();
//! ```
//!
//! ### Set the Alarm1 to a specific time
//!
//! ```no_run
//! use ds323x::{Ds323x, Hours, NaiveTime};
//! use linux_embedded_hal::I2cdev;
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! let time = NaiveTime::from_hms(19, 59, 58);
//! rtc.set_alarm1_hms(time).unwrap();
//! # }
//! ```
#![deny(unsafe_code, missing_docs)]
#![deny(unsafe_code, missing_docs, warnings)]
#![no_std]
extern crate embedded_hal as hal;
use core::marker::PhantomData;
use embedded_hal::spi::{Mode, MODE_1, MODE_3};
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;
/// SPI mode 3 (CPOL = 1, CPHA = 1)
pub const SPI_MODE_3: Mode = MODE_3;
/// All possible errors in this crate
#[derive(Debug)]
@ -381,12 +448,7 @@ pub enum Error<E> {
/// I²C/SPI bus error
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,
InvalidInputData
}
/// Square-wave output frequency
@ -420,50 +482,50 @@ pub enum TempConvRate {
struct Register;
impl Register {
const SECONDS: u8 = 0x00;
const MINUTES: u8 = 0x01;
const HOURS: u8 = 0x02;
const DOW: u8 = 0x03;
const DOM: u8 = 0x04;
const MONTH: u8 = 0x05;
const YEAR: u8 = 0x06;
const ALARM1_SECONDS: u8 = 0x07;
const ALARM2_MINUTES: u8 = 0x0B;
const CONTROL: u8 = 0x0E;
const STATUS: u8 = 0x0F;
const AGING_OFFSET: u8 = 0x10;
const TEMP_MSB: u8 = 0x11;
const TEMP_CONV: u8 = 0x13;
const SECONDS : u8 = 0x00;
const MINUTES : u8 = 0x01;
const HOURS : u8 = 0x02;
const DOW : u8 = 0x03;
const DOM : u8 = 0x04;
const MONTH : u8 = 0x05;
const YEAR : u8 = 0x06;
const ALARM1_SECONDS : u8 = 0x07;
const ALARM2_MINUTES : u8 = 0x0B;
const CONTROL : u8 = 0x0E;
const STATUS : u8 = 0x0F;
const AGING_OFFSET : u8 = 0x10;
const TEMP_MSB : u8 = 0x11;
const TEMP_CONV : u8 = 0x13;
}
struct BitFlags;
impl BitFlags {
const H24_H12: u8 = 0b0100_0000;
const AM_PM: u8 = 0b0010_0000;
const CENTURY: u8 = 0b1000_0000;
const EOSC: u8 = 0b1000_0000;
const BBSQW: u8 = 0b0100_0000;
const TEMP_CONV: u8 = 0b0010_0000;
const RS2: u8 = 0b0001_0000;
const RS1: u8 = 0b0000_1000;
const INTCN: u8 = 0b0000_0100;
const ALARM2_INT_EN: u8 = 0b0000_0010;
const ALARM1_INT_EN: u8 = 0b0000_0001;
const OSC_STOP: u8 = 0b1000_0000;
const BB32KHZ: u8 = 0b0100_0000;
const CRATE1: u8 = 0b0010_0000;
const CRATE0: u8 = 0b0001_0000;
const EN32KHZ: u8 = 0b0000_1000;
const BUSY: u8 = 0b0000_0100;
const ALARM2F: u8 = 0b0000_0010;
const ALARM1F: u8 = 0b0000_0001;
const TEMP_CONV_BAT: u8 = 0b0000_0001;
const ALARM_MATCH: u8 = 0b1000_0000;
const WEEKDAY: u8 = 0b0100_0000;
const H24_H12 : u8 = 0b0100_0000;
const AM_PM : u8 = 0b0010_0000;
const CENTURY : u8 = 0b1000_0000;
const EOSC : u8 = 0b1000_0000;
const BBSQW : u8 = 0b0100_0000;
const TEMP_CONV : u8 = 0b0010_0000;
const RS2 : u8 = 0b0001_0000;
const RS1 : u8 = 0b0000_1000;
const INTCN : u8 = 0b0000_0100;
const ALARM2_INT_EN : u8 = 0b0000_0010;
const ALARM1_INT_EN : u8 = 0b0000_0001;
const OSC_STOP : u8 = 0b1000_0000;
const BB32KHZ : u8 = 0b0100_0000;
const CRATE1 : u8 = 0b0010_0000;
const CRATE0 : u8 = 0b0001_0000;
const EN32KHZ : u8 = 0b0000_1000;
const BUSY : u8 = 0b0000_0100;
const ALARM2F : u8 = 0b0000_0010;
const ALARM1F : u8 = 0b0000_0001;
const TEMP_CONV_BAT : u8 = 0b0000_0001;
const ALARM_MATCH : u8 = 0b1000_0000;
const WEEKDAY : u8 = 0b0100_0000;
}
const DEVICE_ADDRESS: u8 = 0b110_1000;
const DEVICE_ADDRESS : u8 = 0b110_1000;
const CONTROL_POR_VALUE: u8 = 0b0001_1100;
/// IC markers
@ -481,27 +543,14 @@ pub mod ic {
pub struct Ds323x<DI, IC> {
iface: DI,
control: u8,
status: u8,
_ic: PhantomData<IC>,
status : u8,
_ic: PhantomData<IC>
}
mod ds323x;
pub mod interface;
pub use crate::ds323x::{
Alarm1Matching, Alarm2Matching, DayAlarm1, DayAlarm2, WeekdayAlarm1, WeekdayAlarm2,
};
mod ds323x;
pub use ds323x::{ Hours, DateTime, DayAlarm1, WeekdayAlarm1, Alarm1Matching,
DayAlarm2, WeekdayAlarm2, Alarm2Matching };
mod ds3231;
mod ds3232;
mod ds3234;
mod private {
use super::{ic, interface};
pub trait Sealed {}
impl<SPI> Sealed for interface::SpiInterface<SPI> {}
impl<I2C> Sealed for interface::I2cInterface<I2C> {}
impl Sealed for ic::DS3231 {}
impl Sealed for ic::DS3232 {}
impl Sealed for ic::DS3234 {}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,12 @@
use ds323x::{ic, interface, Ds323x};
use embedded_hal_mock::eh1::{
i2c::{Mock as I2cMock, Transaction as I2cTrans},
spi::{Mock as SpiMock, Transaction as SpiTrans},
};
extern crate embedded_hal;
extern crate ds323x;
use self::ds323x::{ Ds323x, interface, ic };
extern crate embedded_hal_mock as hal;
use self::hal::i2c::{ Mock as I2cMock, Transaction as I2cTrans };
use self::hal::spi::{ Mock as SpiMock, Transaction as SpiTrans };
#[allow(unused)]
pub const DEVICE_ADDRESS: u8 = 0b110_1000;
pub const DEVICE_ADDRESS : u8 = 0b110_1000;
#[allow(unused)]
pub const CONTROL_POR_VALUE: u8 = 0b0001_1100;
#[allow(unused)]
@ -17,77 +18,65 @@ pub struct Register;
#[allow(unused)]
impl Register {
pub const SECONDS: u8 = 0x00;
pub const MINUTES: u8 = 0x01;
pub const HOURS: u8 = 0x02;
pub const DOW: u8 = 0x03;
pub const DOM: u8 = 0x04;
pub const MONTH: u8 = 0x05;
pub const ALARM1_SECONDS: u8 = 0x07;
pub const ALARM2_MINUTES: u8 = 0x0B;
pub const CONTROL: u8 = 0x0E;
pub const STATUS: u8 = 0x0F;
pub const AGING_OFFSET: u8 = 0x10;
pub const TEMP_MSB: u8 = 0x11;
pub const TEMP_CONV: u8 = 0x13;
pub const SECONDS : u8 = 0x00;
pub const MINUTES : u8 = 0x01;
pub const HOURS : u8 = 0x02;
pub const DOW : u8 = 0x03;
pub const DOM : u8 = 0x04;
pub const MONTH : u8 = 0x05;
pub const ALARM1_SECONDS : u8 = 0x07;
pub const ALARM2_MINUTES : u8 = 0x0B;
pub const CONTROL : u8 = 0x0E;
pub const STATUS : u8 = 0x0F;
pub const AGING_OFFSET : u8 = 0x10;
pub const TEMP_MSB : u8 = 0x11;
pub const TEMP_CONV : u8 = 0x13;
}
pub struct BitFlags;
#[allow(unused)]
impl BitFlags {
pub const EOSC: u8 = 0b1000_0000;
pub const BBSQW: u8 = 0b0100_0000;
pub const TEMP_CONV: u8 = 0b0010_0000;
pub const RS2: u8 = 0b0001_0000;
pub const RS1: u8 = 0b0000_1000;
pub const INTCN: u8 = 0b0000_0100;
pub const ALARM2_INT_EN: u8 = 0b0000_0010;
pub const ALARM1_INT_EN: u8 = 0b0000_0001;
pub const OSC_STOP: u8 = 0b1000_0000;
pub const BB32KHZ: u8 = 0b0100_0000;
pub const CRATE1: u8 = 0b0010_0000;
pub const CRATE0: u8 = 0b0001_0000;
pub const EN32KHZ: u8 = 0b0000_1000;
pub const BUSY: u8 = 0b0000_0100;
pub const ALARM2F: u8 = 0b0000_0010;
pub const ALARM1F: u8 = 0b0000_0001;
pub const TEMP_CONV_BAT: u8 = 0b0000_0001;
pub const ALARM_MATCH: u8 = 0b1000_0000;
pub const WEEKDAY: u8 = 0b0100_0000;
pub const EOSC : u8 = 0b1000_0000;
pub const BBSQW : u8 = 0b0100_0000;
pub const TEMP_CONV : u8 = 0b0010_0000;
pub const RS2 : u8 = 0b0001_0000;
pub const RS1 : u8 = 0b0000_1000;
pub const INTCN : u8 = 0b0000_0100;
pub const ALARM2_INT_EN : u8 = 0b0000_0010;
pub const ALARM1_INT_EN : u8 = 0b0000_0001;
pub const OSC_STOP : u8 = 0b1000_0000;
pub const BB32KHZ : u8 = 0b0100_0000;
pub const CRATE1 : u8 = 0b0010_0000;
pub const CRATE0 : u8 = 0b0001_0000;
pub const EN32KHZ : u8 = 0b0000_1000;
pub const BUSY : u8 = 0b0000_0100;
pub const ALARM2F : u8 = 0b0000_0010;
pub const ALARM1F : u8 = 0b0000_0001;
pub const TEMP_CONV_BAT : u8 = 0b0000_0001;
pub const ALARM_MATCH : u8 = 0b1000_0000;
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(())
}
fn set_low(&mut self) {}
fn set_high(&mut self) {}
}
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> {
Ds323x::new_ds3231(I2cMock::new(&transactions))
}
pub fn new_ds3231(
transactions: &[I2cTrans],
) -> Ds323x<interface::I2cInterface<I2cMock>, ic::DS3231> {
Ds323x::new_ds3231(I2cMock::new(transactions))
pub fn new_ds3232(transactions: &[I2cTrans]) -> Ds323x<interface::I2cInterface<I2cMock>, ic::DS3232> {
Ds323x::new_ds3232(I2cMock::new(&transactions))
}
pub fn new_ds3232(
transactions: &[I2cTrans],
) -> Ds323x<interface::I2cInterface<I2cMock>, ic::DS3232> {
Ds323x::new_ds3232(I2cMock::new(transactions))
}
pub fn new_ds3234(
transactions: &[SpiTrans<u8>],
) -> Ds323x<interface::SpiInterface<SpiMock<u8>>, ic::DS3234> {
Ds323x::new_ds3234(SpiMock::new(transactions))
pub fn new_ds3234(transactions: &[SpiTrans])
-> Ds323x<interface::SpiInterface<SpiMock, DummyOutputPin>, ic::DS3234> {
Ds323x::new_ds3234(SpiMock::new(&transactions), DummyOutputPin)
}
pub fn destroy_ds3231(dev: Ds323x<interface::I2cInterface<I2cMock>, ic::DS3231>) {
@ -98,8 +87,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<u8>>, ic::DS3234>) {
dev.destroy_ds3234().done();
pub fn destroy_ds3234(dev: Ds323x<interface::SpiInterface<SpiMock, DummyOutputPin>, ic::DS3234>) {
dev.destroy_ds3234().0.done();
}
#[macro_export]
@ -133,7 +122,7 @@ macro_rules! assert_invalid_input_data {
($result:expr) => {
match $result {
Err(Error::InvalidInputData) => (),
_ => panic!("InvalidInputData error not returned."),
_ => panic!("InvalidInputData error not returned.")
}
};
}
@ -168,30 +157,9 @@ macro_rules! _get_param_test {
($name:ident, $method:ident, $value:expr, $i2c_transactions:expr, $spi_transactions:expr) => {
mod $name {
use super::*;
get_test!(
can_get_ds3231,
$method,
new_ds3231,
destroy_ds3231,
$value,
$i2c_transactions
);
get_test!(
can_get_ds3232,
$method,
new_ds3232,
destroy_ds3232,
$value,
$i2c_transactions
);
get_test!(
can_get_ds3234,
$method,
new_ds3234,
destroy_ds3234,
$value,
$spi_transactions
);
get_test!(can_get_ds3231, $method, new_ds3231, destroy_ds3231, $value, $i2c_transactions);
get_test!(can_get_ds3232, $method, new_ds3232, destroy_ds3232, $value, $i2c_transactions);
get_test!(can_get_ds3234, $method, new_ds3234, destroy_ds3234, $value, $spi_transactions);
}
};
}
@ -199,50 +167,18 @@ macro_rules! _get_param_test {
#[macro_export]
macro_rules! get_param_test {
($name:ident, $method:ident, $register:ident, $value:expr, $binary_value:expr) => {
_get_param_test!(
$name,
$method,
$value,
[I2cTrans::write_read(
DEV_ADDR,
vec![Register::$register],
vec![$binary_value]
)],
[
SpiTrans::transaction_start(),
SpiTrans::transfer_in_place(
vec![Register::$register, 0],
vec![Register::$register, $binary_value]
),
SpiTrans::transaction_end(),
]
);
_get_param_test!($name, $method, $value,
[ I2cTrans::write_read(DEV_ADDR, vec![Register::$register], vec![$binary_value]) ],
[ SpiTrans::transfer(vec![Register::$register, 0], vec![Register::$register, $binary_value]) ]);
};
}
#[macro_export]
macro_rules! transactions_i2c_read {
($register1:ident, [ $( $read_bin:expr ),+ ], [ $( $read_bin2:expr ),* ]) => {
[ I2cTrans::write_read(DEV_ADDR, vec![Register::$register1], vec![$( $read_bin ),*]) ]
}
}
#[macro_export]
macro_rules! transactions_spi_read {
($register1:ident, [ $( $read_bin:expr ),+ ], [ $( $read_bin2:expr ),+ ]) => {
[SpiTrans::transaction_start(),
SpiTrans::transfer_in_place(vec![Register::$register1, $( $read_bin2 ),*], vec![Register::$register1, $( $read_bin ),*]),
SpiTrans::transaction_end()
]
}
}
#[macro_export]
macro_rules! get_param_read_array_test {
($name:ident, $method:ident, $value:expr, $register1:ident, [ $( $read_bin:expr ),+ ], [ $( $read_bin2:expr ),+ ]) => {
_get_param_test!($name, $method, $value,
transactions_i2c_read!($register1, [ $( $read_bin ),* ], [ ]),
transactions_spi_read!($register1, [ $( $read_bin ),* ], [ $( $read_bin2 ),* ]) );
[ I2cTrans::write_read(DEV_ADDR, vec![Register::$register1], vec![$( $read_bin ),*]) ],
[ SpiTrans::transfer(vec![Register::$register1, $( $read_bin2 ),*], vec![Register::$register1, $( $read_bin ),*]) ]);
};
}
@ -251,30 +187,9 @@ macro_rules! _set_param_test {
($name:ident, $method:ident, $value:expr, $i2c_transactions:expr, $spi_transactions:expr) => {
mod $name {
use super::*;
set_test!(
can_set_ds3231,
$method,
new_ds3231,
destroy_ds3231,
$value,
$i2c_transactions
);
set_test!(
can_set_ds3232,
$method,
new_ds3232,
destroy_ds3232,
$value,
$i2c_transactions
);
set_test!(
can_set_ds3234,
$method,
new_ds3234,
destroy_ds3234,
$value,
$spi_transactions
);
set_test!(can_set_ds3231, $method, new_ds3231, destroy_ds3231, $value, $i2c_transactions);
set_test!(can_set_ds3232, $method, new_ds3232, destroy_ds3232, $value, $i2c_transactions);
set_test!(can_set_ds3234, $method, new_ds3234, destroy_ds3234, $value, $spi_transactions);
}
};
}
@ -282,19 +197,8 @@ macro_rules! _set_param_test {
#[macro_export]
macro_rules! set_param_test {
($name:ident, $method:ident, $register:ident, $value:expr, $binary_value:expr) => {
_set_param_test!(
$name,
$method,
$value,
[I2cTrans::write(
DEV_ADDR,
vec![Register::$register, $binary_value]
)],
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::$register + 0x80, $binary_value]),
SpiTrans::transaction_end(),
]
);
_set_param_test!($name, $method, $value,
[ I2cTrans::write(DEV_ADDR, vec![Register::$register, $binary_value]) ],
[ SpiTrans::write(vec![Register::$register + 0x80, $binary_value]) ]);
};
}

View File

@ -1,57 +1,32 @@
extern crate embedded_hal_mock as hal;
use hal::i2c::Transaction as I2cTrans;
use hal::spi::Transaction as SpiTrans;
extern crate ds323x;
use ds323x::SqWFreq;
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,
BitFlags as BF, Register, CONTROL_POR_VALUE, DEVICE_ADDRESS as DEV_ADDR, DS3231_POR_STATUS,
DS323X_POR_STATUS,
};
use common::{ DEVICE_ADDRESS as DEV_ADDR, Register, new_ds3231,
new_ds3232, new_ds3234, destroy_ds3231, destroy_ds3232,
destroy_ds3234, BitFlags as BF, CONTROL_POR_VALUE,
DS3231_POR_STATUS, DS323X_POR_STATUS };
macro_rules! call_triple_test {
($name:ident, $method:ident, $i2c_transactions:expr, $spi_transactions:expr) => {
mod $name {
use super::*;
call_test!(
can_call_ds3231,
$method,
new_ds3231,
destroy_ds3231,
$i2c_transactions
);
call_test!(
can_call_ds3232,
$method,
new_ds3232,
destroy_ds3232,
$i2c_transactions
);
call_test!(
can_call_ds3234,
$method,
new_ds3234,
destroy_ds3234,
$spi_transactions
);
call_test!(can_call_ds3231, $method, new_ds3231, destroy_ds3231, $i2c_transactions);
call_test!(can_call_ds3232, $method, new_ds3232, destroy_ds3232, $i2c_transactions);
call_test!(can_call_ds3234, $method, new_ds3234, destroy_ds3234, $spi_transactions);
}
};
}
macro_rules! call_method_test {
($name:ident, $method:ident, $register:ident, $value_enabled:expr) => {
call_triple_test!(
$name,
$method,
[I2cTrans::write(
DEV_ADDR,
vec![Register::$register, $value_enabled]
)],
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::$register + 0x80, $value_enabled]),
SpiTrans::transaction_end(),
]
);
call_triple_test!($name, $method,
[ I2cTrans::write(DEV_ADDR, vec![Register::$register, $value_enabled]) ],
[ SpiTrans::write(vec![Register::$register + 0x80, $value_enabled]) ]);
};
}
@ -59,37 +34,12 @@ macro_rules! call_method_status_test {
($name:ident, $method:ident, $value_ds3231:expr, $value_ds323x:expr) => {
mod $name {
use super::*;
call_test!(
can_call_ds3231,
$method,
new_ds3231,
destroy_ds3231,
[I2cTrans::write(
DEV_ADDR,
vec![Register::STATUS, $value_ds3231]
)]
);
call_test!(
can_call_ds3232,
$method,
new_ds3232,
destroy_ds3232,
[I2cTrans::write(
DEV_ADDR,
vec![Register::STATUS, $value_ds323x]
)]
);
call_test!(
can_call_ds3234,
$method,
new_ds3234,
destroy_ds3234,
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::STATUS + 0x80, $value_ds323x]),
SpiTrans::transaction_end(),
]
);
call_test!(can_call_ds3231, $method, new_ds3231, destroy_ds3231,
[ I2cTrans::write(DEV_ADDR, vec![Register::STATUS, $value_ds3231]) ]);
call_test!(can_call_ds3232, $method, new_ds3232, destroy_ds3232,
[ I2cTrans::write(DEV_ADDR, vec![Register::STATUS, $value_ds323x]) ]);
call_test!(can_call_ds3234, $method, new_ds3234, destroy_ds3234,
[ SpiTrans::write(vec![Register::STATUS + 0x80, $value_ds323x]) ]);
}
};
}
@ -98,196 +48,63 @@ macro_rules! change_if_necessary_test {
($name:ident, $method:ident, $register:ident, $value_enabled:expr, $value_disabled:expr) => {
mod $name {
use super::*;
call_triple_test!(
do_nothing_if_not_necessary,
$method,
[I2cTrans::write_read(
DEV_ADDR,
vec![Register::$register],
vec![$value_enabled]
)],
[
SpiTrans::transaction_start(),
SpiTrans::transfer_in_place(
vec![Register::$register, 0],
vec![Register::$register, $value_enabled]
),
SpiTrans::transaction_end(),
]
);
call_triple_test!(do_nothing_if_not_necessary, $method,
[ I2cTrans::write_read(DEV_ADDR, vec![Register::$register], vec![$value_enabled]) ],
[ SpiTrans::transfer(vec![Register::$register, 0], vec![Register::$register, $value_enabled]) ]);
call_triple_test!(
change,
$method,
[
I2cTrans::write_read(
DEV_ADDR,
vec![Register::$register],
vec![$value_disabled]
),
I2cTrans::write(DEV_ADDR, vec![Register::$register, $value_enabled])
],
[
SpiTrans::transaction_start(),
SpiTrans::transfer_in_place(
vec![Register::$register, 0],
vec![Register::$register, $value_disabled]
),
SpiTrans::transaction_end(),
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::$register + 0x80, $value_enabled]),
SpiTrans::transaction_end(),
]
);
call_triple_test!(change, $method,
[ I2cTrans::write_read(DEV_ADDR, vec![Register::$register], vec![$value_disabled]),
I2cTrans::write(DEV_ADDR, vec![Register::$register, $value_enabled]) ],
[ SpiTrans::transfer(vec![Register::$register, 0], vec![Register::$register, $value_disabled]),
SpiTrans::write(vec![Register::$register + 0x80, $value_enabled]) ]);
}
};
}
call_method_test!(enable, enable, CONTROL, CONTROL_POR_VALUE & !BF::EOSC);
call_method_test!(disable, disable, CONTROL, CONTROL_POR_VALUE | BF::EOSC);
call_method_status_test!(
en_32khz_out,
enable_32khz_output,
DS3231_POR_STATUS | BF::EN32KHZ | BF::ALARM2F | BF::ALARM1F,
DS323X_POR_STATUS | BF::EN32KHZ | BF::ALARM2F | BF::ALARM1F
);
call_method_status_test!(
dis_32khz_out,
disable_32khz_output,
call_method_test!(enable, enable, CONTROL, CONTROL_POR_VALUE & !BF::EOSC);
call_method_test!(disable, disable, CONTROL, CONTROL_POR_VALUE | BF::EOSC);
call_method_status_test!(en_32khz_out, enable_32khz_output,
DS3231_POR_STATUS | BF::EN32KHZ | BF::ALARM2F | BF::ALARM1F,
DS323X_POR_STATUS | BF::EN32KHZ | BF::ALARM2F | BF::ALARM1F);
call_method_status_test!(dis_32khz_out, disable_32khz_output,
DS3231_POR_STATUS & !BF::EN32KHZ | BF::ALARM2F | BF::ALARM1F,
DS323X_POR_STATUS & !BF::EN32KHZ | BF::ALARM2F | BF::ALARM1F
);
DS323X_POR_STATUS & !BF::EN32KHZ | BF::ALARM2F | BF::ALARM1F);
call_method_status_test!(
clear_alarm1_matched,
clear_alarm1_matched_flag,
call_method_status_test!(clear_alarm1_matched, clear_alarm1_matched_flag,
DS3231_POR_STATUS | BF::ALARM2F,
DS323X_POR_STATUS | BF::ALARM2F
);
DS323X_POR_STATUS | BF::ALARM2F);
call_method_status_test!(
clear_alarm2_matched,
clear_alarm2_matched_flag,
call_method_status_test!(clear_alarm2_matched, clear_alarm2_matched_flag,
DS3231_POR_STATUS | BF::ALARM1F,
DS323X_POR_STATUS | BF::ALARM1F
);
DS323X_POR_STATUS | BF::ALARM1F);
call_method_status_test!(
clr_stop,
clear_has_been_stopped_flag,
call_method_status_test!(clr_stop, clear_has_been_stopped_flag,
DS3231_POR_STATUS & !BF::OSC_STOP | BF::ALARM2F | BF::ALARM1F,
DS323X_POR_STATUS & !BF::OSC_STOP | BF::ALARM2F | BF::ALARM1F
);
DS323X_POR_STATUS & !BF::OSC_STOP | BF::ALARM2F | BF::ALARM1F);
change_if_necessary_test!(
conv_temp,
convert_temperature,
CONTROL,
CONTROL_POR_VALUE | BF::TEMP_CONV,
CONTROL_POR_VALUE & !BF::TEMP_CONV
);
change_if_necessary_test!(conv_temp, convert_temperature, CONTROL, CONTROL_POR_VALUE | BF::TEMP_CONV, CONTROL_POR_VALUE & !BF::TEMP_CONV);
call_method_test!(
en_al1_int,
enable_alarm1_interrupts,
CONTROL,
CONTROL_POR_VALUE | BF::ALARM1_INT_EN
);
call_method_test!(
dis_al1_int,
disable_alarm1_interrupts,
CONTROL,
CONTROL_POR_VALUE & !BF::ALARM1_INT_EN
);
call_method_test!(en_al1_int, enable_alarm1_interrupts, CONTROL, CONTROL_POR_VALUE | BF::ALARM1_INT_EN);
call_method_test!(dis_al1_int, disable_alarm1_interrupts, CONTROL, CONTROL_POR_VALUE & !BF::ALARM1_INT_EN);
call_method_test!(
en_al2_int,
enable_alarm2_interrupts,
CONTROL,
CONTROL_POR_VALUE | BF::ALARM2_INT_EN
);
call_method_test!(
dis_al2_int,
disable_alarm2_interrupts,
CONTROL,
CONTROL_POR_VALUE & !BF::ALARM2_INT_EN
);
call_method_test!(en_al2_int, enable_alarm2_interrupts, CONTROL, CONTROL_POR_VALUE | BF::ALARM2_INT_EN);
call_method_test!(dis_al2_int, disable_alarm2_interrupts, CONTROL, CONTROL_POR_VALUE & !BF::ALARM2_INT_EN);
set_param_test!(
set_aging_offset_min,
set_aging_offset,
AGING_OFFSET,
-128,
0b1000_0000
);
set_param_test!(
set_aging_offset_max,
set_aging_offset,
AGING_OFFSET,
127,
127
);
set_param_test!(set_aging_offset_min, set_aging_offset, AGING_OFFSET, -128, 0b1000_0000);
set_param_test!(set_aging_offset_max, set_aging_offset, AGING_OFFSET, 127, 127);
get_param_test!(
get_aging_offset_min,
aging_offset,
AGING_OFFSET,
-128,
0b1000_0000
);
get_param_test!(get_aging_offset_max, aging_offset, AGING_OFFSET, 127, 127);
get_param_test!(get_aging_offset_min, get_aging_offset, AGING_OFFSET, -128, 0b1000_0000);
get_param_test!(get_aging_offset_max, get_aging_offset, AGING_OFFSET, 127, 127);
call_method_test!(
int_sqw_out_int,
use_int_sqw_output_as_interrupt,
CONTROL,
CONTROL_POR_VALUE | BF::INTCN
);
call_method_test!(
int_sqw_out_sqw,
use_int_sqw_output_as_square_wave,
CONTROL,
CONTROL_POR_VALUE & !BF::INTCN
);
call_method_test!(int_sqw_out_int, use_int_sqw_output_as_interrupt, CONTROL, CONTROL_POR_VALUE | BF::INTCN);
call_method_test!(int_sqw_out_sqw, use_int_sqw_output_as_square_wave, CONTROL, CONTROL_POR_VALUE & !BF::INTCN);
call_method_test!(
enable_sqw,
enable_square_wave,
CONTROL,
CONTROL_POR_VALUE | BF::BBSQW
);
call_method_test!(
disable_sqw,
disable_square_wave,
CONTROL,
CONTROL_POR_VALUE & !BF::BBSQW
);
call_method_test!(enable_sqw, enable_square_wave, CONTROL, CONTROL_POR_VALUE | BF::BBSQW);
call_method_test!(disable_sqw, disable_square_wave, CONTROL, CONTROL_POR_VALUE & !BF::BBSQW);
set_param_test!(set_sqw_freq_1, set_square_wave_frequency, CONTROL, SqWFreq::_1Hz, CONTROL_POR_VALUE & !BF::RS2 & !BF::RS1);
set_param_test!(set_sqw_freq_1_024, set_square_wave_frequency, CONTROL, SqWFreq::_1_024Hz, CONTROL_POR_VALUE & !BF::RS2 | BF::RS1);
set_param_test!(set_sqw_freq_4_096, set_square_wave_frequency, CONTROL, SqWFreq::_4_096Hz, CONTROL_POR_VALUE | BF::RS2 & !BF::RS1);
set_param_test!(set_sqw_freq_8_192, set_square_wave_frequency, CONTROL, SqWFreq::_8_192Hz, CONTROL_POR_VALUE | BF::RS2 | BF::RS1);
set_param_test!(
set_sqw_freq_1,
set_square_wave_frequency,
CONTROL,
SqWFreq::_1Hz,
CONTROL_POR_VALUE & !BF::RS2 & !BF::RS1
);
set_param_test!(
set_sqw_freq_1_024,
set_square_wave_frequency,
CONTROL,
SqWFreq::_1_024Hz,
CONTROL_POR_VALUE & !BF::RS2 | BF::RS1
);
set_param_test!(
set_sqw_freq_4_096,
set_square_wave_frequency,
CONTROL,
SqWFreq::_4_096Hz,
CONTROL_POR_VALUE | BF::RS2 & !BF::RS1
);
set_param_test!(
set_sqw_freq_8_192,
set_square_wave_frequency,
CONTROL,
SqWFreq::_8_192Hz,
CONTROL_POR_VALUE | BF::RS2 | BF::RS1
);

View File

@ -1,7 +1,6 @@
mod common;
use self::common::{
destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234,
};
use common::{ new_ds3231, destroy_ds3231, new_ds3232, destroy_ds3232,
new_ds3234, destroy_ds3234 };
macro_rules! construction_test {
($name:ident, $create:ident, $destroy:ident) => {

View File

@ -1,80 +1,40 @@
use embedded_hal_mock::eh1::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
use rtcc::NaiveDateTime;
extern crate embedded_hal_mock as hal;
use hal::i2c::Transaction as I2cTrans;
use hal::spi::Transaction as SpiTrans;
mod common;
use self::common::{
destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234, Register,
DEVICE_ADDRESS as DEV_ADDR,
};
#[allow(unused)] // Rust 1.31.0 is confused due to the macros
use ds323x::Rtcc;
use ds323x::{DateTimeAccess, Error, Hours, NaiveDate, NaiveTime};
use common::{ DEVICE_ADDRESS as DEV_ADDR, Register, new_ds3231,
new_ds3232, new_ds3234, destroy_ds3231, destroy_ds3232,
destroy_ds3234 };
extern crate ds323x;
use ds323x::{ Hours, DateTime, Error };
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! set_param_write_array_test {
($name:ident, $method:ident, $value:expr, $register:ident, [ $( $exp_bin:expr ),+ ] ) => {
_set_param_test!($name, $method, $value,
[ I2cTrans::write(DEV_ADDR, vec![Register::$register, $( $exp_bin ),*]) ],
[ SpiTrans::write(vec![Register::$register + 0x80, $( $exp_bin ),*]) ]);
};
}
macro_rules! read_set_param_write_two_test {
($name:ident, $method:ident, $value:expr, $register:ident, $binary_value1_read:expr, $bin1:expr, $bin2:expr) => {
_set_param_test!(
$name,
$method,
$value,
[
I2cTrans::write_read(
DEV_ADDR,
vec![Register::$register],
vec![$binary_value1_read]
),
I2cTrans::write(DEV_ADDR, vec![Register::$register, $bin1, $bin2])
],
[
SpiTrans::transaction_start(),
SpiTrans::transfer_in_place(
vec![Register::$register, 0],
vec![Register::$register, $binary_value1_read]
),
SpiTrans::transaction_end(),
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::$register + 0x80, $bin1, $bin2]),
SpiTrans::transaction_end(),
]
);
_set_param_test!($name, $method, $value,
[ I2cTrans::write_read(DEV_ADDR, vec![Register::$register], vec![$binary_value1_read]),
I2cTrans::write(DEV_ADDR, vec![Register::$register, $bin1, $bin2]) ],
[ SpiTrans::transfer(vec![Register::$register, 0], vec![Register::$register, $binary_value1_read]),
SpiTrans::write(vec![Register::$register + 0x80, $bin1, $bin2]) ]);
};
}
macro_rules! read_set_param_test {
($name:ident, $method:ident, $register:ident, $value:expr, $binary_value_read:expr, $binary_value_write:expr) => {
_set_param_test!(
$name,
$method,
$value,
[
I2cTrans::write_read(
DEV_ADDR,
vec![Register::$register],
vec![$binary_value_read]
),
I2cTrans::write(DEV_ADDR, vec![Register::$register, $binary_value_write])
],
[
SpiTrans::transaction_start(),
SpiTrans::transfer_in_place(
vec![Register::$register, 0],
vec![Register::$register, $binary_value_read]
),
SpiTrans::transaction_end(),
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::$register + 0x80, $binary_value_write]),
SpiTrans::transaction_end(),
]
);
_set_param_test!($name, $method, $value,
[ I2cTrans::write_read(DEV_ADDR, vec![Register::$register], vec![$binary_value_read]),
I2cTrans::write(DEV_ADDR, vec![Register::$register, $binary_value_write]) ],
[ SpiTrans::transfer(vec![Register::$register, 0], vec![Register::$register, $binary_value_read]),
SpiTrans::write(vec![Register::$register + 0x80, $binary_value_write]) ]);
};
}
@ -82,27 +42,9 @@ macro_rules! set_invalid_param_test {
($name:ident, $method:ident, $value:expr) => {
mod $name {
use super::*;
set_invalid_test!(
cannot_set_invalid_ds3231,
$method,
new_ds3231,
destroy_ds3231,
$value
);
set_invalid_test!(
cannot_set_invalid_ds3232,
$method,
new_ds3232,
destroy_ds3232,
$value
);
set_invalid_test!(
cannot_set_invalid_ds3234,
$method,
new_ds3234,
destroy_ds3234,
$value
);
set_invalid_test!(cannot_set_invalid_ds3231, $method, new_ds3231, destroy_ds3231, $value);
set_invalid_test!(cannot_set_invalid_ds3232, $method, new_ds3232, destroy_ds3232, $value);
set_invalid_test!(cannot_set_invalid_ds3234, $method, new_ds3234, destroy_ds3234, $value);
}
};
}
@ -112,294 +54,118 @@ macro_rules! set_invalid_param_range_test {
mod $name {
use super::*;
set_invalid_param_test!(too_small, $method, $too_small_value);
set_invalid_param_test!(too_big, $method, $too_big_value);
set_invalid_param_test!(too_big, $method, $too_big_value);
}
};
}
macro_rules! for_all {
($name:ident) => {
mod $name {
use super::*;
$name!(for_ds3231, new_ds3231, destroy_ds3231);
$name!(for_ds3232, new_ds3232, destroy_ds3232);
$name!(for_ds3234, new_ds3234, destroy_ds3234);
}
};
}
// TODO set/get date
// TODO set/get time
mod seconds {
use super::*;
get_param_test!(get, seconds, SECONDS, 1, 1);
get_param_test!(get, 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, minutes, MINUTES, 1, 1);
get_param_test!(get, 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, hours, HOURS, Hours::H24(21), 0b0010_0001);
get_param_test!(get, 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, hours, HOURS, Hours::AM(12), 0b0101_0010);
get_param_test!(get, 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, hours, HOURS, Hours::PM(12), 0b0111_0010);
get_param_test!(get, 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, weekday, DOW, 1, 1);
get_param_test!(get, 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, day, DOM, 1, 1);
get_param_test!(get, get_day, DOM, 1, 1);
set_param_test!(set, set_day, DOM, 1, 1);
set_invalid_param_range_test!(invalid, set_day, 0, 32);
set_invalid_param_range_test!(invalid, set_day, 0, 8);
}
mod month {
use super::*;
get_param_test!(get, month, MONTH, 1, 1);
get_param_test!(get, 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);
get_param_test!(get, 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, year, 2099, MONTH, [0, 0b1001_1001], [0, 0]);
read_set_param_write_two_test!(
century0_set,
set_year,
2099,
MONTH,
0b1001_0010,
0b0001_0010,
0b1001_1001
);
get_param_read_array_test!(century0_get, get_year, 2099, MONTH, [ 0, 0b1001_1001 ], [0, 0]);
read_set_param_write_two_test!(century0_set, set_year, 2099, MONTH, 0b1001_0010, 0b0001_0010, 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
);
get_param_read_array_test!(century1_get, 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);
}
macro_rules! invalid_dt_test {
($name:ident, $create_method:ident, $destroy_method:ident) => {
($name:ident, $year:expr, $month:expr, $day:expr, $weekday:expr,
$hour:expr, $minute:expr, $second:expr) => {
mod $name {
use super::*;
#[test]
fn datetime_too_small() {
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 = 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 = 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 = new_date(2101, 1, 2);
let mut dev = $create_method(&[]);
assert_invalid_input_data!(dev.set_date(&d));
$destroy_method(dev);
}
}
};
}
macro_rules! transactions_i2c_write {
($register:ident, [ $( $exp_bin:expr ),+ ]) => {
[ I2cTrans::write(DEV_ADDR, vec![Register::$register, $( $exp_bin ),*]) ]
};
}
macro_rules! transactions_spi_write {
($register:ident, [ $( $exp_bin:expr ),+ ]) => {
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::$register + 0x80, $( $exp_bin ),*]),
SpiTrans::transaction_end()
]
};
}
macro_rules! dt_test {
($name:ident, $create_method:ident, $destroy_method:ident,
$mac_trans_read:ident, $mac_trans_write:ident) => {
mod $name {
use super::*;
#[test]
fn get_datetime() {
let dt = new_datetime(2018, 8, 13, 23, 59, 58);
let mut dev = $create_method(&$mac_trans_read!(
SECONDS,
[
0b0101_1000,
0b0101_1001,
0b0010_0011,
0b0000_0010,
0b0001_0011,
0b0000_1000,
0b0001_1000
],
[0, 0, 0, 0, 0, 0, 0]
));
assert_eq!(dt, dev.datetime().unwrap());
$destroy_method(dev);
}
#[test]
fn set_datetime() {
let dt = new_datetime(2018, 8, 13, 23, 59, 58);
let mut dev = $create_method(&$mac_trans_write!(
SECONDS,
[
0b0101_1000,
0b0101_1001,
0b0010_0011,
0b0000_0010,
0b0001_0011,
0b0000_1000,
0b0001_1000
]
));
dev.set_datetime(&dt).unwrap();
$destroy_method(dev);
}
#[test]
fn get_date() {
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.date().unwrap());
$destroy_method(dev);
}
#[test]
fn set_date() {
let d = new_date(2018, 8, 13);
let mut dev = $create_method(&$mac_trans_write!(
DOW,
[0b0000_0010, 0b0001_0011, 0b0000_1000, 0b0001_1000]
));
dev.set_date(&d).unwrap();
$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();
let mut dev = $create_method(&$mac_trans_read!(
SECONDS,
[0b0101_1000, 0b0101_1001, 0b0010_0011],
[0, 0, 0]
));
assert_eq!(t, dev.time().unwrap());
$destroy_method(dev);
}
#[test]
fn set_time() {
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]
));
dev.set_time(&t).unwrap();
$destroy_method(dev);
}
const DT : DateTime = DateTime { year: $year, month: $month, day: $day, weekday: $weekday,
hour: $hour, minute: $minute, second: $second };
set_invalid_param_test!($name, set_datetime, &DT);
}
};
}
mod datetime {
use super::*;
const DT : DateTime = DateTime { year: 2018, month: 8, day: 13, weekday: 2,
hour: Hours::H24(23), minute: 59, second: 58 };
get_param_read_array_test!(get, get_datetime, DT, SECONDS,
[0b0101_1000, 0b0101_1001, 0b0010_0011, 0b0000_0010,
0b0001_0011, 0b0000_1000, 0b0001_1000],
[0, 0, 0, 0, 0, 0, 0]);
dt_test!(
for_ds3231,
new_ds3231,
destroy_ds3231,
transactions_i2c_read,
transactions_i2c_write
);
dt_test!(
for_ds3232,
new_ds3232,
destroy_ds3232,
transactions_i2c_read,
transactions_i2c_write
);
dt_test!(
for_ds3234,
new_ds3234,
destroy_ds3234,
transactions_spi_read,
transactions_spi_write
);
set_param_write_array_test!(set, set_datetime, &DT, SECONDS,
[0b0101_1000, 0b0101_1001, 0b0010_0011, 0b0000_0010,
0b0001_0011, 0b0000_1000, 0b0001_1000]);
for_all!(invalid_dt_test);
invalid_dt_test!(too_small_year, 1999, 8, 13, 2, Hours::H24(23), 59, 58);
invalid_dt_test!(too_big_year, 2101, 8, 13, 2, Hours::H24(23), 59, 58);
invalid_dt_test!(too_small_month, 2018, 0, 13, 2, Hours::H24(23), 59, 58);
invalid_dt_test!(too_big_month, 2018, 13, 13, 2, Hours::H24(23), 59, 58);
invalid_dt_test!(too_small_day, 2018, 8, 0, 2, Hours::H24(23), 59, 58);
invalid_dt_test!(too_big_day, 2018, 8, 32, 2, Hours::H24(23), 59, 58);
invalid_dt_test!(too_small_wd, 2018, 8, 13, 0, Hours::H24(23), 59, 58);
invalid_dt_test!(too_big_wd, 2018, 8, 13, 8, Hours::H24(23), 59, 58);
invalid_dt_test!(too_big_hours, 2018, 8, 13, 2, Hours::H24(24), 59, 58);
invalid_dt_test!(too_big_min, 2018, 8, 13, 2, Hours::H24(24), 60, 58);
invalid_dt_test!(too_big_seconds, 2018, 8, 13, 2, Hours::H24(24), 59, 60);
}

View File

@ -1,35 +1,24 @@
extern crate embedded_hal_mock as hal;
use hal::i2c::Transaction as I2cTrans;
use hal::spi::Transaction as SpiTrans;
extern crate ds323x;
use ds323x::TempConvRate;
use embedded_hal_mock::eh1::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
#[allow(unused)]
mod common;
use self::common::{
destroy_ds3232, destroy_ds3234, new_ds3232, new_ds3234, BitFlags as BF, Register,
DEVICE_ADDRESS as DEV_ADDR, DS323X_POR_STATUS,
};
use common::{ DEVICE_ADDRESS as DEV_ADDR, Register,
new_ds3232, new_ds3234, destroy_ds3232,
destroy_ds3234, BitFlags as BF, DS323X_POR_STATUS };
macro_rules! call_method_status_test {
($name:ident, $method:ident, $value:expr) => {
mod $name {
use super::*;
call_test!(
can_call_ds3232,
$method,
new_ds3232,
destroy_ds3232,
[I2cTrans::write(DEV_ADDR, vec![Register::STATUS, $value])]
);
call_test!(
can_call_ds3234,
$method,
new_ds3234,
destroy_ds3234,
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::STATUS + 0x80, $value]),
SpiTrans::transaction_end(),
]
);
call_test!(can_call_ds3232, $method, new_ds3232, destroy_ds3232,
[ I2cTrans::write(DEV_ADDR, vec![Register::STATUS, $value]) ]);
call_test!(can_call_ds3234, $method, new_ds3234, destroy_ds3234,
[ SpiTrans::write(vec![Register::STATUS + 0x80, $value]) ]);
}
};
}
@ -39,22 +28,8 @@ macro_rules! _set_param_test_2_4 {
($name:ident, $method:ident, $value:expr, $i2c_transactions:expr, $spi_transactions:expr) => {
mod $name {
use super::*;
set_test!(
can_set_ds3232,
$method,
new_ds3232,
destroy_ds3232,
$value,
$i2c_transactions
);
set_test!(
can_set_ds3234,
$method,
new_ds3234,
destroy_ds3234,
$value,
$spi_transactions
);
set_test!(can_set_ds3232, $method, new_ds3232, destroy_ds3232, $value, $i2c_transactions);
set_test!(can_set_ds3234, $method, new_ds3234, destroy_ds3234, $value, $spi_transactions);
}
};
}
@ -62,61 +37,25 @@ macro_rules! _set_param_test_2_4 {
#[macro_export]
macro_rules! set_param_test_2_4 {
($name:ident, $method:ident, $register:ident, $value:expr, $binary_value:expr) => {
_set_param_test_2_4!(
$name,
$method,
$value,
[I2cTrans::write(
DEV_ADDR,
vec![Register::$register, $binary_value]
)],
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::$register + 0x80, $binary_value]),
SpiTrans::transaction_end(),
]
);
_set_param_test_2_4!($name, $method, $value,
[ I2cTrans::write(DEV_ADDR, vec![Register::$register, $binary_value]) ],
[ SpiTrans::write(vec![Register::$register + 0x80, $binary_value]) ]);
};
}
const DEFAULT_WRITE_STATUS: u8 = DS323X_POR_STATUS | BF::ALARM2F | BF::ALARM1F;
call_method_status_test!(
can_en_32khz_bat,
enable_32khz_output_on_battery,
DEFAULT_WRITE_STATUS | BF::BB32KHZ
);
call_method_status_test!(
can_dis_32khz_bat,
disable_32khz_output_on_battery,
DEFAULT_WRITE_STATUS & !BF::BB32KHZ
);
call_method_status_test!(can_en_32khz_bat, enable_32khz_output_on_battery,
DEFAULT_WRITE_STATUS | BF::BB32KHZ );
call_method_status_test!(can_dis_32khz_bat, disable_32khz_output_on_battery,
DEFAULT_WRITE_STATUS & !BF::BB32KHZ);
set_param_test_2_4!(can_set_cr_64s, set_temperature_conversion_rate, STATUS, TempConvRate::_64s,
DEFAULT_WRITE_STATUS & !BF::CRATE1 & !BF::CRATE0);
set_param_test_2_4!(can_set_cr_128s, set_temperature_conversion_rate, STATUS, TempConvRate::_128s,
DEFAULT_WRITE_STATUS & !BF::CRATE1 | BF::CRATE0);
set_param_test_2_4!(can_set_cr_256s, set_temperature_conversion_rate, STATUS, TempConvRate::_256s,
DEFAULT_WRITE_STATUS | BF::CRATE1 & !BF::CRATE0);
set_param_test_2_4!(can_set_cr_512s, set_temperature_conversion_rate, STATUS, TempConvRate::_512s,
DEFAULT_WRITE_STATUS | BF::CRATE1 | BF::CRATE0);
set_param_test_2_4!(
can_set_cr_64s,
set_temperature_conversion_rate,
STATUS,
TempConvRate::_64s,
DEFAULT_WRITE_STATUS & !BF::CRATE1 & !BF::CRATE0
);
set_param_test_2_4!(
can_set_cr_128s,
set_temperature_conversion_rate,
STATUS,
TempConvRate::_128s,
DEFAULT_WRITE_STATUS & !BF::CRATE1 | BF::CRATE0
);
set_param_test_2_4!(
can_set_cr_256s,
set_temperature_conversion_rate,
STATUS,
TempConvRate::_256s,
DEFAULT_WRITE_STATUS | BF::CRATE1 & !BF::CRATE0
);
set_param_test_2_4!(
can_set_cr_512s,
set_temperature_conversion_rate,
STATUS,
TempConvRate::_512s,
DEFAULT_WRITE_STATUS | BF::CRATE1 | BF::CRATE0
);

View File

@ -1,29 +1,12 @@
use embedded_hal_mock::eh1::spi::Transaction as SpiTrans;
extern crate embedded_hal_mock as hal;
use hal::spi::Transaction as SpiTrans;
#[allow(unused)]
mod common;
use self::common::{destroy_ds3234, new_ds3234, BitFlags, Register};
use common::{ new_ds3234, destroy_ds3234, Register, BitFlags };
call_test!(
can_en_temp_conv_bat,
enable_temperature_conversions_on_battery,
new_ds3234,
destroy_ds3234,
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::TEMP_CONV + 0x80, 0]),
SpiTrans::transaction_end()
]
);
call_test!(can_en_temp_conv_bat, enable_temperature_conversions_on_battery, new_ds3234, destroy_ds3234,
[ SpiTrans::write(vec![Register::TEMP_CONV + 0x80, 0]) ]);
call_test!(
can_dis_temp_conv_bat,
disable_temperature_conversions_on_battery,
new_ds3234,
destroy_ds3234,
[
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::TEMP_CONV + 0x80, BitFlags::TEMP_CONV_BAT]),
SpiTrans::transaction_end(),
]
);
call_test!(can_dis_temp_conv_bat, disable_temperature_conversions_on_battery, new_ds3234, destroy_ds3234,
[ SpiTrans::write(vec![Register::TEMP_CONV + 0x80, BitFlags::TEMP_CONV_BAT]) ]);

View File

@ -1,51 +1,26 @@
use embedded_hal_mock::eh1::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
extern crate embedded_hal_mock as hal;
use hal::i2c::Transaction as I2cTrans;
use hal::spi::Transaction as SpiTrans;
mod common;
use self::common::{
destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234,
BitFlags as BF, Register, DEVICE_ADDRESS as DEV_ADDR,
};
use common::{ DEVICE_ADDRESS as DEV_ADDR, Register, new_ds3231,
new_ds3232, new_ds3234, destroy_ds3231, destroy_ds3232,
destroy_ds3234, BitFlags as BF };
get_param_test!(running, running, CONTROL, true, 0);
get_param_test!(is_not_running, running, CONTROL, false, BF::EOSC);
get_param_test!(is_running, is_running, CONTROL, true, 0);
get_param_test!(is_not_running, is_running, CONTROL, false, BF::EOSC);
get_param_test!(busy, busy, STATUS, true, 0xFF);
get_param_test!(is_not_busy, busy, STATUS, false, !BF::BUSY);
get_param_test!(is_busy, is_busy, STATUS, true, 0xFF);
get_param_test!(is_not_busy, is_busy, STATUS, false, 0xFF & !BF::BUSY);
get_param_test!(stopped, has_been_stopped, STATUS, true, 0xFF);
get_param_test!(not_stopped, has_been_stopped, STATUS, false, !BF::OSC_STOP);
get_param_test!(stopped, has_been_stopped, STATUS, true, 0xFF);
get_param_test!(not_stopped, has_been_stopped, STATUS, false, 0xFF & !BF::OSC_STOP);
get_param_test!(alarm1_matched, has_alarm1_matched, STATUS, true, 0xFF);
get_param_test!(
alarm1_not_matched,
has_alarm1_matched,
STATUS,
false,
!BF::ALARM1F
);
get_param_test!(alarm1_matched, has_alarm1_matched, STATUS, true, 0xFF);
get_param_test!(alarm1_not_matched, has_alarm1_matched, STATUS, false, 0xFF & !BF::ALARM1F);
get_param_test!(alarm2_matched, has_alarm2_matched, STATUS, true, 0xFF);
get_param_test!(
alarm2_not_matched,
has_alarm2_matched,
STATUS,
false,
!BF::ALARM2F
);
get_param_test!(alarm2_matched, has_alarm2_matched, STATUS, true, 0xFF);
get_param_test!(alarm2_not_matched, has_alarm2_matched, STATUS, false, 0xFF & !BF::ALARM2F);
get_param_read_array_test!(temp_0, temperature, 0.0, TEMP_MSB, [0, 0], [0, 0]);
get_param_read_array_test!(
temp_min,
temperature,
-128.0,
TEMP_MSB,
[0b1000_0000, 0],
[0, 0]
);
get_param_read_array_test!(
temp_max,
temperature,
127.75,
TEMP_MSB,
[0b0111_1111, 0b1100_0000],
[0, 0]
);
get_param_read_array_test!(temp_0, get_temperature, 0.0, TEMP_MSB, [0, 0], [0, 0]);
get_param_read_array_test!(temp_min, get_temperature, -128.0, TEMP_MSB, [0b1000_0000, 0], [0, 0]);
get_param_read_array_test!(temp_max, get_temperature, 127.75, TEMP_MSB, [0b0111_1111, 0b1100_0000], [0, 0]);