Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Min support for PCAL6416A #1

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ jobs:
fail-fast: false
matrix:
commit: ${{ fromJSON(needs.commit_list.outputs.commits) }}
msrv: ["1.79"] # We're relying on namespaced-features, which
msrv: ["1.81"] # We're relying on namespaced-features, which
# was released in 1.60
#
# We also depend on `fixed' which requires rust
Expand All @@ -215,6 +215,9 @@ jobs:
#
# embassy-time requires 1.79 due to
# collapse_debuginfo
#
# device-driver requires 1.81 for a stabilized
# core::error module
name: ubuntu / ${{ matrix.msrv }} (${{ matrix.commit }})
steps:
- uses: actions/checkout@v4
Expand Down
39 changes: 39 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[package]
name = "pcal6416a"
version = "0.1.0"
repository = "https://github.com/pop-project/pcal6416a"
license = "MIT"
description = "Platform-agnostic Rust driver for the Texas Instruments BQ25773 battery charge controller."
readme = "README.md"
keywords = ["pcal6416a", "NXP", "IO Expander", "i2c", "driver", "embedded-hal-driver"]
categories = ["embedded", "hardware-support", "no-std"]
documentation = "https://docs.rs/pcal6416a"
include = [
"/**/*.rs",
"/Cargo.toml",
"/README.md",
"/LICENSE",
"/device.yaml",
]
edition = "2021"

[dependencies]
device-driver = { version = "=1.0.0-rc.1", default-features = false, features = ["yaml"] }
defmt = { version = "0.3", optional = true }
embedded-hal = "1.0.0"
embedded-hal-async = "1.0.0"

[lints.rust]
unsafe_code = "forbid"
missing_docs = "deny"


[lints.clippy]
correctness = "forbid"
suspicious = "forbid"
perf = "forbid"
style = "forbid"
pedantic = "deny"

[features]
defmt-03 = ["dep:defmt", "device-driver/defmt-03"]
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,19 @@
# embedded-rust-template
Template repository for Embedded Rust development
# PCAL6416A Rust Device Driver

