Skip to content

Commit

Permalink
Merge pull request #1 from madeleyneVaca/mavaca/io_exp_min_support
Browse files Browse the repository at this point in the history
Min support for PCAL6416A
  • Loading branch information
jerrysxie authored Jan 10, 2025
2 parents c03587e + a146a50 commit 2f2202d
Show file tree
Hide file tree
Showing 8 changed files with 299 additions and 4 deletions.
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)
}
}

0 comments on commit 2f2202d

Please sign in to comment.