Compare commits

..

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

26 changed files with 783 additions and 1263 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'

66
.travis.yml 100644
View File

@ -0,0 +1,66 @@
language: rust
dist: bionic # because of tarpaulin
env:
global:
- RUSTFLAGS="-D warnings"
before_install:
- git clone https://github.com/eldruin/rust-driver-ci-scripts ci
- bash ci/before_install.sh
jobs:
include:
- env: TARGET=x86_64-unknown-linux-gnu
before_script:
- bash ci/coverage_before_script.sh
after_success:
- bash ci/coverage_after_success.sh
addons:
apt:
packages:
- libcurl4-openssl-dev
- libelf-dev
- libdw-dev
- binutils-dev
- cmake
# Minimum Supported Rust Version
- env: TARGET=x86_64-unknown-linux-gnu
rust: 1.31.0
# Raspberry Pi 1
- env: TARGET=arm-unknown-linux-gnueabi DISABLE_EXAMPLES=1 DISABLE_TESTS=1
# Raspberry Pi 2, 3, etc
- env: TARGET=armv7-unknown-linux-gnueabihf DISABLE_EXAMPLES=1 DISABLE_TESTS=1
# Bare metal
- env: TARGET=thumbv6m-none-eabi
- env: TARGET=thumbv7em-none-eabi
- env: TARGET=thumbv7em-none-eabihf
- env: TARGET=thumbv7m-none-eabi
allow_failures:
- env: TARGET=x86_64-unknown-linux-gnu
rust: nightly
- env: TARGET=x86_64-unknown-linux-musl
rust: nightly
install:
bash ci/install.sh
script:
bash ci/script.sh
after_script: set +e
cache: cargo
before_cache:
bash ci/before_cache.sh
notifications:
email:
on_success: never

View File