A `#[no_std]` platform-agnostic driver for the [PCAL6416A](https://www.nxp.com/docs/en/data-sheet/PCAL6416A.pdf) IO Expander

## MSRV

Currently, rust `1.81` and up is supported.

## License

Licensed under the terms of the [MIT license](http://opensource.org/licenses/MIT).

## Contribution

Unless you explicitly state otherwise, any contribution submitted for
inclusion in the work by you shall be licensed under the terms of the
MIT license.

License: MIT
4 changes: 4 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#![allow(missing_docs)]
fn main() {
println!("cargo:rebuild-if-changed=device.yaml");
}
2 changes: 2 additions & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ exceptions = [
# Each entry is the crate and version constraint, and its specific allow
# list
#{ allow = ["Zlib"], crate = "adler32" },
{ allow = ["BSD-3-Clause"], crate = "encoding_rs" },
{ allow = ["Unicode-3.0"], crate = "unicode-ident" },
]

# Some crates don't have (easily) machine readable licensing information,
Expand Down
160 changes: 160 additions & 0 deletions device.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
config:
register_address_type: u8
default_byte_order: LE
default_bit_order: LSB0
defmt_feature: defmt-03

OUTPUT_PORT0:
type: register
address: 0x02
size_bits: 8
reset_value: 0xFF
fields:
O0_0:
base: bool
start: 0
description: Output Port 0 Pin 0. Host writes this bit to set the desired logic output level.
O0_1:
base: bool
start: 1
description: Output Port 0 Pin 1. Host writes this bit to set the desired logic output level.
O0_2:
base: bool
start: 2
description: Output Port 0 Pin 2. Host writes this bit to set the desired logic output level.
O0_3:
base: bool
start: 3
description: Output Port 0 Pin 3. Host writes this bit to set the desired logic output level.
O0_4:
base: bool
start: 4
description: Output Port 0 Pin 4. Host writes this bit to set the desired logic output level.
O0_5:
base: bool
start: 5
description: Output Port 0 Pin 5. Host writes this bit to set the desired logic output level.
O0_6:
base: bool
start: 6
description: Output Port 0 Pin 6. Host writes this bit to set the desired logic output level.
O0_7:
base: bool
start: 7
description: Output Port 0 Pin 7. Host writes this bit to set the desired logic output level.

OUTPUT_PORT1:
type: register
address: 0x03
size_bits: 8
reset_value: 0xFF
fields:
O1_0:
base: bool
start: 0
description: Output Port 1 Pin 0. Host writes this bit to set the desired logic output level.
O1_1:
base: bool
start: 1
description: Output Port 1 Pin 1. Host writes this bit to set the desired logic output level.
O1_2:
base: bool
start: 2
description: Output Port 1 Pin 2. Host writes this bit to set the desired logic output level.
O1_3:
base: bool
start: 3
description: Output Port 1 Pin 3. Host writes this bit to set the desired logic output level.
O1_4:
base: bool
start: 4
description: Output Port 1 Pin 4. Host writes this bit to set the desired logic output level.
O1_5:
base: bool
start: 5
description: Output Port 1 Pin 5. Host writes this bit to set the desired logic output level.
O1_6:
base: bool
start: 6
description: Output Port 1 Pin 6. Host writes this bit to set the desired logic output level.
O1_7:
base: bool
start: 7
description: Output Port 1 Pin 7. Host writes this bit to set the desired logic output level.

CONFIG_PORT0:
type: register
address: 0x06
size_bits: 8
reset_value: 0xFF
fields:
C0_0:
base: bool
start: 0
description: Config Port 0 Pin 0. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
C0_1:
base: bool
start: 1
description: Config Port 0 Pin 1. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
C0_2:
base: bool
start: 2
description: Config Port 0 Pin 2. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
C0_3:
base: bool
start: 3
description: Config Port 0 Pin 3. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
C0_4:
base: bool
start: 4
description: Config Port 0 Pin 4. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
C0_5:
base: bool
start: 5
description: Config Port 0 Pin 5. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
C0_6:
base: bool
start: 6
description: Config Port 0 Pin 6. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
C0_7:
base: bool
start: 7
description: Config Port 0 Pin 7. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
CONFIG_PORT1:
type: register
address: 0x07
size_bits: 8
reset_value: 0xFF
fields:
C1_0:
base: bool
start: 0
description: Config Port 1 Pin 0. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
C1_1:
base: bool
start: 1
description: Config Port 1 Pin 1. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
C1_2:
base: bool
start: 2
description: Config Port 1 Pin 2. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
C1_3:
base: bool
start: 3
description: Config Port 1 Pin 3. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
C1_4:
base: bool
start: 4
description: Config Port 1 Pin 4. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
C1_5:
base: bool
start: 5
description: Config Port 1 Pin 5. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
C1_6:
base: bool
start: 6
description: Config Port 1 Pin 6. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
C1_7:
base: bool
start: 7
description: Config Port 1 Pin 7. Host clears this bit to set the pin as an output. Bit is set by default and configures the pin as an input.
70 changes: 70 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//! This is a platform-agnostic Rust driver for the NXP PCAL6416A IO Expander
//! based on the [`embedded-hal`] traits.
//!
//! [`embedded-hal`]: https://docs.rs/embedded-hal
//!
//! For further details of the device architecture and operation, please refer
//! to the official [`Datasheet`].
//!
//! [`Datasheet`]: https://www.nxp.com/docs/en/data-sheet/PCAL6416A.pdf

#![doc = include_str!("../README.md")]
#![no_std]
#![allow(missing_docs)]

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
pub enum Pcal6416aError<E> {
/// I2C bus error
I2c(E),
}
const IOEXP_ADDR: u8 = 0x20;
const LARGEST_REG_SIZE_BYTES: usize = 2;

pub struct Pcal6416aDevice<I2c: embedded_hal_async::i2c::I2c> {
pub i2cbus: I2c,
}

device_driver::create_device!(
device_name: Device,
manifest: "device.yaml"
);

impl<I2c: embedded_hal_async::i2c::I2c> device_driver::AsyncRegisterInterface for Pcal6416aDevice<I2c> {
type Error = Pcal6416aError<I2c::Error>;
type AddressType = u8;

async fn write_register(
&mut self,
address: Self::AddressType,
_size_bits: u32,
data: &[u8],
) -> Result<(), Self::Error> {
assert!((data.len() <= LARGEST_REG_SIZE_BYTES), "Register size too big");

// Add one byte for register address
let mut buf = [0u8; 1 + LARGEST_REG_SIZE_BYTES];
buf[0] = address;
buf[1..=data.len()].copy_from_slice(data);

// Because the pcal6416a has a mix of 1 byte and 2 byte registers that can be written to,
// we pass in a slice of the appropriate size so we do not accidentally write to the register at
// address + 1 when writing to a 1 byte register
self.i2cbus
.write(IOEXP_ADDR, &buf[..=data.len()])
.await
.map_err(Pcal6416aError::I2c)
}

async fn read_register(
&mut self,
address: Self::AddressType,
_size_bits: u32,
data: &mut [u8],
) -> Result<(), Self::Error> {
self.i2cbus
.write_read(IOEXP_ADDR, &[address], data)
.await
.map_err(Pcal6416aError::I2c)
}
}
Loading