mirror of https://github.com/eldruin/ds323x-rs
Compare commits
128 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
c5fdd9057f | |
![]() |
86dd80a8ad | |
![]() |
3597c7407e | |
![]() |
21546c07d3 | |
![]() |
ddca93fa30 | |
![]() |
b086f3086e | |
![]() |
03e0d9aad9 | |
![]() |
8204a4dfc1 | |
![]() |
4e0998897a | |
![]() |
f546361477 | |
![]() |
fa43172087 | |
![]() |
74009022bf | |
![]() |
fd6a145825 | |
![]() |
de0231b958 | |
![]() |
a8b8428570 | |
![]() |
f7d48398dc | |
![]() |
fe990d7295 | |
![]() |
b06eb59cd6 | |
![]() |
b0ab213098 | |
![]() |
6c6088f890 | |
![]() |
b3323f1a15 | |
![]() |
944e661053 | |
![]() |
faf48c2870 | |
![]() |
92e6de6c47 | |
![]() |
774b3cf527 | |
![]() |
fd66a531e2 | |
![]() |
e4f15715ed | |
![]() |
7329fe7c50 | |
![]() |
1930de500d | |
![]() |
a657eec8d6 | |
![]() |
9c6fe164b8 | |
![]() |
0b17437d4c | |
![]() |
7a566fd59c | |
![]() |
082fb2d159 | |
![]() |
b8ab37f57a | |
![]() |
a28b7c76fc | |
![]() |
2799590575 | |
![]() |
5414721d1d | |
![]() |
75291cde4c | |
![]() |
5ce8722fad | |
![]() |
f146d1d26e | |
![]() |
ff632625b2 | |
![]() |
73302b1866 | |
![]() |
c5a4e3df62 | |
![]() |
b62488f592 | |
![]() |
044051c2d9 | |
![]() |
092af13298 | |
![]() |
8c5bb16030 | |
![]() |
08d5192367 | |
![]() |
4a455b7828 | |
![]() |
9fe57bbdf6 | |
![]() |
b22cba6c63 | |
![]() |
1b13f482ae | |
![]() |
3425e28959 | |
![]() |
17c9230e1f | |
![]() |
fdc71accf3 | |
![]() |
c3106c0f49 | |
![]() |
3c8d378d6f | |
![]() |
d3329b2598 | |
![]() |
ed9502842d | |
![]() |
49f72014a7 | |
![]() |
af20f0321c | |
![]() |
c74f6a86f4 | |
![]() |
68c6ffd0a5 | |
![]() |
e8679f0b8a | |
![]() |
0ef5cec642 | |
![]() |
f9a4918354 | |
![]() |
954ab66f3b | |
![]() |
1be01a3b18 | |
![]() |
79c0b870df | |
![]() |
4db4677ea2 | |
![]() |
ab3739c7c3 | |
![]() |
0222a79f0d | |
![]() |
db921768ee | |
![]() |
a4c1348993 | |
![]() |
cd4a91b79e | |
![]() |
9f3f8be554 | |
![]() |
25f272015a | |
![]() |
9510730e24 | |
![]() |
37ae7aba43 | |
![]() |
fd5f3cc981 | |
![]() |
7491105873 | |
![]() |
e378737f4d | |
![]() |
dfe90b9000 | |
![]() |
7891a53705 | |
![]() |
0451dbb239 | |
![]() |
903e2496f2 | |
![]() |
dc05ca6bee | |
![]() |
b4cfbbb2d9 | |
![]() |
f53554b404 | |
![]() |
d0bcb715ce | |
![]() |
e5ee15439f | |
![]() |
55bbf9098a | |
![]() |
dee3deb073 | |
![]() |
729a4c92e7 | |
![]() |
96168aa696 | |
![]() |
c7da098f80 | |
![]() |
eb87a741f6 | |
![]() |
eb614c73a0 | |
![]() |
a5c8b8afaa | |
![]() |
441eb40368 | |
![]() |
a92bd92af6 | |
![]() |
3459b41fed | |
![]() |
e8516dd9bb | |
![]() |
c10bf7de9d | |
![]() |
5bff87333c | |
![]() |
81e5ac7225 | |
![]() |
7d7008a583 | |
![]() |
f971786c0d | |
![]() |
4674d06546 | |
![]() |
a7c3f0c83a | |
![]() |
06e5aaddf3 | |
![]() |
9ce8d106ce | |
![]() |
ce7463fc51 | |
![]() |
30cb5566d7 | |
![]() |
34f32ee067 | |
![]() |
0de8f5b8b7 | |
![]() |
b717f4e62b | |
![]() |
c3927e46c3 | |
![]() |
46d6656009 | |
![]() |
e6a1c4826f | |
![]() |
7e9bfcff13 | |
![]() |
d883c9c499 | |
![]() |
0dec4954e9 | |
![]() |
1465d116ab | |
![]() |
4675e902ad | |
![]() |
2304e08d1f | |
![]() |
77820cf6d0 |
.github/workflows
examples
|
@ -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'
|
56
.travis.yml
56
.travis.yml
|
@ -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
|
91
CHANGELOG.md
91
CHANGELOG.md
|
@ -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/)
|
||||
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
|
||||
|
||||
This is the initial release to crates.io. All changes will be documented in
|
||||
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
|
27
Cargo.toml
27
Cargo.toml
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ds323x"
|
||||
version = "0.1.0"
|
||||
version = "0.6.0"
|
||||
authors = ["Diego Barrios Romero <eldruin@gmail.com>"]
|
||||
repository = "https://github.com/eldruin/ds323x-rs"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
@ -11,24 +11,23 @@ categories = ["embedded", "hardware-support", "no-std"]
|
|||
homepage = "https://github.com/eldruin/ds323x-rs"
|
||||
documentation = "https://docs.rs/ds323x"
|
||||
include = [
|
||||
"**/*.rs",
|
||||
"Cargo.toml",
|
||||
"README.md",
|
||||
"CHANGELOG.md",
|
||||
"LICENSE-MIT",
|
||||
"LICENSE-APACHE",
|
||||
"/**/*.rs",
|
||||
"/Cargo.toml",
|
||||
"/README.md",
|
||||
"/CHANGELOG.md",
|
||||
"/LICENSE-MIT",
|
||||
"/LICENSE-APACHE",
|
||||
]
|
||||
|
||||
[badges.travis-ci]
|
||||
branch = "master"
|
||||
repository = "eldruin/ds323x-rs"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
embedded-hal = "0.2"
|
||||
embedded-hal = "1.0.0"
|
||||
rtcc = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
linux-embedded-hal = "0.2"
|
||||
embedded-hal-mock = "0.4"
|
||||
embedded-hal-mock = { version = "0.11.1", features = ["eh1"] }
|
||||
embedded-hal-bus = "0.2"
|
||||
linux-embedded-hal = "0.4.0"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
|
|
@ -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
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
|
137
README.md
137
README.md
|
@ -1,4 +1,10 @@
|
|||
# Rust DS3231, DS3232 and DS3234 Extremely Accurate Real-Time Clock Driver [](https://crates.io/crates/ds323x) [](https://docs.rs/ds323x) [](https://travis-ci.org/eldruin/ds323x-rs)
|
||||
# Rust DS3231, DS3232 and DS3234 Extremely Accurate Real-Time Clock Driver
|
||||
|
||||
[](https://crates.io/crates/ds323x)
|
||||
[](https://docs.rs/ds323x)
|
||||

|
||||
[](https://github.com/eldruin/ds323x-rs/actions?query=workflow%3ABuild)
|
||||
[](https://coveralls.io/r/eldruin/ds323x-rs?branch=master)
|
||||
|
||||
This is a platform agnostic Rust driver for the DS3231, DS3232 and DS3234
|
||||
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
|
||||
|
||||
This driver allows you to:
|
||||
- Read and set date and time in 12-hour and 24-hour format. See: `get_datetime`.
|
||||
- Read and set date and time individual elements. For example, see: `get_year`.
|
||||
- Read and set date and time in 12-hour and 24-hour format. See: `datetime`.
|
||||
- Read and set date and time individual elements. For example, see: `year`.
|
||||
- Enable and disable the real-time clock. See: `enable`.
|
||||
- Read the temperature. See `get_temperature`.
|
||||
- Force a temperature conversion and time compensation. See `convert_temperature`.
|
||||
- Read the busy status. See `is_busy`.
|
||||
- Read the busy status. See `busy`.
|
||||
- Read whether the oscillator is or has been stopped. See `has_been_stopped`.
|
||||
- Clear the has-been-stopped flag. See `clear_has_been_stopped_flag`.
|
||||
- Set the aging offset. See `set_aging_offset`.
|
||||
- Enable and disable the 32kHz output. See `enable_32khz_output`.
|
||||
- Set and read the aging offset. See `set_aging_offset`.
|
||||
- Select the function of the INT/SQW output pin. See `use_int_sqw_output_as_interrupt`.
|
||||
- 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 when battery-powered. See `enable_32khz_output_on_battery`.
|
||||
- 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`.
|
||||
- Alarms:
|
||||
- Set alarms 1 and 2 with several matching policies. See `set_alarm1_day`.
|
||||
- Set alarms 1 and 2 for a time. See `set_alarm1_hms`.
|
||||
- Read whether alarms 1 or 2 have matched. See `has_alarm1_matched`.
|
||||
- Clear flag indicating that alarms 1 or 2 have matched. See `clear_alarm1_matched_flag`.
|
||||
- Enable and disable alarms 1 and 2 interrupt generation. See `enable_alarm1_interrupts`.
|
||||
- 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
|
||||
|
||||
This driver is compatible with the DS3231 and DS3232 I2C devices and the
|
||||
DS3234 SPI device.
|
||||
|
||||
### DS3231
|
||||
The DS3231 is a low-cost, extremely accurate I2C real-time clock (RTC) with
|
||||
an integrated temperature-compensated crystal oscillator (TCXO) and crystal.
|
||||
These devices are low-cost temperature-compensated crystal oscillator (TCXO)
|
||||
with a very accurate, temperature-compensated, integrated real-time clock
|
||||
(RTC) including 236/256 bytes of battery-backed SRAM, depending on the model.
|
||||
|
||||
The device incorporates a battery input, and maintains accurate timekeeping
|
||||
when main power to the device is interrupted. The integration of the
|
||||
crystal resonator enhances the long-term accuracy of the device as well as
|
||||
### DS3231 and DS3232 details
|
||||
|
||||
The devices incorporate a battery input, and maintain accurate timekeeping
|
||||
when main power to the devices is interrupted. The integration of the
|
||||
crystal resonator enhances the long-term accuracy of the devices as well as
|
||||
reduces the piece-part count in a manufacturing line.
|
||||
The DS3231 is available in commercial and industrial temperature ranges,
|
||||
and is offered in a 16-pin, 300-mil SO package.
|
||||
The devices are available in commercial and industrial temperature ranges,
|
||||
and are offered in a 16-pin, 300-mil SO package.
|
||||
|
||||
The RTC maintains seconds, minutes, hours, day, date, month, and year
|
||||
information. The date at the end of the month is automatically adjusted for
|
||||
|
@ -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
|
||||
input for generating a μP reset.
|
||||
|
||||
### DS3232
|
||||
The DS3232 is a low-cost temperature-compensated crystal oscillator (TCXO)
|
||||
with a very accurate, temperature-compensated, integrated real-time clock
|
||||
(RTC) and 236 bytes of battery-backed SRAM.
|
||||
|
||||
Additionally, the DS3232 incorporates a battery input and maintains
|
||||
accurate timekeeping when main power to the device is interrupted. The
|
||||
integration of the crystal resonator enhances the long-term accuracy of the
|
||||
device as well as reduces the piece-part count in a manufacturing line.
|
||||
The DS3232 is available in commercial and industrial temperature ranges,
|
||||
and is offered in an industry-standard 20-pin, 300-mil SO package.
|
||||
|
||||
The RTC maintains seconds, minutes, hours, day, date, month, and year
|
||||
information. The date at the end of the month is automatically adjusted for
|
||||
months with fewer than 31 days, including corrections for leap year. The
|
||||
clock operates in either the 24-hour or 12-hour format with an AM/PM
|
||||
indicator. Two programmable time-of-day alarms and a programmable
|
||||
square-wave output are provided. Address and data are transferred serially
|
||||
through an I2C bidirectional bus.
|
||||
|
||||
A precision temperature-compensated voltage reference and comparator
|
||||
circuit monitors the status of VCC to detect power failures, to provide a
|
||||
reset output, and to automatically switch to the backup supply when
|
||||
necessary. Additionally, the RST pin is monitored as a pushbutton input for
|
||||
generating a μP reset.
|
||||
|
||||
### DS3234
|
||||
The DS3234 is a low-cost, extremely accurate SPI bus real-time clock (RTC)
|
||||
with an integrated temperature-compensated crystal oscillator (TCXO) and
|
||||
crystal.
|
||||
### DS3234 details
|
||||
|
||||
The DS3234 incorporates a precision, temperature-compensated voltage
|
||||
reference and comparator circuit to monitor VCC. When VCC drops below the
|
||||
|
@ -111,6 +98,49 @@ Datasheets:
|
|||
- [DS3232](https://datasheets.maximintegrated.com/en/ds/DS3232.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
|
||||
|
||||
Licensed under either of
|
||||
|
@ -118,11 +148,12 @@ Licensed under either of
|
|||
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT) at your option.
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contributing
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
|
||||
be dual licensed as above, without any additional terms or conditions.
|
||||
|
||||
|
|
|
@ -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
|
24
ci/script.sh
24
ci/script.sh
|
@ -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
|
|
@ -1,12 +1,17 @@
|
|||
extern crate embedded_hal;
|
||||
extern crate linux_embedded_hal;
|
||||
extern crate ds323x;
|
||||
|
||||
use ds323x::{DateTimeAccess, Ds323x, NaiveDate, Rtcc};
|
||||
use linux_embedded_hal::I2cdev;
|
||||
use ds323x::Ds323x;
|
||||
|
||||
fn main() {
|
||||
let dev = I2cdev::new("/dev/i2c-1").unwrap();
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -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},
|
||||
]
|
|
@ -1,25 +1,21 @@
|
|||
//! Functions exclusive of DS3231
|
||||
|
||||
extern crate embedded_hal as hal;
|
||||
use hal::blocking;
|
||||
use crate::{ic, interface::I2cInterface, BitFlags, Ds323x, CONTROL_POR_VALUE};
|
||||
use core::marker::PhantomData;
|
||||
use super::{ Ds323x, BitFlags, ic, CONTROL_POR_VALUE };
|
||||
use interface::I2cInterface;
|
||||
use embedded_hal::i2c;
|
||||
|
||||
impl<I2C, E> Ds323x<I2cInterface<I2C>, ic::DS3231>
|
||||
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.
|
||||
pub fn new_ds3231(i2c: I2C) -> Self {
|
||||
const STATUS_POR_VALUE : u8 = BitFlags::OSC_STOP | BitFlags::EN32KHZ;
|
||||
const STATUS_POR_VALUE: u8 = BitFlags::OSC_STOP | BitFlags::EN32KHZ;
|
||||
Ds323x {
|
||||
iface: I2cInterface {
|
||||
i2c,
|
||||
},
|
||||
iface: I2cInterface { i2c },
|
||||
control: CONTROL_POR_VALUE,
|
||||
status: STATUS_POR_VALUE,
|
||||
_ic: PhantomData
|
||||
_ic: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,26 +1,23 @@
|
|||
//! Functions exclusive of DS3232
|
||||
|
||||
extern crate embedded_hal as hal;
|
||||
use hal::blocking;
|
||||
use crate::{
|
||||
ic, interface::I2cInterface, BitFlags, Ds323x, Error, TempConvRate, CONTROL_POR_VALUE,
|
||||
};
|
||||
use core::marker::PhantomData;
|
||||
use super::{ Ds323x, TempConvRate, BitFlags, Error, ic, CONTROL_POR_VALUE };
|
||||
use interface::I2cInterface;
|
||||
|
||||
use embedded_hal::i2c;
|
||||
|
||||
impl<I2C, E> Ds323x<I2cInterface<I2C>, ic::DS3232>
|
||||
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.
|
||||
pub fn new_ds3232(i2c: I2C) -> Self {
|
||||
const STATUS_POR_VALUE : u8 = BitFlags::OSC_STOP | BitFlags::BB32KHZ | BitFlags::EN32KHZ;
|
||||
const STATUS_POR_VALUE: u8 = BitFlags::OSC_STOP | BitFlags::BB32KHZ | BitFlags::EN32KHZ;
|
||||
Ds323x {
|
||||
iface: I2cInterface {
|
||||
i2c,
|
||||
},
|
||||
iface: I2cInterface { i2c },
|
||||
control: CONTROL_POR_VALUE,
|
||||
status: STATUS_POR_VALUE,
|
||||
_ic: PhantomData
|
||||
_ic: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,13 +56,12 @@ where
|
|||
///
|
||||
/// Note: This is only available for DS3232 and DS3234 devices.
|
||||
pub fn set_temperature_conversion_rate(&mut self, rate: TempConvRate) -> Result<(), Error<E>> {
|
||||
let status;
|
||||
match rate {
|
||||
TempConvRate::_64s => status = self.status & !BitFlags::CRATE1 & !BitFlags::CRATE0,
|
||||
TempConvRate::_128s => status = self.status & !BitFlags::CRATE1 | BitFlags::CRATE0,
|
||||
TempConvRate::_256s => status = self.status | BitFlags::CRATE1 & !BitFlags::CRATE0,
|
||||
TempConvRate::_512s => status = self.status | BitFlags::CRATE1 | BitFlags::CRATE0,
|
||||
}
|
||||
let status = match rate {
|
||||
TempConvRate::_64s => self.status & !BitFlags::CRATE1 & !BitFlags::CRATE0,
|
||||
TempConvRate::_128s => self.status & !BitFlags::CRATE1 | BitFlags::CRATE0,
|
||||
TempConvRate::_256s => self.status | BitFlags::CRATE1 & !BitFlags::CRATE0,
|
||||
TempConvRate::_512s => self.status | BitFlags::CRATE1 | BitFlags::CRATE0,
|
||||
};
|
||||
self.write_status_without_clearing_alarm(status)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,33 +1,27 @@
|
|||
//! Functions exclusive of DS3234
|
||||
|
||||
extern crate embedded_hal as hal;
|
||||
use hal::blocking;
|
||||
use crate::interface::{SpiInterface, WriteData};
|
||||
use crate::{ic, BitFlags, Ds323x, Error, Register, TempConvRate, CONTROL_POR_VALUE};
|
||||
use core::marker::PhantomData;
|
||||
use super::{ Ds323x, TempConvRate, Register, BitFlags, Error, ic, CONTROL_POR_VALUE };
|
||||
use interface::{ SpiInterface, WriteData };
|
||||
use embedded_hal::spi;
|
||||
|
||||
impl<SPI, CS, E> Ds323x<SpiInterface<SPI, CS>, ic::DS3234>
|
||||
impl<SPI, E> Ds323x<SpiInterface<SPI>, ic::DS3234>
|
||||
where
|
||||
SPI: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
|
||||
CS: hal::digital::OutputPin
|
||||
SPI: spi::SpiDevice<u8, Error = E>,
|
||||
{
|
||||
/// Create a new instance.
|
||||
pub fn new_ds3234(spi: SPI, chip_select: CS) -> Self {
|
||||
const STATUS_POR_VALUE : u8 = BitFlags::OSC_STOP | BitFlags::BB32KHZ | BitFlags::EN32KHZ;
|
||||
pub fn new_ds3234(spi: SPI) -> Self {
|
||||
const STATUS_POR_VALUE: u8 = BitFlags::OSC_STOP | BitFlags::BB32KHZ | BitFlags::EN32KHZ;
|
||||
Ds323x {
|
||||
iface: SpiInterface {
|
||||
spi,
|
||||
cs: chip_select
|
||||
},
|
||||
iface: SpiInterface { spi },
|
||||
control: CONTROL_POR_VALUE,
|
||||
status: STATUS_POR_VALUE,
|
||||
_ic: PhantomData
|
||||
_ic: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroy driver instance, return SPI bus instance and CS output pin.
|
||||
pub fn destroy_ds3234(self) -> (SPI, CS) {
|
||||
(self.iface.spi, self.iface.cs)
|
||||
pub fn destroy_ds3234(self) -> SPI {
|
||||
self.iface.spi
|
||||
}
|
||||
|
||||
/// Enable the 32kHz output when battery-powered. (enabled per default)
|
||||
|
@ -60,13 +54,12 @@ where
|
|||
///
|
||||
/// Note: This is only available for DS3232 and DS3234 devices.
|
||||
pub fn set_temperature_conversion_rate(&mut self, rate: TempConvRate) -> Result<(), Error<E>> {
|
||||
let status;
|
||||
match rate {
|
||||
TempConvRate::_64s => status = self.status & !BitFlags::CRATE1 & !BitFlags::CRATE0,
|
||||
TempConvRate::_128s => status = self.status & !BitFlags::CRATE1 | BitFlags::CRATE0,
|
||||
TempConvRate::_256s => status = self.status | BitFlags::CRATE1 & !BitFlags::CRATE0,
|
||||
TempConvRate::_512s => status = self.status | BitFlags::CRATE1 | BitFlags::CRATE0,
|
||||
}
|
||||
let status = match rate {
|
||||
TempConvRate::_64s => self.status & !BitFlags::CRATE1 & !BitFlags::CRATE0,
|
||||
TempConvRate::_128s => self.status & !BitFlags::CRATE1 | BitFlags::CRATE0,
|
||||
TempConvRate::_256s => self.status | BitFlags::CRATE1 & !BitFlags::CRATE0,
|
||||
TempConvRate::_512s => self.status | BitFlags::CRATE1 | BitFlags::CRATE0,
|
||||
};
|
||||
self.write_status_without_clearing_alarm(status)
|
||||
}
|
||||
|
||||
|
@ -81,6 +74,7 @@ where
|
|||
///
|
||||
/// Note: This is only available for DS3234 devices.
|
||||
pub fn disable_temperature_conversions_on_battery(&mut self) -> Result<(), Error<E>> {
|
||||
self.iface.write_register(Register::TEMP_CONV, BitFlags::TEMP_CONV_BAT)
|
||||
self.iface
|
||||
.write_register(Register::TEMP_CONV, BitFlags::TEMP_CONV_BAT)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
//! Device configuration
|
||||
|
||||
extern crate embedded_hal as hal;
|
||||
use super::super::{ Ds323x, SqWFreq, Register, BitFlags, Error };
|
||||
use interface::{ ReadData, WriteData };
|
||||
use crate::{
|
||||
interface::{ReadData, WriteData},
|
||||
BitFlags, Ds323x, Error, Register, SqWFreq,
|
||||
};
|
||||
|
||||
impl<DI, IC, E> Ds323x<DI, IC>
|
||||
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).
|
||||
pub fn enable(&mut self) -> Result<(), Error<E>> {
|
||||
|
@ -22,12 +23,13 @@ where
|
|||
|
||||
/// 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>> {
|
||||
let control = self.iface.read_register(Register::CONTROL)?;
|
||||
// do not overwrite if a conversion is in progress
|
||||
if (control & BitFlags::TEMP_CONV) == 0 {
|
||||
self.iface.write_register(Register::CONTROL, control | BitFlags::TEMP_CONV)?;
|
||||
self.iface
|
||||
.write_register(Register::CONTROL, control | BitFlags::TEMP_CONV)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -46,11 +48,12 @@ where
|
|||
|
||||
/// Set the aging offset.
|
||||
pub fn set_aging_offset(&mut self, offset: i8) -> Result<(), Error<E>> {
|
||||
self.iface.write_register(Register::AGING_OFFSET, offset as u8)
|
||||
self.iface
|
||||
.write_register(Register::AGING_OFFSET, offset as u8)
|
||||
}
|
||||
|
||||
/// Read the aging offset.
|
||||
pub fn 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)?;
|
||||
Ok(offset as i8)
|
||||
}
|
||||
|
@ -81,23 +84,49 @@ where
|
|||
|
||||
/// Set the square-wave output frequency.
|
||||
pub fn set_square_wave_frequency(&mut self, freq: SqWFreq) -> Result<(), Error<E>> {
|
||||
let new_control;
|
||||
match freq {
|
||||
SqWFreq::_1Hz => new_control = self.control & !BitFlags::RS2 & !BitFlags::RS1,
|
||||
SqWFreq::_1_024Hz => new_control = self.control & !BitFlags::RS2 | BitFlags::RS1,
|
||||
SqWFreq::_4_096Hz => new_control = self.control | BitFlags::RS2 & !BitFlags::RS1,
|
||||
SqWFreq::_8_192Hz => new_control = self.control | BitFlags::RS2 | BitFlags::RS1,
|
||||
}
|
||||
let new_control = match freq {
|
||||
SqWFreq::_1Hz => self.control & !BitFlags::RS2 & !BitFlags::RS1,
|
||||
SqWFreq::_1_024Hz => self.control & !BitFlags::RS2 | BitFlags::RS1,
|
||||
SqWFreq::_4_096Hz => self.control | BitFlags::RS2 & !BitFlags::RS1,
|
||||
SqWFreq::_8_192Hz => self.control | BitFlags::RS2 | BitFlags::RS1,
|
||||
};
|
||||
self.write_control(new_control)
|
||||
}
|
||||
|
||||
/// Enable Alarm1 interrupts.
|
||||
pub fn enable_alarm1_interrupts(&mut self) -> Result<(), Error<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>> {
|
||||
self.iface.write_register(Register::CONTROL, control)?;
|
||||
self.control = control;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn write_status_without_clearing_alarm(&mut self, status: u8) -> Result<(), Error<E>> {
|
||||
pub(crate) fn write_status_without_clearing_alarm(
|
||||
&mut self,
|
||||
status: u8,
|
||||
) -> Result<(), Error<E>> {
|
||||
// avoid clearing alarm flags
|
||||
let new_status = status | BitFlags::ALARM2F | BitFlags::ALARM1F;
|
||||
self.iface.write_register(Register::STATUS, new_status)?;
|
||||
|
|
|
@ -1,170 +1,170 @@
|
|||
//! Common implementation
|
||||
|
||||
extern crate embedded_hal as hal;
|
||||
use super::super::{ Ds323x, Register, BitFlags, Error };
|
||||
use interface::{ ReadData, WriteData };
|
||||
use super::{
|
||||
decimal_to_packed_bcd, hours_to_register, packed_bcd_to_decimal, some_or_invalid_error,
|
||||
};
|
||||
use crate::{
|
||||
interface::{ReadData, WriteData},
|
||||
BitFlags, DateTimeAccess, Datelike, Ds323x, Error, Hours, NaiveDate, NaiveDateTime, NaiveTime,
|
||||
Register, Rtcc, Timelike,
|
||||
};
|
||||
|
||||
/// Date and time
|
||||
#[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>
|
||||
impl<DI, IC, E> DateTimeAccess for Ds323x<DI, IC>
|
||||
where
|
||||
DI: ReadData<Error = E> + WriteData<Error = E>
|
||||
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
|
||||
{
|
||||
/// Read the seconds.
|
||||
pub fn get_seconds(&mut self) -> Result<u8, Error<E>> {
|
||||
type Error = Error<E>;
|
||||
|
||||
fn datetime(&mut self) -> Result<NaiveDateTime, Self::Error> {
|
||||
let mut data = [0; 8];
|
||||
self.iface.read_data(&mut data)?;
|
||||
|
||||
let year = year_from_registers(
|
||||
data[Register::MONTH as usize + 1],
|
||||
data[Register::YEAR as usize + 1],
|
||||
);
|
||||
let month = packed_bcd_to_decimal(data[Register::MONTH as usize + 1] & !BitFlags::CENTURY);
|
||||
let day = packed_bcd_to_decimal(data[Register::DOM as usize + 1]);
|
||||
let hour = hours_from_register(data[Register::HOURS as usize + 1]);
|
||||
let minute = packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]);
|
||||
let second = packed_bcd_to_decimal(data[Register::SECONDS as usize + 1]);
|
||||
|
||||
let date = NaiveDate::from_ymd_opt(year.into(), month.into(), day.into());
|
||||
let date = some_or_invalid_error(date)?;
|
||||
let datetime = date.and_hms_opt(get_h24(hour).into(), minute.into(), second.into());
|
||||
some_or_invalid_error(datetime)
|
||||
}
|
||||
|
||||
fn set_datetime(&mut self, datetime: &NaiveDateTime) -> Result<(), Self::Error> {
|
||||
if datetime.year() < 2000 || datetime.year() > 2100 {
|
||||
return Err(Error::InvalidInputData);
|
||||
}
|
||||
let (month, year) = month_year_to_registers(datetime.month() as u8, datetime.year() as u16);
|
||||
let mut payload = [
|
||||
Register::SECONDS,
|
||||
decimal_to_packed_bcd(datetime.second() as u8),
|
||||
decimal_to_packed_bcd(datetime.minute() as u8),
|
||||
hours_to_register(Hours::H24(datetime.hour() as u8))?,
|
||||
datetime.weekday().number_from_sunday() as u8,
|
||||
decimal_to_packed_bcd(datetime.day() as u8),
|
||||
month,
|
||||
year,
|
||||
];
|
||||
self.iface.write_data(&mut payload)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
/// Read the minutes.
|
||||
pub fn get_minutes(&mut self) -> Result<u8, Error<E>> {
|
||||
fn minutes(&mut self) -> Result<u8, Self::Error> {
|
||||
self.read_register_decimal(Register::MINUTES)
|
||||
}
|
||||
|
||||
/// Read the hours.
|
||||
pub fn get_hours(&mut self) -> Result<Hours, Error<E>> {
|
||||
fn hours(&mut self) -> Result<Hours, Self::Error> {
|
||||
let data = self.iface.read_register(Register::HOURS)?;
|
||||
Ok(hours_from_register(data))
|
||||
}
|
||||
|
||||
/// Read the day of the week [1-7].
|
||||
pub fn get_weekday(&mut self) -> Result<u8, Error<E>> {
|
||||
fn time(&mut self) -> Result<NaiveTime, Self::Error> {
|
||||
let mut data = [0; 4];
|
||||
self.iface.read_data(&mut data)?;
|
||||
let hour = hours_from_register(data[Register::HOURS as usize + 1]);
|
||||
let minute = packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]);
|
||||
let second = packed_bcd_to_decimal(data[Register::SECONDS as usize + 1]);
|
||||
|
||||
let time = NaiveTime::from_hms_opt(get_h24(hour).into(), minute.into(), second.into());
|
||||
some_or_invalid_error(time)
|
||||
}
|
||||
|
||||
fn weekday(&mut self) -> Result<u8, Self::Error> {
|
||||
self.read_register_decimal(Register::DOW)
|
||||
}
|
||||
|
||||
/// Read the day of the month [1-31].
|
||||
pub fn get_day(&mut self) -> Result<u8, Error<E>> {
|
||||
fn day(&mut self) -> Result<u8, Self::Error> {
|
||||
self.read_register_decimal(Register::DOM)
|
||||
}
|
||||
|
||||
/// Read the month [1-12].
|
||||
pub fn get_month(&mut self) -> Result<u8, Error<E>> {
|
||||
fn month(&mut self) -> Result<u8, Self::Error> {
|
||||
let data = self.iface.read_register(Register::MONTH)?;
|
||||
let value = data & !BitFlags::CENTURY;
|
||||
Ok(packed_bcd_to_decimal(value))
|
||||
}
|
||||
|
||||
/// Read the year [2000-2100].
|
||||
pub fn get_year(&mut self) -> Result<u16, Error<E>> {
|
||||
fn year(&mut self) -> Result<u16, Self::Error> {
|
||||
let mut data = [0; 3];
|
||||
data[0] = Register::MONTH;
|
||||
self.iface.read_data(&mut data)?;
|
||||
Ok(year_from_registers(data[1], data[2]))
|
||||
}
|
||||
|
||||
/// Read the date and time.
|
||||
pub fn get_datetime(&mut self) -> Result<DateTime, Error<E>> {
|
||||
let mut data = [0; 8];
|
||||
fn date(&mut self) -> Result<NaiveDate, Self::Error> {
|
||||
let mut data = [0; 4];
|
||||
data[0] = Register::DOM;
|
||||
self.iface.read_data(&mut data)?;
|
||||
Ok(DateTime {
|
||||
year: year_from_registers(data[Register::MONTH as usize + 1], data[Register::YEAR as usize + 1]),
|
||||
month: packed_bcd_to_decimal(data[Register::MONTH as usize + 1] & !BitFlags::CENTURY),
|
||||
day: packed_bcd_to_decimal(data[Register::DOM as usize + 1]),
|
||||
weekday: packed_bcd_to_decimal(data[Register::DOW as usize + 1]),
|
||||
hour: hours_from_register(data[Register::HOURS as usize + 1]),
|
||||
minute: packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]),
|
||||
second: packed_bcd_to_decimal(data[Register::SECONDS as usize + 1])
|
||||
})
|
||||
|
||||
let offset = Register::DOM as usize;
|
||||
let year = year_from_registers(
|
||||
data[Register::MONTH as usize + 1 - offset],
|
||||
data[Register::YEAR as usize + 1 - offset],
|
||||
);
|
||||
let month =
|
||||
packed_bcd_to_decimal(data[Register::MONTH as usize + 1 - offset] & !BitFlags::CENTURY);
|
||||
let day = packed_bcd_to_decimal(data[Register::DOM as usize + 1 - offset]);
|
||||
let date = NaiveDate::from_ymd_opt(year.into(), month.into(), day.into());
|
||||
some_or_invalid_error(date)
|
||||
}
|
||||
|
||||
fn read_register_decimal(&mut self, register: u8) -> Result<u8, Error<E>> {
|
||||
let data = self.iface.read_register(register)?;
|
||||
Ok(packed_bcd_to_decimal(data))
|
||||
}
|
||||
|
||||
/// Set the seconds [0-59].
|
||||
///
|
||||
/// Will return an `Error::InvalidInputData` if the seconds are out of range.
|
||||
pub fn set_seconds(&mut self, seconds: u8) -> Result<(), Error<E>> {
|
||||
fn set_seconds(&mut self, seconds: u8) -> Result<(), Self::Error> {
|
||||
if seconds > 59 {
|
||||
return Err(Error::InvalidInputData);
|
||||
}
|
||||
self.write_register_decimal(Register::SECONDS, seconds)
|
||||
}
|
||||
|
||||
/// Set the minutes [0-59].
|
||||
///
|
||||
/// Will return an `Error::InvalidInputData` if the minutes are out of range.
|
||||
pub fn set_minutes(&mut self, minutes: u8) -> Result<(), Error<E>> {
|
||||
fn set_minutes(&mut self, minutes: u8) -> Result<(), Self::Error> {
|
||||
if minutes > 59 {
|
||||
return Err(Error::InvalidInputData);
|
||||
}
|
||||
self.write_register_decimal(Register::MINUTES, minutes)
|
||||
}
|
||||
|
||||
/// Set the hours.
|
||||
///
|
||||
/// Changes the operating mode to 12h/24h depending on the parameter.
|
||||
///
|
||||
/// Will return an `Error::InvalidInputData` if the hours are out of range.
|
||||
pub fn set_hours(&mut self, hours: Hours) -> Result<(), Error<E>> {
|
||||
let value = self.get_hours_register_value(&hours)?;
|
||||
fn set_hours(&mut self, hours: Hours) -> Result<(), Self::Error> {
|
||||
let value = hours_to_register(hours)?;
|
||||
self.iface.write_register(Register::HOURS, value)
|
||||
}
|
||||
|
||||
fn get_hours_register_value(&mut self, hours: &Hours) -> Result<u8, Error<E>> {
|
||||
match *hours {
|
||||
Hours::H24(h) if h > 23 => Err(Error::InvalidInputData),
|
||||
Hours::H24(h) => Ok(decimal_to_packed_bcd(h)),
|
||||
Hours::AM(h) if h < 1 || h > 12 => Err(Error::InvalidInputData),
|
||||
Hours::AM(h) => Ok(BitFlags::H24_H12 | decimal_to_packed_bcd(h)),
|
||||
Hours::PM(h) if h < 1 || h > 12 => Err(Error::InvalidInputData),
|
||||
Hours::PM(h) => Ok(BitFlags::H24_H12 | BitFlags::AM_PM | decimal_to_packed_bcd(h)),
|
||||
}
|
||||
fn set_time(&mut self, time: &NaiveTime) -> Result<(), Self::Error> {
|
||||
let mut payload = [
|
||||
Register::SECONDS,
|
||||
decimal_to_packed_bcd(time.second() as u8),
|
||||
decimal_to_packed_bcd(time.minute() as u8),
|
||||
hours_to_register(Hours::H24(time.hour() as u8))?,
|
||||
];
|
||||
self.iface.write_data(&mut payload)
|
||||
}
|
||||
|
||||
/// Set the day of week [1-7].
|
||||
///
|
||||
/// Will return an `Error::InvalidInputData` if the day is out of range.
|
||||
pub fn set_weekday(&mut self, weekday: u8) -> Result<(), Error<E>> {
|
||||
if weekday < 1 || weekday > 7 {
|
||||
fn set_weekday(&mut self, weekday: u8) -> Result<(), Self::Error> {
|
||||
if !(1..=7).contains(&weekday) {
|
||||
return Err(Error::InvalidInputData);
|
||||
}
|
||||
self.iface.write_register(Register::DOW, weekday)
|
||||
}
|
||||
|
||||
/// Set the day of month [1-31].
|
||||
///
|
||||
/// Will return an `Error::InvalidInputData` if the day is out of range.
|
||||
pub fn set_day(&mut self, day: u8) -> Result<(), Error<E>> {
|
||||
if day < 1 || day > 7 {
|
||||
fn set_day(&mut self, day: u8) -> Result<(), Self::Error> {
|
||||
if !(1..=31).contains(&day) {
|
||||
return Err(Error::InvalidInputData);
|
||||
}
|
||||
self.iface.write_register(Register::DOM, day)
|
||||
self.write_register_decimal(Register::DOM, day)
|
||||
}
|
||||
|
||||
/// Set the month [1-12].
|
||||
///
|
||||
/// Will return an `Error::InvalidInputData` if the month is out of range.
|
||||
pub fn set_month(&mut self, month: u8) -> Result<(), Error<E>> {
|
||||
if month < 1 || month > 12 {
|
||||
fn set_month(&mut self, month: u8) -> Result<(), Self::Error> {
|
||||
if !(1..=12).contains(&month) {
|
||||
return Err(Error::InvalidInputData);
|
||||
}
|
||||
// keep the century bit
|
||||
|
@ -173,65 +173,71 @@ where
|
|||
self.iface.write_register(Register::MONTH, value)
|
||||
}
|
||||
|
||||
/// Set the year [2000-2100].
|
||||
///
|
||||
/// Will return an `Error::InvalidInputData` if the year is out of range.
|
||||
pub fn set_year(&mut self, year: u16) -> Result<(), Error<E>> {
|
||||
if year < 2000 || year > 2100 {
|
||||
fn set_year(&mut self, year: u16) -> Result<(), Self::Error> {
|
||||
if !(2000..=2100).contains(&year) {
|
||||
return Err(Error::InvalidInputData);
|
||||
}
|
||||
let data = self.iface.read_register(Register::MONTH)?;
|
||||
let month_bcd = data & !BitFlags::CENTURY;
|
||||
if year > 2099 {
|
||||
let mut data = [ Register::MONTH,
|
||||
BitFlags::CENTURY | month_bcd,
|
||||
decimal_to_packed_bcd((year - 2100) as u8) ];
|
||||
let mut data = [
|
||||
Register::MONTH,
|
||||
BitFlags::CENTURY | month_bcd,
|
||||
decimal_to_packed_bcd((year - 2100) as u8),
|
||||
];
|
||||
self.iface.write_data(&mut data)
|
||||
}
|
||||
else {
|
||||
let mut data = [ Register::MONTH, month_bcd,
|
||||
decimal_to_packed_bcd((year - 2000) as u8) ];
|
||||
} else {
|
||||
let mut data = [
|
||||
Register::MONTH,
|
||||
month_bcd,
|
||||
decimal_to_packed_bcd((year - 2000) as u8),
|
||||
];
|
||||
self.iface.write_data(&mut data)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the date and time.
|
||||
///
|
||||
/// Will return an `Error::InvalidInputData` if any of the parameters is out of range.
|
||||
pub fn set_datetime(&mut self, datetime: &DateTime) -> Result<(), Error<E>> {
|
||||
if datetime.year < 2000 || datetime.year > 2100 ||
|
||||
datetime.month < 1 || datetime.month > 12 ||
|
||||
datetime.day < 1 || datetime.day > 31 ||
|
||||
datetime.weekday < 1 || datetime.weekday > 7 ||
|
||||
datetime.minute > 59 ||
|
||||
datetime.second > 59 {
|
||||
fn set_date(&mut self, date: &rtcc::NaiveDate) -> Result<(), Self::Error> {
|
||||
if date.year() < 2000 || date.year() > 2100 {
|
||||
return Err(Error::InvalidInputData);
|
||||
}
|
||||
let (month, year) = month_year_to_registers(datetime.month, datetime.year);
|
||||
let mut payload = [Register::SECONDS,
|
||||
decimal_to_packed_bcd(datetime.second),
|
||||
decimal_to_packed_bcd(datetime.minute),
|
||||
self.get_hours_register_value(&datetime.hour)?,
|
||||
decimal_to_packed_bcd(datetime.weekday),
|
||||
decimal_to_packed_bcd(datetime.day),
|
||||
month, year];
|
||||
let (month, year) = month_year_to_registers(date.month() as u8, date.year() as u16);
|
||||
let mut payload = [
|
||||
Register::DOW,
|
||||
date.weekday().number_from_sunday() as u8,
|
||||
decimal_to_packed_bcd(date.day() as u8),
|
||||
month,
|
||||
year,
|
||||
];
|
||||
self.iface.write_data(&mut payload)
|
||||
}
|
||||
}
|
||||
|
||||
impl<DI, IC, E> Ds323x<DI, IC>
|
||||
where
|
||||
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
|
||||
{
|
||||
fn read_register_decimal(&mut self, register: u8) -> Result<u8, Error<E>> {
|
||||
let data = self.iface.read_register(register)?;
|
||||
Ok(packed_bcd_to_decimal(data))
|
||||
}
|
||||
|
||||
fn write_register_decimal(&mut self, register: u8, decimal_number: u8) -> Result<(), Error<E>> {
|
||||
self.iface.write_register(register, decimal_to_packed_bcd(decimal_number))
|
||||
self.iface
|
||||
.write_register(register, decimal_to_packed_bcd(decimal_number))
|
||||
}
|
||||
}
|
||||
|
||||
fn hours_from_register(data: u8) -> Hours {
|
||||
if is_24h_format(data) {
|
||||
Hours::H24(packed_bcd_to_decimal(data & !BitFlags::H24_H12))
|
||||
}
|
||||
else if is_am(data) {
|
||||
Hours::AM(packed_bcd_to_decimal(data & !(BitFlags::H24_H12 | BitFlags::AM_PM)))
|
||||
}
|
||||
else {
|
||||
Hours::PM(packed_bcd_to_decimal(data & !(BitFlags::H24_H12 | BitFlags::AM_PM)))
|
||||
} else if is_am(data) {
|
||||
Hours::AM(packed_bcd_to_decimal(
|
||||
data & !(BitFlags::H24_H12 | BitFlags::AM_PM),
|
||||
))
|
||||
} else {
|
||||
Hours::PM(packed_bcd_to_decimal(
|
||||
data & !(BitFlags::H24_H12 | BitFlags::AM_PM),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,10 +245,9 @@ fn year_from_registers(month: u8, year: u8) -> u16 {
|
|||
let century = month & BitFlags::CENTURY;
|
||||
let year = packed_bcd_to_decimal(year);
|
||||
if century != 0 {
|
||||
2100 + (year as u16)
|
||||
}
|
||||
else {
|
||||
2000 + (year as u16)
|
||||
2100 + u16::from(year)
|
||||
} else {
|
||||
2000 + u16::from(year)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,9 +255,11 @@ fn month_year_to_registers(month: u8, year: u16) -> (u8, u8) {
|
|||
if year > 2099 {
|
||||
let month = BitFlags::CENTURY | decimal_to_packed_bcd(month);
|
||||
(month, decimal_to_packed_bcd((year - 2100) as u8))
|
||||
}
|
||||
else {
|
||||
(decimal_to_packed_bcd(month), decimal_to_packed_bcd((year - 2000) as u8))
|
||||
} else {
|
||||
(
|
||||
decimal_to_packed_bcd(month),
|
||||
decimal_to_packed_bcd((year - 2000) as u8),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,14 +271,12 @@ fn is_am(hours_data: u8) -> bool {
|
|||
hours_data & BitFlags::AM_PM == 0
|
||||
}
|
||||
|
||||
// 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 get_h24(hour: Hours) -> u8 {
|
||||
match hour {
|
||||
Hours::H24(h) => h,
|
||||
Hours::AM(h) => h,
|
||||
Hours::PM(h) => h + 12,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -279,28 +284,17 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
#[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));
|
||||
}
|
||||
fn can_convert_to_h24() {
|
||||
assert_eq!(0, get_h24(Hours::H24(0)));
|
||||
assert_eq!(0, get_h24(Hours::AM(0)));
|
||||
assert_eq!(12, get_h24(Hours::PM(0)));
|
||||
|
||||
#[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));
|
||||
assert_eq!(1, get_h24(Hours::H24(1)));
|
||||
assert_eq!(1, get_h24(Hours::AM(1)));
|
||||
assert_eq!(13, get_h24(Hours::PM(1)));
|
||||
|
||||
assert_eq!(23, get_h24(Hours::H24(23)));
|
||||
assert_eq!(12, get_h24(Hours::AM(12)));
|
||||
assert_eq!(23, get_h24(Hours::PM(11)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,84 @@
|
|||
mod alarms;
|
||||
mod configuration;
|
||||
mod status;
|
||||
pub use self::alarms::{
|
||||
Alarm1Matching, Alarm2Matching, DayAlarm1, DayAlarm2, WeekdayAlarm1, WeekdayAlarm2,
|
||||
};
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
//! Device status
|
||||
|
||||
extern crate embedded_hal as hal;
|
||||
use super::super::{ Ds323x, Register, BitFlags, Error };
|
||||
use interface::{ ReadData, WriteData };
|
||||
use crate::{
|
||||
interface::{ReadData, WriteData},
|
||||
BitFlags, Ds323x, Error, Register,
|
||||
};
|
||||
|
||||
impl<DI, IC, E> Ds323x<DI, IC>
|
||||
where
|
||||
DI: ReadData<Error = E> + WriteData<Error = E>
|
||||
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
|
||||
{
|
||||
/// 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)?;
|
||||
Ok((control & BitFlags::EOSC) == 0)
|
||||
}
|
||||
|
||||
/// 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)?;
|
||||
Ok((status & BitFlags::BUSY) != 0)
|
||||
}
|
||||
|
@ -35,31 +36,60 @@ where
|
|||
/// Clear flag signalling whether the oscillator is stopped or has been
|
||||
/// stopped at some point.
|
||||
///
|
||||
/// (Does not alter the device register if already cleared).
|
||||
/// See also: [`has_been_stopped()`](#method.has_been_stopped)
|
||||
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)?;
|
||||
if (status & BitFlags::OSC_STOP) != 0 {
|
||||
self.iface.write_register(Register::STATUS, status & !BitFlags::OSC_STOP)?;
|
||||
}
|
||||
Ok(())
|
||||
Ok((status & BitFlags::ALARM1F) != 0)
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// Note: It is possible to manually force a temperature conversion with
|
||||
/// [`convert_temperature()`](#method.convert_temperature)
|
||||
pub fn get_temperature(&mut self) -> Result<f32, Error<E>> {
|
||||
pub fn temperature(&mut self) -> Result<f32, Error<E>> {
|
||||
let mut data = [Register::TEMP_MSB, 0, 0];
|
||||
self.iface.read_data(&mut data)?;
|
||||
let is_negative = (data[1] & 0b1000_0000) != 0;
|
||||
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 {
|
||||
let temp_sign_extended = temp | 0b1111_1100_0000_0000;
|
||||
Ok(temp_sign_extended as i16 as f32 * 0.25)
|
||||
}
|
||||
else {
|
||||
Ok(temp as f32 * 0.25)
|
||||
Ok(f32::from(temp_sign_extended as i16) * 0.25)
|
||||
} else {
|
||||
Ok(f32::from(temp) * 0.25)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
103
src/interface.rs
103
src/interface.rs
|
@ -1,10 +1,7 @@
|
|||
//! I2C/SPI interfaces
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
extern crate embedded_hal as hal;
|
||||
use hal::blocking;
|
||||
use super::{ DEVICE_ADDRESS, Error };
|
||||
use crate::{private, Error, DEVICE_ADDRESS};
|
||||
use embedded_hal::{i2c, spi};
|
||||
|
||||
/// I2C interface
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -14,87 +11,69 @@ pub struct I2cInterface<I2C> {
|
|||
|
||||
/// SPI interface
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SpiInterface<SPI, CS> {
|
||||
pub struct SpiInterface<SPI> {
|
||||
pub(crate) spi: SPI,
|
||||
pub(crate) cs: CS
|
||||
}
|
||||
|
||||
/// Write data
|
||||
pub trait WriteData {
|
||||
pub trait WriteData: private::Sealed {
|
||||
/// Error type
|
||||
type Error;
|
||||
/// 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.
|
||||
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>
|
||||
where
|
||||
I2C: blocking::i2c::Write<Error = E>
|
||||
I2C: i2c::I2c<Error = E>,
|
||||
{
|
||||
type Error = E;
|
||||
fn write_register(&mut self, register: u8, data: u8) -> Result<(), Error<E>> {
|
||||
type Error = Error<E>;
|
||||
fn write_register(&mut self, register: u8, data: u8) -> Result<(), Self::Error> {
|
||||
let payload: [u8; 2] = [register, data];
|
||||
self.i2c
|
||||
.write(DEVICE_ADDRESS, &payload)
|
||||
.map_err(Error::Comm)
|
||||
}
|
||||
|
||||
fn write_data(&mut self, payload: &mut [u8]) -> Result<(), Error<Self::Error>> {
|
||||
self.i2c
|
||||
.write(DEVICE_ADDRESS, &payload)
|
||||
.map_err(Error::Comm)
|
||||
fn write_data(&mut self, payload: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.i2c.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
|
||||
SPI: blocking::spi::Write<u8, Error = E>,
|
||||
CS: hal::digital::OutputPin
|
||||
SPI: spi::SpiDevice<u8, Error = E>,
|
||||
{
|
||||
type Error = E;
|
||||
fn write_register(&mut self, register: u8, data: u8) -> Result<(), Error<E>> {
|
||||
self.cs.set_low();
|
||||
|
||||
type Error = Error<E>;
|
||||
fn write_register(&mut self, register: u8, data: u8) -> Result<(), Self::Error> {
|
||||
let payload: [u8; 2] = [register + 0x80, data];
|
||||
let result = self.spi
|
||||
.write(&payload)
|
||||
.map_err(Error::Comm);
|
||||
|
||||
self.cs.set_high();
|
||||
result
|
||||
self.spi.write(&payload).map_err(Error::Comm)
|
||||
}
|
||||
|
||||
fn write_data(&mut self, payload: &mut [u8]) -> Result<(), Error<Self::Error>> {
|
||||
self.cs.set_low();
|
||||
fn write_data(&mut self, payload: &mut [u8]) -> Result<(), Self::Error> {
|
||||
payload[0] += 0x80;
|
||||
let result = self.spi
|
||||
.write(&payload)
|
||||
.map_err(Error::Comm);
|
||||
|
||||
self.cs.set_high();
|
||||
result
|
||||
self.spi.write(payload).map_err(Error::Comm)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Read data
|
||||
pub trait ReadData {
|
||||
pub trait ReadData: private::Sealed {
|
||||
/// Error type
|
||||
type Error;
|
||||
/// 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.
|
||||
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>
|
||||
where
|
||||
I2C: blocking::i2c::WriteRead<Error = E>
|
||||
I2C: i2c::I2c<Error = E>,
|
||||
{
|
||||
type Error = E;
|
||||
fn read_register(&mut self, register: u8) -> Result<u8, Error<E>> {
|
||||
type Error = Error<E>;
|
||||
fn read_register(&mut self, register: u8) -> Result<u8, Self::Error> {
|
||||
let mut data = [0];
|
||||
self.i2c
|
||||
.write_read(DEVICE_ADDRESS, &[register], &mut data)
|
||||
|
@ -102,40 +81,26 @@ where
|
|||
.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();
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<SPI, CS, E> ReadData for SpiInterface<SPI, CS>
|
||||
impl<SPI, E> ReadData for SpiInterface<SPI>
|
||||
where
|
||||
SPI: blocking::spi::Transfer<u8, Error = E>,
|
||||
CS: hal::digital::OutputPin
|
||||
SPI: spi::SpiDevice<u8, Error = E>,
|
||||
{
|
||||
type Error = E;
|
||||
fn read_register(&mut self, register: u8) -> Result<u8, Error<E>> {
|
||||
self.cs.set_low();
|
||||
type Error = Error<E>;
|
||||
fn read_register(&mut self, register: u8) -> Result<u8, Self::Error> {
|
||||
let mut data = [register, 0];
|
||||
let result = self.spi
|
||||
.transfer(&mut data)
|
||||
.map_err(Error::Comm);
|
||||
self.cs.set_high();
|
||||
match result {
|
||||
Ok(result) => Ok(result[1]),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
let result = self.spi.transfer_in_place(&mut data).map_err(Error::Comm);
|
||||
result.and(Ok(data[1]))
|
||||
}
|
||||
|
||||
fn read_data(&mut self, mut payload: &mut [u8]) -> Result<(), Error<Self::Error>> {
|
||||
self.cs.set_low();
|
||||
let result = self.spi
|
||||
.transfer(&mut payload)
|
||||
.map_err(Error::Comm);
|
||||
self.cs.set_high();
|
||||
result?;
|
||||
Ok(())
|
||||
fn read_data(&mut self, payload: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.spi.transfer_in_place(payload).map_err(Error::Comm)
|
||||
}
|
||||
}
|
||||
|
|
465
src/lib.rs
465
src/lib.rs
|
@ -4,55 +4,70 @@
|
|||
//! [`embedded-hal`]: https://github.com/rust-embedded/embedded-hal
|
||||
//!
|
||||
//! This driver allows you to:
|
||||
//! - Read and set date and time in 12-hour and 24-hour format. See: [`get_datetime`].
|
||||
//! - Read and set date and time individual elements. For example, see: [`get_year`].
|
||||
//! - Read and set date and time in 12-hour and 24-hour format. See: [`datetime`].
|
||||
//! - Read and set date and time individual elements. For example, see: [`year`].
|
||||
//! - Enable and disable the real-time clock. See: [`enable`].
|
||||
//! - Read the temperature. See [`get_temperature`].
|
||||
//! - Force a temperature conversion and time compensation. See [`convert_temperature`].
|
||||
//! - Read the busy status. See [`is_busy`].
|
||||
//! - Read the busy status. See [`busy`].
|
||||
//! - Read whether the oscillator is or has been stopped. See [`has_been_stopped`].
|
||||
//! - Clear the has-been-stopped flag. See [`clear_has_been_stopped_flag`].
|
||||
//! - Set and read the aging offset. See [`set_aging_offset`].
|
||||
//! - 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`].
|
||||
//! - 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 when battery powered. See [`enable_32khz_output_on_battery`].
|
||||
//! - 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`].
|
||||
//! - Alarms:
|
||||
//! - Set alarms 1 and 2 with several matching policies. See [`set_alarm1_day`].
|
||||
//! - Set alarms 1 and 2 for a time. See [`set_alarm1_hms`].
|
||||
//! - Read whether alarms 1 or 2 have matched. See [`has_alarm1_matched`].
|
||||
//! - Clear flag indicating that alarms 1 or 2 have matched. See [`clear_alarm1_matched_flag`].
|
||||
//! - Enable and disable alarms 1 and 2 interrupt generation. See [`enable_alarm1_interrupts`].
|
||||
//! - 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
|
||||
//! [`get_year`]: struct.Ds323x.html#method.get_year
|
||||
//! [`enable`]: struct.Ds323x.html#method.enable
|
||||
//! [`get_temperature`]: struct.Ds323x.html#method.get_temperature
|
||||
//! [`convert_temperature`]: struct.Ds323x.html#method.convert_temperature
|
||||
//! [`is_busy`]: struct.Ds323x.html#method.is_busy
|
||||
//! [`has_been_stopped`]: struct.Ds323x.html#method.has_been_stopped
|
||||
//! [`clear_has_been_stopped_flag`]: struct.Ds323x.html#method.clear_has_been_stopped_flag
|
||||
//! [`set_aging_offset`]: struct.Ds323x.html#method.set_aging_offset
|
||||
//! [`enable_32khz_output`]: struct.Ds323x.html#method.enable_32khz_output
|
||||
//! [`use_int_sqw_output_as_interrupt`]: struct.Ds323x.html#method.use_int_sqw_output_as_interrupt
|
||||
//! [`enable_square_wave`]: struct.Ds323x.html#method.enable_square_wave
|
||||
//! [`set_square_wave_frequency`]: struct.Ds323x.html#method.set_square_wave_frequency
|
||||
//! [`enable_32khz_output_on_battery`]: struct.Ds323x.html#method.enable_32khz_output_on_battery
|
||||
//! [`set_temperature_conversion_rate`]: struct.Ds323x.html#method.set_temperature_conversion_rate
|
||||
//! [`enable_temperature_conversions_on_battery`]: struct.Ds323x.html#method.enable_temperature_conversions_on_battery
|
||||
//! [`datetime`]: Ds323x::datetime
|
||||
//! [`year`]: Ds323x::year
|
||||
//! [`enable`]: Ds323x::enable
|
||||
//! [`temperature`]: Ds323x::temperature
|
||||
//! [`convert_temperature`]: Ds323x::convert_temperature
|
||||
//! [`busy`]: Ds323x::busy
|
||||
//! [`has_been_stopped`]: Ds323x::has_been_stopped
|
||||
//! [`clear_has_been_stopped_flag`]: Ds323x::clear_has_been_stopped_flag
|
||||
//! [`set_aging_offset`]: Ds323x::set_aging_offset
|
||||
//! [`enable_32khz_output`]: Ds323x::enable_32khz_output
|
||||
//! [`use_int_sqw_output_as_interrupt`]: Ds323x::use_int_sqw_output_as_interrupt
|
||||
//! [`enable_square_wave`]: Ds323x::enable_square_wave
|
||||
//! [`set_square_wave_frequency`]: Ds323x::set_square_wave_frequency
|
||||
//! [`set_alarm1_day`]: Ds323x::set_alarm1_day
|
||||
//! [`set_alarm1_hms`]: Ds323x::set_alarm1_hms
|
||||
//! [`has_alarm1_matched`]: Ds323x::has_alarm1_matched
|
||||
//! [`clear_alarm1_matched_flag`]: Ds323x::clear_alarm1_matched_flag
|
||||
//! [`enable_alarm1_interrupts`]: Ds323x::enable_alarm1_interrupts
|
||||
//! [`enable_32khz_output_on_battery`]: Ds323x::enable_32khz_output_on_battery
|
||||
//! [`set_temperature_conversion_rate`]: Ds323x::set_temperature_conversion_rate
|
||||
//! [`enable_temperature_conversions_on_battery`]: Ds323x::enable_temperature_conversions_on_battery
|
||||
//!
|
||||
//! ## The devices
|
||||
//!
|
||||
//! This driver is compatible with the DS3231 and DS3232 I2C devices and the
|
||||
//! DS3234 SPI device.
|
||||
//!
|
||||
//! ### DS3231
|
||||
//! The DS3231 is a low-cost, extremely accurate I2C real-time clock (RTC) with
|
||||
//! an integrated temperature-compensated crystal oscillator (TCXO) and crystal.
|
||||
//! These devices are low-cost temperature-compensated crystal oscillator (TCXO)
|
||||
//! with a very accurate, temperature-compensated, integrated real-time clock
|
||||
//! (RTC) including 236/256 bytes of battery-backed SRAM, depending on the model.
|
||||
//!
|
||||
//! The device incorporates a battery input, and maintains accurate timekeeping
|
||||
//! when main power to the device is interrupted. The integration of the
|
||||
//! crystal resonator enhances the long-term accuracy of the device as well as
|
||||
//! ### DS3231 and DS3232 details
|
||||
//!
|
||||
//! The devices incorporate a battery input, and maintain accurate timekeeping
|
||||
//! when main power to the devices is interrupted. The integration of the
|
||||
//! crystal resonator enhances the long-term accuracy of the devices as well as
|
||||
//! reduces the piece-part count in a manufacturing line.
|
||||
//! The DS3231 is available in commercial and industrial temperature ranges,
|
||||
//! and is offered in a 16-pin, 300-mil SO package.
|
||||
//! The devices are available in commercial and industrial temperature ranges,
|
||||
//! and are offered in a 16-pin, 300-mil SO package.
|
||||
//!
|
||||
//! The RTC maintains seconds, minutes, hours, day, date, month, and year
|
||||
//! information. The date at the end of the month is automatically adjusted for
|
||||
|
@ -68,36 +83,7 @@
|
|||
//! necessary. Additionally, the RST pin is monitored as a pushbutton
|
||||
//! input for generating a μP reset.
|
||||
//!
|
||||
//! ### DS3232
|
||||
//! The DS3232 is a low-cost temperature-compensated crystal oscillator (TCXO)
|
||||
//! with a very accurate, temperature-compensated, integrated real-time clock
|
||||
//! (RTC) and 236 bytes of battery-backed SRAM.
|
||||
//!
|
||||
//! Additionally, the DS3232 incorporates a battery input and maintains
|
||||
//! accurate timekeeping when main power to the device is interrupted. The
|
||||
//! integration of the crystal resonator enhances the long-term accuracy of the
|
||||
//! device as well as reduces the piece-part count in a manufacturing line.
|
||||
//! The DS3232 is available in commercial and industrial temperature ranges,
|
||||
//! and is offered in an industry-standard 20-pin, 300-mil SO package.
|
||||
//!
|
||||
//! The RTC maintains seconds, minutes, hours, day, date, month, and year
|
||||
//! information. The date at the end of the month is automatically adjusted for
|
||||
//! months with fewer than 31 days, including corrections for leap year. The
|
||||
//! clock operates in either the 24-hour or 12-hour format with an AM/PM
|
||||
//! indicator. Two programmable time-of-day alarms and a programmable
|
||||
//! square-wave output are provided. Address and data are transferred serially
|
||||
//! through an I2C bidirectional bus.
|
||||
//!
|
||||
//! A precision temperature-compensated voltage reference and comparator
|
||||
//! circuit monitors the status of VCC to detect power failures, to provide a
|
||||
//! reset output, and to automatically switch to the backup supply when
|
||||
//! necessary. Additionally, the RST pin is monitored as a pushbutton input for
|
||||
//! generating a μP reset.
|
||||
//!
|
||||
//! ### DS3234
|
||||
//! The DS3234 is a low-cost, extremely accurate SPI bus real-time clock (RTC)
|
||||
//! with an integrated temperature-compensated crystal oscillator (TCXO) and
|
||||
//! crystal.
|
||||
//! ### DS3234 details
|
||||
//!
|
||||
//! The DS3234 incorporates a precision, temperature-compensated voltage
|
||||
//! reference and comparator circuit to monitor VCC. When VCC drops below the
|
||||
|
@ -135,202 +121,156 @@
|
|||
//! DS3231 as an example, except when using features specific to another IC,
|
||||
//! for example, RAM access which is not available in the DS3231 device.
|
||||
//!
|
||||
//! Please find additional examples using hardware in this repository: [driver-examples]
|
||||
//!
|
||||
//! [driver-examples]: https://github.com/eldruin/driver-examples
|
||||
//!
|
||||
//! ### Create a driver instance for the DS3231
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate linux_embedded_hal as hal;
|
||||
//! extern crate ds323x;
|
||||
//! use ds323x::Ds323x;
|
||||
//! use linux_embedded_hal::I2cdev;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let rtc = Ds323x::new_ds3231(dev);
|
||||
//! // do something...
|
||||
//!
|
||||
//! // get the I2C device back
|
||||
//! let dev = rtc.destroy_ds3231();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Create a driver instance for the DS3232
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate linux_embedded_hal as hal;
|
||||
//! extern crate ds323x;
|
||||
//! use ds323x::Ds323x;
|
||||
//! use linux_embedded_hal::I2cdev;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let rtc = Ds323x::new_ds3232(dev);
|
||||
//! // do something...
|
||||
//!
|
||||
//! // get the I2C device back
|
||||
//! let dev = rtc.destroy_ds3232();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Create a driver instance for the DS3234
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate linux_embedded_hal as hal;
|
||||
//! extern crate ds323x;
|
||||
//! use ds323x::Ds323x;
|
||||
//! use embedded_hal_bus::spi::ExclusiveDevice;
|
||||
//! use linux_embedded_hal::{Delay, SpidevBus, SysfsPin};
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let dev = hal::Spidev::open("/dev/spidev0.0").unwrap();
|
||||
//! let chip_select = hal::Pin::new(24);
|
||||
//! let rtc = Ds323x::new_ds3234(dev, chip_select);
|
||||
//! let spi = SpidevBus::open("/dev/spidev0.0").unwrap();
|
||||
//! let chip_select = SysfsPin::new(25);
|
||||
//! let dev = ExclusiveDevice::new(spi, chip_select, Delay).unwrap();
|
||||
//! let rtc = Ds323x::new_ds3234(dev);
|
||||
//! // do something...
|
||||
//!
|
||||
//! // get the SPI device and chip select pin back
|
||||
//! let (dev, chip_select) = rtc.destroy_ds3234();
|
||||
//! # }
|
||||
//! // get the SPI device back
|
||||
//! let dev = rtc.destroy_ds3234();
|
||||
//! ```
|
||||
//!
|
||||
//! ### Set the current date and time at once
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate linux_embedded_hal as hal;
|
||||
//! extern crate ds323x;
|
||||
//! use ds323x::{ Ds323x, DateTime, Hours };
|
||||
//! use ds323x::{Ds323x, NaiveDate, DateTimeAccess};
|
||||
//! use linux_embedded_hal::I2cdev;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let mut rtc = Ds323x::new_ds3231(dev);
|
||||
//! let datetime = DateTime {
|
||||
//! year: 2018,
|
||||
//! month: 08,
|
||||
//! day: 15,
|
||||
//! weekday: 4,
|
||||
//! hour: Hours::H24(19),
|
||||
//! minute: 59,
|
||||
//! second: 58
|
||||
//! };
|
||||
//! let datetime = NaiveDate::from_ymd(2020, 5, 1).and_hms(19, 59, 58);
|
||||
//! rtc.set_datetime(&datetime).unwrap();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Get the current date and time at once
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate linux_embedded_hal as hal;
|
||||
//! extern crate ds323x;
|
||||
//! use ds323x::{ Ds323x, Hours };
|
||||
//! use ds323x::{Ds323x, DateTimeAccess};
|
||||
//! use linux_embedded_hal::I2cdev;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let mut rtc = Ds323x::new_ds3231(dev);
|
||||
//!
|
||||
//! let datetime = rtc.get_datetime().unwrap();
|
||||
//!
|
||||
//! // The hours depend on the RTC running mode
|
||||
//! match datetime.hour {
|
||||
//! Hours::H24(h) => println!("{}-{}-{}, {} {}:{}:{}", datetime.year,
|
||||
//! datetime.month, datetime.day, datetime.weekday,
|
||||
//! h, datetime.minute, datetime.second),
|
||||
//! Hours::AM(h) => println!("{}-{}-{}, {} {}:{}:{} AM", datetime.year,
|
||||
//! datetime.month, datetime.day, datetime.weekday,
|
||||
//! h, datetime.minute, datetime.second),
|
||||
//! Hours::PM(h) => println!("{}-{}-{}, {} {}:{}:{} PM", datetime.year,
|
||||
//! datetime.month, datetime.day, datetime.weekday,
|
||||
//! h, datetime.minute, datetime.second),
|
||||
//! }
|
||||
//! // This will print something like: 2018-08-15, 4 19:59:58
|
||||
//! # }
|
||||
//! let dt = rtc.datetime().unwrap();
|
||||
//! println!("{}", dt);
|
||||
//! // This will print something like: 2020-05-01 19:59:58
|
||||
//! ```
|
||||
//!
|
||||
//! ### 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.
|
||||
//!
|
||||
//! ```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
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate linux_embedded_hal as hal;
|
||||
//! extern crate ds323x;
|
||||
//! use ds323x::{ Ds323x, Hours };
|
||||
//! Similar methods exist for month, day, weekday, hours, minutes and seconds.
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! ```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);
|
||||
//! rtc.set_year(2018).unwrap();
|
||||
//! # }
|
||||
//! ```
|
||||
//! Similar methods exist for month, day, weekday, hours, minutes and seconds.
|
||||
//!
|
||||
//! ### Enable/disable the device
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate linux_embedded_hal as hal;
|
||||
//! extern crate ds323x;
|
||||
//! use ds323x::Ds323x;
|
||||
//! use linux_embedded_hal::I2cdev;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let mut rtc = Ds323x::new_ds3231(dev);
|
||||
//! rtc.disable().unwrap(); // stops the clock
|
||||
//! let is_running = rtc.is_running().unwrap();
|
||||
//! println!("Is running: {}", is_running); // will print false
|
||||
//! let running = rtc.running().unwrap();
|
||||
//! println!("Is running: {}", running); // will print false
|
||||
//! 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
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate linux_embedded_hal as hal;
|
||||
//! extern crate ds323x;
|
||||
//! use ds323x::Ds323x;
|
||||
//! use linux_embedded_hal::I2cdev;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let mut rtc = Ds323x::new_ds3231(dev);
|
||||
//! let temperature = rtc.get_temperature().unwrap();
|
||||
//! # }
|
||||
//! let temperature = rtc.temperature().unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! ### Read busy status
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate linux_embedded_hal as hal;
|
||||
//! extern crate ds323x;
|
||||
//! use ds323x::Ds323x;
|
||||
//! use linux_embedded_hal::I2cdev;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! 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
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate linux_embedded_hal as hal;
|
||||
//! extern crate ds323x;
|
||||
//! use ds323x::{ Ds323x, SqWFreq };
|
||||
//! use ds323x::{Ds323x, SqWFreq};
|
||||
//! use linux_embedded_hal::I2cdev;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let mut rtc = Ds323x::new_ds3231(dev);
|
||||
//! rtc.set_square_wave_frequency(SqWFreq::_4_096Hz).unwrap();
|
||||
//! // The same output pin can be used for interrupts or as square-wave output
|
||||
//! rtc.use_int_sqw_output_as_square_wave().unwrap();
|
||||
//! rtc.enable_square_wave().unwrap();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Enable the 32kHz output except when on battery power
|
||||
|
@ -339,30 +279,24 @@
|
|||
//! available for the devices DS3232 and DS3234.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate linux_embedded_hal as hal;
|
||||
//! extern crate ds323x;
|
||||
//! use ds323x::{ Ds323x, SqWFreq };
|
||||
//! use ds323x::{Ds323x, SqWFreq};
|
||||
//! use linux_embedded_hal::I2cdev;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! 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();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Set the aging offset
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate linux_embedded_hal as hal;
|
||||
//! extern crate ds323x;
|
||||
//! use ds323x::Ds323x;
|
||||
//! use linux_embedded_hal::I2cdev;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let mut rtc = Ds323x::new_ds3231(dev);
|
||||
//! rtc.set_aging_offset(-15).unwrap();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Set the temperature conversion rate to once every 128 seconds
|
||||
|
@ -370,24 +304,76 @@
|
|||
//! This is only available for the devices DS3232 and DS3234.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! extern crate linux_embedded_hal as hal;
|
||||
//! extern crate ds323x;
|
||||
//! use ds323x::{ Ds323x, TempConvRate };
|
||||
//! use ds323x::{Ds323x, TempConvRate};
|
||||
//! use linux_embedded_hal::I2cdev;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
|
||||
//! let mut rtc = Ds323x::new_ds3232(dev);
|
||||
//! rtc.set_temperature_conversion_rate(TempConvRate::_128s).unwrap();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Set the Alarm1 to each week on a week day at a specific time
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use ds323x::{Ds323x, Hours, WeekdayAlarm1, Alarm1Matching};
|
||||
//! use linux_embedded_hal::I2cdev;
|
||||
//!
|
||||
//! 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(missing_docs)]
|
||||
#![deny(warnings)]
|
||||
#![deny(unsafe_code, missing_docs)]
|
||||
#![no_std]
|
||||
|
||||
extern crate embedded_hal as hal;
|
||||
use core::marker::PhantomData;
|
||||
use embedded_hal::spi::{Mode, MODE_1, MODE_3};
|
||||
pub use rtcc::{
|
||||
DateTimeAccess, Datelike, Hours, NaiveDate, NaiveDateTime, NaiveTime, Rtcc, Timelike,
|
||||
};
|
||||
|
||||
/// SPI mode 1 (CPOL = 0, CPHA = 1)
|
||||
pub const SPI_MODE_1: Mode = MODE_1;
|
||||
/// SPI mode 3 (CPOL = 1, CPHA = 1)
|
||||
pub const SPI_MODE_3: Mode = MODE_3;
|
||||
|
||||
/// All possible errors in this crate
|
||||
#[derive(Debug)]
|
||||
|
@ -395,11 +381,16 @@ pub enum Error<E> {
|
|||
/// I²C/SPI bus error
|
||||
Comm(E),
|
||||
/// 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
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum SqWFreq {
|
||||
/// 1 Hz (default)
|
||||
_1Hz,
|
||||
|
@ -414,7 +405,7 @@ pub enum SqWFreq {
|
|||
/// Temperature conversion rate
|
||||
///
|
||||
/// This is only available on the DS3232 and DS3234 devices.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum TempConvRate {
|
||||
/// Once every 64 seconds (default)
|
||||
_64s,
|
||||
|
@ -429,44 +420,50 @@ pub enum TempConvRate {
|
|||
struct Register;
|
||||
|
||||
impl Register {
|
||||
const SECONDS : u8 = 0x00;
|
||||
const MINUTES : u8 = 0x01;
|
||||
const HOURS : u8 = 0x02;
|
||||
const DOW : u8 = 0x03;
|
||||
const DOM : u8 = 0x04;
|
||||
const MONTH : u8 = 0x05;
|
||||
const YEAR : u8 = 0x06;
|
||||
const CONTROL : u8 = 0x0E;
|
||||
const STATUS : u8 = 0x0F;
|
||||
const AGING_OFFSET : u8 = 0x10;
|
||||
const TEMP_MSB : u8 = 0x11;
|
||||
const TEMP_CONV : u8 = 0x13;
|
||||
const SECONDS: u8 = 0x00;
|
||||
const MINUTES: u8 = 0x01;
|
||||
const HOURS: u8 = 0x02;
|
||||
const DOW: u8 = 0x03;
|
||||
const DOM: u8 = 0x04;
|
||||
const MONTH: u8 = 0x05;
|
||||
const YEAR: u8 = 0x06;
|
||||
const ALARM1_SECONDS: u8 = 0x07;
|
||||
const ALARM2_MINUTES: u8 = 0x0B;
|
||||
const CONTROL: u8 = 0x0E;
|
||||
const STATUS: u8 = 0x0F;
|
||||
const AGING_OFFSET: u8 = 0x10;
|
||||
const TEMP_MSB: u8 = 0x11;
|
||||
const TEMP_CONV: u8 = 0x13;
|
||||
}
|
||||
|
||||
struct BitFlags;
|
||||
|
||||
impl BitFlags {
|
||||
const H24_H12 : u8 = 0b0100_0000;
|
||||
const AM_PM : u8 = 0b0010_0000;
|
||||
const CENTURY : u8 = 0b1000_0000;
|
||||
const EOSC : u8 = 0b1000_0000;
|
||||
const BBSQW : u8 = 0b0100_0000;
|
||||
const TEMP_CONV : u8 = 0b0010_0000;
|
||||
const RS2 : u8 = 0b0001_0000;
|
||||
const RS1 : u8 = 0b0000_1000;
|
||||
const INTCN : u8 = 0b0000_0100;
|
||||
const OSC_STOP : u8 = 0b1000_0000;
|
||||
const BB32KHZ : u8 = 0b0100_0000;
|
||||
const CRATE1 : u8 = 0b0010_0000;
|
||||
const CRATE0 : u8 = 0b0001_0000;
|
||||
const EN32KHZ : u8 = 0b0000_1000;
|
||||
const BUSY : u8 = 0b0000_0100;
|
||||
const ALARM2F : u8 = 0b0000_0010;
|
||||
const ALARM1F : u8 = 0b0000_0001;
|
||||
const TEMP_CONV_BAT : u8 = 0b0000_0001;
|
||||
const H24_H12: u8 = 0b0100_0000;
|
||||
const AM_PM: u8 = 0b0010_0000;
|
||||
const CENTURY: u8 = 0b1000_0000;
|
||||
const EOSC: u8 = 0b1000_0000;
|
||||
const BBSQW: u8 = 0b0100_0000;
|
||||
const TEMP_CONV: u8 = 0b0010_0000;
|
||||
const RS2: u8 = 0b0001_0000;
|
||||
const RS1: u8 = 0b0000_1000;
|
||||
const INTCN: u8 = 0b0000_0100;
|
||||
const ALARM2_INT_EN: u8 = 0b0000_0010;
|
||||
const ALARM1_INT_EN: u8 = 0b0000_0001;
|
||||
const OSC_STOP: u8 = 0b1000_0000;
|
||||
const BB32KHZ: u8 = 0b0100_0000;
|
||||
const CRATE1: u8 = 0b0010_0000;
|
||||
const CRATE0: u8 = 0b0001_0000;
|
||||
const EN32KHZ: u8 = 0b0000_1000;
|
||||
const BUSY: u8 = 0b0000_0100;
|
||||
const ALARM2F: u8 = 0b0000_0010;
|
||||
const ALARM1F: u8 = 0b0000_0001;
|
||||
const TEMP_CONV_BAT: u8 = 0b0000_0001;
|
||||
const ALARM_MATCH: u8 = 0b1000_0000;
|
||||
const WEEKDAY: u8 = 0b0100_0000;
|
||||
}
|
||||
|
||||
const DEVICE_ADDRESS : u8 = 0b110_1000;
|
||||
const DEVICE_ADDRESS: u8 = 0b110_1000;
|
||||
const CONTROL_POR_VALUE: u8 = 0b0001_1100;
|
||||
|
||||
/// IC markers
|
||||
|
@ -484,13 +481,27 @@ pub mod ic {
|
|||
pub struct Ds323x<DI, IC> {
|
||||
iface: DI,
|
||||
control: u8,
|
||||
status : u8,
|
||||
_ic: PhantomData<IC>
|
||||
status: u8,
|
||||
_ic: PhantomData<IC>,
|
||||
}
|
||||
|
||||
pub mod interface;
|
||||
mod ds323x;
|
||||
pub use ds323x::{ Hours, DateTime };
|
||||
pub mod interface;
|
||||
pub use crate::ds323x::{
|
||||
Alarm1Matching, Alarm2Matching, DayAlarm1, DayAlarm2, WeekdayAlarm1, WeekdayAlarm2,
|
||||
};
|
||||
mod ds3231;
|
||||
mod ds3232;
|
||||
mod ds3234;
|
||||
|
||||
mod private {
|
||||
use super::{ic, interface};
|
||||
pub trait Sealed {}
|
||||
|
||||
impl<SPI> Sealed for interface::SpiInterface<SPI> {}
|
||||
impl<I2C> Sealed for interface::I2cInterface<I2C> {}
|
||||
|
||||
impl Sealed for ic::DS3231 {}
|
||||
impl Sealed for ic::DS3232 {}
|
||||
impl Sealed for ic::DS3234 {}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,10 +1,11 @@
|
|||
extern crate embedded_hal;
|
||||
extern crate ds323x;
|
||||
use self::ds323x::{ Ds323x, interface, ic };
|
||||
use hal::i2c::{ Mock as I2cMock, Transaction as I2cTrans };
|
||||
use hal::spi::{ Mock as SpiMock, Transaction as SpiTrans };
|
||||
use ds323x::{ic, interface, Ds323x};
|
||||
use embedded_hal_mock::eh1::{
|
||||
i2c::{Mock as I2cMock, Transaction as I2cTrans},
|
||||
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)]
|
||||
pub const CONTROL_POR_VALUE: u8 = 0b0001_1100;
|
||||
#[allow(unused)]
|
||||
|
@ -16,59 +17,77 @@ pub struct Register;
|
|||
|
||||
#[allow(unused)]
|
||||
impl Register {
|
||||
pub const SECONDS : u8 = 0x00;
|
||||
pub const MINUTES : u8 = 0x01;
|
||||
pub const HOURS : u8 = 0x02;
|
||||
pub const DOW : u8 = 0x03;
|
||||
pub const DOM : u8 = 0x04;
|
||||
pub const MONTH : u8 = 0x05;
|
||||
pub const CONTROL : u8 = 0x0E;
|
||||
pub const STATUS : u8 = 0x0F;
|
||||
pub const AGING_OFFSET : u8 = 0x10;
|
||||
pub const TEMP_MSB : u8 = 0x11;
|
||||
pub const TEMP_CONV : u8 = 0x13;
|
||||
pub const SECONDS: u8 = 0x00;
|
||||
pub const MINUTES: u8 = 0x01;
|
||||
pub const HOURS: u8 = 0x02;
|
||||
pub const DOW: u8 = 0x03;
|
||||
pub const DOM: u8 = 0x04;
|
||||
pub const MONTH: u8 = 0x05;
|
||||
pub const ALARM1_SECONDS: u8 = 0x07;
|
||||
pub const ALARM2_MINUTES: u8 = 0x0B;
|
||||
pub const CONTROL: u8 = 0x0E;
|
||||
pub const STATUS: u8 = 0x0F;
|
||||
pub const AGING_OFFSET: u8 = 0x10;
|
||||
pub const TEMP_MSB: u8 = 0x11;
|
||||
pub const TEMP_CONV: u8 = 0x13;
|
||||
}
|
||||
|
||||
pub struct BitFlags;
|
||||
|
||||
#[allow(unused)]
|
||||
impl BitFlags {
|
||||
pub const EOSC : u8 = 0b1000_0000;
|
||||
pub const BBSQW : u8 = 0b0100_0000;
|
||||
pub const TEMP_CONV : u8 = 0b0010_0000;
|
||||
pub const RS2 : u8 = 0b0001_0000;
|
||||
pub const RS1 : u8 = 0b0000_1000;
|
||||
pub const INTCN : u8 = 0b0000_0100;
|
||||
pub const OSC_STOP : u8 = 0b1000_0000;
|
||||
pub const BB32KHZ : u8 = 0b0100_0000;
|
||||
pub const CRATE1 : u8 = 0b0010_0000;
|
||||
pub const CRATE0 : u8 = 0b0001_0000;
|
||||
pub const EN32KHZ : u8 = 0b0000_1000;
|
||||
pub const BUSY : u8 = 0b0000_0100;
|
||||
pub const ALARM2F : u8 = 0b0000_0010;
|
||||
pub const ALARM1F : u8 = 0b0000_0001;
|
||||
pub const TEMP_CONV_BAT : u8 = 0b0000_0001;
|
||||
pub const EOSC: u8 = 0b1000_0000;
|
||||
pub const BBSQW: u8 = 0b0100_0000;
|
||||
pub const TEMP_CONV: u8 = 0b0010_0000;
|
||||
pub const RS2: u8 = 0b0001_0000;
|
||||
pub const RS1: u8 = 0b0000_1000;
|
||||
pub const INTCN: u8 = 0b0000_0100;
|
||||
pub const ALARM2_INT_EN: u8 = 0b0000_0010;
|
||||
pub const ALARM1_INT_EN: u8 = 0b0000_0001;
|
||||
pub const OSC_STOP: u8 = 0b1000_0000;
|
||||
pub const BB32KHZ: u8 = 0b0100_0000;
|
||||
pub const CRATE1: u8 = 0b0010_0000;
|
||||
pub const CRATE0: u8 = 0b0001_0000;
|
||||
pub const EN32KHZ: u8 = 0b0000_1000;
|
||||
pub const BUSY: u8 = 0b0000_0100;
|
||||
pub const ALARM2F: u8 = 0b0000_0010;
|
||||
pub const ALARM1F: u8 = 0b0000_0001;
|
||||
pub const TEMP_CONV_BAT: u8 = 0b0000_0001;
|
||||
pub const ALARM_MATCH: u8 = 0b1000_0000;
|
||||
pub const WEEKDAY: u8 = 0b0100_0000;
|
||||
}
|
||||
|
||||
pub struct DummyOutputPin;
|
||||
|
||||
impl embedded_hal::digital::OutputPin for DummyOutputPin {
|
||||
fn set_low(&mut self) {}
|
||||
fn set_high(&mut self) {}
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn new_ds3231(transactions: &[I2cTrans]) -> Ds323x<interface::I2cInterface<I2cMock>, ic::DS3231> {
|
||||
Ds323x::new_ds3231(I2cMock::new(&transactions))
|
||||
impl embedded_hal::digital::ErrorType for DummyOutputPin {
|
||||
type Error = embedded_hal::digital::ErrorKind;
|
||||
}
|
||||
|
||||
pub fn new_ds3232(transactions: &[I2cTrans]) -> Ds323x<interface::I2cInterface<I2cMock>, ic::DS3232> {
|
||||
Ds323x::new_ds3232(I2cMock::new(&transactions))
|
||||
pub fn new_ds3231(
|
||||
transactions: &[I2cTrans],
|
||||
) -> Ds323x<interface::I2cInterface<I2cMock>, ic::DS3231> {
|
||||
Ds323x::new_ds3231(I2cMock::new(transactions))
|
||||
}
|
||||
|
||||
pub fn new_ds3234(transactions: &[SpiTrans])
|
||||
-> Ds323x<interface::SpiInterface<SpiMock, DummyOutputPin>, ic::DS3234> {
|
||||
Ds323x::new_ds3234(SpiMock::new(&transactions), DummyOutputPin)
|
||||
pub fn new_ds3232(
|
||||
transactions: &[I2cTrans],
|
||||
) -> Ds323x<interface::I2cInterface<I2cMock>, ic::DS3232> {
|
||||
Ds323x::new_ds3232(I2cMock::new(transactions))
|
||||
}
|
||||
|
||||
pub fn new_ds3234(
|
||||
transactions: &[SpiTrans<u8>],
|
||||
) -> Ds323x<interface::SpiInterface<SpiMock<u8>>, ic::DS3234> {
|
||||
Ds323x::new_ds3234(SpiMock::new(transactions))
|
||||
}
|
||||
|
||||
pub fn 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();
|
||||
}
|
||||
|
||||
pub fn destroy_ds3234(dev: Ds323x<interface::SpiInterface<SpiMock, DummyOutputPin>, ic::DS3234>) {
|
||||
dev.destroy_ds3234().0.done();
|
||||
pub fn destroy_ds3234(dev: Ds323x<interface::SpiInterface<SpiMock<u8>>, ic::DS3234>) {
|
||||
dev.destroy_ds3234().done();
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
@ -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_rules! set_invalid_test {
|
||||
($name:ident, $method:ident, $create_method:ident, $destroy_method:ident, $value:expr) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
let mut dev = $create_method(&[]);
|
||||
match dev.$method($value) {
|
||||
Err(Error::InvalidInputData) => (),
|
||||
_ => panic!("InvalidInputData error not returned.")
|
||||
}
|
||||
assert_invalid_input_data!(dev.$method($value));
|
||||
$destroy_method(dev);
|
||||
}
|
||||
};
|
||||
|
@ -142,9 +168,30 @@ macro_rules! _get_param_test {
|
|||
($name:ident, $method:ident, $value:expr, $i2c_transactions:expr, $spi_transactions:expr) => {
|
||||
mod $name {
|
||||
use super::*;
|
||||
get_test!(can_get_ds3231, $method, new_ds3231, destroy_ds3231, $value, $i2c_transactions);
|
||||
get_test!(can_get_ds3232, $method, new_ds3232, destroy_ds3232, $value, $i2c_transactions);
|
||||
get_test!(can_get_ds3234, $method, new_ds3234, destroy_ds3234, $value, $spi_transactions);
|
||||
get_test!(
|
||||
can_get_ds3231,
|
||||
$method,
|
||||
new_ds3231,
|
||||
destroy_ds3231,
|
||||
$value,
|
||||
$i2c_transactions
|
||||
);
|
||||
get_test!(
|
||||
can_get_ds3232,
|
||||
$method,
|
||||
new_ds3232,
|
||||
destroy_ds3232,
|
||||
$value,
|
||||
$i2c_transactions
|
||||
);
|
||||
get_test!(
|
||||
can_get_ds3234,
|
||||
$method,
|
||||
new_ds3234,
|
||||
destroy_ds3234,
|
||||
$value,
|
||||
$spi_transactions
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -152,18 +199,50 @@ macro_rules! _get_param_test {
|
|||
#[macro_export]
|
||||
macro_rules! get_param_test {
|
||||
($name:ident, $method:ident, $register:ident, $value:expr, $binary_value:expr) => {
|
||||
_get_param_test!($name, $method, $value,
|
||||
[ I2cTrans::write_read(DEV_ADDR, vec![Register::$register], vec![$binary_value]) ],
|
||||
[ SpiTrans::transfer(vec![Register::$register, 0], vec![Register::$register, $binary_value]) ]);
|
||||
_get_param_test!(
|
||||
$name,
|
||||
$method,
|
||||
$value,
|
||||
[I2cTrans::write_read(
|
||||
DEV_ADDR,
|
||||
vec![Register::$register],
|
||||
vec![$binary_value]
|
||||
)],
|
||||
[
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::transfer_in_place(
|
||||
vec![Register::$register, 0],
|
||||
vec![Register::$register, $binary_value]
|
||||
),
|
||||
SpiTrans::transaction_end(),
|
||||
]
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! transactions_i2c_read {
|
||||
($register1:ident, [ $( $read_bin:expr ),+ ], [ $( $read_bin2:expr ),* ]) => {
|
||||
[ I2cTrans::write_read(DEV_ADDR, vec![Register::$register1], vec![$( $read_bin ),*]) ]
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! transactions_spi_read {
|
||||
($register1:ident, [ $( $read_bin:expr ),+ ], [ $( $read_bin2:expr ),+ ]) => {
|
||||
[SpiTrans::transaction_start(),
|
||||
SpiTrans::transfer_in_place(vec![Register::$register1, $( $read_bin2 ),*], vec![Register::$register1, $( $read_bin ),*]),
|
||||
SpiTrans::transaction_end()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! get_param_read_array_test {
|
||||
($name:ident, $method:ident, $value:expr, $register1:ident, [ $( $read_bin:expr ),+ ], [ $( $read_bin2:expr ),+ ]) => {
|
||||
_get_param_test!($name, $method, $value,
|
||||
[ I2cTrans::write_read(DEV_ADDR, vec![Register::$register1], vec![$( $read_bin ),*]) ],
|
||||
[ SpiTrans::transfer(vec![Register::$register1, $( $read_bin2 ),*], vec![Register::$register1, $( $read_bin ),*]) ]);
|
||||
transactions_i2c_read!($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) => {
|
||||
mod $name {
|
||||
use super::*;
|
||||
set_test!(can_set_ds3231, $method, new_ds3231, destroy_ds3231, $value, $i2c_transactions);
|
||||
set_test!(can_set_ds3232, $method, new_ds3232, destroy_ds3232, $value, $i2c_transactions);
|
||||
set_test!(can_set_ds3234, $method, new_ds3234, destroy_ds3234, $value, $spi_transactions);
|
||||
set_test!(
|
||||
can_set_ds3231,
|
||||
$method,
|
||||
new_ds3231,
|
||||
destroy_ds3231,
|
||||
$value,
|
||||
$i2c_transactions
|
||||
);
|
||||
set_test!(
|
||||
can_set_ds3232,
|
||||
$method,
|
||||
new_ds3232,
|
||||
destroy_ds3232,
|
||||
$value,
|
||||
$i2c_transactions
|
||||
);
|
||||
set_test!(
|
||||
can_set_ds3234,
|
||||
$method,
|
||||
new_ds3234,
|
||||
destroy_ds3234,
|
||||
$value,
|
||||
$spi_transactions
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -182,8 +282,19 @@ macro_rules! _set_param_test {
|
|||
#[macro_export]
|
||||
macro_rules! set_param_test {
|
||||
($name:ident, $method:ident, $register:ident, $value:expr, $binary_value:expr) => {
|
||||
_set_param_test!($name, $method, $value,
|
||||
[ I2cTrans::write(DEV_ADDR, vec![Register::$register, $binary_value]) ],
|
||||
[ SpiTrans::write(vec![Register::$register + 0x80, $binary_value]) ]);
|
||||
_set_param_test!(
|
||||
$name,
|
||||
$method,
|
||||
$value,
|
||||
[I2cTrans::write(
|
||||
DEV_ADDR,
|
||||
vec![Register::$register, $binary_value]
|
||||
)],
|
||||
[
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::write_vec(vec![Register::$register + 0x80, $binary_value]),
|
||||
SpiTrans::transaction_end(),
|
||||
]
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 embedded_hal_mock::eh1::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
|
||||
|
||||
mod common;
|
||||
use common::{ DEVICE_ADDRESS as DEV_ADDR, Register, new_ds3231,
|
||||
new_ds3232, new_ds3234, destroy_ds3231, destroy_ds3232,
|
||||
destroy_ds3234, BitFlags as BF, CONTROL_POR_VALUE,
|
||||
DS3231_POR_STATUS, DS323X_POR_STATUS };
|
||||
use self::common::{
|
||||
destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234,
|
||||
BitFlags as BF, Register, CONTROL_POR_VALUE, DEVICE_ADDRESS as DEV_ADDR, DS3231_POR_STATUS,
|
||||
DS323X_POR_STATUS,
|
||||
};
|
||||
|
||||
macro_rules! call_triple_test {
|
||||
($name:ident, $method:ident, $i2c_transactions:expr, $spi_transactions:expr) => {
|
||||
mod $name {
|
||||
use super::*;
|
||||
call_test!(can_call_ds3231, $method, new_ds3231, destroy_ds3231, $i2c_transactions);
|
||||
call_test!(can_call_ds3232, $method, new_ds3232, destroy_ds3232, $i2c_transactions);
|
||||
call_test!(can_call_ds3234, $method, new_ds3234, destroy_ds3234, $spi_transactions);
|
||||
call_test!(
|
||||
can_call_ds3231,
|
||||
$method,
|
||||
new_ds3231,
|
||||
destroy_ds3231,
|
||||
$i2c_transactions
|
||||
);
|
||||
call_test!(
|
||||
can_call_ds3232,
|
||||
$method,
|
||||
new_ds3232,
|
||||
destroy_ds3232,
|
||||
$i2c_transactions
|
||||
);
|
||||
call_test!(
|
||||
can_call_ds3234,
|
||||
$method,
|
||||
new_ds3234,
|
||||
destroy_ds3234,
|
||||
$spi_transactions
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! call_method_test {
|
||||
($name:ident, $method:ident, $register:ident, $value_enabled:expr) => {
|
||||
call_triple_test!($name, $method,
|
||||
[ I2cTrans::write(DEV_ADDR, vec![Register::$register, $value_enabled]) ],
|
||||
[ SpiTrans::write(vec![Register::$register + 0x80, $value_enabled]) ]);
|
||||
call_triple_test!(
|
||||
$name,
|
||||
$method,
|
||||
[I2cTrans::write(
|
||||
DEV_ADDR,
|
||||
vec![Register::$register, $value_enabled]
|
||||
)],
|
||||
[
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::write_vec(vec![Register::$register + 0x80, $value_enabled]),
|
||||
SpiTrans::transaction_end(),
|
||||
]
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -36,12 +59,37 @@ macro_rules! call_method_status_test {
|
|||
($name:ident, $method:ident, $value_ds3231:expr, $value_ds323x:expr) => {
|
||||
mod $name {
|
||||
use super::*;
|
||||
call_test!(can_call_ds3231, $method, new_ds3231, destroy_ds3231,
|
||||
[ I2cTrans::write(DEV_ADDR, vec![Register::STATUS, $value_ds3231]) ]);
|
||||
call_test!(can_call_ds3232, $method, new_ds3232, destroy_ds3232,
|
||||
[ I2cTrans::write(DEV_ADDR, vec![Register::STATUS, $value_ds323x]) ]);
|
||||
call_test!(can_call_ds3234, $method, new_ds3234, destroy_ds3234,
|
||||
[ SpiTrans::write(vec![Register::STATUS + 0x80, $value_ds323x]) ]);
|
||||
call_test!(
|
||||
can_call_ds3231,
|
||||
$method,
|
||||
new_ds3231,
|
||||
destroy_ds3231,
|
||||
[I2cTrans::write(
|
||||
DEV_ADDR,
|
||||
vec![Register::STATUS, $value_ds3231]
|
||||
)]
|
||||
);
|
||||
call_test!(
|
||||
can_call_ds3232,
|
||||
$method,
|
||||
new_ds3232,
|
||||
destroy_ds3232,
|
||||
[I2cTrans::write(
|
||||
DEV_ADDR,
|
||||
vec![Register::STATUS, $value_ds323x]
|
||||
)]
|
||||
);
|
||||
call_test!(
|
||||
can_call_ds3234,
|
||||
$method,
|
||||
new_ds3234,
|
||||
destroy_ds3234,
|
||||
[
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::write_vec(vec![Register::STATUS + 0x80, $value_ds323x]),
|
||||
SpiTrans::transaction_end(),
|
||||
]
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -50,45 +98,196 @@ macro_rules! change_if_necessary_test {
|
|||
($name:ident, $method:ident, $register:ident, $value_enabled:expr, $value_disabled:expr) => {
|
||||
mod $name {
|
||||
use super::*;
|
||||
call_triple_test!(do_nothing_if_not_necessary, $method,
|
||||
[ I2cTrans::write_read(DEV_ADDR, vec![Register::$register], vec![$value_enabled]) ],
|
||||
[ SpiTrans::transfer(vec![Register::$register, 0], vec![Register::$register, $value_enabled]) ]);
|
||||
call_triple_test!(
|
||||
do_nothing_if_not_necessary,
|
||||
$method,
|
||||
[I2cTrans::write_read(
|
||||
DEV_ADDR,
|
||||
vec![Register::$register],
|
||||
vec![$value_enabled]
|
||||
)],
|
||||
[
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::transfer_in_place(
|
||||
vec![Register::$register, 0],
|
||||
vec![Register::$register, $value_enabled]
|
||||
),
|
||||
SpiTrans::transaction_end(),
|
||||
]
|
||||
);
|
||||
|
||||
call_triple_test!(change, $method,
|
||||
[ I2cTrans::write_read(DEV_ADDR, vec![Register::$register], vec![$value_disabled]),
|
||||
I2cTrans::write(DEV_ADDR, vec![Register::$register, $value_enabled]) ],
|
||||
|
||||
[ SpiTrans::transfer(vec![Register::$register, 0], vec![Register::$register, $value_disabled]),
|
||||
SpiTrans::write(vec![Register::$register + 0x80, $value_enabled]) ]);
|
||||
call_triple_test!(
|
||||
change,
|
||||
$method,
|
||||
[
|
||||
I2cTrans::write_read(
|
||||
DEV_ADDR,
|
||||
vec![Register::$register],
|
||||
vec![$value_disabled]
|
||||
),
|
||||
I2cTrans::write(DEV_ADDR, vec![Register::$register, $value_enabled])
|
||||
],
|
||||
[
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::transfer_in_place(
|
||||
vec![Register::$register, 0],
|
||||
vec![Register::$register, $value_disabled]
|
||||
),
|
||||
SpiTrans::transaction_end(),
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::write_vec(vec![Register::$register + 0x80, $value_enabled]),
|
||||
SpiTrans::transaction_end(),
|
||||
]
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
call_method_test!(enable, enable, CONTROL, CONTROL_POR_VALUE & !BF::EOSC);
|
||||
call_method_test!(disable, disable, CONTROL, CONTROL_POR_VALUE | BF::EOSC);
|
||||
call_method_status_test!(en_32khz_out, enable_32khz_output,
|
||||
DS3231_POR_STATUS | BF::EN32KHZ | BF::ALARM2F | BF::ALARM1F,
|
||||
DS323X_POR_STATUS | BF::EN32KHZ | BF::ALARM2F | BF::ALARM1F);
|
||||
call_method_status_test!(dis_32khz_out, disable_32khz_output,
|
||||
call_method_status_test!(
|
||||
en_32khz_out,
|
||||
enable_32khz_output,
|
||||
DS3231_POR_STATUS | BF::EN32KHZ | BF::ALARM2F | BF::ALARM1F,
|
||||
DS323X_POR_STATUS | BF::EN32KHZ | BF::ALARM2F | BF::ALARM1F
|
||||
);
|
||||
call_method_status_test!(
|
||||
dis_32khz_out,
|
||||
disable_32khz_output,
|
||||
DS3231_POR_STATUS & !BF::EN32KHZ | BF::ALARM2F | BF::ALARM1F,
|
||||
DS323X_POR_STATUS & !BF::EN32KHZ | BF::ALARM2F | BF::ALARM1F);
|
||||
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);
|
||||
DS323X_POR_STATUS & !BF::EN32KHZ | BF::ALARM2F | BF::ALARM1F
|
||||
);
|
||||
|
||||
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);
|
||||
call_method_status_test!(
|
||||
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);
|
||||
get_param_test!(get_aging_offset_max, get_aging_offset, AGING_OFFSET, 127, 127);
|
||||
call_method_status_test!(
|
||||
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_test!(int_sqw_out_sqw, use_int_sqw_output_as_square_wave, CONTROL, CONTROL_POR_VALUE & !BF::INTCN);
|
||||
call_method_status_test!(
|
||||
clr_stop,
|
||||
clear_has_been_stopped_flag,
|
||||
DS3231_POR_STATUS & !BF::OSC_STOP | BF::ALARM2F | BF::ALARM1F,
|
||||
DS323X_POR_STATUS & !BF::OSC_STOP | BF::ALARM2F | BF::ALARM1F
|
||||
);
|
||||
|
||||
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);
|
||||
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_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);
|
||||
call_method_test!(
|
||||
en_al1_int,
|
||||
enable_alarm1_interrupts,
|
||||
CONTROL,
|
||||
CONTROL_POR_VALUE | BF::ALARM1_INT_EN
|
||||
);
|
||||
call_method_test!(
|
||||
dis_al1_int,
|
||||
disable_alarm1_interrupts,
|
||||
CONTROL,
|
||||
CONTROL_POR_VALUE & !BF::ALARM1_INT_EN
|
||||
);
|
||||
|
||||
call_method_test!(
|
||||
en_al2_int,
|
||||
enable_alarm2_interrupts,
|
||||
CONTROL,
|
||||
CONTROL_POR_VALUE | BF::ALARM2_INT_EN
|
||||
);
|
||||
call_method_test!(
|
||||
dis_al2_int,
|
||||
disable_alarm2_interrupts,
|
||||
CONTROL,
|
||||
CONTROL_POR_VALUE & !BF::ALARM2_INT_EN
|
||||
);
|
||||
|
||||
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
|
||||
);
|
||||
|
|
|
@ -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);
|
|
@ -1,42 +1,80 @@
|
|||
#[deny(warnings)]
|
||||
|
||||
extern crate embedded_hal_mock as hal;
|
||||
use hal::i2c::Transaction as I2cTrans;
|
||||
use hal::spi::Transaction as SpiTrans;
|
||||
use embedded_hal_mock::eh1::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
|
||||
use rtcc::NaiveDateTime;
|
||||
mod common;
|
||||
use common::{ DEVICE_ADDRESS as DEV_ADDR, Register, new_ds3231,
|
||||
new_ds3232, new_ds3234, destroy_ds3231, destroy_ds3232,
|
||||
destroy_ds3234 };
|
||||
extern crate ds323x;
|
||||
use ds323x::{ Hours, DateTime, Error };
|
||||
use self::common::{
|
||||
destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234, Register,
|
||||
DEVICE_ADDRESS as DEV_ADDR,
|
||||
};
|
||||
#[allow(unused)] // Rust 1.31.0 is confused due to the macros
|
||||
use ds323x::Rtcc;
|
||||
use ds323x::{DateTimeAccess, Error, Hours, NaiveDate, NaiveTime};
|
||||
|
||||
macro_rules! set_param_write_array_test {
|
||||
($name:ident, $method:ident, $value:expr, $register:ident, [ $( $exp_bin:expr ),+ ] ) => {
|
||||
_set_param_test!($name, $method, $value,
|
||||
[ I2cTrans::write(DEV_ADDR, vec![Register::$register, $( $exp_bin ),*]) ],
|
||||
[ SpiTrans::write(vec![Register::$register + 0x80, $( $exp_bin ),*]) ]);
|
||||
};
|
||||
fn new_datetime(y: i32, mo: u32, d: u32, h: u32, min: u32, s: u32) -> NaiveDateTime {
|
||||
NaiveDate::from_ymd_opt(y, mo, d)
|
||||
.unwrap()
|
||||
.and_hms_opt(h, min, s)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn new_date(y: i32, mo: u32, d: u32) -> NaiveDate {
|
||||
NaiveDate::from_ymd_opt(y, mo, d).unwrap()
|
||||
}
|
||||
|
||||
macro_rules! read_set_param_write_two_test {
|
||||
($name:ident, $method:ident, $value:expr, $register:ident, $binary_value1_read:expr, $bin1:expr, $bin2:expr) => {
|
||||
_set_param_test!($name, $method, $value,
|
||||
[ I2cTrans::write_read(DEV_ADDR, vec![Register::$register], vec![$binary_value1_read]),
|
||||
I2cTrans::write(DEV_ADDR, vec![Register::$register, $bin1, $bin2]) ],
|
||||
|
||||
[ SpiTrans::transfer(vec![Register::$register, 0], vec![Register::$register, $binary_value1_read]),
|
||||
SpiTrans::write(vec![Register::$register + 0x80, $bin1, $bin2]) ]);
|
||||
_set_param_test!(
|
||||
$name,
|
||||
$method,
|
||||
$value,
|
||||
[
|
||||
I2cTrans::write_read(
|
||||
DEV_ADDR,
|
||||
vec![Register::$register],
|
||||
vec![$binary_value1_read]
|
||||
),
|
||||
I2cTrans::write(DEV_ADDR, vec![Register::$register, $bin1, $bin2])
|
||||
],
|
||||
[
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::transfer_in_place(
|
||||
vec![Register::$register, 0],
|
||||
vec![Register::$register, $binary_value1_read]
|
||||
),
|
||||
SpiTrans::transaction_end(),
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::write_vec(vec![Register::$register + 0x80, $bin1, $bin2]),
|
||||
SpiTrans::transaction_end(),
|
||||
]
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! read_set_param_test {
|
||||
($name:ident, $method:ident, $register:ident, $value:expr, $binary_value_read:expr, $binary_value_write:expr) => {
|
||||
_set_param_test!($name, $method, $value,
|
||||
[ I2cTrans::write_read(DEV_ADDR, vec![Register::$register], vec![$binary_value_read]),
|
||||
I2cTrans::write(DEV_ADDR, vec![Register::$register, $binary_value_write]) ],
|
||||
|
||||
[ SpiTrans::transfer(vec![Register::$register, 0], vec![Register::$register, $binary_value_read]),
|
||||
SpiTrans::write(vec![Register::$register + 0x80, $binary_value_write]) ]);
|
||||
_set_param_test!(
|
||||
$name,
|
||||
$method,
|
||||
$value,
|
||||
[
|
||||
I2cTrans::write_read(
|
||||
DEV_ADDR,
|
||||
vec![Register::$register],
|
||||
vec![$binary_value_read]
|
||||
),
|
||||
I2cTrans::write(DEV_ADDR, vec![Register::$register, $binary_value_write])
|
||||
],
|
||||
[
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::transfer_in_place(
|
||||
vec![Register::$register, 0],
|
||||
vec![Register::$register, $binary_value_read]
|
||||
),
|
||||
SpiTrans::transaction_end(),
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::write_vec(vec![Register::$register + 0x80, $binary_value_write]),
|
||||
SpiTrans::transaction_end(),
|
||||
]
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -44,9 +82,27 @@ macro_rules! set_invalid_param_test {
|
|||
($name:ident, $method:ident, $value:expr) => {
|
||||
mod $name {
|
||||
use super::*;
|
||||
set_invalid_test!(cannot_set_invalid_ds3231, $method, new_ds3231, destroy_ds3231, $value);
|
||||
set_invalid_test!(cannot_set_invalid_ds3232, $method, new_ds3232, destroy_ds3232, $value);
|
||||
set_invalid_test!(cannot_set_invalid_ds3234, $method, new_ds3234, destroy_ds3234, $value);
|
||||
set_invalid_test!(
|
||||
cannot_set_invalid_ds3231,
|
||||
$method,
|
||||
new_ds3231,
|
||||
destroy_ds3231,
|
||||
$value
|
||||
);
|
||||
set_invalid_test!(
|
||||
cannot_set_invalid_ds3232,
|
||||
$method,
|
||||
new_ds3232,
|
||||
destroy_ds3232,
|
||||
$value
|
||||
);
|
||||
set_invalid_test!(
|
||||
cannot_set_invalid_ds3234,
|
||||
$method,
|
||||
new_ds3234,
|
||||
destroy_ds3234,
|
||||
$value
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -56,119 +112,294 @@ macro_rules! set_invalid_param_range_test {
|
|||
mod $name {
|
||||
use super::*;
|
||||
set_invalid_param_test!(too_small, $method, $too_small_value);
|
||||
set_invalid_param_test!(too_big, $method, $too_big_value);
|
||||
set_invalid_param_test!(too_big, $method, $too_big_value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! for_all {
|
||||
($name:ident) => {
|
||||
mod $name {
|
||||
use super::*;
|
||||
$name!(for_ds3231, new_ds3231, destroy_ds3231);
|
||||
$name!(for_ds3232, new_ds3232, destroy_ds3232);
|
||||
$name!(for_ds3234, new_ds3234, destroy_ds3234);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO set/get date
|
||||
// TODO set/get time
|
||||
|
||||
mod seconds {
|
||||
use super::*;
|
||||
get_param_test!(get, get_seconds, SECONDS, 1, 1);
|
||||
get_param_test!(get, seconds, SECONDS, 1, 1);
|
||||
set_param_test!(set, set_seconds, SECONDS, 1, 1);
|
||||
set_invalid_param_test!(invalid, set_seconds, 60);
|
||||
}
|
||||
|
||||
mod minutes {
|
||||
use super::*;
|
||||
get_param_test!(get, get_minutes, MINUTES, 1, 1);
|
||||
get_param_test!(get, minutes, MINUTES, 1, 1);
|
||||
set_param_test!(set, set_minutes, MINUTES, 1, 1);
|
||||
set_invalid_param_test!(invalid, set_minutes, 60);
|
||||
}
|
||||
|
||||
mod hours_24h {
|
||||
use super::*;
|
||||
get_param_test!(get, get_hours, HOURS, Hours::H24(21), 0b0010_0001);
|
||||
get_param_test!(get, hours, HOURS, Hours::H24(21), 0b0010_0001);
|
||||
set_param_test!(set, set_hours, HOURS, Hours::H24(21), 0b0010_0001);
|
||||
set_invalid_param_test!(invalid, set_hours, Hours::H24(24));
|
||||
}
|
||||
|
||||
mod hours_12h_am {
|
||||
use super::*;
|
||||
get_param_test!(get, get_hours, HOURS, Hours::AM(12), 0b0101_0010);
|
||||
get_param_test!(get, hours, HOURS, Hours::AM(12), 0b0101_0010);
|
||||
set_param_test!(set, set_hours, HOURS, Hours::AM(12), 0b0101_0010);
|
||||
set_invalid_param_range_test!(invalid, set_hours, Hours::AM(0), Hours::AM(13));
|
||||
}
|
||||
|
||||
mod hours_12h_pm {
|
||||
use super::*;
|
||||
get_param_test!(get, get_hours, HOURS, Hours::PM(12), 0b0111_0010);
|
||||
get_param_test!(get, hours, HOURS, Hours::PM(12), 0b0111_0010);
|
||||
set_param_test!(set, set_hours, HOURS, Hours::PM(12), 0b0111_0010);
|
||||
set_invalid_param_range_test!(invalid, set_hours, Hours::PM(0), Hours::PM(13));
|
||||
}
|
||||
|
||||
mod weekday {
|
||||
use super::*;
|
||||
get_param_test!(get, get_weekday, DOW, 1, 1);
|
||||
get_param_test!(get, weekday, DOW, 1, 1);
|
||||
set_param_test!(set, set_weekday, DOW, 1, 1);
|
||||
set_invalid_param_range_test!(invalid, set_weekday, 0, 8);
|
||||
}
|
||||
|
||||
mod day {
|
||||
use super::*;
|
||||
get_param_test!(get, get_day, DOM, 1, 1);
|
||||
get_param_test!(get, day, DOM, 1, 1);
|
||||
set_param_test!(set, set_day, DOM, 1, 1);
|
||||
set_invalid_param_range_test!(invalid, set_day, 0, 8);
|
||||
set_invalid_param_range_test!(invalid, set_day, 0, 32);
|
||||
}
|
||||
|
||||
mod month {
|
||||
use super::*;
|
||||
get_param_test!(get, get_month, MONTH, 1, 1);
|
||||
get_param_test!(get, month, MONTH, 1, 1);
|
||||
read_set_param_test!(set, set_month, MONTH, 12, 0b0000_0010, 0b0001_0010);
|
||||
set_invalid_param_range_test!(invalid, set_month, 0, 13);
|
||||
|
||||
mod keeps_century {
|
||||
use super::*;
|
||||
get_param_test!(get, get_month, MONTH, 12, 0b1001_0010);
|
||||
get_param_test!(get, month, MONTH, 12, 0b1001_0010);
|
||||
read_set_param_test!(set, set_month, MONTH, 12, 0b1000_0010, 0b1001_0010);
|
||||
}
|
||||
}
|
||||
|
||||
mod year {
|
||||
use super::*;
|
||||
get_param_read_array_test!(century0_get, get_year, 2099, MONTH, [ 0, 0b1001_1001 ], [0, 0]);
|
||||
read_set_param_write_two_test!(century0_set, set_year, 2099, MONTH, 0b1001_0010, 0b0001_0010, 0b1001_1001);
|
||||
get_param_read_array_test!(century0_get, year, 2099, MONTH, [0, 0b1001_1001], [0, 0]);
|
||||
read_set_param_write_two_test!(
|
||||
century0_set,
|
||||
set_year,
|
||||
2099,
|
||||
MONTH,
|
||||
0b1001_0010,
|
||||
0b0001_0010,
|
||||
0b1001_1001
|
||||
);
|
||||
|
||||
get_param_read_array_test!(century1_get, get_year, 2100, MONTH, [ 0b1000_0000, 0 ], [0, 0]);
|
||||
read_set_param_write_two_test!(century1_set, set_year, 2100, MONTH, 0b0001_0010, 0b1001_0010, 0);
|
||||
get_param_read_array_test!(century1_get, year, 2100, MONTH, [0b1000_0000, 0], [0, 0]);
|
||||
read_set_param_write_two_test!(
|
||||
century1_set,
|
||||
set_year,
|
||||
2100,
|
||||
MONTH,
|
||||
0b0001_0010,
|
||||
0b1001_0010,
|
||||
0
|
||||
);
|
||||
|
||||
set_invalid_param_range_test!(invalid, set_year, 1999, 2101);
|
||||
}
|
||||
|
||||
macro_rules! invalid_dt_test {
|
||||
($name:ident, $year:expr, $month:expr, $day:expr, $weekday:expr,
|
||||
$hour:expr, $minute:expr, $second:expr) => {
|
||||
($name:ident, $create_method:ident, $destroy_method:ident) => {
|
||||
mod $name {
|
||||
use super::*;
|
||||
const DT : DateTime = DateTime { year: $year, month: $month, day: $day, weekday: $weekday,
|
||||
hour: $hour, minute: $minute, second: $second };
|
||||
set_invalid_param_test!($name, set_datetime, &DT);
|
||||
#[test]
|
||||
fn datetime_too_small() {
|
||||
let dt = new_datetime(1999, 1, 2, 3, 4, 5);
|
||||
let mut dev = $create_method(&[]);
|
||||
assert_invalid_input_data!(dev.set_datetime(&dt));
|
||||
$destroy_method(dev);
|
||||
}
|
||||
#[test]
|
||||
fn datetime_too_big() {
|
||||
let dt = new_datetime(2101, 1, 2, 3, 4, 5);
|
||||
let mut dev = $create_method(&[]);
|
||||
assert_invalid_input_data!(dev.set_datetime(&dt));
|
||||
$destroy_method(dev);
|
||||
}
|
||||
#[test]
|
||||
fn date_too_small() {
|
||||
let d = new_date(1999, 1, 2);
|
||||
let mut dev = $create_method(&[]);
|
||||
assert_invalid_input_data!(dev.set_date(&d));
|
||||
$destroy_method(dev);
|
||||
}
|
||||
#[test]
|
||||
fn date_too_big() {
|
||||
let d = new_date(2101, 1, 2);
|
||||
let mut dev = $create_method(&[]);
|
||||
assert_invalid_input_data!(dev.set_date(&d));
|
||||
$destroy_method(dev);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! transactions_i2c_write {
|
||||
($register:ident, [ $( $exp_bin:expr ),+ ]) => {
|
||||
[ I2cTrans::write(DEV_ADDR, vec![Register::$register, $( $exp_bin ),*]) ]
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! transactions_spi_write {
|
||||
($register:ident, [ $( $exp_bin:expr ),+ ]) => {
|
||||
[
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::write_vec(vec![Register::$register + 0x80, $( $exp_bin ),*]),
|
||||
SpiTrans::transaction_end()
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! dt_test {
|
||||
($name:ident, $create_method:ident, $destroy_method:ident,
|
||||
$mac_trans_read:ident, $mac_trans_write:ident) => {
|
||||
mod $name {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn get_datetime() {
|
||||
let dt = new_datetime(2018, 8, 13, 23, 59, 58);
|
||||
let mut dev = $create_method(&$mac_trans_read!(
|
||||
SECONDS,
|
||||
[
|
||||
0b0101_1000,
|
||||
0b0101_1001,
|
||||
0b0010_0011,
|
||||
0b0000_0010,
|
||||
0b0001_0011,
|
||||
0b0000_1000,
|
||||
0b0001_1000
|
||||
],
|
||||
[0, 0, 0, 0, 0, 0, 0]
|
||||
));
|
||||
assert_eq!(dt, dev.datetime().unwrap());
|
||||
$destroy_method(dev);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_datetime() {
|
||||
let dt = new_datetime(2018, 8, 13, 23, 59, 58);
|
||||
let mut dev = $create_method(&$mac_trans_write!(
|
||||
SECONDS,
|
||||
[
|
||||
0b0101_1000,
|
||||
0b0101_1001,
|
||||
0b0010_0011,
|
||||
0b0000_0010,
|
||||
0b0001_0011,
|
||||
0b0000_1000,
|
||||
0b0001_1000
|
||||
]
|
||||
));
|
||||
dev.set_datetime(&dt).unwrap();
|
||||
$destroy_method(dev);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_date() {
|
||||
let d = new_date(2018, 8, 13);
|
||||
let mut dev = $create_method(&$mac_trans_read!(
|
||||
DOM,
|
||||
[0b0001_0011, 0b0000_1000, 0b0001_1000],
|
||||
[0, 0, 0]
|
||||
));
|
||||
assert_eq!(d, dev.date().unwrap());
|
||||
$destroy_method(dev);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_date() {
|
||||
let d = new_date(2018, 8, 13);
|
||||
let mut dev = $create_method(&$mac_trans_write!(
|
||||
DOW,
|
||||
[0b0000_0010, 0b0001_0011, 0b0000_1000, 0b0001_1000]
|
||||
));
|
||||
dev.set_date(&d).unwrap();
|
||||
$destroy_method(dev);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_date_century() {
|
||||
let d = new_date(2100, 8, 13);
|
||||
let mut dev = $create_method(&$mac_trans_write!(
|
||||
DOW,
|
||||
[0b0000_0110, 0b0001_0011, 0b1000_1000, 0]
|
||||
));
|
||||
dev.set_date(&d).unwrap();
|
||||
$destroy_method(dev);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_time() {
|
||||
let t = NaiveTime::from_hms_opt(23, 59, 58).unwrap();
|
||||
let mut dev = $create_method(&$mac_trans_read!(
|
||||
SECONDS,
|
||||
[0b0101_1000, 0b0101_1001, 0b0010_0011],
|
||||
[0, 0, 0]
|
||||
));
|
||||
assert_eq!(t, dev.time().unwrap());
|
||||
$destroy_method(dev);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_time() {
|
||||
let t = NaiveTime::from_hms_opt(23, 59, 58).unwrap();
|
||||
let mut dev = $create_method(&$mac_trans_write!(
|
||||
SECONDS,
|
||||
[0b0101_1000, 0b0101_1001, 0b0010_0011]
|
||||
));
|
||||
dev.set_time(&t).unwrap();
|
||||
$destroy_method(dev);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mod datetime {
|
||||
use super::*;
|
||||
const DT : DateTime = DateTime { year: 2018, month: 8, day: 13, weekday: 2,
|
||||
hour: Hours::H24(23), minute: 59, second: 58 };
|
||||
get_param_read_array_test!(get, get_datetime, DT, SECONDS,
|
||||
[0b0101_1000, 0b0101_1001, 0b0010_0011, 0b0000_0010,
|
||||
0b0001_0011, 0b0000_1000, 0b0001_1000],
|
||||
[0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
set_param_write_array_test!(set, set_datetime, &DT, SECONDS,
|
||||
[0b0101_1000, 0b0101_1001, 0b0010_0011, 0b0000_0010,
|
||||
0b0001_0011, 0b0000_1000, 0b0001_1000]);
|
||||
dt_test!(
|
||||
for_ds3231,
|
||||
new_ds3231,
|
||||
destroy_ds3231,
|
||||
transactions_i2c_read,
|
||||
transactions_i2c_write
|
||||
);
|
||||
dt_test!(
|
||||
for_ds3232,
|
||||
new_ds3232,
|
||||
destroy_ds3232,
|
||||
transactions_i2c_read,
|
||||
transactions_i2c_write
|
||||
);
|
||||
dt_test!(
|
||||
for_ds3234,
|
||||
new_ds3234,
|
||||
destroy_ds3234,
|
||||
transactions_spi_read,
|
||||
transactions_spi_write
|
||||
);
|
||||
|
||||
invalid_dt_test!(too_small_year, 1999, 8, 13, 2, Hours::H24(23), 59, 58);
|
||||
invalid_dt_test!(too_big_year, 2101, 8, 13, 2, Hours::H24(23), 59, 58);
|
||||
invalid_dt_test!(too_small_month, 2018, 0, 13, 2, Hours::H24(23), 59, 58);
|
||||
invalid_dt_test!(too_big_month, 2018, 13, 13, 2, Hours::H24(23), 59, 58);
|
||||
invalid_dt_test!(too_small_day, 2018, 8, 0, 2, Hours::H24(23), 59, 58);
|
||||
invalid_dt_test!(too_big_day, 2018, 8, 32, 2, Hours::H24(23), 59, 58);
|
||||
invalid_dt_test!(too_small_wd, 2018, 8, 13, 0, Hours::H24(23), 59, 58);
|
||||
invalid_dt_test!(too_big_wd, 2018, 8, 13, 8, Hours::H24(23), 59, 58);
|
||||
invalid_dt_test!(too_big_hours, 2018, 8, 13, 2, Hours::H24(24), 59, 58);
|
||||
invalid_dt_test!(too_big_min, 2018, 8, 13, 2, Hours::H24(24), 60, 58);
|
||||
invalid_dt_test!(too_big_seconds, 2018, 8, 13, 2, Hours::H24(24), 59, 60);
|
||||
for_all!(invalid_dt_test);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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 embedded_hal_mock::eh1::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
|
||||
|
||||
#[allow(unused)]
|
||||
mod common;
|
||||
use common::{ DEVICE_ADDRESS as DEV_ADDR, Register,
|
||||
new_ds3232, new_ds3234, destroy_ds3232,
|
||||
destroy_ds3234, BitFlags as BF, DS323X_POR_STATUS };
|
||||
use self::common::{
|
||||
destroy_ds3232, destroy_ds3234, new_ds3232, new_ds3234, BitFlags as BF, Register,
|
||||
DEVICE_ADDRESS as DEV_ADDR, DS323X_POR_STATUS,
|
||||
};
|
||||
|
||||
macro_rules! call_method_status_test {
|
||||
($name:ident, $method:ident, $value:expr) => {
|
||||
mod $name {
|
||||
use super::*;
|
||||
call_test!(can_call_ds3232, $method, new_ds3232, destroy_ds3232,
|
||||
[ I2cTrans::write(DEV_ADDR, vec![Register::STATUS, $value]) ]);
|
||||
call_test!(can_call_ds3234, $method, new_ds3234, destroy_ds3234,
|
||||
[ SpiTrans::write(vec![Register::STATUS + 0x80, $value]) ]);
|
||||
call_test!(
|
||||
can_call_ds3232,
|
||||
$method,
|
||||
new_ds3232,
|
||||
destroy_ds3232,
|
||||
[I2cTrans::write(DEV_ADDR, vec![Register::STATUS, $value])]
|
||||
);
|
||||
call_test!(
|
||||
can_call_ds3234,
|
||||
$method,
|
||||
new_ds3234,
|
||||
destroy_ds3234,
|
||||
[
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::write_vec(vec![Register::STATUS + 0x80, $value]),
|
||||
SpiTrans::transaction_end(),
|
||||
]
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -30,8 +39,22 @@ macro_rules! _set_param_test_2_4 {
|
|||
($name:ident, $method:ident, $value:expr, $i2c_transactions:expr, $spi_transactions:expr) => {
|
||||
mod $name {
|
||||
use super::*;
|
||||
set_test!(can_set_ds3232, $method, new_ds3232, destroy_ds3232, $value, $i2c_transactions);
|
||||
set_test!(can_set_ds3234, $method, new_ds3234, destroy_ds3234, $value, $spi_transactions);
|
||||
set_test!(
|
||||
can_set_ds3232,
|
||||
$method,
|
||||
new_ds3232,
|
||||
destroy_ds3232,
|
||||
$value,
|
||||
$i2c_transactions
|
||||
);
|
||||
set_test!(
|
||||
can_set_ds3234,
|
||||
$method,
|
||||
new_ds3234,
|
||||
destroy_ds3234,
|
||||
$value,
|
||||
$spi_transactions
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -39,25 +62,61 @@ macro_rules! _set_param_test_2_4 {
|
|||
#[macro_export]
|
||||
macro_rules! set_param_test_2_4 {
|
||||
($name:ident, $method:ident, $register:ident, $value:expr, $binary_value:expr) => {
|
||||
_set_param_test_2_4!($name, $method, $value,
|
||||
[ I2cTrans::write(DEV_ADDR, vec![Register::$register, $binary_value]) ],
|
||||
[ SpiTrans::write(vec![Register::$register + 0x80, $binary_value]) ]);
|
||||
_set_param_test_2_4!(
|
||||
$name,
|
||||
$method,
|
||||
$value,
|
||||
[I2cTrans::write(
|
||||
DEV_ADDR,
|
||||
vec![Register::$register, $binary_value]
|
||||
)],
|
||||
[
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::write_vec(vec![Register::$register + 0x80, $binary_value]),
|
||||
SpiTrans::transaction_end(),
|
||||
]
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const DEFAULT_WRITE_STATUS: u8 = DS323X_POR_STATUS | BF::ALARM2F | BF::ALARM1F;
|
||||
|
||||
call_method_status_test!(can_en_32khz_bat, enable_32khz_output_on_battery,
|
||||
DEFAULT_WRITE_STATUS | BF::BB32KHZ );
|
||||
call_method_status_test!(can_dis_32khz_bat, disable_32khz_output_on_battery,
|
||||
DEFAULT_WRITE_STATUS & !BF::BB32KHZ);
|
||||
|
||||
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);
|
||||
call_method_status_test!(
|
||||
can_en_32khz_bat,
|
||||
enable_32khz_output_on_battery,
|
||||
DEFAULT_WRITE_STATUS | BF::BB32KHZ
|
||||
);
|
||||
call_method_status_test!(
|
||||
can_dis_32khz_bat,
|
||||
disable_32khz_output_on_battery,
|
||||
DEFAULT_WRITE_STATUS & !BF::BB32KHZ
|
||||
);
|
||||
|
||||
set_param_test_2_4!(
|
||||
can_set_cr_64s,
|
||||
set_temperature_conversion_rate,
|
||||
STATUS,
|
||||
TempConvRate::_64s,
|
||||
DEFAULT_WRITE_STATUS & !BF::CRATE1 & !BF::CRATE0
|
||||
);
|
||||
set_param_test_2_4!(
|
||||
can_set_cr_128s,
|
||||
set_temperature_conversion_rate,
|
||||
STATUS,
|
||||
TempConvRate::_128s,
|
||||
DEFAULT_WRITE_STATUS & !BF::CRATE1 | BF::CRATE0
|
||||
);
|
||||
set_param_test_2_4!(
|
||||
can_set_cr_256s,
|
||||
set_temperature_conversion_rate,
|
||||
STATUS,
|
||||
TempConvRate::_256s,
|
||||
DEFAULT_WRITE_STATUS | BF::CRATE1 & !BF::CRATE0
|
||||
);
|
||||
set_param_test_2_4!(
|
||||
can_set_cr_512s,
|
||||
set_temperature_conversion_rate,
|
||||
STATUS,
|
||||
TempConvRate::_512s,
|
||||
DEFAULT_WRITE_STATUS | BF::CRATE1 | BF::CRATE0
|
||||
);
|
||||
|
|
|
@ -1,21 +1,29 @@
|
|||
#[deny(warnings)]
|
||||
|
||||
extern crate embedded_hal_mock as hal;
|
||||
use hal::spi::Transaction as SpiTrans;
|
||||
extern crate ds323x;
|
||||
use embedded_hal_mock::eh1::spi::Transaction as SpiTrans;
|
||||
|
||||
#[allow(unused)]
|
||||
mod common;
|
||||
use common::{ new_ds3234, destroy_ds3234, Register, BitFlags };
|
||||
use self::common::{destroy_ds3234, new_ds3234, BitFlags, Register};
|
||||
|
||||
#[test]
|
||||
fn can_create_and_destroy_ds3234() {
|
||||
let dev = new_ds3234(&[]);
|
||||
destroy_ds3234(dev);
|
||||
}
|
||||
call_test!(
|
||||
can_en_temp_conv_bat,
|
||||
enable_temperature_conversions_on_battery,
|
||||
new_ds3234,
|
||||
destroy_ds3234,
|
||||
[
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::write_vec(vec![Register::TEMP_CONV + 0x80, 0]),
|
||||
SpiTrans::transaction_end()
|
||||
]
|
||||
);
|
||||
|
||||
call_test!(can_en_temp_conv_bat, enable_temperature_conversions_on_battery, new_ds3234, destroy_ds3234,
|
||||
[ SpiTrans::write(vec![Register::TEMP_CONV + 0x80, 0]) ]);
|
||||
|
||||
call_test!(can_dis_temp_conv_bat, disable_temperature_conversions_on_battery, new_ds3234, destroy_ds3234,
|
||||
[ SpiTrans::write(vec![Register::TEMP_CONV + 0x80, BitFlags::TEMP_CONV_BAT]) ]);
|
||||
call_test!(
|
||||
can_dis_temp_conv_bat,
|
||||
disable_temperature_conversions_on_battery,
|
||||
new_ds3234,
|
||||
destroy_ds3234,
|
||||
[
|
||||
SpiTrans::transaction_start(),
|
||||
SpiTrans::write_vec(vec![Register::TEMP_CONV + 0x80, BitFlags::TEMP_CONV_BAT]),
|
||||
SpiTrans::transaction_end(),
|
||||
]
|
||||
);
|
||||
|
|
|
@ -1,22 +1,51 @@
|
|||
#[deny(warnings)]
|
||||
|
||||
extern crate embedded_hal_mock as hal;
|
||||
use hal::i2c::Transaction as I2cTrans;
|
||||
use hal::spi::Transaction as SpiTrans;
|
||||
use embedded_hal_mock::eh1::{i2c::Transaction as I2cTrans, spi::Transaction as SpiTrans};
|
||||
mod common;
|
||||
use common::{ DEVICE_ADDRESS as DEV_ADDR, Register, new_ds3231,
|
||||
new_ds3232, new_ds3234, destroy_ds3231, destroy_ds3232,
|
||||
destroy_ds3234, BitFlags as BF };
|
||||
use self::common::{
|
||||
destroy_ds3231, destroy_ds3232, destroy_ds3234, new_ds3231, new_ds3232, new_ds3234,
|
||||
BitFlags as BF, Register, DEVICE_ADDRESS as DEV_ADDR,
|
||||
};
|
||||
|
||||
get_param_test!(is_running, is_running, CONTROL, true, 0);
|
||||
get_param_test!(is_not_running, is_running, CONTROL, false, BF::EOSC);
|
||||
get_param_test!(running, running, CONTROL, true, 0);
|
||||
get_param_test!(is_not_running, running, CONTROL, false, BF::EOSC);
|
||||
|
||||
get_param_test!(is_busy, is_busy, STATUS, true, 0xFF);
|
||||
get_param_test!(is_not_busy, is_busy, STATUS, false, 0xFF & !BF::BUSY);
|
||||
get_param_test!(busy, busy, STATUS, true, 0xFF);
|
||||
get_param_test!(is_not_busy, busy, STATUS, false, !BF::BUSY);
|
||||
|
||||
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!(stopped, has_been_stopped, STATUS, true, 0xFF);
|
||||
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_read_array_test!(temp_min, get_temperature, -128.0, TEMP_MSB, [0b1000_0000, 0], [0, 0]);
|
||||
get_param_read_array_test!(temp_max, get_temperature, 127.75, TEMP_MSB, [0b0111_1111, 0b1100_0000], [0, 0]);
|
||||
get_param_test!(alarm1_matched, has_alarm1_matched, STATUS, true, 0xFF);
|
||||
get_param_test!(
|
||||
alarm1_not_matched,
|
||||
has_alarm1_matched,
|
||||
STATUS,
|
||||
false,
|
||||
!BF::ALARM1F
|
||||
);
|
||||
|
||||
get_param_test!(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]
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue