Compare commits

...

128 Commits

Author SHA1 Message Date
Diego Barrios Romero c5fdd9057f chore: Release ds323x version 0.6.0 2025-01-02 09:26:23 +01:00
Diego Barrios Romero 86dd80a8ad Fix clippy warnings 2025-01-02 08:40:04 +01:00
Diego Barrios Romero 3597c7407e Finish updating to embedded-hal 1.0.0 2025-01-02 08:35:42 +01:00
Diego Barrios Romero 21546c07d3 Update changelog 2025-01-02 08:32:26 +01:00
Diego Barrios Romero ddca93fa30 Update dependency 2025-01-02 08:31:23 +01:00
Diego Barrios Romero b086f3086e Fix formatting 2025-01-02 08:31:08 +01:00
Louis Dupré Bertoni 03e0d9aad9 Update dependencies 2025-01-02 08:27:52 +01:00
Diego Barrios Romero 8204a4dfc1 Support cargo-release 2025-01-02 08:18:02 +01:00
Diego Barrios Romero 4e0998897a Bump MSRV 2025-01-02 08:15:40 +01:00
Diego Barrios Romero f546361477 Update CI 2025-01-02 08:14:10 +01:00
Diego Barrios Romero fa43172087 Prepare 0.5.1 release 2023-07-17 21:53:42 +02:00
Diego Barrios Romero 74009022bf Update changelog 2023-07-17 07:51:52 +02:00
kirbylife fd6a145825 fix set_day method
The method receives in decimal the value to be set as the day of the month, but the ds3231 module expects it in bcd format.  
Using the 'write_register_decimal' method, can be written in the correct format.
2023-07-17 13:50:17 +02:00
Diego Barrios Romero de0231b958 Raise MSRV and update CI 2023-07-16 13:44:50 +02:00
Diego Barrios Romero a8b8428570 Update dependency 2023-07-16 13:44:14 +02:00
Diego Barrios Romero f7d48398dc Update copyright 2023-07-16 13:43:59 +02:00
Diego Barrios Romero fe990d7295 Avoid using deprecated methods 2023-07-16 13:43:46 +02:00
Diego Barrios Romero b06eb59cd6 Add changelog link 2022-02-21 07:11:36 +01:00
Diego Barrios Romero b0ab213098 Prepare 0.5.0 release 2022-02-21 07:10:05 +01:00
Diego Barrios Romero 6c6088f890 Raise MSRV to 1.35.0 2022-02-20 10:55:37 +01:00
Diego Barrios Romero b3323f1a15 Enhance support notice 2022-02-20 10:39:28 +01:00
Diego Barrios Romero 944e661053 Update CI 2022-02-20 10:35:59 +01:00
Diego Barrios Romero faf48c2870 Fix small clippy warnings and run it on tests as well on CI 2022-02-20 10:27:12 +01:00
Diego Barrios Romero 92e6de6c47 Update docs 2022-02-20 10:19:50 +01:00
Diego Barrios Romero 774b3cf527 Update to rtcc 0.3 2022-02-20 10:17:55 +01:00
Diego Barrios Romero fd66a531e2 Document MSRV in readme 2022-02-20 10:08:15 +01:00
Rafael Bachmann e4f15715ed Bump linux-embedded-hal to 0.3.2 2021-10-26 11:18:03 +02:00
Rafael Bachmann 7329fe7c50 Upgrade dependencies 2021-10-26 11:18:03 +02:00
Rafael Bachmann 1930de500d Fix clippy warnings 2021-10-26 11:18:03 +02:00
Diego Barrios Romero a657eec8d6 Prepare 0.4.0 release 2021-05-22 23:16:44 +02:00
Diego Barrios Romero 9c6fe164b8 Fix returning error if the device state is invalid and leads to invalid date/time values 2021-05-22 23:09:10 +02:00
Diego Barrios Romero 0b17437d4c Prepare 0.3.2 release 2021-02-22 11:02:06 +01:00
Diego Barrios Romero 7a566fd59c Improve Readme 2021-02-22 11:01:56 +01:00
Diego Barrios Romero 082fb2d159 Update changelog after merging 2021-02-22 10:55:04 +01:00
James Munns b8ab37f57a Correct day bounds 2021-02-22 10:48:56 +01:00
Diego Barrios Romero a28b7c76fc Use GHA instead of Travis for CI 2021-01-30 20:22:32 +01:00
Diego Barrios Romero 2799590575 Fix imports 2020-07-10 22:42:39 +02:00
Diego Barrios Romero 5414721d1d Add docs html root url 2020-07-10 22:32:08 +02:00
Diego Barrios Romero 75291cde4c Bump patch version 2020-07-10 22:26:12 +02:00
Diego Barrios Romero 5ce8722fad Add changelog entry for release 2020-07-10 22:25:41 +02:00
Diego Barrios Romero f146d1d26e Use Rust edition 2018 2020-07-10 22:24:29 +02:00
Diego Barrios Romero ff632625b2 Add example of new alarm setting method 2020-07-10 21:59:54 +02:00
Diego Barrios Romero 73302b1866 Add new methods to documentation 2020-07-10 21:53:59 +02:00
Diego Barrios Romero c5a4e3df62 Remove maintenance badges 2020-07-10 21:43:02 +02:00
Diego Barrios Romero b62488f592 Improve documentation 2020-07-10 21:35:03 +02:00
Marc Poulhiès 044051c2d9 Support setting alarms from a NaiveTime
It allows for more concise user code when setting alarm from such object.
2020-07-05 09:41:59 +02:00
Marc Poulhiès 092af13298 Better detect incorrect input for alarms, refactor some test macros
Alarm can be triggered on a subset of input, no need to reject ignored
fields (eg. don't care about day field when matching on time only). We still
need to set these to correct values as they are still used to set alarm
registers.

Test macro have been refactored to accept variable number of arguments.
2020-07-05 09:41:59 +02:00
Diego Barrios Romero 8c5bb16030 Add changelog release entry 2020-05-02 12:59:31 +02:00
Diego Barrios Romero 08d5192367 Extract method for H24 conversion 2020-05-02 12:45:05 +02:00
Diego Barrios Romero 4a455b7828 Add test setting max year date 2020-05-02 12:37:47 +02:00
Diego Barrios Romero 9fe57bbdf6 Code formatting 2020-05-02 12:17:55 +02:00
Diego Barrios Romero b22cba6c63 Small simplification 2020-05-02 12:17:49 +02:00
Diego Barrios Romero 1b13f482ae Fix build for Rust 1.31.0 2020-05-02 12:11:22 +02:00
Diego Barrios Romero 3425e28959 Bump version 2020-05-02 11:55:32 +02:00
Diego Barrios Romero 17c9230e1f Update CI 2020-05-02 11:50:43 +02:00
Diego Barrios Romero fdc71accf3 Update changelog 2020-05-02 11:49:23 +02:00
Diego Barrios Romero c3106c0f49 Rename methods due to Rust conventions 2020-05-02 11:48:45 +02:00
Diego Barrios Romero 3c8d378d6f Implement Rtcc trait 2020-05-02 11:36:42 +02:00
Diego Barrios Romero d3329b2598 Update dependencies 2020-05-01 21:15:17 +02:00
Diego Barrios Romero ed9502842d Seal public types 2019-05-16 19:24:25 +02:00
Diego Barrios Romero 49f72014a7 Remove unnecessary statements 2019-05-16 19:24:13 +02:00
Diego Barrios Romero af20f0321c Code formatting 2019-05-16 19:18:07 +02:00
Diego Barrios Romero c74f6a86f4 Add SPI mode constants 2019-05-16 19:12:39 +02:00
Diego Barrios Romero 68c6ffd0a5 Use default ubuntu 2019-05-16 19:00:02 +02:00
Diego Barrios Romero e8679f0b8a Bump embedded-hal dependency 2019-05-16 18:50:54 +02:00
Diego Barrios Romero 0ef5cec642 Use embedded_hal::digital::v2::OutputPin traits 2019-05-16 18:50:11 +02:00
Diego Barrios Romero f9a4918354 Remove leading 0 to avoid confusion 2019-04-17 17:38:23 +02:00
Diego Barrios Romero 954ab66f3b Small simplification 2019-04-17 17:38:23 +02:00
Diego Barrios Romero 1be01a3b18 Add links to driver-examples repo 2019-04-17 17:38:23 +02:00
Diego Barrios Romero 79c0b870df Add support notice 2019-04-17 17:38:23 +02:00
Diego Barrios Romero 4db4677ea2 Do not deny warnings 2019-04-17 17:38:23 +02:00
Diego Barrios Romero ab3739c7c3 Improve file inclusion pattern 2019-04-17 17:38:23 +02:00
Diego Barrios Romero 0222a79f0d Add MSRV to CI 2019-04-17 17:38:23 +02:00
Diego Barrios Romero db921768ee Fix formatting 2019-04-17 17:38:23 +02:00
Diego Barrios Romero a4c1348993 Use ubuntu xenial to work around current musl problem 2019-04-17 17:38:23 +02:00
Diego Barrios Romero cd4a91b79e Use stable and beta channels 2019-03-16 19:12:36 +01:00
Diego Barrios Romero 9f3f8be554 Define variable as global 2018-11-20 20:34:42 +01:00
Diego Barrios Romero 25f272015a Disable clippy for the moment 2018-11-20 19:35:26 +01:00
Diego Barrios Romero 9510730e24 Use centralized ci for coverage 2018-11-19 22:05:44 +01:00
Diego Barrios Romero 37ae7aba43 Combine and extract before_install 2018-11-18 17:52:16 +01:00
Diego Barrios Romero fd5f3cc981 Use before install phase for ci scripts 2018-11-18 17:42:35 +01:00
Diego Barrios Romero 7491105873 Clone ci scripts into folder 2018-11-18 17:37:57 +01:00
Diego Barrios Romero e378737f4d Use full urls 2018-11-18 17:30:10 +01:00
Diego Barrios Romero dfe90b9000 Put paths in quotes 2018-11-18 17:27:39 +01:00
Diego Barrios Romero 7891a53705 Use CI scripts from separate repository 2018-11-18 17:23:38 +01:00
Diego Barrios Romero 0451dbb239 Remove duplicated examples 2018-11-16 20:22:40 +01:00
Diego Barrios Romero 903e2496f2 Run tests in rust stable 2018-11-16 20:22:29 +01:00
Diego Barrios Romero dc05ca6bee Run coverage in rust stable 2018-11-16 20:22:12 +01:00
Diego Barrios Romero b4cfbbb2d9 Run coverage in rust nightly 2018-11-16 20:03:23 +01:00
Diego Barrios Romero f53554b404 Add alarm 1 and 2 examples 2018-11-16 19:48:56 +01:00
Diego Barrios Romero d0bcb715ce destroy device at the end 2018-11-16 19:42:47 +01:00
Diego Barrios Romero e5ee15439f Remove redundant statement 2018-11-16 19:34:43 +01:00
Diego Barrios Romero 55bbf9098a Put construction tests together 2018-11-16 19:32:33 +01:00
Diego Barrios Romero dee3deb073 allow unused constant 2018-11-16 19:30:45 +01:00
Diego Barrios Romero 729a4c92e7 Import crate in module 2018-11-16 19:30:35 +01:00
Diego Barrios Romero 96168aa696 Add coverage badge 2018-11-16 19:18:42 +01:00
Diego Barrios Romero c7da098f80 Simplify code 2018-11-16 19:12:36 +01:00
Diego Barrios Romero eb87a741f6 Remove redundant statements 2018-11-16 19:11:13 +01:00
Diego Barrios Romero eb614c73a0 Consolidate variables 2018-11-16 18:46:38 +01:00
Diego Barrios Romero a5c8b8afaa Add coverage support 2018-11-16 18:46:06 +01:00
Diego Barrios Romero 441eb40368 Update changelog 2018-11-16 18:36:39 +01:00
Diego Barrios Romero a92bd92af6 Bump version 2018-11-16 18:36:33 +01:00
Diego Barrios Romero 3459b41fed Move alarm section up in documentation 2018-11-16 18:33:45 +01:00
Diego Barrios Romero e8516dd9bb Add doc generation to CI 2018-11-16 18:23:56 +01:00
Diego Barrios Romero c10bf7de9d Rename date to day as it is a day of the month. 2018-11-16 18:22:30 +01:00
Diego Barrios Romero 5bff87333c Add maintenance intention badge 2018-11-16 18:07:02 +01:00
Diego Barrios Romero 81e5ac7225 Put badges in separate lines 2018-11-16 18:06:42 +01:00
Diego Barrios Romero 7d7008a583 Make type conversions explicit 2018-11-16 17:59:17 +01:00
Diego Barrios Romero f971786c0d Pass variablue per value 2018-11-16 17:50:06 +01:00
Diego Barrios Romero 4674d06546 Make types copyable 2018-11-16 17:47:48 +01:00
Diego Barrios Romero a7c3f0c83a Simplify range 2018-11-16 17:45:03 +01:00
Diego Barrios Romero 06e5aaddf3 Combine statement 2018-11-16 17:37:04 +01:00
Diego Barrios Romero 9ce8d106ce Extend linux example 2018-11-16 17:36:15 +01:00
Diego Barrios Romero ce7463fc51 Add usage example to readme 2018-11-16 17:34:10 +01:00
Diego Barrios Romero 30cb5566d7 Reorganize driver capabilities 2018-11-16 17:25:24 +01:00
Diego Barrios Romero 34f32ee067 Update changelog 2018-11-03 08:12:01 +01:00
Diego Barrios Romero 0de8f5b8b7 Add examples of setting alarms 2018-11-03 08:11:53 +01:00
Diego Barrios Romero b717f4e62b Add support for setting alarm 2 2018-11-03 08:11:38 +01:00
Diego Barrios Romero c3927e46c3 Add support for settin alarm1 to a weekday 2018-11-03 08:09:32 +01:00
Diego Barrios Romero 46d6656009 Add function to set the alarm1 to a date 2018-11-03 08:06:18 +01:00
Diego Barrios Romero e6a1c4826f Improve documentation 2018-11-03 07:59:59 +01:00
Diego Barrios Romero 7e9bfcff13 Add support for enabling/disabling alarm interrupts 2018-11-03 07:59:41 +01:00
Diego Barrios Romero d883c9c499 Add support for checking if alarms have matched and clearing the flag 2018-11-03 07:58:24 +01:00
Diego Barrios Romero 0dec4954e9 Fix alignment 2018-11-03 07:56:36 +01:00
Diego Barrios Romero 1465d116ab Extract function to convert Hours to a register value 2018-11-03 07:55:39 +01:00
Diego Barrios Romero 4675e902ad Move conversion function up for reuse 2018-11-03 07:54:47 +01:00
Diego Barrios Romero 2304e08d1f Change: clear_has_been_stopped_flag always clears without checking first 2018-11-03 07:53:01 +01:00
Diego Barrios Romero 77820cf6d0 Extract assert invalid input data macro 2018-11-03 07:51:24 +01:00
30 changed files with 3329 additions and 1008 deletions

110
.github/workflows/build.yml vendored 100644
View File

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

View File

@ -1,56 +0,0 @@
language: rust
matrix:
include:
- env: TARGET=x86_64-unknown-linux-gnu
- env: TARGET=x86_64-unknown-linux-gnu
rust: nightly
- env: TARGET=x86_64-unknown-linux-musl
rust: nightly
# Raspberry Pi 1
- env: TARGET=arm-unknown-linux-gnueabi DISABLE_EXAMPLES=1 DISABLE_TESTS=1
rust: nightly
# Raspberry Pi 2, 3, etc
- env: TARGET=armv7-unknown-linux-gnueabihf DISABLE_EXAMPLES=1 DISABLE_TESTS=1
rust: nightly
# Bare metal
- env: TARGET=thumbv6m-none-eabi
rust: beta
- env: TARGET=thumbv7em-none-eabi
rust: beta
- env: TARGET=thumbv7em-none-eabihf
rust: beta
- env: TARGET=thumbv7m-none-eabi
rust: beta
before_install:
- set -e
- rustup self update
install:
- bash ci/install.sh
script:
- bash ci/script.sh
after_script: set +e
cache: cargo
before_cache:
# Travis can't cache files that are not readable by "others"
- chmod -R a+r $HOME/.cargo
branches:
only:
- master
- staging
- trying
notifications:
email:
on_success: never

View File

@ -5,13 +5,98 @@ 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).
## [Unreleased] <!-- next-header -->
## [Unreleased] - ReleaseDate
... ## [0.6.0] - 2025-01-02
### Changed
- [breaking-change] Removed `Error::Pin` variant.
- [breaking-change] Update to `embedded-hal` 1.0.0.
- Raised MSRV to version 1.75.0
## [0.5.1] - 2023-07-17
### Fixed
- Fixed `set_day` method. See: [PR #9](https://github.com/eldruin/ds323x-rs/pull/9)
### Changed
- Raised MSRV to version 1.60.0
## [0.5.0] - 2022-02-21
### Changed
- [breaking-change] Update `rtcc` to version 0.3.
- [breaking-change] Remove `get_` from all public method names to comply with the Rust API guidelines.
- Raise MSRV to version 1.35.0
## [0.4.0] - 2021-05-22
### Changed
- [breaking-change] Return `Error::InvalidDeviceState` if it was not possible to read the
date and/or time from the device because the state of the device corresponds to
an invalid date and/or time.
## [0.3.2] - 2021-02-22
### Fixed
- Day bounds on the `set_day()` method. Thanks to @jamesmunns. See:
[PR #5](https://github.com/eldruin/ds323x-rs/pull/5)
## [0.3.1] - 2020-07-10
### Added
- Added methods to set alarms 1 and 2 with a `chrono::NaiveTime`: `set_alarm1_hms()`
and `set_alarm2_hm()`.
### Changed
- Changed alarm setting methods to automatically correct invalid values to irrelevant
input parameters due to the selected matching strategy.
## [0.3.0] - 2020-05-02
### Changed
- [breaking-change] Renamed `is_busy()` and `is_running()` methods `busy()` and `running()`
due to Rust naming conventions.
- Implement trait from [`rtcc`] crate.
- Changed `get_datetime()` and `set_datetime()` parameter from `DateTime`
to `chrono::NaiveDateTime`.
### Added
- Methods to set and get date and time using `chrono::NaiveDate` and `chrono::NaiveTime`:
- `get_time()`
- `set_time()`
- `get_date()`
- `set_date()`
- [`chrono`] (through [`rtcc`]) dependency.
### Removed
- `DateTime` data structure was replaced by `chrono::NaiveDateTime`.
## [0.2.0] - 2018-11-16
### Added
- Support for configuration of alarms 1 and 2.
### Changed
- [breaking-change] `clear_has_been_stopped_flag()` always sets the value of the status register.
## 0.1.0 - 2018-10-31 ## 0.1.0 - 2018-10-31
This is the initial release to crates.io. All changes will be documented in This is the initial release to crates.io. All changes will be documented in
this CHANGELOG. this CHANGELOG.
[Unreleased]: https://github.com/eldruin/ds323x-rs/compare/v0.1.0...HEAD [`chrono`]: https://crates.io/crates/chrono
[`rtcc`]: https://crates.io/crates/rtcc
<!-- next-url -->
[Unreleased]: https://github.com/eldruin/ds323x-rs/compare/v0.6.0...HEAD
[0.6.0]: https://github.com/eldruin/ds323x-rs/compare/v0.5.1...v0.6.0
[0.5.1]: https://github.com/eldruin/ds323x-rs/compare/v0.5.0...v0.5.1
[0.5.0]: https://github.com/eldruin/ds323x-rs/compare/v0.4.0...v0.5.0
[0.4.0]: https://github.com/eldruin/ds323x-rs/compare/v0.3.2...v0.4.0
[0.3.2]: https://github.com/eldruin/ds323x-rs/compare/v0.3.1...v0.3.2
[0.3.1]: https://github.com/eldruin/ds323x-rs/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/eldruin/ds323x-rs/compare/v0.2.0...v0.3.0
[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.1.0" version = "0.6.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"
@ -11,24 +11,23 @@ categories = ["embedded", "hardware-support", "no-std"]
homepage = "https://github.com/eldruin/ds323x-rs" homepage = "https://github.com/eldruin/ds323x-rs"
documentation = "https://docs.rs/ds323x" documentation = "https://docs.rs/ds323x"
include = [ include = [
"**/*.rs", "/**/*.rs",
"Cargo.toml", "/Cargo.toml",
"README.md", "/README.md",
"CHANGELOG.md", "/CHANGELOG.md",
"LICENSE-MIT", "/LICENSE-MIT",
"LICENSE-APACHE", "/LICENSE-APACHE",
] ]
edition = "2018"
[badges.travis-ci]
branch = "master"
repository = "eldruin/ds323x-rs"
[dependencies] [dependencies]
embedded-hal = "0.2" embedded-hal = "1.0.0"
rtcc = "0.3"
[dev-dependencies] [dev-dependencies]
linux-embedded-hal = "0.2" embedded-hal-mock = { version = "0.11.1", features = ["eh1"] }
embedded-hal-mock = "0.4" embedded-hal-bus = "0.2"
linux-embedded-hal = "0.4.0"
[profile.release] [profile.release]
lto = true lto = true

View File

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

137
README.md
View File

@ -1,4 +1,10 @@
# Rust DS3231, DS3232 and DS3234 Extremely Accurate Real-Time Clock Driver [![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) [![Build Status](https://travis-ci.org/eldruin/ds323x-rs.svg?branch=master)](https://travis-ci.org/eldruin/ds323x-rs) # Rust DS3231, DS3232 and DS3234 Extremely Accurate Real-Time Clock Driver
[![crates.io](https://img.shields.io/crates/v/ds323x.svg)](https://crates.io/crates/ds323x)
[![Docs](https://docs.rs/ds323x/badge.svg)](https://docs.rs/ds323x)
![MSRV](https://img.shields.io/badge/rustc-1.75+-blue.svg)
[![Build Status](https://github.com/eldruin/ds323x-rs/workflows/Build/badge.svg)](https://github.com/eldruin/ds323x-rs/actions?query=workflow%3ABuild)
[![Coverage Status](https://coveralls.io/repos/eldruin/ds323x-rs/badge.svg?branch=master)](https://coveralls.io/r/eldruin/ds323x-rs?branch=master)
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.
@ -6,38 +12,48 @@ 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: `get_datetime`. - Read and set date and time in 12-hour and 24-hour format. See: `datetime`.
- Read and set date and time individual elements. For example, see: `get_year`. - Read and set date and time individual elements. For example, see: `year`.
- Enable and disable the real-time clock. See: `enable`. - Enable and disable the real-time clock. See: `enable`.
- Read the temperature. See `get_temperature`. - Read the busy status. See `busy`.
- Force a temperature conversion and time compensation. See `convert_temperature`.
- Read the busy status. See `is_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`.
- Clear the has-been-stopped flag. See `clear_has_been_stopped_flag`. - Clear the has-been-stopped flag. See `clear_has_been_stopped_flag`.
- Set the aging offset. See `set_aging_offset`. - Set and read the aging offset. See `set_aging_offset`.
- Enable and disable the 32kHz output. See `enable_32khz_output`.
- 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`.
- Enable and disable the square-wave generation. See `enable_square_wave`. - Alarms:
- Select the square-wave frequency. See `set_square_wave_frequency`. - Set alarms 1 and 2 with several matching policies. See `set_alarm1_day`.
- Enable and disable the 32kHz output when battery-powered. See `enable_32khz_output_on_battery`. - Set alarms 1 and 2 for a time. See `set_alarm1_hms`.
- Set the temperature conversion rate. See `set_temperature_conversion_rate`. - Read whether alarms 1 or 2 have matched. See `has_alarm1_matched`.
- Enable and disable the temperature conversions when battery-powered. See `enable_temperature_conversions_on_battery`. - 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`.
- Wave generation:
- Enable and disable the square-wave generation. See `enable_square_wave`.
- Select the square-wave frequency. See `set_square_wave_frequency`.
- Enable and disable the 32kHz output. See `enable_32khz_output`.
- Enable and disable the 32kHz output when battery powered. See `enable_32khz_output_on_battery`.
- Temperature conversion:
- Read the temperature. See `temperature`.
- Force a temperature conversion and time compensation. See `convert_temperature`.
- Set the temperature conversion rate. See `set_temperature_conversion_rate`.
- Enable and disable the temperature conversions when battery-powered. See `enable_temperature_conversions_on_battery`.
## 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.
### DS3231 These devices are low-cost temperature-compensated crystal oscillator (TCXO)
The DS3231 is a low-cost, extremely accurate I2C real-time clock (RTC) with with a very accurate, temperature-compensated, integrated real-time clock
an integrated temperature-compensated crystal oscillator (TCXO) and crystal. (RTC) including 236/256 bytes of battery-backed SRAM, depending on the model.
The device incorporates a battery input, and maintains accurate timekeeping ### DS3231 and DS3232 details
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 The devices incorporate a battery input, and maintain accurate timekeeping
when main power to the devices is interrupted. The integration of the
crystal resonator enhances the long-term accuracy of the devices as well as
reduces the piece-part count in a manufacturing line. reduces the piece-part count in a manufacturing line.
The DS3231 is available in commercial and industrial temperature ranges, The devices are available in commercial and industrial temperature ranges,
and is offered in a 16-pin, 300-mil SO package. and are 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
@ -53,36 +69,7 @@ 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.
### DS3232 ### DS3234 details
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,6 +98,49 @@ Datasheets:
- [DS3232](https://datasheets.maximintegrated.com/en/ds/DS3232.pdf) - [DS3232](https://datasheets.maximintegrated.com/en/ds/DS3232.pdf)
- [DS3234](https://datasheets.maximintegrated.com/en/ds/DS3234.pdf) - [DS3234](https://datasheets.maximintegrated.com/en/ds/DS3234.pdf)
## Usage
To use this driver, import this crate and an `embedded_hal` implementation,
then instantiate the appropriate device.
In the following example an instance of the device DS3231 will be created.
Other devices can be created with similar methods like:
`Ds323x::new_ds3234(...)`.
Please find additional examples using hardware in this repository: [driver-examples]
[driver-examples]: https://github.com/eldruin/driver-examples
```rust
use ds323x::{DateTimeAccess, Ds323x, NaiveDate, Rtcc};
use linux_embedded_hal::I2cdev;
fn main() {
let dev = I2cdev::new("/dev/i2c-1").unwrap();
let mut rtc = Ds323x::new_ds3231(dev);
let datetime = NaiveDate::from_ymd_opt(2020, 5, 1)
.unwrap()
.and_hms_opt(19, 59, 58)
.unwrap();
rtc.set_datetime(&datetime).unwrap();
// do something else...
let time = rtc.time().unwrap();
println!("Time: {}", time);
let _dev = rtc.destroy_ds3231();
}
```
## Support
For questions, issues, feature requests like compatibility with other devices and other
changes, please file an
[issue in the github project](https://github.com/eldruin/ds323x-rs/issues).
## Minimum Supported Rust Version (MSRV)
This crate is guaranteed to compile on stable Rust 1.75 and up. It *might*
compile with older versions but that may change in any new patch release.
## License ## License
Licensed under either of Licensed under either of
@ -118,11 +148,12 @@ Licensed under either of
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0) http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or * MIT license ([LICENSE-MIT](LICENSE-MIT) or
http://opensource.org/licenses/MIT) at your option. http://opensource.org/licenses/MIT)
at your option.
### Contributing ### Contributing
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,13 +0,0 @@
set -euxo pipefail
main() {
rustup component add rust-src
SYSROOT=$(rustc --print sysroot)
if [[ ! "$SYSROOT" =~ "$TARGET" ]]; then
rustup target add $TARGET
else
echo "Target $TARGET is already installed"
fi
}
main

View File

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

View File

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

7
release.toml 100644
View File

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

View File

@ -1,25 +1,21 @@
//! Functions exclusive of DS3231 //! Functions exclusive of DS3231
extern crate embedded_hal as hal; use crate::{ic, interface::I2cInterface, BitFlags, Ds323x, CONTROL_POR_VALUE};
use hal::blocking;
use core::marker::PhantomData; use core::marker::PhantomData;
use super::{ Ds323x, BitFlags, ic, CONTROL_POR_VALUE }; use embedded_hal::i2c;
use interface::I2cInterface;
impl<I2C, E> Ds323x<I2cInterface<I2C>, ic::DS3231> impl<I2C, E> Ds323x<I2cInterface<I2C>, ic::DS3231>
where where
I2C: blocking::i2c::Write<Error = E> + blocking::i2c::WriteRead<Error = E> I2C: i2c::I2c<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 {
const STATUS_POR_VALUE : u8 = BitFlags::OSC_STOP | BitFlags::EN32KHZ; const STATUS_POR_VALUE: u8 = BitFlags::OSC_STOP | BitFlags::EN32KHZ;
Ds323x { Ds323x {
iface: I2cInterface { iface: I2cInterface { i2c },
i2c,
},
control: CONTROL_POR_VALUE, control: CONTROL_POR_VALUE,
status: STATUS_POR_VALUE, status: STATUS_POR_VALUE,
_ic: PhantomData _ic: PhantomData,
} }
} }

View File

@ -1,26 +1,23 @@
//! Functions exclusive of DS3232 //! Functions exclusive of DS3232
extern crate embedded_hal as hal; use crate::{
use hal::blocking; ic, interface::I2cInterface, BitFlags, Ds323x, Error, TempConvRate, CONTROL_POR_VALUE,
};
use core::marker::PhantomData; use core::marker::PhantomData;
use super::{ Ds323x, TempConvRate, BitFlags, Error, ic, CONTROL_POR_VALUE }; use embedded_hal::i2c;
use interface::I2cInterface;
impl<I2C, E> Ds323x<I2cInterface<I2C>, ic::DS3232> impl<I2C, E> Ds323x<I2cInterface<I2C>, ic::DS3232>
where where
I2C: blocking::i2c::Write<Error = E> + blocking::i2c::WriteRead<Error = E> I2C: i2c::I2c<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 {
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: I2cInterface { iface: I2cInterface { i2c },
i2c,
},
control: CONTROL_POR_VALUE, control: CONTROL_POR_VALUE,
status: STATUS_POR_VALUE, status: STATUS_POR_VALUE,
_ic: PhantomData _ic: PhantomData,
} }
} }
@ -59,13 +56,12 @@ where
/// ///
/// 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; let status = match rate {
match rate { TempConvRate::_64s => self.status & !BitFlags::CRATE1 & !BitFlags::CRATE0,
TempConvRate::_64s => status = self.status & !BitFlags::CRATE1 & !BitFlags::CRATE0, TempConvRate::_128s => self.status & !BitFlags::CRATE1 | BitFlags::CRATE0,
TempConvRate::_128s => status = self.status & !BitFlags::CRATE1 | BitFlags::CRATE0, TempConvRate::_256s => self.status | BitFlags::CRATE1 & !BitFlags::CRATE0,
TempConvRate::_256s => status = self.status | BitFlags::CRATE1 & !BitFlags::CRATE0, TempConvRate::_512s => self.status | BitFlags::CRATE1 | BitFlags::CRATE0,
TempConvRate::_512s => status = self.status | BitFlags::CRATE1 | BitFlags::CRATE0, };
}
self.write_status_without_clearing_alarm(status) self.write_status_without_clearing_alarm(status)
} }
} }

View File

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

View File

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

View File

@ -1,12 +1,13 @@
//! Device configuration //! Device configuration
extern crate embedded_hal as hal; use crate::{
use super::super::{ Ds323x, SqWFreq, Register, BitFlags, Error }; interface::{ReadData, WriteData},
use interface::{ ReadData, WriteData }; BitFlags, Ds323x, Error, Register, SqWFreq,
};
impl<DI, IC, E> Ds323x<DI, IC> impl<DI, IC, E> Ds323x<DI, IC>
where where
DI: ReadData<Error = E> + WriteData<Error = E> DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
{ {
/// 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<E>> {
@ -22,12 +23,13 @@ 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 [`is_busy()`](#method.is_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<E>> {
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 {
self.iface.write_register(Register::CONTROL, control | BitFlags::TEMP_CONV)?; self.iface
.write_register(Register::CONTROL, control | BitFlags::TEMP_CONV)?;
} }
Ok(()) Ok(())
} }
@ -46,11 +48,12 @@ where
/// 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<E>> {
self.iface.write_register(Register::AGING_OFFSET, offset as u8) self.iface
.write_register(Register::AGING_OFFSET, offset as u8)
} }
/// Read the aging offset. /// Read the aging offset.
pub fn get_aging_offset(&mut self) -> Result<i8, Error<E>> { pub fn aging_offset(&mut self) -> Result<i8, Error<E>> {
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)
} }
@ -81,23 +84,49 @@ where
/// 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<E>> {
let new_control; let new_control = match freq {
match freq { SqWFreq::_1Hz => self.control & !BitFlags::RS2 & !BitFlags::RS1,
SqWFreq::_1Hz => new_control = self.control & !BitFlags::RS2 & !BitFlags::RS1, SqWFreq::_1_024Hz => self.control & !BitFlags::RS2 | BitFlags::RS1,
SqWFreq::_1_024Hz => new_control = self.control & !BitFlags::RS2 | BitFlags::RS1, SqWFreq::_4_096Hz => self.control | BitFlags::RS2 & !BitFlags::RS1,
SqWFreq::_4_096Hz => new_control = self.control | BitFlags::RS2 & !BitFlags::RS1, SqWFreq::_8_192Hz => 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.
pub fn enable_alarm1_interrupts(&mut self) -> Result<(), Error<E>> {
let control = self.control;
self.write_control(control | BitFlags::ALARM1_INT_EN)
}
/// Disable Alarm1 interrupts.
pub fn disable_alarm1_interrupts(&mut self) -> Result<(), Error<E>> {
let control = self.control;
self.write_control(control & !BitFlags::ALARM1_INT_EN)
}
/// Enable Alarm2 interrupts.
pub fn enable_alarm2_interrupts(&mut self) -> Result<(), Error<E>> {
let control = self.control;
self.write_control(control | BitFlags::ALARM2_INT_EN)
}
/// Disable Alarm2 interrupts.
pub fn disable_alarm2_interrupts(&mut self) -> Result<(), Error<E>> {
let control = self.control;
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<E>> {
self.iface.write_register(Register::CONTROL, control)?; self.iface.write_register(Register::CONTROL, control)?;
self.control = control; self.control = control;
Ok(()) Ok(())
} }
pub(crate) fn write_status_without_clearing_alarm(&mut self, status: u8) -> Result<(), Error<E>> { pub(crate) fn write_status_without_clearing_alarm(
&mut self,
status: u8,
) -> Result<(), Error<E>> {
// avoid clearing alarm flags // 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,170 +1,170 @@
//! Common implementation //! Common implementation
extern crate embedded_hal as hal; use super::{
use super::super::{ Ds323x, Register, BitFlags, Error }; decimal_to_packed_bcd, hours_to_register, packed_bcd_to_decimal, some_or_invalid_error,
use interface::{ ReadData, WriteData }; };
use crate::{
interface::{ReadData, WriteData},
BitFlags, DateTimeAccess, Datelike, Ds323x, Error, Hours, NaiveDate, NaiveDateTime, NaiveTime,
Register, Rtcc, Timelike,
};
/// Date and time impl<DI, IC, E> DateTimeAccess for Ds323x<DI, IC>
#[derive(Debug, Clone, PartialEq)]
pub struct DateTime {
/// Year [2000-2099]
pub year : u16,
/// Month [1-12]
pub month : u8,
/// Day [1-31]
pub day : u8,
/// Weekday [1-7]
pub weekday : u8,
/// Hour in 24h/12h format
pub hour : Hours,
/// Minute [0-59]
pub minute : u8,
/// Second [0-59]
pub second : u8,
}
/// Hours in either 12-hour (AM/PM) or 24-hour format
#[derive(Debug, Clone, PartialEq)]
pub enum Hours {
/// AM [1-12]
AM(u8),
/// PM [1-12]
PM(u8),
/// 24H format [0-23]
H24(u8),
}
impl<DI, IC, E> Ds323x<DI, IC>
where where
DI: ReadData<Error = E> + WriteData<Error = E> DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
{ {
/// Read the seconds. type Error = Error<E>;
pub fn get_seconds(&mut self) -> Result<u8, Error<E>> {
fn datetime(&mut self) -> Result<NaiveDateTime, Self::Error> {
let mut data = [0; 8];
self.iface.read_data(&mut data)?;
let year = year_from_registers(
data[Register::MONTH as usize + 1],
data[Register::YEAR as usize + 1],
);
let month = packed_bcd_to_decimal(data[Register::MONTH as usize + 1] & !BitFlags::CENTURY);
let day = packed_bcd_to_decimal(data[Register::DOM as usize + 1]);
let hour = hours_from_register(data[Register::HOURS as usize + 1]);
let minute = packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]);
let second = packed_bcd_to_decimal(data[Register::SECONDS as usize + 1]);
let date = NaiveDate::from_ymd_opt(year.into(), month.into(), day.into());
let date = some_or_invalid_error(date)?;
let datetime = date.and_hms_opt(get_h24(hour).into(), minute.into(), second.into());
some_or_invalid_error(datetime)
}
fn set_datetime(&mut self, datetime: &NaiveDateTime) -> Result<(), Self::Error> {
if datetime.year() < 2000 || datetime.year() > 2100 {
return Err(Error::InvalidInputData);
}
let (month, year) = month_year_to_registers(datetime.month() as u8, datetime.year() as u16);
let mut payload = [
Register::SECONDS,
decimal_to_packed_bcd(datetime.second() as u8),
decimal_to_packed_bcd(datetime.minute() as u8),
hours_to_register(Hours::H24(datetime.hour() as u8))?,
datetime.weekday().number_from_sunday() as u8,
decimal_to_packed_bcd(datetime.day() as u8),
month,
year,
];
self.iface.write_data(&mut payload)
}
}
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) self.read_register_decimal(Register::SECONDS)
} }
/// Read the minutes. fn minutes(&mut self) -> Result<u8, Self::Error> {
pub fn get_minutes(&mut self) -> Result<u8, Error<E>> {
self.read_register_decimal(Register::MINUTES) self.read_register_decimal(Register::MINUTES)
} }
/// Read the hours. fn hours(&mut self) -> Result<Hours, Self::Error> {
pub fn get_hours(&mut self) -> Result<Hours, Error<E>> {
let data = self.iface.read_register(Register::HOURS)?; let data = self.iface.read_register(Register::HOURS)?;
Ok(hours_from_register(data)) Ok(hours_from_register(data))
} }
/// Read the day of the week [1-7]. fn time(&mut self) -> Result<NaiveTime, Self::Error> {
pub fn get_weekday(&mut self) -> Result<u8, Error<E>> { 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) self.read_register_decimal(Register::DOW)
} }
/// Read the day of the month [1-31]. fn day(&mut self) -> Result<u8, Self::Error> {
pub fn get_day(&mut self) -> Result<u8, Error<E>> {
self.read_register_decimal(Register::DOM) self.read_register_decimal(Register::DOM)
} }
/// Read the month [1-12]. fn month(&mut self) -> Result<u8, Self::Error> {
pub fn get_month(&mut self) -> Result<u8, Error<E>> {
let data = self.iface.read_register(Register::MONTH)?; let data = self.iface.read_register(Register::MONTH)?;
let value = data & !BitFlags::CENTURY; let value = data & !BitFlags::CENTURY;
Ok(packed_bcd_to_decimal(value)) Ok(packed_bcd_to_decimal(value))
} }
/// Read the year [2000-2100]. fn year(&mut self) -> Result<u16, Self::Error> {
pub fn get_year(&mut self) -> Result<u16, Error<E>> {
let mut data = [0; 3]; let mut data = [0; 3];
data[0] = Register::MONTH; data[0] = Register::MONTH;
self.iface.read_data(&mut data)?; self.iface.read_data(&mut data)?;
Ok(year_from_registers(data[1], data[2])) Ok(year_from_registers(data[1], data[2]))
} }
/// Read the date and time. fn date(&mut self) -> Result<NaiveDate, Self::Error> {
pub fn get_datetime(&mut self) -> Result<DateTime, Error<E>> { let mut data = [0; 4];
let mut data = [0; 8]; data[0] = Register::DOM;
self.iface.read_data(&mut data)?; self.iface.read_data(&mut data)?;
Ok(DateTime {
year: year_from_registers(data[Register::MONTH as usize + 1], data[Register::YEAR as usize + 1]), let offset = Register::DOM as usize;
month: packed_bcd_to_decimal(data[Register::MONTH as usize + 1] & !BitFlags::CENTURY), let year = year_from_registers(
day: packed_bcd_to_decimal(data[Register::DOM as usize + 1]), data[Register::MONTH as usize + 1 - offset],
weekday: packed_bcd_to_decimal(data[Register::DOW as usize + 1]), data[Register::YEAR as usize + 1 - offset],
hour: hours_from_register(data[Register::HOURS as usize + 1]), );
minute: packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]), let month =
second: packed_bcd_to_decimal(data[Register::SECONDS as usize + 1]) 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 read_register_decimal(&mut self, register: u8) -> Result<u8, Error<E>> { fn set_seconds(&mut self, seconds: u8) -> Result<(), Self::Error> {
let data = self.iface.read_register(register)?;
Ok(packed_bcd_to_decimal(data))
}
/// Set the seconds [0-59].
///
/// Will return an `Error::InvalidInputData` if the seconds are out of range.
pub fn set_seconds(&mut self, seconds: u8) -> Result<(), Error<E>> {
if seconds > 59 { if seconds > 59 {
return Err(Error::InvalidInputData); return Err(Error::InvalidInputData);
} }
self.write_register_decimal(Register::SECONDS, seconds) self.write_register_decimal(Register::SECONDS, seconds)
} }
/// Set the minutes [0-59]. fn set_minutes(&mut self, minutes: u8) -> Result<(), Self::Error> {
///
/// Will return an `Error::InvalidInputData` if the minutes are out of range.
pub fn set_minutes(&mut self, minutes: u8) -> Result<(), Error<E>> {
if minutes > 59 { if minutes > 59 {
return Err(Error::InvalidInputData); return Err(Error::InvalidInputData);
} }
self.write_register_decimal(Register::MINUTES, minutes) self.write_register_decimal(Register::MINUTES, minutes)
} }
/// Set the hours. fn set_hours(&mut self, hours: Hours) -> Result<(), Self::Error> {
/// let value = hours_to_register(hours)?;
/// Changes the operating mode to 12h/24h depending on the parameter.
///
/// Will return an `Error::InvalidInputData` if the hours are out of range.
pub fn set_hours(&mut self, hours: Hours) -> Result<(), Error<E>> {
let value = self.get_hours_register_value(&hours)?;
self.iface.write_register(Register::HOURS, value) self.iface.write_register(Register::HOURS, value)
} }
fn get_hours_register_value(&mut self, hours: &Hours) -> Result<u8, Error<E>> { fn set_time(&mut self, time: &NaiveTime) -> Result<(), Self::Error> {
match *hours { let mut payload = [
Hours::H24(h) if h > 23 => Err(Error::InvalidInputData), Register::SECONDS,
Hours::H24(h) => Ok(decimal_to_packed_bcd(h)), decimal_to_packed_bcd(time.second() as u8),
Hours::AM(h) if h < 1 || h > 12 => Err(Error::InvalidInputData), decimal_to_packed_bcd(time.minute() as u8),
Hours::AM(h) => Ok(BitFlags::H24_H12 | decimal_to_packed_bcd(h)), hours_to_register(Hours::H24(time.hour() as u8))?,
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)), self.iface.write_data(&mut payload)
}
} }
/// Set the day of week [1-7]. fn set_weekday(&mut self, weekday: u8) -> Result<(), Self::Error> {
/// if !(1..=7).contains(&weekday) {
/// Will return an `Error::InvalidInputData` if the day is out of range.
pub fn set_weekday(&mut self, weekday: u8) -> Result<(), Error<E>> {
if weekday < 1 || weekday > 7 {
return Err(Error::InvalidInputData); return Err(Error::InvalidInputData);
} }
self.iface.write_register(Register::DOW, weekday) self.iface.write_register(Register::DOW, weekday)
} }
/// Set the day of month [1-31]. fn set_day(&mut self, day: u8) -> Result<(), Self::Error> {
/// if !(1..=31).contains(&day) {
/// Will return an `Error::InvalidInputData` if the day is out of range.
pub fn set_day(&mut self, day: u8) -> Result<(), Error<E>> {
if day < 1 || day > 7 {
return Err(Error::InvalidInputData); return Err(Error::InvalidInputData);
} }
self.iface.write_register(Register::DOM, day) self.write_register_decimal(Register::DOM, day)
} }
/// Set the month [1-12]. fn set_month(&mut self, month: u8) -> Result<(), Self::Error> {
/// if !(1..=12).contains(&month) {
/// Will return an `Error::InvalidInputData` if the month is out of range.
pub fn set_month(&mut self, month: u8) -> Result<(), Error<E>> {
if month < 1 || month > 12 {
return Err(Error::InvalidInputData); return Err(Error::InvalidInputData);
} }
// keep the century bit // keep the century bit
@ -173,65 +173,71 @@ where
self.iface.write_register(Register::MONTH, value) self.iface.write_register(Register::MONTH, value)
} }
/// Set the year [2000-2100]. fn set_year(&mut self, year: u16) -> Result<(), Self::Error> {
/// if !(2000..=2100).contains(&year) {
/// Will return an `Error::InvalidInputData` if the year is out of range.
pub fn set_year(&mut self, year: u16) -> Result<(), Error<E>> {
if year < 2000 || year > 2100 {
return Err(Error::InvalidInputData); return Err(Error::InvalidInputData);
} }
let data = self.iface.read_register(Register::MONTH)?; let data = self.iface.read_register(Register::MONTH)?;
let month_bcd = data & !BitFlags::CENTURY; let month_bcd = data & !BitFlags::CENTURY;
if year > 2099 { if year > 2099 {
let mut data = [ Register::MONTH, let mut data = [
BitFlags::CENTURY | month_bcd, Register::MONTH,
decimal_to_packed_bcd((year - 2100) as u8) ]; BitFlags::CENTURY | month_bcd,
decimal_to_packed_bcd((year - 2100) as u8),
];
self.iface.write_data(&mut data) self.iface.write_data(&mut data)
} } else {
else { let mut data = [
let mut data = [ Register::MONTH, month_bcd, Register::MONTH,
decimal_to_packed_bcd((year - 2000) as u8) ]; month_bcd,
decimal_to_packed_bcd((year - 2000) as u8),
];
self.iface.write_data(&mut data) self.iface.write_data(&mut data)
} }
} }
/// Set the date and time. fn set_date(&mut self, date: &rtcc::NaiveDate) -> Result<(), Self::Error> {
/// if date.year() < 2000 || date.year() > 2100 {
/// Will return an `Error::InvalidInputData` if any of the parameters is out of range.
pub fn set_datetime(&mut self, datetime: &DateTime) -> Result<(), Error<E>> {
if datetime.year < 2000 || datetime.year > 2100 ||
datetime.month < 1 || datetime.month > 12 ||
datetime.day < 1 || datetime.day > 31 ||
datetime.weekday < 1 || datetime.weekday > 7 ||
datetime.minute > 59 ||
datetime.second > 59 {
return Err(Error::InvalidInputData); return Err(Error::InvalidInputData);
} }
let (month, year) = month_year_to_registers(datetime.month, datetime.year); let (month, year) = month_year_to_registers(date.month() as u8, date.year() as u16);
let mut payload = [Register::SECONDS, let mut payload = [
decimal_to_packed_bcd(datetime.second), Register::DOW,
decimal_to_packed_bcd(datetime.minute), date.weekday().number_from_sunday() as u8,
self.get_hours_register_value(&datetime.hour)?, decimal_to_packed_bcd(date.day() as u8),
decimal_to_packed_bcd(datetime.weekday), month,
decimal_to_packed_bcd(datetime.day), year,
month, year]; ];
self.iface.write_data(&mut payload) self.iface.write_data(&mut payload)
}
} }
impl<DI, IC, E> Ds323x<DI, IC>
where
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
{
fn read_register_decimal(&mut self, register: u8) -> Result<u8, Error<E>> {
let data = self.iface.read_register(register)?;
Ok(packed_bcd_to_decimal(data))
}
fn write_register_decimal(&mut self, register: u8, decimal_number: u8) -> Result<(), Error<E>> { fn write_register_decimal(&mut self, register: u8, decimal_number: u8) -> Result<(), Error<E>> {
self.iface.write_register(register, decimal_to_packed_bcd(decimal_number)) self.iface
.write_register(register, decimal_to_packed_bcd(decimal_number))
} }
} }
fn hours_from_register(data: u8) -> Hours { fn hours_from_register(data: u8) -> Hours {
if is_24h_format(data) { if is_24h_format(data) {
Hours::H24(packed_bcd_to_decimal(data & !BitFlags::H24_H12)) Hours::H24(packed_bcd_to_decimal(data & !BitFlags::H24_H12))
} } else if is_am(data) {
else if is_am(data) { Hours::AM(packed_bcd_to_decimal(
Hours::AM(packed_bcd_to_decimal(data & !(BitFlags::H24_H12 | BitFlags::AM_PM))) data & !(BitFlags::H24_H12 | BitFlags::AM_PM),
} ))
else { } else {
Hours::PM(packed_bcd_to_decimal(data & !(BitFlags::H24_H12 | BitFlags::AM_PM))) Hours::PM(packed_bcd_to_decimal(
data & !(BitFlags::H24_H12 | BitFlags::AM_PM),
))
} }
} }
@ -239,10 +245,9 @@ fn year_from_registers(month: u8, year: u8) -> u16 {
let century = month & BitFlags::CENTURY; let century = month & BitFlags::CENTURY;
let year = packed_bcd_to_decimal(year); let year = packed_bcd_to_decimal(year);
if century != 0 { if century != 0 {
2100 + (year as u16) 2100 + u16::from(year)
} } else {
else { 2000 + u16::from(year)
2000 + (year as u16)
} }
} }
@ -250,9 +255,11 @@ fn month_year_to_registers(month: u8, year: u16) -> (u8, u8) {
if year > 2099 { if year > 2099 {
let month = BitFlags::CENTURY | decimal_to_packed_bcd(month); let month = BitFlags::CENTURY | decimal_to_packed_bcd(month);
(month, decimal_to_packed_bcd((year - 2100) as u8)) (month, decimal_to_packed_bcd((year - 2100) as u8))
} } else {
else { (
(decimal_to_packed_bcd(month), decimal_to_packed_bcd((year - 2000) as u8)) decimal_to_packed_bcd(month),
decimal_to_packed_bcd((year - 2000) as u8),
)
} }
} }
@ -264,14 +271,12 @@ fn is_am(hours_data: u8) -> bool {
hours_data & BitFlags::AM_PM == 0 hours_data & BitFlags::AM_PM == 0
} }
// Transforms a decimal number to packed BCD format fn get_h24(hour: Hours) -> u8 {
fn decimal_to_packed_bcd(dec: u8) -> u8 { match hour {
((dec / 10) << 4) | (dec % 10) Hours::H24(h) => h,
} Hours::AM(h) => h,
Hours::PM(h) => h + 12,
// Transforms a number in packed BCD format to decimal }
fn packed_bcd_to_decimal(bcd: u8) -> u8 {
(bcd >> 4) * 10 + (bcd & 0xF)
} }
#[cfg(test)] #[cfg(test)]
@ -279,28 +284,17 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn can_convert_packed_bcd_to_decimal() { fn can_convert_to_h24() {
assert_eq!(0, packed_bcd_to_decimal(0b0000_0000)); assert_eq!(0, get_h24(Hours::H24(0)));
assert_eq!(1, packed_bcd_to_decimal(0b0000_0001)); assert_eq!(0, get_h24(Hours::AM(0)));
assert_eq!(9, packed_bcd_to_decimal(0b0000_1001)); assert_eq!(12, get_h24(Hours::PM(0)));
assert_eq!(10, packed_bcd_to_decimal(0b0001_0000));
assert_eq!(11, packed_bcd_to_decimal(0b0001_0001));
assert_eq!(19, packed_bcd_to_decimal(0b0001_1001));
assert_eq!(20, packed_bcd_to_decimal(0b0010_0000));
assert_eq!(21, packed_bcd_to_decimal(0b0010_0001));
assert_eq!(59, packed_bcd_to_decimal(0b0101_1001));
}
#[test] assert_eq!(1, get_h24(Hours::H24(1)));
fn can_convert_decimal_to_packed_bcd() { assert_eq!(1, get_h24(Hours::AM(1)));
assert_eq!(0b0000_0000, decimal_to_packed_bcd( 0)); assert_eq!(13, get_h24(Hours::PM(1)));
assert_eq!(0b0000_0001, decimal_to_packed_bcd( 1));
assert_eq!(0b0000_1001, decimal_to_packed_bcd( 9)); assert_eq!(23, get_h24(Hours::H24(23)));
assert_eq!(0b0001_0000, decimal_to_packed_bcd(10)); assert_eq!(12, get_h24(Hours::AM(12)));
assert_eq!(0b0001_0001, decimal_to_packed_bcd(11)); assert_eq!(23, get_h24(Hours::PM(11)));
assert_eq!(0b0001_1001, decimal_to_packed_bcd(19));
assert_eq!(0b0010_0000, decimal_to_packed_bcd(20));
assert_eq!(0b0010_0001, decimal_to_packed_bcd(21));
assert_eq!(0b0101_1001, decimal_to_packed_bcd(59));
} }
} }