@ -5,56 +5,7 @@ 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/) 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). and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
<!-- next-header --> ## [Unreleased]
## [Unreleased] - ReleaseDate
## [0.6.0] - 2025-01-02
### Changed
- [breaking-change] Removed `Error::Pin` variant.
- [breaking-change] Update to `embedded-hal` 1.0.0.
- Raised MSRV to version 1.75.0
## [0.5.1] - 2023-07-17
### Fixed
- Fixed `set_day` method. See: [PR #9](https://github.com/eldruin/ds323x-rs/pull/9)
### Changed
- Raised MSRV to version 1.60.0
## [0.5.0] - 2022-02-21
### Changed
- [breaking-change] Update `rtcc` to version 0.3.
- [breaking-change] Remove `get_` from all public method names to comply with the Rust API guidelines.
- Raise MSRV to version 1.35.0
## [0.4.0] - 2021-05-22
### Changed
- [breaking-change] Return `Error::InvalidDeviceState` if it was not possible to read the
date and/or time from the device because the state of the device corresponds to
an invalid date and/or time.
## [0.3.2] - 2021-02-22
### 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 ### Changed
- [breaking-change] Renamed `is_busy()` and `is_running()` methods `busy()` and `running()` - [breaking-change] Renamed `is_busy()` and `is_running()` methods `busy()` and `running()`
@ -90,13 +41,5 @@ this CHANGELOG.
[`chrono`]: https://crates.io/crates/chrono [`chrono`]: https://crates.io/crates/chrono
[`rtcc`]: https://crates.io/crates/rtcc [`rtcc`]: https://crates.io/crates/rtcc
<!-- next-url --> [Unreleased]: https://github.com/eldruin/ds323x-rs/compare/v0.2.0...HEAD
[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
[0.2.0]: https://github.com/eldruin/ds323x-rs/compare/v0.1.0...v0.2.0 [0.2.0]: https://github.com/eldruin/ds323x-rs/compare/v0.1.0...v0.2.0

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ds323x" name = "ds323x"
version = "0.6.0" version = "0.3.0"
authors = ["Diego Barrios Romero <eldruin@gmail.com>"] authors = ["Diego Barrios Romero <eldruin@gmail.com>"]
repository = "https://github.com/eldruin/ds323x-rs" repository = "https://github.com/eldruin/ds323x-rs"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
@ -18,16 +18,19 @@ include = [
"/LICENSE-MIT", "/LICENSE-MIT",
"/LICENSE-APACHE", "/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] [dependencies]
embedded-hal = "1.0.0" embedded-hal = "0.2.3"
rtcc = "0.3" rtcc = "0.2"
[dev-dependencies] [dev-dependencies]
embedded-hal-mock = { version = "0.11.1", features = ["eh1"] } linux-embedded-hal = "0.3"
embedded-hal-bus = "0.2" embedded-hal-mock = "0.7"
linux-embedded-hal = "0.4.0"
[profile.release] [profile.release]
lto = true 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 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 this software and associated documentation files (the "Software"), to deal in

View File

@ -2,9 +2,9 @@
[![crates.io](https://img.shields.io/crates/v/ds323x.svg)](https://crates.io/crates/ds323x) [![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) [![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://travis-ci.org/eldruin/ds323x-rs.svg?branch=master)](https://travis-ci.org/eldruin/ds323x-rs)
[![Build Status](https://github.com/eldruin/ds323x-rs/workflows/Build/badge.svg)](https://github.com/eldruin/ds323x-rs/actions?query=workflow%3ABuild)
[![Coverage Status](https://coveralls.io/repos/eldruin/ds323x-rs/badge.svg?branch=master)](https://coveralls.io/r/eldruin/ds323x-rs?branch=master) [![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 This is a platform agnostic Rust driver for the DS3231, DS3232 and DS3234
extremely accurate real-time clocks, based on the [`embedded-hal`] traits. extremely accurate real-time clocks, based on the [`embedded-hal`] traits.
@ -12,8 +12,8 @@ extremely accurate real-time clocks, based on the [`embedded-hal`] traits.
[`embedded-hal`]: https://github.com/rust-embedded/embedded-hal [`embedded-hal`]: https://github.com/rust-embedded/embedded-hal
This driver allows you to: 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 in 12-hour and 24-hour format. See: `get_datetime`.
- Read and set date and time individual elements. For example, see: `year`. - Read and set date and time individual elements. For example, see: `get_year`.
- Enable and disable the real-time clock. See: `enable`. - Enable and disable the real-time clock. See: `enable`.
- Read the busy status. See `busy`. - Read the busy status. See `busy`.
- Read whether the oscillator is or has been stopped. See `has_been_stopped`. - Read whether the oscillator is or has been stopped. See `has_been_stopped`.
@ -22,7 +22,6 @@ This driver allows you to:
- Select the function of the INT/SQW output pin. See `use_int_sqw_output_as_interrupt`. - Select the function of the INT/SQW output pin. See `use_int_sqw_output_as_interrupt`.
- Alarms: - Alarms:
- Set alarms 1 and 2 with several matching policies. See `set_alarm1_day`. - 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`. - 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`. - 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`. - 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. See `enable_32khz_output`.
- Enable and disable the 32kHz output when battery powered. See `enable_32khz_output_on_battery`. - Enable and disable the 32kHz output when battery powered. See `enable_32khz_output_on_battery`.
- Temperature conversion: - Temperature conversion:
- Read the temperature. See `temperature`. - Read the temperature. See `get_temperature`.
- Force a temperature conversion and time compensation. See `convert_temperature`. - Force a temperature conversion and time compensation. See `convert_temperature`.
- Set the temperature conversion rate. See `set_temperature_conversion_rate`. - 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`. - 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 This driver is compatible with the DS3231 and DS3232 I2C devices and the
DS3234 SPI device. DS3234 SPI device.
These devices are low-cost temperature-compensated crystal oscillator (TCXO) ### DS3231
with a very accurate, temperature-compensated, integrated real-time clock The DS3231 is a low-cost, extremely accurate I2C real-time clock (RTC) with
(RTC) including 236/256 bytes of battery-backed SRAM, depending on the model. an integrated temperature-compensated crystal oscillator (TCXO) and crystal.
### DS3231 and DS3232 details The device incorporates a battery input, and maintains accurate timekeeping
when main power to the device is interrupted. The integration of the
The devices incorporate a battery input, and maintain accurate timekeeping crystal resonator enhances the long-term accuracy of the device as well as
when main power to the devices is interrupted. The integration of the
crystal resonator enhances the long-term accuracy of the devices as well as
reduces the piece-part count in a manufacturing line. reduces the piece-part count in a manufacturing line.
The devices are available in commercial and industrial temperature ranges, The DS3231 is available in commercial and industrial temperature ranges,
and are offered in a 16-pin, 300-mil SO package. and is offered in a 16-pin, 300-mil SO package.
The RTC maintains seconds, minutes, hours, day, date, month, and year The RTC maintains seconds, minutes, hours, day, date, month, and year
information. The date at the end of the month is automatically adjusted for 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 necessary. Additionally, the RST pin is monitored as a pushbutton
input for generating a μP reset. 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 The DS3234 incorporates a precision, temperature-compensated voltage
reference and comparator circuit to monitor VCC. When VCC drops below the reference and comparator circuit to monitor VCC. When VCC drops below the
@ -111,19 +137,17 @@ Please find additional examples using hardware in this repository: [driver-examp
[driver-examples]: https://github.com/eldruin/driver-examples [driver-examples]: https://github.com/eldruin/driver-examples
```rust ```rust
use ds323x::{DateTimeAccess, Ds323x, NaiveDate, Rtcc}; extern crate ds323x;
use linux_embedded_hal::I2cdev; extern crate linux_embedded_hal as hal;
use ds323x::{Ds323x, NaiveDate, Rtcc};
fn main() { 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 mut rtc = Ds323x::new_ds3231(dev);
let datetime = NaiveDate::from_ymd_opt(2020, 5, 1) let datetime = NaiveDate::from_ymd(2020, 5, 1).and_hms(19, 59, 58);
.unwrap()
.and_hms_opt(19, 59, 58)
.unwrap();
rtc.set_datetime(&datetime).unwrap(); rtc.set_datetime(&datetime).unwrap();
// do something else... // do something else...
let time = rtc.time().unwrap(); let time = rtc.get_time().unwrap();
println!("Time: {}", time); println!("Time: {}", time);
let _dev = rtc.destroy_ds3231(); let _dev = rtc.destroy_ds3231();
@ -132,15 +156,9 @@ fn main() {
## Support ## Support
For questions, issues, feature requests like compatibility with other devices and other For questions, issues, feature requests, and other changes, please file an
changes, please file an
[issue in the github project](https://github.com/eldruin/ds323x-rs/issues). [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 ## License
Licensed under either of Licensed under either of
@ -157,3 +175,4 @@ at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted 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 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. be dual licensed as above, without any additional terms or conditions.

View File

@ -1,16 +1,14 @@
use ds323x::{DateTimeAccess, Ds323x, NaiveDate, Rtcc}; extern crate ds323x;
use linux_embedded_hal::I2cdev; extern crate linux_embedded_hal as hal;
use ds323x::{Ds323x, NaiveDate, Rtcc};
fn main() { 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 mut rtc = Ds323x::new_ds3231(dev);
let datetime = NaiveDate::from_ymd_opt(2020, 5, 1) let datetime = NaiveDate::from_ymd(2020, 5, 1).and_hms(19, 59, 58);
.unwrap()
.and_hms_opt(19, 59, 58)
.unwrap();
rtc.set_datetime(&datetime).unwrap(); rtc.set_datetime(&datetime).unwrap();
// do something else... // do something else...
let time = rtc.time().unwrap(); let time = rtc.get_time().unwrap();
println!("Time: {}", time); println!("Time: {}", time);
let _dev = rtc.destroy_ds3231(); 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,12 +1,13 @@
//! Functions exclusive of DS3231 //! Functions exclusive of DS3231
use crate::{ic, interface::I2cInterface, BitFlags, Ds323x, CONTROL_POR_VALUE}; use super::{ic, BitFlags, Ds323x, CONTROL_POR_VALUE};
use core::marker::PhantomData; use core::marker::PhantomData;
use embedded_hal::i2c; use hal::blocking;
use interface::I2cInterface;
impl<I2C, E> Ds323x<I2cInterface<I2C>, ic::DS3231> impl<I2C, E> Ds323x<I2cInterface<I2C>, ic::DS3231>
where 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. /// Create a new instance of the DS3231 device.
pub fn new_ds3231(i2c: I2C) -> Self { pub fn new_ds3231(i2c: I2C) -> Self {

View File

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

View File

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

View File

@ -1,19 +1,10 @@
//! Alarm support //! Alarm support
use super::super::{BitFlags, Ds323x, Error, Hours, Register};
use super::{decimal_to_packed_bcd, hours_to_register}; use super::{decimal_to_packed_bcd, hours_to_register};
use crate::{ use interface::{ReadData, WriteData};
ds323x::{NaiveTime, Timelike},
interface::{ReadData, WriteData},
BitFlags, Ds323x, Error, Hours, Register,
};
/// Parameters for setting Alarm1 on a day of the month /// 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)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct DayAlarm1 { pub struct DayAlarm1 {
/// Day of the month [1-31] /// Day of the month [1-31]
@ -27,12 +18,6 @@ pub struct DayAlarm1 {
} }
/// Parameters for setting Alarm1 on a weekday /// 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)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct WeekdayAlarm1 { pub struct WeekdayAlarm1 {
/// Weekday [1-7] /// Weekday [1-7]
@ -61,12 +46,6 @@ pub enum Alarm1Matching {
} }
/// Parameters for setting Alarm2 on a day of the month /// 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)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct DayAlarm2 { pub struct DayAlarm2 {
/// Day of month [1-31] /// Day of month [1-31]
@ -78,12 +57,6 @@ pub struct DayAlarm2 {
} }
/// Parameters for setting Alarm2 on a weekday /// 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)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct WeekdayAlarm2 { pub struct WeekdayAlarm2 {
/// Weekday [1-7] /// Weekday [1-7]
@ -128,212 +101,92 @@ fn get_matching_mask_alarm2(matching: Alarm2Matching) -> [u8; 3] {
} }
} }
/// Test if hour value is valid impl<DI, IC, CommE, PinE> Ds323x<DI, IC>
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 where
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>, DI: ReadData<Error = Error<CommE, PinE>> + WriteData<Error = Error<CommE, PinE>>,
{ {
/// Set Alarm1 for day of the month. /// Set Alarm1 for day of the month.
/// ///
/// Will return an `Error::InvalidInputData` if any of the used parameters /// Will return an `Error::InvalidInputData` if any of the parameters is out of range.
/// (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( pub fn set_alarm1_day(
&mut self, &mut self,
when: DayAlarm1, when: DayAlarm1,
matching: Alarm1Matching, matching: Alarm1Matching,
) -> Result<(), Error<E>> { ) -> Result<(), Error<CommE, PinE>> {
let day_invalid = when.day < 1 || when.day > 31; if when.day < 1 || when.day > 31 || when.minute > 59 || when.second > 59 {
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
{
return Err(Error::InvalidInputData); return Err(Error::InvalidInputData);
} }
let match_mask = get_matching_mask_alarm1(matching); let match_mask = get_matching_mask_alarm1(matching);
let mut data = [ let mut data = [
Register::ALARM1_SECONDS, Register::ALARM1_SECONDS,
decimal_to_packed_bcd(when.second) | match_mask[0], decimal_to_packed_bcd(when.second) | match_mask[0],
decimal_to_packed_bcd(minute) | match_mask[1], decimal_to_packed_bcd(when.minute) | match_mask[1],
hours_to_register(hour)? | match_mask[2], hours_to_register(when.hour)? | match_mask[2],
decimal_to_packed_bcd(day) | match_mask[3], decimal_to_packed_bcd(when.day) | match_mask[3],
]; ];
self.iface.write_data(&mut data) 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. /// Set Alarm1 for weekday.
/// ///
/// Will return an `Error::InvalidInputData` if any of the used parameters /// Will return an `Error::InvalidInputData` if any of the parameters is out of range.
/// (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( pub fn set_alarm1_weekday(
&mut self, &mut self,
when: WeekdayAlarm1, when: WeekdayAlarm1,
matching: Alarm1Matching, matching: Alarm1Matching,
) -> Result<(), Error<E>> { ) -> Result<(), Error<CommE, PinE>> {
let weekday_invalid = when.weekday < 1 || when.weekday > 7; if when.weekday < 1 || when.weekday > 7 || when.minute > 59 || when.second > 59 {
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)
{
return Err(Error::InvalidInputData); return Err(Error::InvalidInputData);
} }
let match_mask = get_matching_mask_alarm1(matching); let match_mask = get_matching_mask_alarm1(matching);
let mut data = [ let mut data = [
Register::ALARM1_SECONDS, Register::ALARM1_SECONDS,
decimal_to_packed_bcd(second) | match_mask[0], decimal_to_packed_bcd(when.second) | match_mask[0],
decimal_to_packed_bcd(minute) | match_mask[1], decimal_to_packed_bcd(when.minute) | match_mask[1],
hours_to_register(hour)? | match_mask[2], hours_to_register(when.hour)? | match_mask[2],
decimal_to_packed_bcd(weekday) | match_mask[3] | BitFlags::WEEKDAY, decimal_to_packed_bcd(when.weekday) | match_mask[3] | BitFlags::WEEKDAY,
]; ];
self.iface.write_data(&mut data) self.iface.write_data(&mut data)
} }
/// Set Alarm2 for date (day of month). /// Set Alarm2 for date (day of month).
/// ///
/// Will return an `Error::InvalidInputData` if any of the used parameters /// Will return an `Error::InvalidInputData` if any of the parameters is out of range.
/// (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( pub fn set_alarm2_day(
&mut self, &mut self,
when: DayAlarm2, when: DayAlarm2,
matching: Alarm2Matching, matching: Alarm2Matching,
) -> Result<(), Error<E>> { ) -> Result<(), Error<CommE, PinE>> {
let day_invalid = when.day < 1 || when.day > 31; if when.day < 1 || when.day > 31 || when.minute > 59 {
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)
{
return Err(Error::InvalidInputData); return Err(Error::InvalidInputData);
} }
let match_mask = get_matching_mask_alarm2(matching); let match_mask = get_matching_mask_alarm2(matching);
let mut data = [ let mut data = [
Register::ALARM2_MINUTES, Register::ALARM2_MINUTES,
decimal_to_packed_bcd(minute) | match_mask[0], decimal_to_packed_bcd(when.minute) | match_mask[0],
hours_to_register(hour)? | match_mask[1], hours_to_register(when.hour)? | match_mask[1],
decimal_to_packed_bcd(day) | match_mask[2], decimal_to_packed_bcd(when.day) | match_mask[2],
]; ];
self.iface.write_data(&mut data) 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. /// Set Alarm2 for weekday.
/// ///
/// Will return an `Error::InvalidInputData` if any of the used parameters /// Will return an `Error::InvalidInputData` if any of the parameters is out of range.
/// (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( pub fn set_alarm2_weekday(
&mut self, &mut self,
when: WeekdayAlarm2, when: WeekdayAlarm2,
matching: Alarm2Matching, matching: Alarm2Matching,
) -> Result<(), Error<E>> { ) -> Result<(), Error<CommE, PinE>> {
let weekday_invalid = when.weekday < 1 || when.weekday > 7; if when.weekday < 1 || when.weekday > 7 || when.minute > 59 {
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)
{
return Err(Error::InvalidInputData); return Err(Error::InvalidInputData);
} }
let match_mask = get_matching_mask_alarm2(matching); let match_mask = get_matching_mask_alarm2(matching);
let mut data = [ let mut data = [
Register::ALARM2_MINUTES, Register::ALARM2_MINUTES,
decimal_to_packed_bcd(minute) | match_mask[0], decimal_to_packed_bcd(when.minute) | match_mask[0],
hours_to_register(hour)? | match_mask[1], hours_to_register(when.hour)? | match_mask[1],
decimal_to_packed_bcd(weekday) | match_mask[2] | BitFlags::WEEKDAY, decimal_to_packed_bcd(when.weekday) | match_mask[2] | BitFlags::WEEKDAY,
]; ];
self.iface.write_data(&mut data) self.iface.write_data(&mut data)
} }

View File

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

View File

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

View File

@ -5,7 +5,8 @@ pub use self::alarms::{
Alarm1Matching, Alarm2Matching, DayAlarm1, DayAlarm2, WeekdayAlarm1, WeekdayAlarm2, Alarm1Matching, Alarm2Matching, DayAlarm1, DayAlarm2, WeekdayAlarm1, WeekdayAlarm2,
}; };
mod datetime; mod datetime;
use crate::{BitFlags, Error, Hours, NaiveTime, Timelike}; use super::{BitFlags, Error};
use super::{Datelike, Hours, NaiveDate, NaiveDateTime, NaiveTime, Rtcc, Timelike};
// Transforms a decimal number to packed BCD format // Transforms a decimal number to packed BCD format
fn decimal_to_packed_bcd(dec: u8) -> u8 { fn decimal_to_packed_bcd(dec: u8) -> u8 {
@ -17,45 +18,21 @@ fn packed_bcd_to_decimal(bcd: u8) -> u8 {
(bcd >> 4) * 10 + (bcd & 0xF) (bcd >> 4) * 10 + (bcd & 0xF)
} }
fn hours_to_register<E>(hours: Hours) -> Result<u8, Error<E>> { fn hours_to_register<CommE, PinE>(hours: Hours) -> Result<u8, Error<CommE, PinE>> {
match hours { match hours {
Hours::H24(h) if h > 23 => Err(Error::InvalidInputData), Hours::H24(h) if h > 23 => Err(Error::InvalidInputData),
Hours::H24(h) => Ok(decimal_to_packed_bcd(h)), Hours::H24(h) => Ok(decimal_to_packed_bcd(h)),
Hours::AM(h) if !(1..=12).contains(&h) => Err(Error::InvalidInputData), Hours::AM(h) if h < 1 || h > 12 => Err(Error::InvalidInputData),
Hours::AM(h) => Ok(BitFlags::H24_H12 | decimal_to_packed_bcd(h)), 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) if h < 1 || h > 12 => Err(Error::InvalidInputData),
Hours::PM(h) => Ok(BitFlags::H24_H12 | BitFlags::AM_PM | decimal_to_packed_bcd(h)), 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; 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] #[test]
fn can_convert_packed_bcd_to_decimal() { fn can_convert_packed_bcd_to_decimal() {
assert_eq!(0, packed_bcd_to_decimal(0b0000_0000)); assert_eq!(0, packed_bcd_to_decimal(0b0000_0000));

View File

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

View File

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

View File

@ -4,8 +4,8 @@
//! [`embedded-hal`]: https://github.com/rust-embedded/embedded-hal //! [`embedded-hal`]: https://github.com/rust-embedded/embedded-hal
//! //!
//! This driver allows you to: //! 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 in 12-hour and 24-hour format. See: [`get_datetime`].
//! - Read and set date and time individual elements. For example, see: [`year`]. //! - Read and set date and time individual elements. For example, see: [`get_year`].
//! - Enable and disable the real-time clock. See: [`enable`]. //! - Enable and disable the real-time clock. See: [`enable`].
//! - Read the busy status. See [`busy`]. //! - Read the busy status. See [`busy`].
//! - Read whether the oscillator is or has been stopped. See [`has_been_stopped`]. //! - Read whether the oscillator is or has been stopped. See [`has_been_stopped`].
@ -14,7 +14,6 @@
//! - Select the function of the INT/SQW output pin. See [`use_int_sqw_output_as_interrupt`]. //! - Select the function of the INT/SQW output pin. See [`use_int_sqw_output_as_interrupt`].
//! - Alarms: //! - Alarms:
//! - Set alarms 1 and 2 with several matching policies. See [`set_alarm1_day`]. //! - 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`]. //! - 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`]. //! - 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`]. //! - 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. See [`enable_32khz_output`].
//! - Enable and disable the 32kHz output when battery powered. See [`enable_32khz_output_on_battery`]. //! - Enable and disable the 32kHz output when battery powered. See [`enable_32khz_output_on_battery`].
//! - Temperature conversion: //! - Temperature conversion:
//! - Read the temperature. See [`temperature`]. //! - Read the temperature. See [`get_temperature`].
//! - Force a temperature conversion and time compensation. See [`convert_temperature`]. //! - Force a temperature conversion and time compensation. See [`convert_temperature`].
//! - Set the temperature conversion rate. See [`set_temperature_conversion_rate`]. //! - 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`]. //! - Enable and disable the temperature conversions when battery-powered. See [`enable_temperature_conversions_on_battery`].
//! //!
//! [`datetime`]: Ds323x::datetime //! [`get_datetime`]: struct.Ds323x.html#method.get_datetime
//! [`year`]: Ds323x::year //! [`get_year`]: struct.Ds323x.html#method.get_year
//! [`enable`]: Ds323x::enable //! [`enable`]: struct.Ds323x.html#method.enable
//! [`temperature`]: Ds323x::temperature //! [`get_temperature`]: struct.Ds323x.html#method.get_temperature
//! [`convert_temperature`]: Ds323x::convert_temperature //! [`convert_temperature`]: struct.Ds323x.html#method.convert_temperature
//! [`busy`]: Ds323x::busy //! [`busy`]: struct.Ds323x.html#method.busy
//! [`has_been_stopped`]: Ds323x::has_been_stopped //! [`has_been_stopped`]: struct.Ds323x.html#method.has_been_stopped
//! [`clear_has_been_stopped_flag`]: Ds323x::clear_has_been_stopped_flag //! [`clear_has_been_stopped_flag`]: struct.Ds323x.html#method.clear_has_been_stopped_flag
//! [`set_aging_offset`]: Ds323x::set_aging_offset //! [`set_aging_offset`]: struct.Ds323x.html#method.set_aging_offset
//! [`enable_32khz_output`]: Ds323x::enable_32khz_output //! [`enable_32khz_output`]: struct.Ds323x.html#method.enable_32khz_output
//! [`use_int_sqw_output_as_interrupt`]: Ds323x::use_int_sqw_output_as_interrupt //! [`use_int_sqw_output_as_interrupt`]: struct.Ds323x.html#method.use_int_sqw_output_as_interrupt
//! [`enable_square_wave`]: Ds323x::enable_square_wave //! [`enable_square_wave`]: struct.Ds323x.html#method.enable_square_wave
//! [`set_square_wave_frequency`]: Ds323x::set_square_wave_frequency //! [`set_square_wave_frequency`]: struct.Ds323x.html#method.set_square_wave_frequency
//! [`set_alarm1_day`]: Ds323x::set_alarm1_day //! [`set_alarm1_day`]: struct.Ds323x.html#method.set_alarm1_day
//! [`set_alarm1_hms`]: Ds323x::set_alarm1_hms //! [`has_alarm1_matched`]: struct.Ds323x.html#method.has_alarm1_matched
//! [`has_alarm1_matched`]: Ds323x::has_alarm1_matched //! [`clear_alarm1_matched_flag`]: struct.Ds323x.html#method.clear_alarm1_matched_flag
//! [`clear_alarm1_matched_flag`]: Ds323x::clear_alarm1_matched_flag //! [`enable_alarm1_interrupts`]: struct.Ds323x.html#method.enable_alarm1_interrupts
//! [`enable_alarm1_interrupts`]: Ds323x::enable_alarm1_interrupts //! [`enable_32khz_output_on_battery`]: struct.Ds323x.html#method.enable_32khz_output_on_battery
//! [`enable_32khz_output_on_battery`]: Ds323x::enable_32khz_output_on_battery //! [`set_temperature_conversion_rate`]: struct.Ds323x.html#method.set_temperature_conversion_rate
//! [`set_temperature_conversion_rate`]: Ds323x::set_temperature_conversion_rate //! [`enable_temperature_conversions_on_battery`]: struct.Ds323x.html#method.enable_temperature_conversions_on_battery
//! [`enable_temperature_conversions_on_battery`]: Ds323x::enable_temperature_conversions_on_battery
//! //!
//! ## The devices //! ## The devices
//! //!
//! This driver is compatible with the DS3231 and DS3232 I2C devices and the //! This driver is compatible with the DS3231 and DS3232 I2C devices and the
//! DS3234 SPI device. //! DS3234 SPI device.
//! //!
//! These devices are low-cost temperature-compensated crystal oscillator (TCXO) //! ### DS3231
//! with a very accurate, temperature-compensated, integrated real-time clock //! The DS3231 is a low-cost, extremely accurate I2C real-time clock (RTC) with
//! (RTC) including 236/256 bytes of battery-backed SRAM, depending on the model. //! an integrated temperature-compensated crystal oscillator (TCXO) and crystal.
//! //!
//! ### DS3231 and DS3232 details //! The device incorporates a battery input, and maintains accurate timekeeping
//! //! when main power to the device is interrupted. The integration of the
//! The devices incorporate a battery input, and maintain accurate timekeeping //! crystal resonator enhances the long-term accuracy of the device as well as
//! when main power to the devices is interrupted. The integration of the
//! crystal resonator enhances the long-term accuracy of the devices as well as
//! reduces the piece-part count in a manufacturing line. //! reduces the piece-part count in a manufacturing line.
//! The devices are available in commercial and industrial temperature ranges, //! The DS3231 is available in commercial and industrial temperature ranges,
//! and are offered in a 16-pin, 300-mil SO package. //! and is offered in a 16-pin, 300-mil SO package.
//! //!
//! The RTC maintains seconds, minutes, hours, day, date, month, and year //! The RTC maintains seconds, minutes, hours, day, date, month, and year
//! information. The date at the end of the month is automatically adjusted for //! 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 //! necessary. Additionally, the RST pin is monitored as a pushbutton
//! input for generating a μP reset. //! 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 //! The DS3234 incorporates a precision, temperature-compensated voltage
//! reference and comparator circuit to monitor VCC. When VCC drops below the //! reference and comparator circuit to monitor VCC. When VCC drops below the
@ -128,71 +153,84 @@
//! ### Create a driver instance for the DS3231 //! ### Create a driver instance for the DS3231
//! //!
//! ```no_run //! ```no_run
//! extern crate ds323x;
//! extern crate linux_embedded_hal as hal;
//! use ds323x::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); //! let rtc = Ds323x::new_ds3231(dev);
//! // do something... //! // do something...
//! //!
//! // get the I2C device back //! // get the I2C device back
//! let dev = rtc.destroy_ds3231(); //! let dev = rtc.destroy_ds3231();
//! # }
//! ``` //! ```
//! //!
//! ### Create a driver instance for the DS3232 //! ### Create a driver instance for the DS3232
//! //!
//! ```no_run //! ```no_run
//! extern crate ds323x;
//! extern crate linux_embedded_hal as hal;
//! use ds323x::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); //! let rtc = Ds323x::new_ds3232(dev);
//! // do something... //! // do something...
//! //!
//! // get the I2C device back //! // get the I2C device back
//! let dev = rtc.destroy_ds3232(); //! let dev = rtc.destroy_ds3232();
//! # }
//! ``` //! ```
//! //!
//! ### Create a driver instance for the DS3234 //! ### Create a driver instance for the DS3234
//! //!
//! ```no_run //! ```no_run
//! extern crate ds323x;
//! extern crate linux_embedded_hal as hal;
//! use ds323x::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(); //! # fn main() {
//! let chip_select = SysfsPin::new(25); //! let dev = hal::Spidev::open("/dev/spidev0.0").unwrap();
//! let dev = ExclusiveDevice::new(spi, chip_select, Delay).unwrap(); //! let chip_select = hal::Pin::new(24);
//! let rtc = Ds323x::new_ds3234(dev); //! let rtc = Ds323x::new_ds3234(dev, chip_select);
//! // do something... //! // do something...
//! //!
//! // get the SPI device back //! // get the SPI device and chip select pin back
//! let dev = rtc.destroy_ds3234(); //! let (dev, chip_select) = rtc.destroy_ds3234();
//! # }
//! ``` //! ```
//! //!
//! ### Set the current date and time at once //! ### Set the current date and time at once
//! //!
//! ```no_run //! ```no_run
//! use ds323x::{Ds323x, NaiveDate, DateTimeAccess}; //! extern crate ds323x;
//! use linux_embedded_hal::I2cdev; //! extern crate linux_embedded_hal as hal;
//! use ds323x::{Ds323x, NaiveDate, Rtcc};
//! //!
//! 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 mut rtc = Ds323x::new_ds3231(dev);
//! let datetime = NaiveDate::from_ymd(2020, 5, 1).and_hms(19, 59, 58); //! let datetime = NaiveDate::from_ymd(2020, 5, 1).and_hms(19, 59, 58);
//! rtc.set_datetime(&datetime).unwrap(); //! rtc.set_datetime(&datetime).unwrap();
//! # }
//! ``` //! ```
//! //!
//! ### Get the current date and time at once //! ### Get the current date and time at once
//! //!
//! ```no_run //! ```no_run
//! use ds323x::{Ds323x, DateTimeAccess}; //! extern crate ds323x;
//! use linux_embedded_hal::I2cdev; //! extern crate linux_embedded_hal as hal;
//! use ds323x::{Ds323x, Rtcc};
//! //!
//! 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 mut rtc = Ds323x::new_ds3231(dev);
//! let dt = rtc.datetime().unwrap(); //! let dt = rtc.get_datetime().unwrap();
//! println!("{}", dt); //! println!("{}", dt);
//! // This will print something like: 2020-05-01 19:59:58 //! // This will print something like: 2020-05-01 19:59:58
//! # }
//! ``` //! ```
//! //!
//! ### Get the year //! ### Get the year
@ -200,13 +238,16 @@
//! Similar methods exist for month, day, weekday, hours, minutes and seconds. //! Similar methods exist for month, day, weekday, hours, minutes and seconds.
//! //!
//! ```no_run //! ```no_run
//! extern crate ds323x;
//! extern crate linux_embedded_hal as hal;
//! use ds323x::{Ds323x, Rtcc}; //! use ds323x::{Ds323x, Rtcc};
//! 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 mut rtc = Ds323x::new_ds3231(dev);
//! let year = rtc.year().unwrap(); //! let year = rtc.get_year().unwrap();
//! println!("Year: {}", year); //! println!("Year: {}", year);
//! # }
//! ``` //! ```
//! //!
//! ### Set the year //! ### Set the year
@ -214,63 +255,78 @@
//! Similar methods exist for month, day, weekday, hours, minutes and seconds. //! Similar methods exist for month, day, weekday, hours, minutes and seconds.
//! //!
//! ```no_run //! ```no_run
//! extern crate ds323x;
//! extern crate linux_embedded_hal as hal;
//! use ds323x::{Ds323x, Rtcc}; //! use ds323x::{Ds323x, Rtcc};
//! 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 mut rtc = Ds323x::new_ds3231(dev);
//! rtc.set_year(2018).unwrap(); //! rtc.set_year(2018).unwrap();
//! # }
//! ``` //! ```
//! //!
//! ### Enable/disable the device //! ### Enable/disable the device
//! //!
//! ```no_run //! ```no_run
//! extern crate ds323x;
//! extern crate linux_embedded_hal as hal;
//! use ds323x::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 mut rtc = Ds323x::new_ds3231(dev);
//! rtc.disable().unwrap(); // stops the clock //! rtc.disable().unwrap(); // stops the clock
//! let running = rtc.running().unwrap(); //! let running = rtc.running().unwrap();
//! println!("Is running: {}", running); // will print false //! println!("Is running: {}", running); // will print false
//! rtc.enable().unwrap(); // set clock to run //! rtc.enable().unwrap(); // set clock to run
//! println!("Is running: {}", running); // will print true //! println!("Is running: {}", running); // will print true
//! # }
//! ``` //! ```
//! //!
//! ### Read the temperature //! ### Read the temperature
//! //!
//! ```no_run //! ```no_run
//! extern crate ds323x;
//! extern crate linux_embedded_hal as hal;
//! use ds323x::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 mut rtc = Ds323x::new_ds3231(dev);
//! let temperature = rtc.temperature().unwrap(); //! let temperature = rtc.get_temperature().unwrap();
//! # }
//! ``` //! ```
//! //!
//! ### Read busy status //! ### Read busy status
//! //!
//! ```no_run //! ```no_run
//! extern crate ds323x;
//! extern crate linux_embedded_hal as hal;
//! use ds323x::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 mut rtc = Ds323x::new_ds3231(dev);
//! let busy = rtc.busy().unwrap(); //! let busy = rtc.busy().unwrap();
//! # }
//! ``` //! ```
//! //!
//! ### Enable the square-wave output with a frequency of 4.096Hz //! ### Enable the square-wave output with a frequency of 4.096Hz
//! //!
//! ```no_run //! ```no_run
//! extern crate ds323x;
//! extern crate linux_embedded_hal as hal;
//! use ds323x::{Ds323x, SqWFreq}; //! use ds323x::{Ds323x, SqWFreq};
//! 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 mut rtc = Ds323x::new_ds3231(dev);
//! rtc.set_square_wave_frequency(SqWFreq::_4_096Hz).unwrap(); //! rtc.set_square_wave_frequency(SqWFreq::_4_096Hz).unwrap();
//! // The same output pin can be used for interrupts or as square-wave output //! // The same output pin can be used for interrupts or as square-wave output
//! rtc.use_int_sqw_output_as_square_wave().unwrap(); //! rtc.use_int_sqw_output_as_square_wave().unwrap();
//! rtc.enable_square_wave().unwrap(); //! rtc.enable_square_wave().unwrap();
//! # }
//! ``` //! ```
//! //!
//! ### Enable the 32kHz output except when on battery power //! ### Enable the 32kHz output except when on battery power
@ -279,24 +335,30 @@
//! available for the devices DS3232 and DS3234. //! available for the devices DS3232 and DS3234.
//! //!
//! ```no_run //! ```no_run
//! extern crate ds323x;
//! extern crate linux_embedded_hal as hal;
//! use ds323x::{Ds323x, SqWFreq}; //! use ds323x::{Ds323x, SqWFreq};
//! 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_ds3232(dev); //! let mut rtc = Ds323x::new_ds3232(dev);
//! rtc.disable_32khz_output_on_battery().unwrap(); // only available for DS3232 and DS3234 //! rtc.disable_32khz_output_on_battery().unwrap(); // only available for DS3232 and DS3234
//! rtc.enable_32khz_output().unwrap(); //! rtc.enable_32khz_output().unwrap();
//! # }
//! ``` //! ```
//! //!
//! ### Set the aging offset //! ### Set the aging offset
//! //!
//! ```no_run //! ```no_run
//! extern crate ds323x;
//! extern crate linux_embedded_hal as hal;
//! use ds323x::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 mut rtc = Ds323x::new_ds3231(dev);
//! rtc.set_aging_offset(-15).unwrap(); //! rtc.set_aging_offset(-15).unwrap();
//! # }
//! ``` //! ```
//! //!
//! ### Set the temperature conversion rate to once every 128 seconds //! ### Set the temperature conversion rate to once every 128 seconds
@ -304,21 +366,26 @@
//! This is only available for the devices DS3232 and DS3234. //! This is only available for the devices DS3232 and DS3234.
//! //!
//! ```no_run //! ```no_run
//! extern crate ds323x;
//! extern crate linux_embedded_hal as hal;
//! use ds323x::{Ds323x, TempConvRate}; //! use ds323x::{Ds323x, TempConvRate};
//! 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_ds3232(dev); //! let mut rtc = Ds323x::new_ds3232(dev);
//! rtc.set_temperature_conversion_rate(TempConvRate::_128s).unwrap(); //! rtc.set_temperature_conversion_rate(TempConvRate::_128s).unwrap();
//! # }
//! ``` //! ```
//! //!
//! ### Set the Alarm1 to each week on a week day at a specific time //! ### Set the Alarm1 to each week on a week day at a specific time
//! //!
//! ```no_run //! ```no_run
//! extern crate ds323x;
//! extern crate linux_embedded_hal as hal;
//! use ds323x::{Ds323x, Hours, WeekdayAlarm1, Alarm1Matching}; //! use ds323x::{Ds323x, Hours, WeekdayAlarm1, Alarm1Matching};
//! 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 mut rtc = Ds323x::new_ds3231(dev);
//! let alarm1 = WeekdayAlarm1 { //! let alarm1 = WeekdayAlarm1 {
//! weekday: 1, //! weekday: 1,
@ -327,6 +394,7 @@
//! second: 15 //! second: 15
//! }; //! };
//! rtc.set_alarm1_weekday(alarm1, Alarm1Matching::AllMatch).unwrap(); //! rtc.set_alarm1_weekday(alarm1, Alarm1Matching::AllMatch).unwrap();
//! # }
//! ``` //! ```
//! //!
//! ### Set the Alarm2 to each day at the same time and enable interrupts on output //! ### Set the Alarm2 to each day at the same time and enable interrupts on output
@ -334,10 +402,12 @@
//! The INT/SQW output pin will be set to 1 when it the alarm matches. //! The INT/SQW output pin will be set to 1 when it the alarm matches.
//! //!
//! ```no_run //! ```no_run
//! extern crate ds323x;
//! extern crate linux_embedded_hal as hal;
//! use ds323x::{Ds323x, Hours, DayAlarm2, Alarm2Matching}; //! use ds323x::{Ds323x, Hours, DayAlarm2, Alarm2Matching};
//! 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 mut rtc = Ds323x::new_ds3231(dev);
//! let alarm2 = DayAlarm2 { //! let alarm2 = DayAlarm2 {
//! day: 1, // does not matter given the chosen matching //! day: 1, // does not matter given the chosen matching
@ -347,28 +417,17 @@
//! rtc.set_alarm2_day(alarm2, Alarm2Matching::HoursAndMinutesMatch).unwrap(); //! rtc.set_alarm2_day(alarm2, Alarm2Matching::HoursAndMinutesMatch).unwrap();
//! rtc.use_int_sqw_output_as_interrupt().unwrap(); //! rtc.use_int_sqw_output_as_interrupt().unwrap();
//! rtc.enable_alarm2_interrupts().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)]
#![no_std] #![no_std]
extern crate embedded_hal as hal;
use core::marker::PhantomData; use core::marker::PhantomData;
use embedded_hal::spi::{Mode, MODE_1, MODE_3}; use hal::spi::{Mode, MODE_1, MODE_3};
pub use rtcc::{ extern crate rtcc;
DateTimeAccess, Datelike, Hours, NaiveDate, NaiveDateTime, NaiveTime, Rtcc, Timelike, pub use self::rtcc::{Datelike, Hours, NaiveDate, NaiveDateTime, NaiveTime, Rtcc, Timelike};
};
/// SPI mode 1 (CPOL = 0, CPHA = 1) /// SPI mode 1 (CPOL = 0, CPHA = 1)
pub const SPI_MODE_1: Mode = MODE_1; pub const SPI_MODE_1: Mode = MODE_1;
@ -377,16 +436,13 @@ pub const SPI_MODE_3: Mode = MODE_3;
/// All possible errors in this crate /// All possible errors in this crate
#[derive(Debug)] #[derive(Debug)]
pub enum Error<E> { pub enum Error<CommE, PinE> {
/// I²C/SPI bus error /// I²C/SPI bus error
Comm(E), Comm(CommE),
/// Pin setting error
Pin(PinE),
/// Invalid input data provided /// Invalid input data provided
InvalidInputData, InvalidInputData,
/// Internal device state is invalid.
///
/// It was not possible to read a valid date and/or time.
/// The device is probably missing initialization.
InvalidDeviceState,
} }
/// Square-wave output frequency /// Square-wave output frequency
@ -487,7 +543,7 @@ pub struct Ds323x<DI, IC> {
mod ds323x; mod ds323x;
pub mod interface; pub mod interface;
pub use crate::ds323x::{ pub use ds323x::{
Alarm1Matching, Alarm2Matching, DayAlarm1, DayAlarm2, WeekdayAlarm1, WeekdayAlarm2, Alarm1Matching, Alarm2Matching, DayAlarm1, DayAlarm2, WeekdayAlarm1, WeekdayAlarm2,
}; };
mod ds3231; mod ds3231;
@ -498,7 +554,7 @@ mod private {
use super::{ic, interface}; use super::{ic, interface};
pub trait Sealed {} pub trait Sealed {}
impl<SPI> Sealed for interface::SpiInterface<SPI> {} impl<SPI, CS> Sealed for interface::SpiInterface<SPI, CS> {}
impl<I2C> Sealed for interface::I2cInterface<I2C> {} impl<I2C> Sealed for interface::I2cInterface<I2C> {}
impl Sealed for ic::DS3231 {} impl Sealed for ic::DS3231 {}

View File

@ -1,28 +1,31 @@
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; mod common;
use self::common::{ use common::{
destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234, destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234,
BitFlags as BF, Register, DEVICE_ADDRESS as DEV_ADDR, BitFlags as BF, Register, DEVICE_ADDRESS as DEV_ADDR,
}; };
extern crate ds323x;
use ds323x::{ use ds323x::{
Alarm1Matching as A1M, Alarm2Matching as A2M, DayAlarm1, DayAlarm2, Error, Hours, NaiveTime, Alarm1Matching as A1M, Alarm2Matching as A2M, DayAlarm1, DayAlarm2, Error, Hours,
WeekdayAlarm1, WeekdayAlarm2, WeekdayAlarm1, WeekdayAlarm2,
}; };
#[macro_export] #[macro_export]
macro_rules! _set_invalid_alarm_test { macro_rules! _set_invalid_alarm_test {
($name:ident, $method:ident, $create_method:ident, $destroy_method:ident, $( $value:expr ),+) => { ($name:ident, $method:ident, $create_method:ident, $destroy_method:ident, $alarm:expr, $matching:expr) => {
#[test] #[test]
fn $name() { fn $name() {
let mut dev = $create_method(&[]); let mut dev = $create_method(&[]);
assert_invalid_input_data!(dev.$method($($value),*)); assert_invalid_input_data!(dev.$method($alarm, $matching));
$destroy_method(dev); $destroy_method(dev);
} }
}; };
} }
macro_rules! set_invalid_alarm_test { macro_rules! set_invalid_alarm_test {
($name:ident, $method:ident, $( $value:expr ),+) => { ($name:ident, $method:ident, $alarm:expr, $matching:expr) => {
mod $name { mod $name {
use super::*; use super::*;
_set_invalid_alarm_test!( _set_invalid_alarm_test!(
@ -30,21 +33,24 @@ macro_rules! set_invalid_alarm_test {
$method, $method,
new_ds3231, new_ds3231,
destroy_ds3231, destroy_ds3231,
$($value),* $alarm,
$matching
); );
_set_invalid_alarm_test!( _set_invalid_alarm_test!(
cannot_set_invalid_ds3232, cannot_set_invalid_ds3232,
$method, $method,
new_ds3232, new_ds3232,
destroy_ds3232, destroy_ds3232,
$($value),* $alarm,
$matching
); );
_set_invalid_alarm_test!( _set_invalid_alarm_test!(
cannot_set_invalid_ds3234, cannot_set_invalid_ds3234,
$method, $method,
new_ds3234, new_ds3234,
destroy_ds3234, destroy_ds3234,
$($value),* $alarm,
$matching
); );
} }
}; };
@ -74,28 +80,6 @@ mod alarm1 {
}, },
A1M::AllMatch A1M::AllMatch
); );
set_invalid_alarm_test!(
day_invalid_second,
set_alarm1_day,
DayAlarm1 {
day: 1,
hour: Hours::H24(1),
minute: 59,
second: 60
},
A1M::SecondsMatch
);
set_invalid_alarm_test!(
day_invalid_minute,
set_alarm1_day,
DayAlarm1 {
day: 1,
hour: Hours::H24(1),
minute: 60,
second: 10
},
A1M::MinutesAndSecondsMatch
);
set_invalid_alarm_test!( set_invalid_alarm_test!(
day_invalid_h, day_invalid_h,
set_alarm1_day, set_alarm1_day,
@ -107,17 +91,6 @@ mod alarm1 {
}, },
A1M::AllMatch A1M::AllMatch
); );
set_invalid_alarm_test!(
day_invalid_h_hmasm,
set_alarm1_day,
DayAlarm1 {
day: 1,
hour: Hours::H24(24),
minute: 1,
second: 1
},
A1M::HoursMinutesAndSecondsMatch
);
set_invalid_alarm_test!( set_invalid_alarm_test!(
day_invalid_am1, day_invalid_am1,
set_alarm1_day, set_alarm1_day,
@ -218,18 +191,6 @@ mod alarm1 {
}, },
A1M::AllMatch A1M::AllMatch
); );
set_invalid_alarm_test!(
wd_invalid_h_hmasm,
set_alarm1_weekday,
WeekdayAlarm1 {
weekday: 1,
hour: Hours::H24(24),
minute: 1,
second: 1
},
A1M::HoursMinutesAndSecondsMatch
);
set_invalid_alarm_test!( set_invalid_alarm_test!(
wd_invalid_am1, wd_invalid_am1,
set_alarm1_weekday, set_alarm1_weekday,
@ -296,42 +257,10 @@ mod alarm1 {
}, },
A1M::AllMatch A1M::AllMatch
); );
set_invalid_alarm_test!(
wd_invalid_sec_sm,
set_alarm1_weekday,
WeekdayAlarm1 {
weekday: 1,
hour: Hours::H24(1),
minute: 1,
second: 60
},
A1M::SecondsMatch
);
set_invalid_alarm_test!(
wd_invalid_min_masm,
set_alarm1_weekday,
WeekdayAlarm1 {
weekday: 1,
hour: Hours::H24(1),
minute: 60,
second: 1
},
A1M::MinutesAndSecondsMatch
);
} }
mod alarm2 { mod alarm2 {
use super::*; use super::*;
set_invalid_alarm_test!(
day_invalid_min_mm,
set_alarm2_day,
DayAlarm2 {
day: 1,
hour: Hours::H24(1),
minute: 60
},
A2M::MinutesMatch
);
set_invalid_alarm_test!( set_invalid_alarm_test!(
day_invalid_min, day_invalid_min,
set_alarm2_day, set_alarm2_day,
@ -352,16 +281,6 @@ mod alarm2 {
}, },
A2M::AllMatch A2M::AllMatch
); );
set_invalid_alarm_test!(
day_invalid_h_hamm,
set_alarm2_day,
DayAlarm2 {
day: 1,
hour: Hours::H24(24),
minute: 1
},
A2M::HoursAndMinutesMatch
);
set_invalid_alarm_test!( set_invalid_alarm_test!(
day_invalid_am1, day_invalid_am1,
set_alarm2_day, set_alarm2_day,
@ -423,16 +342,6 @@ mod alarm2 {
A2M::AllMatch A2M::AllMatch
); );
set_invalid_alarm_test!(
wd_invalid_min_mm,
set_alarm2_weekday,
WeekdayAlarm2 {
weekday: 1,
hour: Hours::H24(1),
minute: 60
},
A2M::MinutesMatch
);
set_invalid_alarm_test!( set_invalid_alarm_test!(
wd_invalid_min, wd_invalid_min,
set_alarm2_weekday, set_alarm2_weekday,
@ -453,16 +362,6 @@ mod alarm2 {
}, },
A2M::AllMatch A2M::AllMatch
); );
set_invalid_alarm_test!(
wd_invalid_h_hmm,
set_alarm2_weekday,
WeekdayAlarm2 {
weekday: 1,
hour: Hours::H24(24),
minute: 1
},
A2M::HoursAndMinutesMatch
);
set_invalid_alarm_test!( set_invalid_alarm_test!(
wd_invalid_am1, wd_invalid_am1,
set_alarm2_weekday, set_alarm2_weekday,
@ -523,32 +422,22 @@ mod alarm2 {
}, },
A2M::AllMatch A2M::AllMatch
); );
set_invalid_alarm_test!(
wd_invalid_minute,
set_alarm2_weekday,
WeekdayAlarm2 {
weekday: 1,
hour: Hours::H24(1),
minute: 60
},
A2M::HoursAndMinutesMatch
);
} }
macro_rules! _set_values_test { macro_rules! _set_values_test {
($name:ident, $method:ident, $create_method:ident, $destroy_method:ident, $transactions:expr, $( $value:expr ),+) => { ($name:ident, $method:ident, $create_method:ident, $destroy_method:ident, $value1:expr, $value2:expr, $transactions:expr) => {
#[test] #[test]
fn $name() { fn $name() {
let trans = $transactions; let trans = $transactions;
let mut dev = $create_method(&trans); let mut dev = $create_method(&trans);
dev.$method($($value),*).unwrap(); dev.$method($value1, $value2).unwrap();
$destroy_method(dev); $destroy_method(dev);
} }
}; };
} }
macro_rules! set_values_test { macro_rules! set_values_test {
($name:ident, $method:ident, $i2c_transactions:expr, $spi_transactions:expr, $( $value:expr ),+) => { ($name:ident, $method:ident, $value1:expr, $value2:expr, $i2c_transactions:expr, $spi_transactions:expr) => {
mod $name { mod $name {
use super::*; use super::*;
_set_values_test!( _set_values_test!(
@ -556,36 +445,37 @@ macro_rules! set_values_test {
$method, $method,
new_ds3231, new_ds3231,
destroy_ds3231, destroy_ds3231,
$i2c_transactions, $value1,
$($value),* $value2,
$i2c_transactions
); );
_set_values_test!( _set_values_test!(
can_set_ds3232, can_set_ds3232,
$method, $method,
new_ds3232, new_ds3232,
destroy_ds3232, destroy_ds3232,
$i2c_transactions, $value1,
$($value),* $value2,
$i2c_transactions
); );
_set_values_test!( _set_values_test!(
can_set_ds3234, can_set_ds3234,
$method, $method,
new_ds3234, new_ds3234,
destroy_ds3234, destroy_ds3234,
$spi_transactions, $value1,
$($value),* $value2,
$spi_transactions
); );
} }
}; };
} }
macro_rules! set_alarm_test { macro_rules! set_alarm_test {
($name:ident, $method:ident, $register:ident, [ $( $registers:expr ),+ ], $( $value:expr ),+) => { ($name:ident, $method:ident, $alarm:expr, $matching:expr, $register:ident, [ $( $registers:expr ),+ ]) => {
set_values_test!($name, $method, set_values_test!($name, $method, $alarm, $matching,
[ I2cTrans::write(DEV_ADDR, vec![Register::$register, $( $registers ),*]) ], [ I2cTrans::write(DEV_ADDR, vec![Register::$register, $( $registers ),*]) ],
[ SpiTrans::transaction_start(), SpiTrans::write_vec(vec![Register::$register + 0x80, $( $registers ),*]), SpiTrans::transaction_end() ], [ SpiTrans::write(vec![Register::$register + 0x80, $( $registers ),*]) ]);
$($value),*
);
}; };
} }
@ -596,152 +486,94 @@ mod alarm1_day {
set_alarm_test!( set_alarm_test!(
h24, h24,
set_alarm1_day, set_alarm1_day,
ALARM1_SECONDS,
[4, 3, 2, 1],
DayAlarm1 { DayAlarm1 {
day: 1, day: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3, minute: 3,
second: 4 second: 4
}, },
A1M::AllMatch A1M::AllMatch,
ALARM1_SECONDS,
[4, 3, 2, 1]
); );
set_alarm_test!( set_alarm_test!(
am, am,
set_alarm1_day, set_alarm1_day,
ALARM1_SECONDS,
[4, 3, 0b0100_0010, 1],
DayAlarm1 { DayAlarm1 {
day: 1, day: 1,
hour: Hours::AM(2), hour: Hours::AM(2),
minute: 3, minute: 3,
second: 4 second: 4
}, },
A1M::AllMatch A1M::AllMatch,
ALARM1_SECONDS,
[4, 3, 0b0100_0010, 1]
); );
set_alarm_test!( set_alarm_test!(
pm, pm,
set_alarm1_day, set_alarm1_day,
ALARM1_SECONDS,
[4, 3, 0b0110_0010, 1],
DayAlarm1 { DayAlarm1 {
day: 1, day: 1,
hour: Hours::PM(2), hour: Hours::PM(2),
minute: 3, minute: 3,
second: 4 second: 4
}, },
A1M::AllMatch A1M::AllMatch,
);
set_alarm_test!(
match_hms_naivetime,
set_alarm1_hms,
ALARM1_SECONDS, ALARM1_SECONDS,
[4, 3, 2, AM | 1], [4, 3, 0b0110_0010, 1]
NaiveTime::from_hms_opt(2, 3, 4).unwrap()
); );
set_alarm_test!( set_alarm_test!(
match_hms, match_hms,
set_alarm1_day, set_alarm1_day,
ALARM1_SECONDS,
[4, 3, 2, AM | 1],
DayAlarm1 { DayAlarm1 {
day: 1, day: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3, minute: 3,
second: 4 second: 4
}, },
A1M::HoursMinutesAndSecondsMatch A1M::HoursMinutesAndSecondsMatch,
);
set_alarm_test!(
match_hms_ignore_incorrect_day,
set_alarm1_day,
ALARM1_SECONDS, ALARM1_SECONDS,
[4, 3, 2, AM | 1], [4, 3, 2, AM | 1]
DayAlarm1 {
day: 0,
hour: Hours::H24(2),
minute: 3,
second: 4
},
A1M::HoursMinutesAndSecondsMatch
);
set_alarm_test!(
match_ms_ignore_invalid_hour,
set_alarm1_day,
ALARM1_SECONDS,
[4, 3, AM, AM | 1],
DayAlarm1 {
day: 1,
hour: Hours::H24(24),
minute: 3,
second: 4
},
A1M::MinutesAndSecondsMatch
); );
set_alarm_test!( set_alarm_test!(
match_ms, match_ms,
set_alarm1_day, set_alarm1_day,
ALARM1_SECONDS,
[4, 3, AM | 2, AM | 1],
DayAlarm1 { DayAlarm1 {
day: 1, day: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3, minute: 3,
second: 4 second: 4
}, },
A1M::MinutesAndSecondsMatch A1M::MinutesAndSecondsMatch,
);
set_alarm_test!(
match_ms_ignore_incorrect_day,
set_alarm1_day,
ALARM1_SECONDS, ALARM1_SECONDS,
[4, 3, AM | 2, AM | 1], [4, 3, AM | 2, AM | 1]
DayAlarm1 {
day: 0,
hour: Hours::H24(2),
minute: 3,
second: 4
},
A1M::MinutesAndSecondsMatch
); );
set_alarm_test!( set_alarm_test!(
match_s, match_s,
set_alarm1_day, set_alarm1_day,
ALARM1_SECONDS,
[4, AM | 3, AM | 2, AM | 1],
DayAlarm1 { DayAlarm1 {
day: 1, day: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3, minute: 3,
second: 4 second: 4
}, },
A1M::SecondsMatch A1M::SecondsMatch,
);
set_alarm_test!(
match_s_ignore_incorrect_min,
set_alarm1_day,
ALARM1_SECONDS, ALARM1_SECONDS,
[4, AM, AM | 2, AM | 1], [4, AM | 3, AM | 2, AM | 1]
DayAlarm1 {
day: 1,
hour: Hours::H24(2),
minute: 60,
second: 4
},
A1M::SecondsMatch
); );
set_alarm_test!( set_alarm_test!(
match_ops, match_ops,
set_alarm1_day, set_alarm1_day,
ALARM1_SECONDS,
[AM | 4, AM | 3, AM | 2, AM | 1],
DayAlarm1 { DayAlarm1 {
day: 1, day: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3, minute: 3,
second: 4 second: 4
}, },
A1M::OncePerSecond A1M::OncePerSecond,
ALARM1_SECONDS,
[AM | 4, AM | 3, AM | 2, AM | 1]
); );
} }
@ -750,132 +582,94 @@ mod alarm1_weekday {
set_alarm_test!( set_alarm_test!(
h24, h24,
set_alarm1_weekday, set_alarm1_weekday,
ALARM1_SECONDS,
[4, 3, 2, BF::WEEKDAY | 1],
WeekdayAlarm1 { WeekdayAlarm1 {
weekday: 1, weekday: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3, minute: 3,
second: 4 second: 4
}, },
A1M::AllMatch A1M::AllMatch,
ALARM1_SECONDS,
[4, 3, 2, BF::WEEKDAY | 1]
); );
set_alarm_test!( set_alarm_test!(
am, am,
set_alarm1_weekday, set_alarm1_weekday,
ALARM1_SECONDS,
[4, 3, 0b0100_0010, BF::WEEKDAY | 1],
WeekdayAlarm1 { WeekdayAlarm1 {
weekday: 1, weekday: 1,
hour: Hours::AM(2), hour: Hours::AM(2),
minute: 3, minute: 3,
second: 4 second: 4
}, },
A1M::AllMatch A1M::AllMatch,
ALARM1_SECONDS,
[4, 3, 0b0100_0010, BF::WEEKDAY | 1]
); );
set_alarm_test!( set_alarm_test!(
pm, pm,
set_alarm1_weekday, set_alarm1_weekday,
ALARM1_SECONDS,
[4, 3, 0b0110_0010, BF::WEEKDAY | 1],
WeekdayAlarm1 { WeekdayAlarm1 {
weekday: 1, weekday: 1,
hour: Hours::PM(2), hour: Hours::PM(2),
minute: 3, minute: 3,
second: 4 second: 4
}, },
A1M::AllMatch A1M::AllMatch,
);
set_alarm_test!(
match_hms_ignore_incorrect_wd,
set_alarm1_weekday,
ALARM1_SECONDS, ALARM1_SECONDS,
[4, 3, 2, AM | BF::WEEKDAY | 1], [4, 3, 0b0110_0010, BF::WEEKDAY | 1]
WeekdayAlarm1 {
weekday: 0,
hour: Hours::H24(2),
minute: 3,
second: 4
},
A1M::HoursMinutesAndSecondsMatch
); );
set_alarm_test!( set_alarm_test!(
match_hms, match_hms,
set_alarm1_weekday, set_alarm1_weekday,
ALARM1_SECONDS,
[4, 3, 2, AM | BF::WEEKDAY | 1],
WeekdayAlarm1 { WeekdayAlarm1 {
weekday: 1, weekday: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3, minute: 3,
second: 4 second: 4
}, },
A1M::HoursMinutesAndSecondsMatch A1M::HoursMinutesAndSecondsMatch,
ALARM1_SECONDS,
[4, 3, 2, AM | BF::WEEKDAY | 1]
); );
set_alarm_test!( set_alarm_test!(
match_ms, match_ms,
set_alarm1_weekday, set_alarm1_weekday,
ALARM1_SECONDS,
[4, 3, AM | 2, AM | BF::WEEKDAY | 1],
WeekdayAlarm1 { WeekdayAlarm1 {
weekday: 1, weekday: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3, minute: 3,
second: 4 second: 4
}, },
A1M::MinutesAndSecondsMatch A1M::MinutesAndSecondsMatch,
ALARM1_SECONDS,
[4, 3, AM | 2, AM | BF::WEEKDAY | 1]
); );
set_alarm_test!( set_alarm_test!(
match_s, match_s,
set_alarm1_weekday, set_alarm1_weekday,
ALARM1_SECONDS,
[4, AM | 3, AM | 2, AM | BF::WEEKDAY | 1],
WeekdayAlarm1 { WeekdayAlarm1 {
weekday: 1, weekday: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3, minute: 3,
second: 4 second: 4
}, },
A1M::SecondsMatch A1M::SecondsMatch,
);
set_alarm_test!(
match_s_ignore_incorrect_min,
set_alarm1_weekday,
ALARM1_SECONDS, ALARM1_SECONDS,
[4, AM, AM | 2, AM | BF::WEEKDAY | 1], [4, AM | 3, AM | 2, AM | BF::WEEKDAY | 1]
WeekdayAlarm1 {
weekday: 1,
hour: Hours::H24(2),
minute: 60,
second: 4
},
A1M::SecondsMatch
); );
set_alarm_test!( set_alarm_test!(
match_ops, match_ops,
set_alarm1_weekday, set_alarm1_weekday,
ALARM1_SECONDS,
[AM | 4, AM | 3, AM | 2, AM | BF::WEEKDAY | 1],
WeekdayAlarm1 { WeekdayAlarm1 {
weekday: 1, weekday: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3, minute: 3,
second: 4 second: 4
}, },
A1M::OncePerSecond A1M::OncePerSecond,
);
set_alarm_test!(
match_ops_ignore_incorrect_sec,
set_alarm1_weekday,
ALARM1_SECONDS, ALARM1_SECONDS,
[AM, AM | 3, AM | 2, AM | BF::WEEKDAY | 1], [AM | 4, AM | 3, AM | 2, AM | BF::WEEKDAY | 1]
WeekdayAlarm1 {
weekday: 1,
hour: Hours::H24(2),
minute: 3,
second: 60
},
A1M::OncePerSecond
); );
} }
@ -884,117 +678,75 @@ mod alarm2_day {
set_alarm_test!( set_alarm_test!(
h24, h24,
set_alarm2_day, set_alarm2_day,
ALARM2_MINUTES,
[3, 2, 1],
DayAlarm2 { DayAlarm2 {
day: 1, day: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3 minute: 3
}, },
A2M::AllMatch A2M::AllMatch,
ALARM2_MINUTES,
[3, 2, 1]
); );
set_alarm_test!( set_alarm_test!(
am, am,
set_alarm2_day, set_alarm2_day,
ALARM2_MINUTES,
[3, 0b0100_0010, 1],
DayAlarm2 { DayAlarm2 {
day: 1, day: 1,
hour: Hours::AM(2), hour: Hours::AM(2),
minute: 3 minute: 3
}, },
A2M::AllMatch A2M::AllMatch,
ALARM2_MINUTES,
[3, 0b0100_0010, 1]
); );
set_alarm_test!( set_alarm_test!(
pm, pm,
set_alarm2_day, set_alarm2_day,
ALARM2_MINUTES,
[3, 0b0110_0010, 1],
DayAlarm2 { DayAlarm2 {
day: 1, day: 1,
hour: Hours::PM(2), hour: Hours::PM(2),
minute: 3 minute: 3
}, },
A2M::AllMatch A2M::AllMatch,
);
set_alarm_test!(
match_hm_naivetime,
set_alarm2_hm,
ALARM2_MINUTES, ALARM2_MINUTES,
[3, 2, AM | 1], [3, 0b0110_0010, 1]
NaiveTime::from_hms_opt(2, 3, 0).unwrap()
); );
set_alarm_test!( set_alarm_test!(
match_hm, match_hm,
set_alarm2_day, set_alarm2_day,
ALARM2_MINUTES,
[3, 2, AM | 1],
DayAlarm2 { DayAlarm2 {
day: 1, day: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3 minute: 3
}, },
A2M::HoursAndMinutesMatch A2M::HoursAndMinutesMatch,
);
set_alarm_test!(
match_hm_ignore_incorrect_day,
set_alarm2_day,
ALARM2_MINUTES, ALARM2_MINUTES,
[3, 2, AM | 1], [3, 2, AM | 1]
DayAlarm2 {
day: 0,
hour: Hours::H24(2),
minute: 3
},
A2M::HoursAndMinutesMatch
); );
set_alarm_test!( set_alarm_test!(
match_m, match_m,
set_alarm2_day, set_alarm2_day,
ALARM2_MINUTES,
[3, AM | 2, AM | 1],
DayAlarm2 { DayAlarm2 {
day: 1, day: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3 minute: 3
}, },
A2M::MinutesMatch A2M::MinutesMatch,
);
set_alarm_test!(
match_m_ignore_invalid_h,
set_alarm2_day,
ALARM2_MINUTES, ALARM2_MINUTES,
[3, AM, AM | 1], [3, AM | 2, AM | 1]
DayAlarm2 {
day: 1,
hour: Hours::H24(25),
minute: 3
},
A2M::MinutesMatch
); );
set_alarm_test!( set_alarm_test!(
match_opm, match_opm,
set_alarm2_day, set_alarm2_day,
ALARM2_MINUTES,
[AM | 3, AM | 2, AM | 1],
DayAlarm2 { DayAlarm2 {
day: 1, day: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3 minute: 3
}, },
A2M::OncePerMinute A2M::OncePerMinute,
);
set_alarm_test!(
match_opm_ignore_incorrect_min,
set_alarm2_day,
ALARM2_MINUTES, ALARM2_MINUTES,
[AM, AM | 2, AM | 1], [AM | 3, AM | 2, AM | 1]
DayAlarm2 {
day: 1,
hour: Hours::H24(2),
minute: 60
},
A2M::OncePerMinute
); );
} }
@ -1003,109 +755,74 @@ mod alarm2_weekday {
set_alarm_test!( set_alarm_test!(
h24, h24,
set_alarm2_weekday, set_alarm2_weekday,
ALARM2_MINUTES,
[3, 2, BF::WEEKDAY | 1],
WeekdayAlarm2 { WeekdayAlarm2 {
weekday: 1, weekday: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3 minute: 3
}, },
A2M::AllMatch A2M::AllMatch,
ALARM2_MINUTES,
[3, 2, BF::WEEKDAY | 1]
); );
set_alarm_test!( set_alarm_test!(
am, am,
set_alarm2_weekday, set_alarm2_weekday,
ALARM2_MINUTES,
[3, 0b0100_0010, BF::WEEKDAY | 1],
WeekdayAlarm2 { WeekdayAlarm2 {
weekday: 1, weekday: 1,
hour: Hours::AM(2), hour: Hours::AM(2),
minute: 3 minute: 3
}, },
A2M::AllMatch A2M::AllMatch,
ALARM2_MINUTES,
[3, 0b0100_0010, BF::WEEKDAY | 1]
); );
set_alarm_test!( set_alarm_test!(
pm, pm,
set_alarm2_weekday, set_alarm2_weekday,
ALARM2_MINUTES,
[3, 0b0110_0010, BF::WEEKDAY | 1],
WeekdayAlarm2 { WeekdayAlarm2 {
weekday: 1, weekday: 1,
hour: Hours::PM(2), hour: Hours::PM(2),
minute: 3 minute: 3
}, },
A2M::AllMatch A2M::AllMatch,
ALARM2_MINUTES,
[3, 0b0110_0010, BF::WEEKDAY | 1]
); );
set_alarm_test!( set_alarm_test!(
match_hm, match_hm,
set_alarm2_weekday, set_alarm2_weekday,
ALARM2_MINUTES,
[3, 2, AM | BF::WEEKDAY | 1],
WeekdayAlarm2 { WeekdayAlarm2 {
weekday: 1, weekday: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3 minute: 3
}, },
A2M::HoursAndMinutesMatch A2M::HoursAndMinutesMatch,
);
set_alarm_test!(
match_hm_ignore_incorrect_wd,
set_alarm2_weekday,
ALARM2_MINUTES, ALARM2_MINUTES,
[3, 2, AM | BF::WEEKDAY | 1], [3, 2, AM | BF::WEEKDAY | 1]
WeekdayAlarm2 {
weekday: 0,
hour: Hours::H24(2),
minute: 3
},
A2M::HoursAndMinutesMatch
); );
set_alarm_test!( set_alarm_test!(
match_m, match_m,
set_alarm2_weekday, set_alarm2_weekday,
ALARM2_MINUTES,
[3, AM | 2, AM | BF::WEEKDAY | 1],
WeekdayAlarm2 { WeekdayAlarm2 {
weekday: 1, weekday: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3 minute: 3
}, },
A2M::MinutesMatch A2M::MinutesMatch,
);
set_alarm_test!(
match_m_ignore_invalid_hour,
set_alarm2_weekday,
ALARM2_MINUTES, ALARM2_MINUTES,
[3, AM, AM | BF::WEEKDAY | 1], [3, AM | 2, AM | BF::WEEKDAY | 1]
WeekdayAlarm2 {
weekday: 1,
hour: Hours::H24(24),
minute: 3
},
A2M::MinutesMatch
); );
set_alarm_test!( set_alarm_test!(
match_opm, match_opm,
set_alarm2_weekday, set_alarm2_weekday,
ALARM2_MINUTES,
[AM | 3, AM | 2, AM | BF::WEEKDAY | 1],
WeekdayAlarm2 { WeekdayAlarm2 {
weekday: 1, weekday: 1,
hour: Hours::H24(2), hour: Hours::H24(2),
minute: 3 minute: 3
}, },
A2M::OncePerMinute A2M::OncePerMinute,
);
set_alarm_test!(
match_opm_ignore_incorrect_min,
set_alarm2_weekday,
ALARM2_MINUTES, ALARM2_MINUTES,
[AM, AM | 2, AM | BF::WEEKDAY | 1], [AM | 3, AM | 2, AM | BF::WEEKDAY | 1]
WeekdayAlarm2 {
weekday: 1,
hour: Hours::H24(2),
minute: 60
},
A2M::OncePerMinute
); );
} }

View File

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

View File

@ -1,8 +1,12 @@
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 ds323x::SqWFreq;
use embedded_hal_mock::eh1::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
mod common; mod common;
use self::common::{ use common::{
destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234, 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, BitFlags as BF, Register, CONTROL_POR_VALUE, DEVICE_ADDRESS as DEV_ADDR, DS3231_POR_STATUS,
DS323X_POR_STATUS, DS323X_POR_STATUS,
@ -46,11 +50,10 @@ macro_rules! call_method_test {
DEV_ADDR, DEV_ADDR,
vec![Register::$register, $value_enabled] vec![Register::$register, $value_enabled]
)], )],
[ [SpiTrans::write(vec![
SpiTrans::transaction_start(), Register::$register + 0x80,
SpiTrans::write_vec(vec![Register::$register + 0x80, $value_enabled]), $value_enabled
SpiTrans::transaction_end(), ])]
]
); );
}; };
} }
@ -84,11 +87,10 @@ macro_rules! call_method_status_test {
$method, $method,
new_ds3234, new_ds3234,
destroy_ds3234, destroy_ds3234,
[ [SpiTrans::write(vec![
SpiTrans::transaction_start(), Register::STATUS + 0x80,
SpiTrans::write_vec(vec![Register::STATUS + 0x80, $value_ds323x]), $value_ds323x
SpiTrans::transaction_end(), ])]
]
); );
} }
}; };
@ -106,14 +108,10 @@ macro_rules! change_if_necessary_test {
vec![Register::$register], vec![Register::$register],
vec![$value_enabled] vec![$value_enabled]
)], )],
[ [SpiTrans::transfer(
SpiTrans::transaction_start(), vec![Register::$register, 0],
SpiTrans::transfer_in_place( vec![Register::$register, $value_enabled]
vec![Register::$register, 0], )]
vec![Register::$register, $value_enabled]
),
SpiTrans::transaction_end(),
]
); );
call_triple_test!( call_triple_test!(
@ -128,15 +126,11 @@ macro_rules! change_if_necessary_test {
I2cTrans::write(DEV_ADDR, vec![Register::$register, $value_enabled]) I2cTrans::write(DEV_ADDR, vec![Register::$register, $value_enabled])
], ],
[ [
SpiTrans::transaction_start(), SpiTrans::transfer(
SpiTrans::transfer_in_place(
vec![Register::$register, 0], vec![Register::$register, 0],
vec![Register::$register, $value_disabled] vec![Register::$register, $value_disabled]
), ),
SpiTrans::transaction_end(), SpiTrans::write(vec![Register::$register + 0x80, $value_enabled])
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::$register + 0x80, $value_enabled]),
SpiTrans::transaction_end(),
] ]
); );
} }
@ -230,12 +224,18 @@ set_param_test!(
get_param_test!( get_param_test!(
get_aging_offset_min, get_aging_offset_min,
aging_offset, get_aging_offset,
AGING_OFFSET, AGING_OFFSET,
-128, -128,
0b1000_0000 0b1000_0000
); );
get_param_test!(get_aging_offset_max, aging_offset, AGING_OFFSET, 127, 127); get_param_test!(
get_aging_offset_max,
get_aging_offset,
AGING_OFFSET,
127,
127
);
call_method_test!( call_method_test!(
int_sqw_out_int, int_sqw_out_int,

View File

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

View File

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

View File

@ -1,9 +1,13 @@
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 ds323x::TempConvRate;
use embedded_hal_mock::eh1::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
#[allow(unused)] #[allow(unused)]
mod common; mod common;
use self::common::{ use common::{
destroy_ds3232, destroy_ds3234, new_ds3232, new_ds3234, BitFlags as BF, Register, destroy_ds3232, destroy_ds3234, new_ds3232, new_ds3234, BitFlags as BF, Register,
DEVICE_ADDRESS as DEV_ADDR, DS323X_POR_STATUS, DEVICE_ADDRESS as DEV_ADDR, DS323X_POR_STATUS,
}; };
@ -24,11 +28,7 @@ macro_rules! call_method_status_test {
$method, $method,
new_ds3234, new_ds3234,
destroy_ds3234, destroy_ds3234,
[ [SpiTrans::write(vec![Register::STATUS + 0x80, $value])]
SpiTrans::transaction_start(),
SpiTrans::write_vec(vec![Register::STATUS + 0x80, $value]),
SpiTrans::transaction_end(),
]
); );
} }
}; };
@ -70,11 +70,10 @@ macro_rules! set_param_test_2_4 {
DEV_ADDR, DEV_ADDR,
vec![Register::$register, $binary_value] vec![Register::$register, $binary_value]
)], )],
[ [SpiTrans::write(vec![
SpiTrans::transaction_start(), Register::$register + 0x80,
SpiTrans::write_vec(vec![Register::$register + 0x80, $binary_value]), $binary_value
SpiTrans::transaction_end(), ])]
]
); );
}; };
} }

View File

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

View File

@ -1,6 +1,8 @@
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; mod common;
use self::common::{ use common::{
destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234, destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234,
BitFlags as BF, Register, DEVICE_ADDRESS as DEV_ADDR, BitFlags as BF, Register, DEVICE_ADDRESS as DEV_ADDR,
}; };
@ -32,10 +34,10 @@ get_param_test!(
!BF::ALARM2F !BF::ALARM2F
); );
get_param_read_array_test!(temp_0, temperature, 0.0, TEMP_MSB, [0, 0], [0, 0]); get_param_read_array_test!(temp_0, get_temperature, 0.0, TEMP_MSB, [0, 0], [0, 0]);
get_param_read_array_test!( get_param_read_array_test!(
temp_min, temp_min,
temperature, get_temperature,
-128.0, -128.0,
TEMP_MSB, TEMP_MSB,
[0b1000_0000, 0], [0b1000_0000, 0],
@ -43,7 +45,7 @@ get_param_read_array_test!(
); );
get_param_read_array_test!( get_param_read_array_test!(
temp_max, temp_max,
temperature, get_temperature,
127.75, 127.75,
TEMP_MSB, TEMP_MSB,
[0b0111_1111, 0b1100_0000], [0b0111_1111, 0b1100_0000],