Skip to content

Commit

Permalink
Initial commit for PCAL6416A
Browse files Browse the repository at this point in the history
Provides support to set pin direction (input, output) and output pin level
  • Loading branch information
madeleyneVaca committed Jan 8, 2025
1 parent c03587e commit 38d3bbb
Show file tree
Hide file tree
Showing 5 changed files with 297 additions and 2 deletions.
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");
}
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"]
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.
75 changes: 75 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//! 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 38d3bbb

Please sign in to comment.