View File

@ -1,4 +1,84 @@
mod alarms;
mod configuration; mod configuration;
mod status; mod status;
pub use self::alarms::{
Alarm1Matching, Alarm2Matching, DayAlarm1, DayAlarm2, WeekdayAlarm1, WeekdayAlarm2,
};
mod datetime; mod datetime;
pub use self::datetime::{ Hours, DateTime }; use crate::{BitFlags, Error, Hours, NaiveTime, Timelike};
// Transforms a decimal number to packed BCD format
fn decimal_to_packed_bcd(dec: u8) -> u8 {
((dec / 10) << 4) | (dec % 10)
}
// Transforms a number in packed BCD format to decimal
fn packed_bcd_to_decimal(bcd: u8) -> u8 {
(bcd >> 4) * 10 + (bcd & 0xF)
}
fn hours_to_register<E>(hours: Hours) -> Result<u8, Error<E>> {
match hours {
Hours::H24(h) if h > 23 => Err(Error::InvalidInputData),
Hours::H24(h) => Ok(decimal_to_packed_bcd(h)),
Hours::AM(h) if !(1..=12).contains(&h) => Err(Error::InvalidInputData),
Hours::AM(h) => Ok(BitFlags::H24_H12 | decimal_to_packed_bcd(h)),
Hours::PM(h) if !(1..=12).contains(&h) => Err(Error::InvalidInputData),
Hours::PM(h) => Ok(BitFlags::H24_H12 | BitFlags::AM_PM | decimal_to_packed_bcd(h)),
}
}
fn some_or_invalid_error<T, E>(data: Option<T>) -> Result<T, Error<E>> {
if let Some(data) = data {
Ok(data)
} else {
Err(Error::InvalidDeviceState)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn if_some_then_get_inner() {
match some_or_invalid_error::<u8, ()>(Some(1)) {
Ok(1) => (),
_ => panic!(),
}
}
#[test]
fn if_none_then_error() {
match some_or_invalid_error::<u8, ()>(None) {
Err(Error::InvalidDeviceState) => (),
_ => panic!(),
}
}
#[test]
fn can_convert_packed_bcd_to_decimal() {
assert_eq!(0, packed_bcd_to_decimal(0b0000_0000));
assert_eq!(1, packed_bcd_to_decimal(0b0000_0001));
assert_eq!(9, packed_bcd_to_decimal(0b0000_1001));
assert_eq!(10, packed_bcd_to_decimal(0b0001_0000));
assert_eq!(11, packed_bcd_to_decimal(0b0001_0001));
assert_eq!(19, packed_bcd_to_decimal(0b0001_1001));
assert_eq!(20, packed_bcd_to_decimal(0b0010_0000));
assert_eq!(21, packed_bcd_to_decimal(0b0010_0001));
assert_eq!(59, packed_bcd_to_decimal(0b0101_1001));
}
#[test]
fn can_convert_decimal_to_packed_bcd() {
assert_eq!(0b0000_0000, decimal_to_packed_bcd(0));
assert_eq!(0b0000_0001, decimal_to_packed_bcd(1));
assert_eq!(0b0000_1001, decimal_to_packed_bcd(9));
assert_eq!(0b0001_0000, decimal_to_packed_bcd(10));
assert_eq!(0b0001_0001, decimal_to_packed_bcd(11));
assert_eq!(0b0001_1001, decimal_to_packed_bcd(19));
assert_eq!(0b0010_0000, decimal_to_packed_bcd(20));
assert_eq!(0b0010_0001, decimal_to_packed_bcd(21));
assert_eq!(0b0101_1001, decimal_to_packed_bcd(59));
}
}

View File

@ -1,21 +1,22 @@
//! Device status //! Device status
extern crate embedded_hal as hal; use crate::{
use super::super::{ Ds323x, Register, BitFlags, Error }; interface::{ReadData, WriteData},
use interface::{ ReadData, WriteData }; BitFlags, Ds323x, Error, Register,
};
impl<DI, IC, E> Ds323x<DI, IC> impl<DI, IC, E> Ds323x<DI, IC>
where where
DI: ReadData<Error = E> + WriteData<Error = E> DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
{ {
/// Read whether the oscillator is running /// Read whether the oscillator is running
pub fn is_running(&mut self) -> Result<bool, Error<E>> { pub fn running(&mut self) -> Result<bool, Error<E>> {
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 is_busy(&mut self) -> Result<bool, Error<E>> { pub fn busy(&mut self) -> Result<bool, Error<E>> {
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)
} }
@ -35,31 +36,60 @@ where
/// Clear flag signalling whether the oscillator is stopped or has been /// Clear flag signalling whether the oscillator is stopped or has been
/// stopped at some point. /// stopped at some point.
/// ///
/// (Does not alter the device register if already cleared).
/// 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<E>> {
let status = self.status & !BitFlags::OSC_STOP;
self.write_status_without_clearing_alarm(status)
}
/// Read whether the Alarm1 has matched at some point.
///
/// Once this is true, it will stay as such until cleared with
/// [`clear_alarm1_matched_flag()`](#method.clear_alarm1_matched_flag)
pub fn has_alarm1_matched(&mut self) -> Result<bool, Error<E>> {
let status = self.iface.read_register(Register::STATUS)?; let status = self.iface.read_register(Register::STATUS)?;
if (status & BitFlags::OSC_STOP) != 0 { Ok((status & BitFlags::ALARM1F) != 0)
self.iface.write_register(Register::STATUS, status & !BitFlags::OSC_STOP)?; }
}
Ok(()) /// Clear flag signalling whether the Alarm1 has matched at some point.
///
/// See also: [`has_alarm1_matched()`](#method.has_alarm1_matched)
pub fn clear_alarm1_matched_flag(&mut self) -> Result<(), Error<E>> {
let status = self.status | BitFlags::ALARM2F;
self.iface.write_register(Register::STATUS, status)
}
/// Read whether the Alarm2 has matched at some point.
///
/// Once this is true, it will stay as such until cleared with
/// [`clear_alarm2_matched_flag()`](#method.clear_alarm2_matched_flag)
pub fn has_alarm2_matched(&mut self) -> Result<bool, Error<E>> {
let status = self.iface.read_register(Register::STATUS)?;
Ok((status & BitFlags::ALARM2F) != 0)
}
/// Clear flag signalling whether the Alarm2 has matched at some point.
///
/// See also: [`has_alarm2_matched()`](#method.has_alarm2_matched)
pub fn clear_alarm2_matched_flag(&mut self) -> Result<(), Error<E>> {
let status = self.status | BitFlags::ALARM1F;
self.iface.write_register(Register::STATUS, status)
} }
/// Read the temperature. /// Read the temperature.
/// ///
/// 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 get_temperature(&mut self) -> Result<f32, Error<E>> { pub fn temperature(&mut self) -> Result<f32, Error<E>> {
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;
let temp = ((data[1] as u16) << 2) | (data[2] >> 6) as u16; let temp = (u16::from(data[1]) << 2) | u16::from(data[2] >> 6);
if is_negative { if is_negative {
let temp_sign_extended = temp | 0b1111_1100_0000_0000; let temp_sign_extended = temp | 0b1111_1100_0000_0000;
Ok(temp_sign_extended as i16 as f32 * 0.25) Ok(f32::from(temp_sign_extended as i16) * 0.25)
} } else {
else { Ok(f32::from(temp) * 0.25)
Ok(temp as f32 * 0.25)
} }
} }
} }

View File

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

View File

@ -4,55 +4,70 @@
//! [`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: [`get_datetime`]. //! - Read and set date and time in 12-hour and 24-hour format. See: [`datetime`].
//! - Read and set date and time individual elements. For example, see: [`get_year`]. //! - Read and set date and time individual elements. For example, see: [`year`].
//! - Enable and disable the real-time clock. See: [`enable`]. //! - Enable and disable the real-time clock. See: [`enable`].
//! - Read the temperature. See [`get_temperature`]. //! - Read the busy status. See [`busy`].
//! - Force a temperature conversion and time compensation. See [`convert_temperature`].
//! - Read the busy status. See [`is_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`].
//! - Clear the has-been-stopped flag. See [`clear_has_been_stopped_flag`]. //! - Clear the has-been-stopped flag. See [`clear_has_been_stopped_flag`].
//! - Set and read the aging offset. See [`set_aging_offset`]. //! - Set and read the aging offset. See [`set_aging_offset`].
//! - Enable and disable the 32kHz output. See [`enable_32khz_output`].
//! - 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`].
//! - Enable and disable the square-wave generation. See [`enable_square_wave`]. //! - Alarms:
//! - Select the square-wave frequency. See [`set_square_wave_frequency`]. //! - Set alarms 1 and 2 with several matching policies. See [`set_alarm1_day`].
//! - Enable and disable the 32kHz output when battery powered. See [`enable_32khz_output_on_battery`]. //! - Set alarms 1 and 2 for a time. See [`set_alarm1_hms`].
//! - Set the temperature conversion rate. See [`set_temperature_conversion_rate`]. //! - Read whether alarms 1 or 2 have matched. See [`has_alarm1_matched`].
//! - Enable and disable the temperature conversions when battery-powered. See [`enable_temperature_conversions_on_battery`]. //! - 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`].
//! - Wave generation:
//! - Enable and disable the square-wave generation. See [`enable_square_wave`].
//! - Select the square-wave frequency. See [`set_square_wave_frequency`].
//! - Enable and disable the 32kHz output. See [`enable_32khz_output`].
//! - Enable and disable the 32kHz output when battery powered. See [`enable_32khz_output_on_battery`].
//! - Temperature conversion:
//! - Read the temperature. See [`temperature`].
//! - Force a temperature conversion and time compensation. See [`convert_temperature`].
//! - Set the temperature conversion rate. See [`set_temperature_conversion_rate`].
//! - Enable and disable the temperature conversions when battery-powered. See [`enable_temperature_conversions_on_battery`].
//! //!
//! [`get_datetime`]: struct.Ds323x.html#method.get_datetime //! [`datetime`]: Ds323x::datetime
//! [`get_year`]: struct.Ds323x.html#method.get_year //! [`year`]: Ds323x::year
//! [`enable`]: struct.Ds323x.html#method.enable //! [`enable`]: Ds323x::enable
//! [`get_temperature`]: struct.Ds323x.html#method.get_temperature //! [`temperature`]: Ds323x::temperature
//! [`convert_temperature`]: struct.Ds323x.html#method.convert_temperature //! [`convert_temperature`]: Ds323x::convert_temperature
//! [`is_busy`]: struct.Ds323x.html#method.is_busy //! [`busy`]: Ds323x::busy
//! [`has_been_stopped`]: struct.Ds323x.html#method.has_been_stopped //! [`has_been_stopped`]: Ds323x::has_been_stopped
//! [`clear_has_been_stopped_flag`]: struct.Ds323x.html#method.clear_has_been_stopped_flag //! [`clear_has_been_stopped_flag`]: Ds323x::clear_has_been_stopped_flag
//! [`set_aging_offset`]: struct.Ds323x.html#method.set_aging_offset //! [`set_aging_offset`]: Ds323x::set_aging_offset
//! [`enable_32khz_output`]: struct.Ds323x.html#method.enable_32khz_output //! [`enable_32khz_output`]: Ds323x::enable_32khz_output
//! [`use_int_sqw_output_as_interrupt`]: struct.Ds323x.html#method.use_int_sqw_output_as_interrupt //! [`use_int_sqw_output_as_interrupt`]: Ds323x::use_int_sqw_output_as_interrupt
//! [`enable_square_wave`]: struct.Ds323x.html#method.enable_square_wave //! [`enable_square_wave`]: Ds323x::enable_square_wave
//! [`set_square_wave_frequency`]: struct.Ds323x.html#method.set_square_wave_frequency //! [`set_square_wave_frequency`]: Ds323x::set_square_wave_frequency
//! [`enable_32khz_output_on_battery`]: struct.Ds323x.html#method.enable_32khz_output_on_battery //! [`set_alarm1_day`]: Ds323x::set_alarm1_day
//! [`set_temperature_conversion_rate`]: struct.Ds323x.html#method.set_temperature_conversion_rate //! [`set_alarm1_hms`]: Ds323x::set_alarm1_hms
//! [`enable_temperature_conversions_on_battery`]: struct.Ds323x.html#method.enable_temperature_conversions_on_battery //! [`has_alarm1_matched`]: Ds323x::has_alarm1_matched
//! [`clear_alarm1_matched_flag`]: Ds323x::clear_alarm1_matched_flag
//! [`enable_alarm1_interrupts`]: Ds323x::enable_alarm1_interrupts
//! [`enable_32khz_output_on_battery`]: Ds323x::enable_32khz_output_on_battery
//! [`set_temperature_conversion_rate`]: Ds323x::set_temperature_conversion_rate
//! [`enable_temperature_conversions_on_battery`]: Ds323x::enable_temperature_conversions_on_battery
//! //!
//! ## The devices //! ## 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.
//! //!
//! ### DS3231 //! These devices are low-cost temperature-compensated crystal oscillator (TCXO)
//! The DS3231 is a low-cost, extremely accurate I2C real-time clock (RTC) with //! with a very accurate, temperature-compensated, integrated real-time clock
//! an integrated temperature-compensated crystal oscillator (TCXO) and crystal. //! (RTC) including 236/256 bytes of battery-backed SRAM, depending on the model.
//! //!
//! The device incorporates a battery input, and maintains accurate timekeeping //! ### DS3231 and DS3232 details
//! 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 //! The devices incorporate a battery input, and maintain accurate timekeeping
//! when main power to the devices is interrupted. The integration of the
//! crystal resonator enhances the long-term accuracy of the devices as well as
//! reduces the piece-part count in a manufacturing line. //! reduces the piece-part count in a manufacturing line.
//! The DS3231 is available in commercial and industrial temperature ranges, //! The devices are available in commercial and industrial temperature ranges,
//! and is offered in a 16-pin, 300-mil SO package. //! and are 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
@ -68,36 +83,7 @@
//! 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.
//! //!
//! ### DS3232 //! ### DS3234 details
//! 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
@ -135,202 +121,156 @@
//! DS3231 as an example, except when using features specific to another IC, //! DS3231 as an example, except when using features specific to another IC,
//! for example, RAM access which is not available in the DS3231 device. //! for example, RAM access which is not available in the DS3231 device.
//! //!
//! Please find additional examples using hardware in this repository: [driver-examples]
//!
//! [driver-examples]: https://github.com/eldruin/driver-examples
//!
//! ### Create a driver instance for the DS3231 //! ### Create a driver instance for the DS3231
//! //!
//! ```no_run //! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::Ds323x; //! use ds323x::Ds323x;
//! use linux_embedded_hal::I2cdev;
//! //!
//! # fn main() { //! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! 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 linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::Ds323x; //! use ds323x::Ds323x;
//! use linux_embedded_hal::I2cdev;
//! //!
//! # fn main() { //! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! 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 linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::Ds323x; //! use ds323x::Ds323x;
//! use embedded_hal_bus::spi::ExclusiveDevice;
//! use linux_embedded_hal::{Delay, SpidevBus, SysfsPin};
//! //!
//! # fn main() { //! let spi = SpidevBus::open("/dev/spidev0.0").unwrap();
//! let dev = hal::Spidev::open("/dev/spidev0.0").unwrap(); //! let chip_select = SysfsPin::new(25);
//! let chip_select = hal::Pin::new(24); //! let dev = ExclusiveDevice::new(spi, chip_select, Delay).unwrap();
//! let rtc = Ds323x::new_ds3234(dev, chip_select); //! let rtc = Ds323x::new_ds3234(dev);
//! // do something... //! // do something...
//! //!
//! // get the SPI device and chip select pin back //! // get the SPI device back
//! let (dev, chip_select) = rtc.destroy_ds3234(); //! let dev = rtc.destroy_ds3234();
//! # }
//! ``` //! ```
//! //!
//! ### Set the current date and time at once //! ### Set the current date and time at once
//! //!
//! ```no_run //! ```no_run
//! extern crate linux_embedded_hal as hal; //! use ds323x::{Ds323x, NaiveDate, DateTimeAccess};
//! extern crate ds323x; //! use linux_embedded_hal::I2cdev;
//! use ds323x::{ Ds323x, DateTime, Hours };
//! //!
//! # fn main() { //! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev); //! let mut rtc = Ds323x::new_ds3231(dev);
//! let datetime = DateTime { //! let datetime = NaiveDate::from_ymd(2020, 5, 1).and_hms(19, 59, 58);
//! year: 2018,
//! month: 08,
//! day: 15,
//! weekday: 4,
//! hour: Hours::H24(19),
//! minute: 59,
//! second: 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
//! extern crate linux_embedded_hal as hal; //! use ds323x::{Ds323x, DateTimeAccess};
//! extern crate ds323x; //! use linux_embedded_hal::I2cdev;
//! use ds323x::{ Ds323x, Hours };
//! //!
//! # fn main() { //! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev); //! let mut rtc = Ds323x::new_ds3231(dev);
//! //! let dt = rtc.datetime().unwrap();
//! let datetime = rtc.get_datetime().unwrap(); //! println!("{}", dt);
//! //! // This will print something like: 2020-05-01 19:59:58
//! // The hours depend on the RTC running mode
//! match datetime.hour {
//! Hours::H24(h) => println!("{}-{}-{}, {} {}:{}:{}", datetime.year,
//! datetime.month, datetime.day, datetime.weekday,
//! h, datetime.minute, datetime.second),
//! Hours::AM(h) => println!("{}-{}-{}, {} {}:{}:{} AM", datetime.year,
//! datetime.month, datetime.day, datetime.weekday,
//! h, datetime.minute, datetime.second),
//! Hours::PM(h) => println!("{}-{}-{}, {} {}:{}:{} PM", datetime.year,
//! datetime.month, datetime.day, datetime.weekday,
//! h, datetime.minute, datetime.second),
//! }
//! // This will print something like: 2018-08-15, 4 19:59:58
//! # }
//! ``` //! ```
//! //!
//! ### Get the year //! ### Get the year
//! //!
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::{ Ds323x, Hours };
//!
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! let year = rtc.get_year().unwrap();
//! println!("Year: {}", year);
//! # }
//! ```
//! Similar methods exist for month, day, weekday, hours, minutes and seconds. //! Similar methods exist for month, day, weekday, hours, minutes and seconds.
//! //!
//! ```no_run
//! use ds323x::{Ds323x, Rtcc};
//! use linux_embedded_hal::I2cdev;
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! let year = rtc.year().unwrap();
//! println!("Year: {}", year);
//! ```
//!
//! ### Set the year //! ### Set the year
//! //!
//! ```no_run //! Similar methods exist for month, day, weekday, hours, minutes and seconds.
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::{ Ds323x, Hours };
//! //!
//! # fn main() { //! ```no_run
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap(); //! use ds323x::{Ds323x, Rtcc};
//! use linux_embedded_hal::I2cdev;
//!
//! let dev = 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();
//! # }
//! ``` //! ```
//! Similar methods exist for month, day, weekday, hours, minutes and seconds.
//! //!
//! ### Enable/disable the device //! ### Enable/disable the device
//! //!
//! ```no_run //! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::Ds323x; //! use ds323x::Ds323x;
//! use linux_embedded_hal::I2cdev;
//! //!
//! # 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);
//! rtc.disable().unwrap(); // stops the clock //! rtc.disable().unwrap(); // stops the clock
//! let is_running = rtc.is_running().unwrap(); //! let running = rtc.running().unwrap();
//! println!("Is running: {}", is_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: {}", is_running); // will print true //! println!("Is running: {}", running); // will print true
//! # }
//! ``` //! ```
//! //!
//! ### Read the temperature //! ### Read the temperature
//! //!
//! ```no_run //! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::Ds323x; //! use ds323x::Ds323x;
//! use linux_embedded_hal::I2cdev;
//! //!
//! # 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 temperature = rtc.get_temperature().unwrap(); //! let temperature = rtc.temperature().unwrap();
//! # }
//! ``` //! ```
//! //!
//! ### Read busy status //! ### Read busy status
//! //!
//! ```no_run //! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::Ds323x; //! use ds323x::Ds323x;
//! use linux_embedded_hal::I2cdev;
//! //!
//! # 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 is_busy = rtc.is_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 linux_embedded_hal as hal; //! use ds323x::{Ds323x, SqWFreq};
//! extern crate ds323x; //! use linux_embedded_hal::I2cdev;
//! use ds323x::{ Ds323x, SqWFreq };
//! //!
//! # 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);
//! 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
@ -339,30 +279,24 @@
//! available for the devices DS3232 and DS3234. //! available for the devices DS3232 and DS3234.
//! //!
//! ```no_run //! ```no_run
//! extern crate linux_embedded_hal as hal; //! use ds323x::{Ds323x, SqWFreq};
//! extern crate ds323x; //! use linux_embedded_hal::I2cdev;
//! use ds323x::{ Ds323x, SqWFreq };
//! //!
//! # fn main() { //! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! 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(); //! 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 linux_embedded_hal as hal;
//! extern crate ds323x;
//! use ds323x::Ds323x; //! use ds323x::Ds323x;
//! use linux_embedded_hal::I2cdev;
//! //!
//! # 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);
//! 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
@ -370,24 +304,76 @@
//! 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 linux_embedded_hal as hal; //! use ds323x::{Ds323x, TempConvRate};
//! extern crate ds323x; //! use linux_embedded_hal::I2cdev;
//! use ds323x::{ Ds323x, TempConvRate };
//! //!
//! # fn main() { //! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! 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
//!
//! ```no_run
//! use ds323x::{Ds323x, Hours, WeekdayAlarm1, Alarm1Matching};
//! use linux_embedded_hal::I2cdev;
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! let alarm1 = WeekdayAlarm1 {
//! weekday: 1,
//! hour: Hours::H24(7),
//! minute: 2,
//! second: 15
//! };
//! rtc.set_alarm1_weekday(alarm1, Alarm1Matching::AllMatch).unwrap();
//! ```
//!
//! ### Set the Alarm2 to each day at the same time and enable interrupts on output
//!
//! The INT/SQW output pin will be set to 1 when it the alarm matches.
//!
//! ```no_run
//! use ds323x::{Ds323x, Hours, DayAlarm2, Alarm2Matching};
//! use linux_embedded_hal::I2cdev;
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! let alarm2 = DayAlarm2 {
//! day: 1, // does not matter given the chosen matching
//! hour: Hours::AM(11),
//! minute: 2
//! };
//! rtc.set_alarm2_day(alarm2, Alarm2Matching::HoursAndMinutesMatch).unwrap();
//! rtc.use_int_sqw_output_as_interrupt().unwrap();
//! rtc.enable_alarm2_interrupts().unwrap();
//! ```
//!
//! ### Set the Alarm1 to a specific time
//!
//! ```no_run
//! use ds323x::{Ds323x, Hours, NaiveTime};
//! use linux_embedded_hal::I2cdev;
//!
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! let mut rtc = Ds323x::new_ds3231(dev);
//! let time = NaiveTime::from_hms(19, 59, 58);
//! rtc.set_alarm1_hms(time).unwrap();
//! ``` //! ```
#![deny(unsafe_code)] #![deny(unsafe_code, missing_docs)]
#![deny(missing_docs)]
#![deny(warnings)]
#![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};
pub use rtcc::{
DateTimeAccess, Datelike, Hours, NaiveDate, NaiveDateTime, NaiveTime, Rtcc, Timelike,
};
/// SPI mode 1 (CPOL = 0, CPHA = 1)
pub const SPI_MODE_1: Mode = MODE_1;
/// SPI mode 3 (CPOL = 1, CPHA = 1)
pub const SPI_MODE_3: Mode = MODE_3;
/// All possible errors in this crate /// All possible errors in this crate
#[derive(Debug)] #[derive(Debug)]
@ -395,11 +381,16 @@ pub enum Error<E> {
/// I²C/SPI bus error /// I²C/SPI bus error
Comm(E), Comm(E),
/// 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
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum SqWFreq { pub enum SqWFreq {
/// 1 Hz (default) /// 1 Hz (default)
_1Hz, _1Hz,
@ -414,7 +405,7 @@ pub enum SqWFreq {
/// Temperature conversion rate /// Temperature conversion rate
/// ///
/// This is only available on the DS3232 and DS3234 devices. /// This is only available on the DS3232 and DS3234 devices.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum TempConvRate { pub enum TempConvRate {
/// Once every 64 seconds (default) /// Once every 64 seconds (default)
_64s, _64s,
@ -429,44 +420,50 @@ pub enum TempConvRate {
struct Register; struct Register;
impl Register { impl Register {
const SECONDS : u8 = 0x00; const SECONDS: u8 = 0x00;
const MINUTES : u8 = 0x01; const MINUTES: u8 = 0x01;
const HOURS : u8 = 0x02; const HOURS: u8 = 0x02;
const DOW : u8 = 0x03; const DOW: u8 = 0x03;
const DOM : u8 = 0x04; const DOM: u8 = 0x04;
const MONTH : u8 = 0x05; const MONTH: u8 = 0x05;
const YEAR : u8 = 0x06; const YEAR: u8 = 0x06;
const CONTROL : u8 = 0x0E; const ALARM1_SECONDS: u8 = 0x07;
const STATUS : u8 = 0x0F; const ALARM2_MINUTES: u8 = 0x0B;
const AGING_OFFSET : u8 = 0x10; const CONTROL: u8 = 0x0E;
const TEMP_MSB : u8 = 0x11; const STATUS: u8 = 0x0F;
const TEMP_CONV : u8 = 0x13; const AGING_OFFSET: u8 = 0x10;
const TEMP_MSB: u8 = 0x11;
const TEMP_CONV: u8 = 0x13;
} }
struct BitFlags; struct BitFlags;
impl BitFlags { impl BitFlags {
const H24_H12 : u8 = 0b0100_0000; const H24_H12: u8 = 0b0100_0000;
const AM_PM : u8 = 0b0010_0000; const AM_PM: u8 = 0b0010_0000;
const CENTURY : u8 = 0b1000_0000; const CENTURY: u8 = 0b1000_0000;
const EOSC : u8 = 0b1000_0000; const EOSC: u8 = 0b1000_0000;
const BBSQW : u8 = 0b0100_0000; const BBSQW: u8 = 0b0100_0000;
const TEMP_CONV : u8 = 0b0010_0000; const TEMP_CONV: u8 = 0b0010_0000;
const RS2 : u8 = 0b0001_0000; const RS2: u8 = 0b0001_0000;
const RS1 : u8 = 0b0000_1000; const RS1: u8 = 0b0000_1000;
const INTCN : u8 = 0b0000_0100; const INTCN: u8 = 0b0000_0100;
const OSC_STOP : u8 = 0b1000_0000; const ALARM2_INT_EN: u8 = 0b0000_0010;
const BB32KHZ : u8 = 0b0100_0000; const ALARM1_INT_EN: u8 = 0b0000_0001;
const CRATE1 : u8 = 0b0010_0000; const OSC_STOP: u8 = 0b1000_0000;
const CRATE0 : u8 = 0b0001_0000; const BB32KHZ: u8 = 0b0100_0000;
const EN32KHZ : u8 = 0b0000_1000; const CRATE1: u8 = 0b0010_0000;
const BUSY : u8 = 0b0000_0100; const CRATE0: u8 = 0b0001_0000;
const ALARM2F : u8 = 0b0000_0010; const EN32KHZ: u8 = 0b0000_1000;
const ALARM1F : u8 = 0b0000_0001; const BUSY: u8 = 0b0000_0100;
const TEMP_CONV_BAT : u8 = 0b0000_0001; const ALARM2F: u8 = 0b0000_0010;
const ALARM1F: u8 = 0b0000_0001;
const TEMP_CONV_BAT: u8 = 0b0000_0001;
const ALARM_MATCH: u8 = 0b1000_0000;
const WEEKDAY: u8 = 0b0100_0000;
} }
const DEVICE_ADDRESS : u8 = 0b110_1000; const DEVICE_ADDRESS: u8 = 0b110_1000;
const CONTROL_POR_VALUE: u8 = 0b0001_1100; const CONTROL_POR_VALUE: u8 = 0b0001_1100;
/// IC markers /// IC markers
@ -484,13 +481,27 @@ pub mod ic {
pub struct Ds323x<DI, IC> { pub struct Ds323x<DI, IC> {
iface: DI, iface: DI,
control: u8, control: u8,
status : u8, status: u8,
_ic: PhantomData<IC> _ic: PhantomData<IC>,
} }
pub mod interface;
mod ds323x; mod ds323x;
pub use ds323x::{ Hours, DateTime }; pub mod interface;
pub use crate::ds323x::{
Alarm1Matching, Alarm2Matching, DayAlarm1, DayAlarm2, WeekdayAlarm1, WeekdayAlarm2,
};
mod ds3231; mod ds3231;
mod ds3232; mod ds3232;
mod ds3234; mod ds3234;
mod private {
use super::{ic, interface};
pub trait Sealed {}
impl<SPI> Sealed for interface::SpiInterface<SPI> {}
impl<I2C> Sealed for interface::I2cInterface<I2C> {}
impl Sealed for ic::DS3231 {}
impl Sealed for ic::DS3232 {}
impl Sealed for ic::DS3234 {}
}

1111
tests/alarms.rs 100644

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -0,0 +1,18 @@
mod common;
use self::common::{
destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234,
};
macro_rules! construction_test {
($name:ident, $create:ident, $destroy:ident) => {
#[test]
fn $name() {
let dev = $create(&[]);
$destroy(dev);
}
};
}
construction_test!(can_create_ds3231, new_ds3231, destroy_ds3231);
construction_test!(can_create_ds3232, new_ds3232, destroy_ds3232);
construction_test!(can_create_ds3234, new_ds3234, destroy_ds3234);

View File

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

View File

@ -1,12 +0,0 @@
#[deny(warnings)]
extern crate embedded_hal_mock as hal;
extern crate ds323x;
use ds323x::Ds323x;
#[test]
fn can_create_and_destroy() {
let dev = Ds323x::new_ds3231(hal::i2c::Mock::new(&[]));
let mut i2c = dev.destroy_ds3231();
i2c.done();
}

View File

@ -1,12 +0,0 @@
#[deny(warnings)]
extern crate embedded_hal_mock as hal;
extern crate ds323x;
use ds323x::Ds323x;
#[test]
fn can_create_and_destroy() {
let dev = Ds323x::new_ds3232(hal::i2c::Mock::new(&[]));
let mut i2c = dev.destroy_ds3232();
i2c.done();
}

View File

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

View File

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

View File

@ -1,22 +1,51 @@
#[deny(warnings)] 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 common::{ DEVICE_ADDRESS as DEV_ADDR, Register, new_ds3231, use self::common::{
new_ds3232, new_ds3234, destroy_ds3231, destroy_ds3232, destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234,
destroy_ds3234, BitFlags as BF }; BitFlags as BF, Register, DEVICE_ADDRESS as DEV_ADDR,
};
get_param_test!(is_running, is_running, CONTROL, true, 0); get_param_test!(running, running, CONTROL, true, 0);
get_param_test!(is_not_running, is_running, CONTROL, false, BF::EOSC); get_param_test!(is_not_running, running, CONTROL, false, BF::EOSC);
get_param_test!(is_busy, is_busy, STATUS, true, 0xFF); get_param_test!(busy, busy, STATUS, true, 0xFF);
get_param_test!(is_not_busy, is_busy, STATUS, false, 0xFF & !BF::BUSY); get_param_test!(is_not_busy, busy, STATUS, false, !BF::BUSY);
get_param_test!(stopped, has_been_stopped, STATUS, true, 0xFF); get_param_test!(stopped, has_been_stopped, STATUS, true, 0xFF);
get_param_test!(not_stopped, has_been_stopped, STATUS, false, 0xFF & !BF::OSC_STOP); get_param_test!(not_stopped, has_been_stopped, STATUS, false, !BF::OSC_STOP);
get_param_read_array_test!(temp_0, get_temperature, 0.0, TEMP_MSB, [0, 0], [0, 0]); get_param_test!(alarm1_matched, has_alarm1_matched, STATUS, true, 0xFF);
get_param_read_array_test!(temp_min, get_temperature, -128.0, TEMP_MSB, [0b1000_0000, 0], [0, 0]); get_param_test!(
get_param_read_array_test!(temp_max, get_temperature, 127.75, TEMP_MSB, [0b0111_1111, 0b1100_0000], [0, 0]); alarm1_not_matched,
has_alarm1_matched,
STATUS,
false,
!BF::ALARM1F
);
get_param_test!(alarm2_matched, has_alarm2_matched, STATUS, true, 0xFF);
get_param_test!(
alarm2_not_matched,
has_alarm2_matched,
STATUS,
false,
!BF::ALARM2F
);
get_param_read_array_test!(temp_0, temperature, 0.0, TEMP_MSB, [0, 0], [0, 0]);
get_param_read_array_test!(
temp_min,
temperature,
-128.0,
TEMP_MSB,
[0b1000_0000, 0],
[0, 0]
);
get_param_read_array_test!(
temp_max,
temperature,
127.75,
TEMP_MSB,
[0b0111_1111, 0b1100_0000],
[0, 0]
);